From d6a740e2db57c710c662bf50425f4a5211b3dfc9 Mon Sep 17 00:00:00 2001 From: Jeremy McAnally Date: Sun, 31 Jan 2010 20:27:32 -0600 Subject: [PATCH] Initial commit of Rake task/plugin version of rails-upgrade script --- MIT-LICENSE | 20 ++ README | 14 ++ Rakefile | 23 +++ init.rb | 1 + install.rb | 1 + lib/application_checker.rb | 294 +++++++++++++++++++++++++++ lib/gemfile_generator.rb | 95 +++++++++ lib/rails_upgrade.rb | 0 lib/routes_upgrader.rb | 329 +++++++++++++++++++++++++++++++ tasks/rails_upgrade_tasks.rake | 30 +++ test/application_checker_test.rb | 138 +++++++++++++ test/gemfile_generator_test.rb | 73 +++++++ test/routes_upgrader_test.rb | 137 +++++++++++++ test/test_helper.rb | 3 + uninstall.rb | 1 + 15 files changed, 1159 insertions(+) create mode 100644 MIT-LICENSE create mode 100644 README create mode 100644 Rakefile create mode 100644 init.rb create mode 100644 install.rb create mode 100644 lib/application_checker.rb create mode 100644 lib/gemfile_generator.rb create mode 100644 lib/rails_upgrade.rb create mode 100644 lib/routes_upgrader.rb create mode 100644 tasks/rails_upgrade_tasks.rake create mode 100644 test/application_checker_test.rb create mode 100644 test/gemfile_generator_test.rb create mode 100644 test/routes_upgrader_test.rb create mode 100644 test/test_helper.rb create mode 100644 uninstall.rb diff --git a/MIT-LICENSE b/MIT-LICENSE new file mode 100644 index 0000000..ec4ddd4 --- /dev/null +++ b/MIT-LICENSE @@ -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. diff --git a/README b/README new file mode 100644 index 0000000..499b6fb --- /dev/null +++ b/README @@ -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 diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..56d2fd2 --- /dev/null +++ b/Rakefile @@ -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 diff --git a/init.rb b/init.rb new file mode 100644 index 0000000..3c19a74 --- /dev/null +++ b/init.rb @@ -0,0 +1 @@ +# Include hook code here diff --git a/install.rb b/install.rb new file mode 100644 index 0000000..f7732d3 --- /dev/null +++ b/install.rb @@ -0,0 +1 @@ +# Install hook code here diff --git a/lib/application_checker.rb b/lib/application_checker.rb new file mode 100644 index 0000000..e058702 --- /dev/null +++ b/lib/application_checker.rb @@ -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 \ No newline at end of file diff --git a/lib/gemfile_generator.rb b/lib/gemfile_generator.rb new file mode 100644 index 0000000..4f834de --- /dev/null +++ b/lib/gemfile_generator.rb @@ -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 = < "{*/,}*.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 \ No newline at end of file diff --git a/lib/rails_upgrade.rb b/lib/rails_upgrade.rb new file mode 100644 index 0000000..e69de29 diff --git a/lib/routes_upgrader.rb b/lib/routes_upgrader.rb new file mode 100644 index 0000000..b527238 --- /dev/null +++ b/lib/routes_upgrader.rb @@ -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 \ No newline at end of file diff --git a/tasks/rails_upgrade_tasks.rake b/tasks/rails_upgrade_tasks.rake new file mode 100644 index 0000000..4e66a1b --- /dev/null +++ b/tasks/rails_upgrade_tasks.rake @@ -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 \ No newline at end of file diff --git a/test/application_checker_test.rb b/test/application_checker_test.rb new file mode 100644 index 0000000..cbab14a --- /dev/null +++ b/test/application_checker_test.rb @@ -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 \ No newline at end of file diff --git a/test/gemfile_generator_test.rb b/test/gemfile_generator_test.rb new file mode 100644 index 0000000..1ce70d9 --- /dev/null +++ b/test/gemfile_generator_test.rb @@ -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 = < "{*/,}*.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 \ No newline at end of file diff --git a/test/routes_upgrader_test.rb b/test/routes_upgrader_test.rb new file mode 100644 index 0000000..7765960 --- /dev/null +++ b/test/routes_upgrader_test.rb @@ -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 \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 0000000..e3a92c7 --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,3 @@ +require 'rubygems' +require 'active_support' +require 'active_support/test_case' diff --git a/uninstall.rb b/uninstall.rb new file mode 100644 index 0000000..9738333 --- /dev/null +++ b/uninstall.rb @@ -0,0 +1 @@ +# Uninstall hook code here