Initial commit of Rake task/plugin version of rails-upgrade script
This commit is contained in:
commit
d6a740e2db
20
MIT-LICENSE
Normal file
20
MIT-LICENSE
Normal file
@ -0,0 +1,20 @@
|
||||
Copyright (c) 2010 [name of plugin creator]
|
||||
|
||||
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.
|
14
README
Normal file
14
README
Normal file
@ -0,0 +1,14 @@
|
||||
= rails-upgrade
|
||||
|
||||
A simple battery of scripts for upgrading Rails app/checking them for required updates. This application should work on Rails 2.x and 3.0, with a focus on upgrading to 3.0.
|
||||
|
||||
== Usage
|
||||
|
||||
# Check your app for required upgrades
|
||||
rake rails:upgrade:check
|
||||
|
||||
# Generate a new route file
|
||||
rake rails:upgrade:routes
|
||||
|
||||
# Generate a Gemfile from your config.gem directives
|
||||
rake rails:upgrade:gems
|
23
Rakefile
Normal file
23
Rakefile
Normal file
@ -0,0 +1,23 @@
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
|
||||
desc 'Default: run unit tests.'
|
||||
task :default => :test
|
||||
|
||||
desc 'Test the rails_upgrade plugin.'
|
||||
Rake::TestTask.new(:test) do |t|
|
||||
t.libs << 'lib'
|
||||
t.libs << 'test'
|
||||
t.pattern = 'test/*_test.rb'
|
||||
t.verbose = true
|
||||
end
|
||||
|
||||
desc 'Generate documentation for the rails_upgrade plugin.'
|
||||
Rake::RDocTask.new(:rdoc) do |rdoc|
|
||||
rdoc.rdoc_dir = 'rdoc'
|
||||
rdoc.title = 'Rails-upgrade'
|
||||
rdoc.options << '--line-numbers' << '--inline-source'
|
||||
rdoc.rdoc_files.include('README')
|
||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||
end
|
1
install.rb
Normal file
1
install.rb
Normal file
@ -0,0 +1 @@
|
||||
# Install hook code here
|
294
lib/application_checker.rb
Normal file
294
lib/application_checker.rb
Normal file
@ -0,0 +1,294 @@
|
||||
require 'open3'
|
||||
|
||||
module Rails
|
||||
module Upgrading
|
||||
class ApplicationChecker
|
||||
def initialize
|
||||
@issues = []
|
||||
|
||||
raise NotInRailsAppError unless in_rails_app?
|
||||
end
|
||||
|
||||
def in_rails_app?
|
||||
File.exist?("config/environment.rb")
|
||||
end
|
||||
|
||||
# Run all the check methods
|
||||
def run
|
||||
the_methods = (self.public_methods - Object.methods) - ["run", "initialize"]
|
||||
|
||||
the_methods.each {|m| send m }
|
||||
end
|
||||
|
||||
# Check for deprecated ActiveRecord calls
|
||||
def check_ar_methods
|
||||
files = []
|
||||
["find(:all", "find(:first", ":conditions =>", ":joins =>"].each do |v|
|
||||
lines = grep_for(v, "app/")
|
||||
files += extract_filenames(lines) || []
|
||||
end
|
||||
|
||||
unless files.empty?
|
||||
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
|
||||
|
||||
# Check for deprecated router syntax
|
||||
def check_routes
|
||||
lines = ["map.", "ActionController::Routing::Routes", ".resources"].map do |v|
|
||||
grep_for(v, "config/routes.rb").empty? ? nil : true
|
||||
end.compact
|
||||
|
||||
unless lines.empty?
|
||||
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
|
||||
|
||||
# Check for old (pre-application.rb) environment.rb file
|
||||
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")
|
||||
|
||||
unless lines.empty?
|
||||
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
|
||||
|
||||
# Check for old-style config.gem calls
|
||||
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
|
||||
|
||||
# Checks for old mailer syntax in both mailer classes and those
|
||||
# classes utilizing the mailers
|
||||
def check_mailers
|
||||
lines = grep_for("deliver_", "app/models/ #{base_path}app/controllers/ #{base_path}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
|
||||
|
||||
unless files.empty?
|
||||
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
|
||||
|
||||
# Checks for old-style generators
|
||||
def check_generators
|
||||
generators = Dir.glob(base_path + "vendor/plugins/**/generators/**/")
|
||||
|
||||
unless generators.empty?
|
||||
files = generators.map do |g|
|
||||
grep_for("def manifest", g).empty? ? g : nil
|
||||
end.compact
|
||||
|
||||
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
|
||||
|
||||
# Checks a list of known broken plugins and gems
|
||||
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", "devise",
|
||||
"inherited_resources"]
|
||||
|
||||
bad_plugins = bad_plugins.map do |p|
|
||||
p if File.exist?("#{base_path}vendor/plugins/#{p}") || !Dir.glob("#{base_path}vendor/gems/#{p}-*").empty?
|
||||
end.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
|
||||
# Find a string in a set of files; calls +find_with_grep+ and +find_with_rak+
|
||||
# depending on platform.
|
||||
#
|
||||
# TODO: Figure out if this works on Windows.
|
||||
def grep_for(text, where = "./")
|
||||
# If they're on Windows, they probably don't have grep.
|
||||
@probably_has_grep ||= (Config::CONFIG['host_os'].downcase =~ /mswin|windows|mingw/).nil?
|
||||
|
||||
if @probably_has_grep
|
||||
find_with_grep(text, base_path + where)
|
||||
else
|
||||
find_with_rak(text, base_path + where)
|
||||
end
|
||||
end
|
||||
|
||||
# Sets a base path for finding files; mostly for testing
|
||||
def base_path
|
||||
Dir.pwd + "/"
|
||||
end
|
||||
|
||||
# Use the grep utility to find a string in a set of files
|
||||
def find_with_grep(text, where)
|
||||
value = ""
|
||||
|
||||
Open3.popen3("grep -r '#{text}' #{where}") do |stdin, stdout, stderr|
|
||||
value = stdout.read
|
||||
end
|
||||
|
||||
value
|
||||
end
|
||||
|
||||
# Use the rak gem to grep the files (not yet implemented)
|
||||
def find_with_rak(text, where)
|
||||
value = ""
|
||||
|
||||
Open3.popen3("rak --nogroup -l '#{Regexp.escape(text)}' #{where}") do |stdin, stdout, stderr|
|
||||
value = stdout.read
|
||||
end
|
||||
|
||||
value
|
||||
end
|
||||
|
||||
# Extract the filenames from the grep output
|
||||
def extract_filenames(output)
|
||||
if @probably_has_grep
|
||||
extract_filenames_from_grep(output)
|
||||
else
|
||||
extract_filenames_from_rak(output)
|
||||
end
|
||||
end
|
||||
|
||||
def extract_filenames_from_grep(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 do |fn|
|
||||
fn.match(/^(.+?):/)[1] rescue nil
|
||||
end.compact
|
||||
|
||||
fnames.uniq
|
||||
end
|
||||
|
||||
def extract_filenames_from_rak(output)
|
||||
return nil if output.empty?
|
||||
|
||||
output.split("\n").uniq
|
||||
end
|
||||
|
||||
# Terminal colors, borrowed from Thor
|
||||
CLEAR = "\e[0m"
|
||||
BOLD = "\e[1m"
|
||||
RED = "\e[31m"
|
||||
YELLOW = "\e[33m"
|
||||
CYAN = "\e[36m"
|
||||
WHITE = "\e[37m"
|
||||
|
||||
# Show an upgrade alert to the user
|
||||
def alert(title, text, more_info_url, culprits)
|
||||
if Config::CONFIG['host_os'].downcase =~ /mswin|windows|mingw/
|
||||
basic_alert(title, text, more_info_url, culprits)
|
||||
else
|
||||
color_alert(title, text, more_info_url, culprits)
|
||||
end
|
||||
end
|
||||
|
||||
# Show an upgrade alert to the user. If we're on Windows, we can't
|
||||
# use terminal colors, hence this method.
|
||||
def basic_alert(title, text, more_info_url, culprits)
|
||||
puts "** " + title
|
||||
puts text
|
||||
puts "More information: #{more_info_url}"
|
||||
puts
|
||||
puts "The culprits: "
|
||||
culprits.each do |c|
|
||||
puts "\t- #{c}"
|
||||
end
|
||||
puts
|
||||
end
|
||||
|
||||
# Show a colorful alert to the user
|
||||
def color_alert(title, text, more_info_url, culprits)
|
||||
puts "#{RED}#{BOLD}#{title}#{CLEAR}"
|
||||
puts "#{WHITE}#{text}"
|
||||
puts "#{BOLD}More information:#{CLEAR} #{CYAN}#{more_info_url}"
|
||||
puts
|
||||
puts "#{WHITE}The culprits: "
|
||||
culprits.each do |c|
|
||||
puts "#{YELLOW}\t- #{c}"
|
||||
end
|
||||
ensure
|
||||
puts "#{CLEAR}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
95
lib/gemfile_generator.rb
Normal file
95
lib/gemfile_generator.rb
Normal file
@ -0,0 +1,95 @@
|
||||
module Rails
|
||||
module Upgrading
|
||||
class GemfileGenerator
|
||||
def generate_new_gemfile
|
||||
if has_environment?
|
||||
generate_gemfile
|
||||
else
|
||||
raise FileNotFoundError, "Can't find environment.rb [config/environment.rb]!"
|
||||
end
|
||||
end
|
||||
|
||||
def has_environment?
|
||||
File.exists?("config/environment.rb")
|
||||
end
|
||||
|
||||
def environment_code
|
||||
File.open("config/environment.rb").read
|
||||
end
|
||||
|
||||
def generate_gemfile
|
||||
environment_file = environment_code
|
||||
|
||||
# Get each line that starts with config.gem
|
||||
gem_lines = environment_file.split("\n").select {|l| l =~ /^\s*config\.gem/}
|
||||
|
||||
# Toss those lines to a generator class; the lines are evaluated in the
|
||||
# context of that instance.
|
||||
config = GemfileGenerator.new
|
||||
config.instance_eval(gem_lines.join("\n"))
|
||||
|
||||
config.output
|
||||
end
|
||||
end
|
||||
|
||||
class GemfileGenerator
|
||||
# Creates a target for the config.gem calls
|
||||
def config
|
||||
self
|
||||
end
|
||||
|
||||
def initialize
|
||||
@gems = []
|
||||
end
|
||||
|
||||
# Receive a call to add a gem to the list
|
||||
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
|
||||
|
||||
# Generate the Gemfile output
|
||||
def output
|
||||
# Generic preamble, taken from Yehuda Katz's blog
|
||||
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
|
||||
|
||||
# Get Gemfile call for all the gems
|
||||
def generate_upgraded_code
|
||||
code = @gems.map do |name, version, data|
|
||||
version_string = (version ? "'#{version}'" : nil)
|
||||
source = data.delete(:source)
|
||||
|
||||
data_string = nil
|
||||
unless data.empty?
|
||||
data_string = data.to_a.map {|k, v| ":#{k} => '#{v}'"}.join(", ")
|
||||
end
|
||||
|
||||
# If we have a source, generate a call to +source+ then output the
|
||||
# gem call. Otherwise, just generate the gem requirement.
|
||||
if source
|
||||
str = ["'#{name}'", version_string, data_string].compact.join(", ")
|
||||
"source '#{source}'\ngem #{str}"
|
||||
else
|
||||
str = ["'#{name}'", version_string, data_string].compact.join(", ")
|
||||
"gem #{str}"
|
||||
end
|
||||
end.join("\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
0
lib/rails_upgrade.rb
Normal file
0
lib/rails_upgrade.rb
Normal file
329
lib/routes_upgrader.rb
Normal file
329
lib/routes_upgrader.rb
Normal file
@ -0,0 +1,329 @@
|
||||
# TODO: Fix formatting on member/collection methods
|
||||
module Rails
|
||||
module Upgrading
|
||||
class RoutesUpgrader
|
||||
def generate_new_routes
|
||||
if has_routes_file?
|
||||
upgrade_routes
|
||||
else
|
||||
raise FileNotFoundError, "Can't find your routes file [config/routes.rb]!"
|
||||
end
|
||||
end
|
||||
|
||||
def has_routes_file?
|
||||
File.exists?("config/routes.rb")
|
||||
end
|
||||
|
||||
def routes_code
|
||||
File.read("config/routes.rb")
|
||||
end
|
||||
|
||||
def upgrade_routes
|
||||
ActionController::Routing::Routes.setup
|
||||
|
||||
# Read and eval the file; our fake route mapper will capture
|
||||
# the calls to draw routes and generate new route code
|
||||
eval(routes_code)
|
||||
|
||||
# Give the route set to the code generator and get its output
|
||||
generator = RouteGenerator.new(ActionController::Routing::Routes.redrawer.routes)
|
||||
generator.generate
|
||||
end
|
||||
end
|
||||
|
||||
class RouteRedrawer
|
||||
attr_accessor :routes
|
||||
cattr_accessor :stack
|
||||
|
||||
def initialize
|
||||
@routes = []
|
||||
|
||||
# The old default route was actually two routes; we generate the new style
|
||||
# one only if we haven't generated it for the first old default route.
|
||||
@default_route_generated = false
|
||||
|
||||
# Setup the stack for parents; used use proper indentation
|
||||
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 ENV['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.flatten.map {|l| "#{@indent}#{l.chomp}"}.join("\n") + "\n"
|
||||
else
|
||||
"#{@indent}#{code_lines.shift}"
|
||||
end
|
||||
end
|
||||
|
||||
def opts_to_string(opts)
|
||||
opts.is_a?(Hash) ? opts.to_a.map {|o, v| ":#{o} => '#{v}'"}.join(", ") : 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 = Rails::Upgrading::RouteRedrawer.new
|
||||
end
|
||||
|
||||
def self.redrawer
|
||||
@redrawer
|
||||
end
|
||||
|
||||
def self.draw
|
||||
yield @redrawer
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
30
tasks/rails_upgrade_tasks.rake
Normal file
30
tasks/rails_upgrade_tasks.rake
Normal file
@ -0,0 +1,30 @@
|
||||
$:.unshift(File.dirname(__FILE__) + "/../lib")
|
||||
require 'routes_upgrader'
|
||||
require 'gemfile_generator'
|
||||
require 'application_checker'
|
||||
|
||||
namespace :rails do
|
||||
namespace :upgrade do
|
||||
desc "Runs a battery of checks on your Rails 2.x app and generates a report on required upgrades for Rails 3"
|
||||
task :check do
|
||||
checker = Rails::Upgrading::ApplicationChecker.new
|
||||
checker.run
|
||||
end
|
||||
|
||||
desc "Generates a Gemfile for your Rails 3 app out of your config.gem directives"
|
||||
task :gems do
|
||||
generator = Rails::Upgrading::GemfileGenerator.new
|
||||
new_gemfile = generator.generate_new_gemfile
|
||||
|
||||
puts new_gemfile
|
||||
end
|
||||
|
||||
desc "Create a new, upgraded route file from your current routes.rb"
|
||||
task :routes do
|
||||
upgrader = Rails::Upgrading::RoutesUpgrader.new
|
||||
new_routes = upgrader.generate_new_routes
|
||||
|
||||
puts new_routes
|
||||
end
|
||||
end
|
||||
end
|
138
test/application_checker_test.rb
Normal file
138
test/application_checker_test.rb
Normal file
@ -0,0 +1,138 @@
|
||||
require 'test_helper'
|
||||
require 'application_checker'
|
||||
|
||||
tmp_dir = "#{File.dirname(__FILE__)}/fixtures/tmp"
|
||||
|
||||
if defined? BASE_ROOT
|
||||
BASE_ROOT.replace tmp_dir
|
||||
else
|
||||
BASE_ROOT = tmp_dir
|
||||
end
|
||||
FileUtils.mkdir_p BASE_ROOT
|
||||
|
||||
# Stub out methods on upgrader class
|
||||
module Rails
|
||||
module Upgrading
|
||||
class ApplicationChecker
|
||||
attr_reader :alerts
|
||||
|
||||
def base_path
|
||||
BASE_ROOT + "/"
|
||||
end
|
||||
|
||||
def in_rails_app?
|
||||
true
|
||||
end
|
||||
|
||||
def initialize
|
||||
@alerts = {}
|
||||
end
|
||||
|
||||
def alert(title, text, more_info_url, culprits)
|
||||
@alerts[title] = [text, more_info_url, culprits]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class ApplicationCheckerTest < ActiveSupport::TestCase
|
||||
def setup
|
||||
@checker = Rails::Upgrading::ApplicationChecker.new
|
||||
@old_dir = Dir.pwd
|
||||
|
||||
Dir.chdir(BASE_ROOT)
|
||||
end
|
||||
|
||||
def test_check_ar_methods_in_controller
|
||||
make_file("app/controllers", "post_controller.rb", "Post.find(:all)")
|
||||
@checker.check_ar_methods
|
||||
|
||||
assert @checker.alerts.has_key?("Soon-to-be-deprecated ActiveRecord calls")
|
||||
end
|
||||
|
||||
def test_check_ar_methods_in_models
|
||||
make_file("app/models", "post.rb", "Post.find(:all)")
|
||||
@checker.check_ar_methods
|
||||
|
||||
assert @checker.alerts.has_key?("Soon-to-be-deprecated ActiveRecord calls")
|
||||
end
|
||||
|
||||
def test_named_scope_left_over
|
||||
make_file("app/models", "post.rb", "named_scope :failure")
|
||||
@checker.check_ar_methods
|
||||
|
||||
assert @checker.alerts.has_key?("named_scope is now just scope")
|
||||
end
|
||||
|
||||
def test_check_routes
|
||||
make_file("config/", "routes.rb", " map.connect 'fail'")
|
||||
@checker.check_routes
|
||||
|
||||
assert @checker.alerts.has_key?("Old router API")
|
||||
end
|
||||
|
||||
def test_check_lack_of_app_dot_rb
|
||||
@checker.check_environment
|
||||
|
||||
assert @checker.alerts.has_key?("New file needed: config/application.rb")
|
||||
end
|
||||
|
||||
def test_check_environment_syntax
|
||||
make_file("config/", "environment.rb", "config.frameworks = []")
|
||||
@checker.check_environment
|
||||
|
||||
assert @checker.alerts.has_key?("Old environment.rb")
|
||||
end
|
||||
|
||||
def test_check_gems
|
||||
make_file("config/", "environment.rb", "config.gem 'rails'")
|
||||
@checker.check_gems
|
||||
|
||||
assert @checker.alerts.has_key?("Old gem bundling (config.gems)")
|
||||
end
|
||||
|
||||
def test_check_mailer_syntax
|
||||
make_file("app/models/", "notifications.rb", "def signup\nrecipients @users\n end")
|
||||
@checker.check_mailers
|
||||
|
||||
assert @checker.alerts.has_key?("Old ActionMailer class API")
|
||||
end
|
||||
|
||||
def test_check_mailer_api
|
||||
make_file("app/controllers/", "thing_controller.rb", "def signup\n Notifications.deliver_signup\n end")
|
||||
@checker.check_mailers
|
||||
|
||||
assert @checker.alerts.has_key?("Deprecated ActionMailer API")
|
||||
end
|
||||
|
||||
def test_check_generators
|
||||
make_file("vendor/plugins/thing/generators/thing/", "thing_generator.rb", "def manifest\n m.whatever\n end")
|
||||
@checker.check_generators
|
||||
|
||||
assert @checker.alerts.has_key?("Old Rails generator API")
|
||||
end
|
||||
|
||||
def test_check_plugins
|
||||
make_file("vendor/plugins/rspec-rails/", "whatever.rb", "def rspec; end")
|
||||
@checker.check_plugins
|
||||
|
||||
assert @checker.alerts.has_key?("Known broken plugins")
|
||||
end
|
||||
|
||||
def teardown
|
||||
clear_files
|
||||
|
||||
Dir.chdir(@old_dir)
|
||||
end
|
||||
|
||||
def make_file(where, name=nil, contents=nil)
|
||||
FileUtils.mkdir_p "#{BASE_ROOT}/#{where}"
|
||||
File.open("#{BASE_ROOT}/#{where}/#{name}", "w+") do |f|
|
||||
f.write(contents)
|
||||
end if name
|
||||
end
|
||||
|
||||
def clear_files
|
||||
FileUtils.rm_rf(Dir.glob("#{BASE_ROOT}/*"))
|
||||
end
|
||||
end
|
73
test/gemfile_generator_test.rb
Normal file
73
test/gemfile_generator_test.rb
Normal file
@ -0,0 +1,73 @@
|
||||
require 'test_helper'
|
||||
require 'gemfile_generator'
|
||||
|
||||
# Stub out methods on upgrader class
|
||||
module Rails
|
||||
module Upgrading
|
||||
class GemfileGenerator
|
||||
attr_writer :environment_code
|
||||
|
||||
def has_environment?
|
||||
true
|
||||
end
|
||||
|
||||
def environment_code
|
||||
@environment_code
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class GemfileGeneratorTest < ActiveSupport::TestCase
|
||||
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
|
||||
|
||||
def test_generates_with_no_gems
|
||||
generator = Rails::Upgrading::GemfileGenerator.new
|
||||
generator.environment_code = ""
|
||||
|
||||
assert_equal PREAMBLE, generator.generate_gemfile
|
||||
end
|
||||
|
||||
def test_generates_with_gem
|
||||
generator = Rails::Upgrading::GemfileGenerator.new
|
||||
generator.environment_code = "config.gem 'camping'"
|
||||
|
||||
assert_equal PREAMBLE + "gem 'camping'", generator.generate_gemfile
|
||||
end
|
||||
|
||||
def test_generates_with_version
|
||||
generator = Rails::Upgrading::GemfileGenerator.new
|
||||
generator.environment_code = "config.gem 'camping', :version => '2.1.1'"
|
||||
|
||||
assert_equal PREAMBLE + "gem 'camping', '2.1.1'", generator.generate_gemfile
|
||||
end
|
||||
|
||||
def test_can_add_sources
|
||||
generator = Rails::Upgrading::GemfileGenerator.new
|
||||
generator.environment_code = "config.gem 'camping', :source => 'http://code.whytheluckystiff.net'"
|
||||
|
||||
assert_equal PREAMBLE + "source 'http://code.whytheluckystiff.net'\ngem 'camping'", generator.generate_gemfile
|
||||
end
|
||||
|
||||
def test_changes_lib_to_new_key
|
||||
generator = Rails::Upgrading::GemfileGenerator.new
|
||||
generator.environment_code = "config.gem 'camping', :lib => 'kamping'"
|
||||
|
||||
assert_equal PREAMBLE + "gem 'camping', :require_as => 'kamping'", generator.generate_gemfile
|
||||
end
|
||||
|
||||
def test_generates_with_all_options
|
||||
generator = Rails::Upgrading::GemfileGenerator.new
|
||||
generator.environment_code = "config.gem 'camping', :lib => 'kamping', :source => 'http://code.whytheluckystiff.net', :version => '2.1.1'"
|
||||
|
||||
assert_equal PREAMBLE + "source 'http://code.whytheluckystiff.net'\ngem 'camping', '2.1.1', :require_as => 'kamping'", generator.generate_gemfile
|
||||
end
|
||||
end
|
137
test/routes_upgrader_test.rb
Normal file
137
test/routes_upgrader_test.rb
Normal file
@ -0,0 +1,137 @@
|
||||
require 'test_helper'
|
||||
require 'routes_upgrader'
|
||||
|
||||
# Stub out methods on upgrader class
|
||||
module Rails
|
||||
module Upgrading
|
||||
class RoutesUpgrader
|
||||
attr_writer :routes_code
|
||||
|
||||
def has_routes_file?
|
||||
true
|
||||
end
|
||||
|
||||
def routes_code
|
||||
@routes_code
|
||||
end
|
||||
end
|
||||
|
||||
class RouteGenerator
|
||||
def app_name
|
||||
"MyApplication"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class RoutesUpgraderTest < ActiveSupport::TestCase
|
||||
def setup
|
||||
Rails::Upgrading::RouteRedrawer.stack = []
|
||||
end
|
||||
|
||||
def test_generates_routes_file
|
||||
routes_code = "
|
||||
ActionController::Routing::Routes.draw do |map|
|
||||
map.connect '/home', :controller => 'home', :action => 'index'
|
||||
map.login '/login', :controller => 'sessions', :action => 'new'
|
||||
|
||||
map.resources :hats
|
||||
map.resource :store
|
||||
end
|
||||
"
|
||||
|
||||
new_routes_code = "MyApplication::Application.routes do
|
||||
match '/home' => 'home#index'
|
||||
match '/login' => 'sessions#new', :as => :login
|
||||
resources :hats
|
||||
resource :store
|
||||
end
|
||||
"
|
||||
|
||||
upgrader = Rails::Upgrading::RoutesUpgrader.new
|
||||
upgrader.routes_code = routes_code
|
||||
|
||||
result = upgrader.generate_new_routes
|
||||
|
||||
assert_equal new_routes_code, result
|
||||
end
|
||||
|
||||
def test_generates_code_for_regular_route
|
||||
route = Rails::Upgrading::FakeRoute.new("/about", {:controller => 'static', :action => 'about'})
|
||||
assert_equal "match '/about' => 'static#about'", route.to_route_code
|
||||
end
|
||||
|
||||
def test_generates_code_for_named_route
|
||||
route = Rails::Upgrading::FakeRoute.new("/about", {:controller => 'static', :action => 'about'}, "about")
|
||||
assert_equal "match '/about' => 'static#about', :as => :about", route.to_route_code
|
||||
end
|
||||
|
||||
def test_generates_code_for_namespace
|
||||
ns = Rails::Upgrading::FakeNamespace.new("static")
|
||||
# Add a route to the namespace
|
||||
ns << Rails::Upgrading::FakeRoute.new("/about", {:controller => 'static', :action => 'about'})
|
||||
|
||||
assert_equal "namespace :static do\nmatch '/about' => 'static#about'\nend\n", ns.to_route_code
|
||||
end
|
||||
|
||||
def test_generates_code_for_resources
|
||||
route = Rails::Upgrading::FakeResourceRoute.new("hats")
|
||||
assert_equal "resources :hats", route.to_route_code
|
||||
end
|
||||
|
||||
def test_generates_code_for_resources
|
||||
route = Rails::Upgrading::FakeSingletonResourceRoute.new("hat")
|
||||
assert_equal "resource :hat", route.to_route_code
|
||||
end
|
||||
|
||||
def test_generates_code_for_resources_with_special_methods
|
||||
route = Rails::Upgrading::FakeResourceRoute.new("hats", {:member => {:wear => :get}, :collection => {:toss => :post}})
|
||||
assert_equal "resources :hats do\ncollection do\npost :toss\nend\nmember do\nget :wear\nend\n\nend\n", route.to_route_code
|
||||
end
|
||||
|
||||
def test_generates_code_for_route_with_extra_params
|
||||
route = Rails::Upgrading::FakeRoute.new("/about", {:controller => 'static', :action => 'about', :something => 'extra'})
|
||||
assert_equal "match '/about' => 'static#about', :something => 'extra'", route.to_route_code
|
||||
end
|
||||
|
||||
def test_generates_code_for_root
|
||||
routes_code = "
|
||||
ActionController::Routing::Routes.draw do |map|
|
||||
map.root :controller => 'home', :action => 'index'
|
||||
end
|
||||
"
|
||||
|
||||
new_routes_code = "MyApplication::Application.routes do
|
||||
match '/' => 'home#index'
|
||||
end
|
||||
"
|
||||
|
||||
upgrader = Rails::Upgrading::RoutesUpgrader.new
|
||||
upgrader.routes_code = routes_code
|
||||
|
||||
result = upgrader.generate_new_routes
|
||||
|
||||
assert_equal new_routes_code, result
|
||||
end
|
||||
|
||||
def test_generates_code_for_default_route
|
||||
routes_code = "
|
||||
ActionController::Routing::Routes.draw do |map|
|
||||
map.connect ':controller/:action/:id.:format'
|
||||
map.connect ':controller/:action/:id'
|
||||
end
|
||||
"
|
||||
|
||||
new_routes_code = "MyApplication::Application.routes do
|
||||
match '/:controller(/:action(/:id))'
|
||||
end
|
||||
"
|
||||
|
||||
upgrader = Rails::Upgrading::RoutesUpgrader.new
|
||||
upgrader.routes_code = routes_code
|
||||
|
||||
result = upgrader.generate_new_routes
|
||||
|
||||
assert_equal new_routes_code, result
|
||||
end
|
||||
end
|
3
test/test_helper.rb
Normal file
3
test/test_helper.rb
Normal file
@ -0,0 +1,3 @@
|
||||
require 'rubygems'
|
||||
require 'active_support'
|
||||
require 'active_support/test_case'
|
1
uninstall.rb
Normal file
1
uninstall.rb
Normal file
@ -0,0 +1 @@
|
||||
# Uninstall hook code here
|
Loading…
Reference in New Issue
Block a user