Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit

  • Loading branch information...
commit b419c1e025ecd9c4b3c208c3185bb2dc36de4e5a 0 parents
@jm authored
4 History.txt
@@ -0,0 +1,4 @@
+=== 0.0.1 2010-01-23
+
+* 1 major enhancement:
+ * Initial release
14 Manifest.txt
@@ -0,0 +1,14 @@
+History.txt
+Manifest.txt
+PostInstall.txt
+README.rdoc
+Rakefile
+bin/rails-upgrade
+lib/rails-upgrade.rb
+lib/rails-upgrade/cli.rb
+script/console
+script/destroy
+script/generate
+test/test_helper.rb
+test/test_rails-upgrade.rb
+test/test_rails-upgrade_cli.rb
7 PostInstall.txt
@@ -0,0 +1,7 @@
+
+For more information on rails-upgrade, see http://rails-upgrade.rubyforge.org
+
+NOTE: Change this information in PostInstall.txt
+You can also delete it if you don't want it.
+
+
43 README.mdown
@@ -0,0 +1,43 @@
+# rails-upgrade
+
+*http://github.com/jm/rails-upgrade*
+
+## Description
+
+A simple battery of scripts for upgrading Rails app/checking them for required updates.
+
+## Usage
+
+ # Check your app for required upgrades
+ rails-upgrade check
+
+ # Generate a new route file
+ rails-upgrade routes
+
+ # Generate a Gemfile from your config.gem directives
+ rails-upgrade gems
+
+## License
+
+(The MIT License)
+
+Copyright (c) 2010 Jeremy McAnally
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 Rakefile
@@ -0,0 +1,25 @@
+require 'rubygems'
+gem 'hoe', '>= 2.1.0'
+require 'hoe'
+require 'fileutils'
+require './lib/rails-upgrade'
+
+Hoe.plugin :newgem
+# Hoe.plugin :website
+# Hoe.plugin :cucumberfeatures
+
+# Generate all the Rake tasks
+# Run 'rake -T' to see list of generated tasks (from gem root directory)
+$hoe = Hoe.spec 'rails-upgrade' do
+ self.developer 'Jeremy McAnally', 'jeremy@intridea.com'
+ self.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
+ self.rubyforge_name = self.name
+ self.extra_deps = [['activesupport','>= 2.0.2'], ['term-ansicolor', '> 0.0.0']]
+end
+
+require 'newgem/tasks'
+Dir['tasks/**/*.rake'].each { |t| load t }
+
+# TODO - want other tests/tasks run by default? Add them to the list
+# remove_task :default
+# task :default => [:spec, :features]
21 bin/rails-upgrade
@@ -0,0 +1,21 @@
+#!/usr/bin/env ruby
+#
+# Created by Jeremy McAnally on 2010-1-23.
+# Copyright (c) 2010. All rights reserved.
+
+require 'rubygems'
+require File.expand_path(File.dirname(__FILE__) + "/../lib/rails-upgrade")
+require "rails-upgrade/cli"
+require 'term/ansicolor'
+
+class String
+ include Term::ANSIColor
+end
+
+begin
+ RailsUpgrade::CLI.execute(STDOUT, ARGV)
+rescue StandardError => e
+ puts
+ puts "Error: ".red + e.message.white
+ puts "".reset
+end
15 lib/rails-upgrade.rb
@@ -0,0 +1,15 @@
+$:.unshift(File.dirname(__FILE__)) unless
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
+
+module RailsUpgrade
+ VERSION = '0.0.1'
+end
+
+require 'active_support'
+
+require 'rails-upgrade/errors'
+
+require 'rails-upgrade/upgraders/routes'
+require 'rails-upgrade/upgraders/gems'
+require 'rails-upgrade/upgraders/check'
+
22 lib/rails-upgrade/cli.rb
@@ -0,0 +1,22 @@
+module RailsUpgrade
+ class CLI
+ class <<self
+ def execute(stdout, arguments=[])
+ command = arguments.pop.capitalize
+
+ if RailsUpgrade::Upgraders.const_defined?(command)
+ klass = RailsUpgrade::Upgraders.const_get(command)
+ instance = klass.new
+
+ instance.upgrade!(arguments)
+ else
+ usage
+ end
+ end
+
+ def usage
+ puts "fail"
+ end
+ end
+ end
+end
7 lib/rails-upgrade/errors.rb
@@ -0,0 +1,7 @@
+class FileNotFoundError < StandardError; end
+
+class NotInRailsAppError < StandardError
+ def message
+ "I'm not in a Rails application!"
+ end
+end
201 lib/rails-upgrade/upgraders/check.rb
@@ -0,0 +1,201 @@
+# This is badly named/architected. Guess who doesn't care right now...?? :)
+require 'open3'
+
+module RailsUpgrade
+ module Upgraders
+ class Check
+ def initialize
+ @issues = []
+
+ raise NotInRailsAppError unless File.exist?("config/environment.rb")
+ end
+
+ def check(args)
+ the_methods = (self.public_methods - Object.methods) - ["upgrade!", "check"]
+
+ the_methods.each {|m| send m }
+ end
+ alias upgrade! check
+
+ def check_ar_methods
+ files = []
+ ["find(:all", "find(:first", ":conditions =>", ":joins =>"].each do |v|
+ lines = grep_for(v, "app/models/*")
+ files += extract_filenames(lines)
+ end
+
+ if files
+ alert(
+ "Soon-to-be-deprecated ActiveRecord calls",
+ "Methods such as find(:all), find(:first), finds with conditions, and the :joins option will soon be deprecated.",
+ "http://m.onkey.org/2010/1/22/active-record-query-interface",
+ files
+ )
+ end
+
+ lines = grep_for("named_scope", "app/models/*")
+ files = extract_filenames(lines)
+
+ if files
+ alert(
+ "named_scope is now just scope",
+ "The named_scope method has been renamed to just scope.",
+ "http://github.com/rails/rails/commit/d60bb0a9e4be2ac0a9de9a69041a4ddc2e0cc914",
+ files
+ )
+ end
+ end
+
+ def check_routes
+ files = []
+ ["map.", "ActionController::Routing::Routes", ".resources"].each do |v|
+ lines = grep_for(v, "config/routes.rb")
+ files += extract_filenames(lines)
+ end
+
+ if files
+ alert(
+ "Old router API",
+ "The router API has totally changed.",
+ "http://yehudakatz.com/2009/12/26/the-rails-3-router-rack-it-up/",
+ "config/routes.rb"
+ )
+ end
+ end
+
+ def check_environment
+ unless File.exist?("config/application.rb")
+ alert(
+ "New file needed: config/application.rb",
+ "You need to add a config/application.rb.",
+ "http://omgbloglol.com/post/353978923/the-path-to-rails-3-approaching-the-upgrade",
+ "config/application.rb"
+ )
+ end
+
+ lines = grep_for("config.", "config/environment.rb")
+ files = extract_filenames(lines)
+
+ if files
+ alert(
+ "Old environment.rb",
+ "environment.rb doesn't do what it used to; you'll need to move some of that into application.rb.",
+ "http://omgbloglol.com/post/353978923/the-path-to-rails-3-approaching-the-upgrade",
+ "config/environment.rb"
+ )
+ end
+ end
+
+ def check_gems
+ lines = grep_for("config.gem ", "config/*.rb")
+ files = extract_filenames(lines)
+
+ if files
+ alert(
+ "Old gem bundling (config.gems)",
+ "The old way of bundling is gone now. You need a Gemfile for bundler.",
+ "http://omgbloglol.com/post/353978923/the-path-to-rails-3-approaching-the-upgrade",
+ files
+ )
+ end
+ end
+
+ def check_mailers
+ lines = grep_for("deliver_", "app/models/* app/controllers/* app/observers/*")
+ files = extract_filenames(lines)
+
+ if files
+ alert(
+ "Deprecated ActionMailer API",
+ "You're using the old ActionMailer API to send e-mails in a controller, model, or observer.",
+ "http://lindsaar.net/2010/1/26/new-actionmailer-api-in-rails-3",
+ files
+ )
+ end
+
+ files = []
+ ["recipients ", "attachment ", "subject ", "from "].each do |v|
+ lines = grep_for(v, "app/models/*")
+ files += extract_filenames(lines)
+ end
+
+ if files
+ alert(
+ "Old ActionMailer class API",
+ "You're using the old API in a mailer class.",
+ "http://lindsaar.net/2010/1/26/new-actionmailer-api-in-rails-3",
+ files
+ )
+ end
+ end
+
+ def check_generators
+ generators = Dir.glob("vendor/plugins/**/generators/**/*.rb")
+
+ unless generators.empty?
+ lines = grep_for("def manifest", generators.join(" "))
+ files = extract_filenames(lines)
+
+ if files
+ alert(
+ "Old Rails generator API",
+ "A plugin in the app is using the old generator API (a new one may be available at http://github.com/trydionel/rails3-generators).",
+ "http://blog.plataformatec.com.br/2010/01/discovering-rails-3-generators/",
+ files
+ )
+ end
+ end
+ end
+
+ def check_plugins
+ # This list is off the wiki; will need to be updated often, esp. since RSpec is working on it
+ bad_plugins = ["rspec", "rspec-rails", "hoptoad", "authlogic", "nifty-generators",
+ "restful_authentication", "searchlogic", "cucumber", "cucumber-rails"]
+
+ bad_plugins = bad_plugins.map {|p| p if File.exist?("vendor/plugins/#{p}") || !Dir.glob("vendor/gems/#{p}-*").empty?}.compact
+
+ unless bad_plugins.empty?
+ alert(
+ "Known broken plugins",
+ "At least one plugin in your app is broken (according to the wiki). Most of project maintainers are rapidly working towards compatability, but do be aware you may encounter issues.",
+ "http://wiki.rubyonrails.org/rails/version3/plugins_and_gems",
+ bad_plugins
+ )
+ end
+ end
+
+ private
+ def grep_for(text, where = "*")
+ value = ""
+
+ # TODO: Figure out a pure Ruby way to do this that doesn't suck
+ Open3.popen3("grep -r '#{text}' #{where}") do |stdin, stdout, stderr|
+ value = stdout.read
+ end
+
+ value
+ end
+
+ def extract_filenames(output)
+ return nil if output.empty?
+
+ # I hate rescue nil as much as the next guy but I have a reason here at least...
+ fnames = output.split("\n").map {|fn| fn.match(/^(.+?):/)[1] rescue nil}.compact
+ fnames.uniq
+ end
+
+ def alert(title, text, more_info_url, culprits)
+ puts title.red.bold
+ puts text.white
+ puts "More information: ".white.bold + more_info_url.blue
+ puts
+ puts "The culprits: ".white
+ culprits.each do |c|
+ puts "\t- #{c}".yellow
+ end
+ ensure
+ puts "".reset
+ end
+ end
+ end
+end
64 lib/rails-upgrade/upgraders/gems.rb
@@ -0,0 +1,64 @@
+module RailsUpgrade
+ module Upgraders
+ class Gems
+ def upgrade!(args)
+ if File.exists?("config/environment.rb")
+ generate_gemfile
+ else
+ raise FileNotFoundError, "Can't find environment.rb [config/environment.rb]!"
+ end
+ end
+
+ def generate_gemfile
+ environment_file = File.open("config/environment.rb").read
+
+ # Get each line that starts with config.gem
+ gem_lines = environment_file.split("\n").select {|l| l =~ /^\s*config\.gem/}
+
+ # yay hax
+ config = GemfileGenerator.new
+ eval(gem_lines.join("\n"))
+ puts config.output
+ end
+ end
+
+ class GemfileGenerator
+ def initialize
+ @gems = []
+ end
+
+ def gem(name, options={})
+ data = {}
+
+ # Add new keys from old keys
+ data[:require_as] = options[:lib] if options[:lib]
+ data[:source] = options[:source] if options[:source]
+
+ version = options[:version]
+ @gems << [name, version, data]
+ end
+
+ def output
+ preamble = <<STR
+# Edit this Gemfile to bundle your application's dependencies.
+# This preamble is the current preamble for Rails 3 apps; edit as needed.
+directory "/path/to/rails", :glob => "{*/,}*.gemspec"
+git "git://github.com/rails/arel.git"
+git "git://github.com/rails/rack.git"
+gem "rails", "3.0.pre"
+STR
+ preamble + "\n" + generate_upgraded_code
+ end
+
+ def generate_upgraded_code
+ code = @gems.map do |name, version, data|
+ version_string = (version ? ", '#{version}'" : "")
+ # omg hax. again.
+ data_string = data.inspect.match(/^\{(.*)\}$/)[1]
+
+ "gem '#{name}'#{version_string}, #{data_string}"
+ end.join("\n")
+ end
+ end
+ end
+end
318 lib/rails-upgrade/upgraders/routes.rb
@@ -0,0 +1,318 @@
+DEBUG = false
+
+# TODO: Formatting on member/collection methods
+
+module RailsUpgrade
+ module Upgraders
+ class Routes
+ def upgrade!(args)
+ if File.exists?("config/routes.rb")
+ upgrade_routes
+ else
+ raise FileNotFoundError, "Can't find your routes file [config/routes.rb]!"
+ end
+ end
+
+ def upgrade_routes
+ ActionController::Routing::Routes.setup
+
+ eval(File.read("config/routes.rb"))
+
+ generator = RouteGenerator.new(ActionController::Routing::Routes.redrawer.routes)
+ puts generator.generate
+ end
+ end
+
+ class RouteRedrawer
+ attr_accessor :routes
+ cattr_accessor :stack
+
+ def initialize
+ @routes = []
+ @default_route_generated = false
+
+ # Setup the stack for parents
+ self.class.stack = [@routes]
+ end
+
+ def root(options)
+ debug "mapping root"
+ @routes << FakeRoute.new("/", options)
+ end
+
+ def connect(path, options={})
+ debug "connecting #{path}"
+
+ if (path == ":controller/:action/:id.:format" || path == ":controller/:action/:id")
+ if !@default_route_generated
+ current_parent << FakeRoute.new("/:controller(/:action(/:id))", {:default_route => true})
+
+ @default_route_generated = true
+ end
+ else
+ current_parent << FakeRoute.new(path, options)
+ end
+ end
+
+ def resources(*args)
+ if block_given?
+ parent = FakeResourceRoute.new(args.shift)
+ debug "mapping resources #{parent.name} with block"
+
+ parent = stack(parent) do
+ yield(self)
+ end
+
+ current_parent << parent
+ else
+ if args.last.is_a?(Hash)
+ current_parent << FakeResourceRoute.new(args.shift, args.pop)
+ debug "mapping resources #{current_parent.last.name} w/o block with args"
+ else
+ args.each do |a|
+ current_parent << FakeResourceRoute.new(a)
+ debug "mapping resources #{current_parent.last.name}"
+ end
+ end
+ end
+ end
+
+ def resource(*args)
+ if block_given?
+ parent = FakeSingletonResourceRoute.new(args.shift)
+ debug "mapping resource #{parent.name} with block"
+
+ parent = stack(parent) do
+ yield(self)
+ end
+
+ current_parent << parent
+ else
+ if args.last.is_a?(Hash)
+ current_parent << FakeSingletonResourceRoute.new(args.shift, args.pop)
+ debug "mapping resources #{current_parent.last.name} w/o block with args"
+ else
+ args.each do |a|
+ current_parent << FakeSingletonResourceRoute.new(a)
+ debug "mapping resources #{current_parent.last.name}"
+ end
+ end
+ end
+ end
+
+ def namespace(name)
+ debug "mapping namespace #{name}"
+ namespace = FakeNamespace.new(name)
+
+ namespace = stack(namespace) do
+ yield(self)
+ end
+
+ current_parent << namespace
+ end
+
+ def method_missing(m, *args)
+ debug "named route: #{m}"
+ current_parent << FakeRoute.new(args.shift, args.pop, m.to_s)
+ end
+
+ def self.indent
+ ' ' * ((stack.length) * 2)
+ end
+
+ private
+ def debug(txt)
+ puts txt if DEBUG
+ end
+
+ def stack(obj)
+ self.class.stack << obj
+ yield
+ self.class.stack.pop
+ end
+
+ def current_parent
+ self.class.stack.last
+ end
+ end
+
+ class RouteObject
+ def indent_lines(code_lines)
+ if code_lines.length > 1
+ code_lines.map {|l| "#{@indent}#{l.chomp}"}.join("\n") + "\n"
+ else
+ "#{@indent}#{code_lines.shift}"
+ end
+ end
+
+ def opts_to_string(opts)
+ # omg hax. again.
+ opts.is_a?(Hash) ? opts.inspect.match(/^\{(.*)\}$/)[1] : nil
+ end
+ end
+
+ class FakeNamespace < RouteObject
+ attr_accessor :routes, :name
+
+ def initialize(name)
+ @routes = []
+ @name = name
+ @indent = RouteRedrawer.indent
+ end
+
+ def to_route_code
+ lines = ["namespace :#{@name} do", @routes.map {|r| r.to_route_code}, "end"]
+
+ indent_lines(lines)
+ end
+
+ def <<(val)
+ @routes << val
+ end
+
+ def last
+ @routes.last
+ end
+ end
+
+ class FakeRoute < RouteObject
+ attr_accessor :name, :path, :options
+
+ def initialize(path, options, name = "")
+ @path = path
+ @options = options || {}
+ @name = name
+ @indent = RouteRedrawer.indent
+ end
+
+ def to_route_code
+ if @options[:default_route]
+ indent_lines ["match '#{@path}'"]
+ else
+ base = "match '%s' => '%s#%s'"
+ extra_options = []
+
+ if not name.empty?
+ extra_options << ":as => :#{name}"
+ end
+
+ if @options[:requirements]
+ @options[:constraints] = @options.delete(:requirements)
+ end
+
+ if @options[:conditions]
+ @options[:via] = @options.delete(:conditions).delete(:method)
+ end
+
+ @options ||= {}
+ base = (base % [@path, @options.delete(:controller), (@options.delete(:action) || "index")])
+ opts = opts_to_string(@options)
+
+ route_pieces = ([base] + extra_options + [opts])
+ route_pieces.delete("")
+
+ indent_lines [route_pieces.join(", ")]
+ end
+ end
+ end
+
+ class FakeResourceRoute < RouteObject
+ attr_accessor :name, :children
+
+ def initialize(name, options = {})
+ @name = name
+ @children = []
+ @options = options
+ @indent = RouteRedrawer.indent
+ end
+
+ def to_route_code
+
+ if !@children.empty? || @options.has_key?(:collection) || @options.has_key?(:member)
+ prefix = ["#{route_method} :#{@name} do"]
+ lines = prefix + custom_methods + [@children.map {|r| r.to_route_code}.join("\n"), "end"]
+
+ indent_lines(lines)
+ else
+ base = "#{route_method} :%s"
+ indent_lines [base % [@name]]
+ end
+ end
+
+ def custom_methods
+ collection_code = generate_custom_methods_for(:collection)
+ member_code = generate_custom_methods_for(:member)
+ [collection_code, member_code]
+ end
+
+ def generate_custom_methods_for(group)
+ return "" unless @options[group]
+
+ method_code = []
+
+ RouteRedrawer.stack << self
+ @options[group].each do |k, v|
+ method_code << "#{v} :#{k}"
+ end
+ RouteRedrawer.stack.pop
+
+ indent_lines ["#{group} do", method_code, "end"].flatten
+ end
+
+ def route_method
+ "resources"
+ end
+
+ def <<(val)
+ @children << val
+ end
+
+ def last
+ @children.last
+ end
+ end
+
+ class FakeSingletonResourceRoute < FakeResourceRoute
+ def route_method
+ "resource"
+ end
+ end
+
+ class RouteGenerator
+ def initialize(routes)
+ @routes = routes
+ @new_code = ""
+ end
+
+ def generate
+ @new_code = @routes.map do |r|
+ r.to_route_code
+ end.join("\n")
+
+ "#{app_name.classify}::Application.routes do\n#{@new_code}\nend\n"
+ end
+ private
+ def app_name
+ File.basename(Dir.pwd)
+ end
+ end
+ end
+end
+
+module ActionController
+ module Routing
+ class Routes
+ def self.setup
+ @redrawer = RailsUpgrade::Upgraders::RouteRedrawer.new
+ end
+
+ def self.redrawer
+ @redrawer
+ end
+
+ def self.draw
+ yield @redrawer
+ end
+ end
+ end
+end
10 script/console
@@ -0,0 +1,10 @@
+#!/usr/bin/env ruby
+# File: script/console
+irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
+
+libs = " -r irb/completion"
+# Perhaps use a console_lib to store any extra methods I may want available in the cosole
+# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
+libs << " -r #{File.dirname(__FILE__) + '/../lib/rails-upgrade.rb'}"
+puts "Loading rails-upgrade gem"
+exec "#{irb} #{libs} --simple-prompt"
14 script/destroy
@@ -0,0 +1,14 @@
+#!/usr/bin/env ruby
+APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
+
+begin
+ require 'rubigen'
+rescue LoadError
+ require 'rubygems'
+ require 'rubigen'
+end
+require 'rubigen/scripts/destroy'
+
+ARGV.shift if ['--help', '-h'].include?(ARGV[0])
+RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
+RubiGen::Scripts::Destroy.new.run(ARGV)
14 script/generate
@@ -0,0 +1,14 @@
+#!/usr/bin/env ruby
+APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
+
+begin
+ require 'rubigen'
+rescue LoadError
+ require 'rubygems'
+ require 'rubigen'
+end
+require 'rubigen/scripts/generate'
+
+ARGV.shift if ['--help', '-h'].include?(ARGV[0])
+RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
+RubiGen::Scripts::Generate.new.run(ARGV)
3  test/test_helper.rb
@@ -0,0 +1,3 @@
+require 'stringio'
+require 'test/unit'
+require File.dirname(__FILE__) + '/../lib/rails-upgrade'
11 test/test_rails-upgrade.rb
@@ -0,0 +1,11 @@
+require File.dirname(__FILE__) + '/test_helper.rb'
+
+class TestRailsUpgrade < Test::Unit::TestCase
+
+ def setup
+ end
+
+ def test_truth
+ assert true
+ end
+end
14 test/test_rails-upgrade_cli.rb
@@ -0,0 +1,14 @@
+require File.join(File.dirname(__FILE__), "test_helper.rb")
+require 'rails-upgrade/cli'
+
+class TestRailsUpgradeCli < Test::Unit::TestCase
+ def setup
+ RailsUpgrade::CLI.execute(@stdout_io = StringIO.new, [])
+ @stdout_io.rewind
+ @stdout = @stdout_io.read
+ end
+
+ def test_print_default_output
+ assert_match(/To update this executable/, @stdout)
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.