Mon, 03 Dec 2007

Rails 2 Upgrade Notes

Posted by Ben Mon, 03 Dec 2007 22:59:00 GMT

Over the weekend I decided to try out Rails 2 by upgrading an existing site (http://www.trawlr.com/) from Rails 1.2.6 to Rails 2 RC2.

Check for deprecations before you upgrade

You may want to check your existing application for deprecated code before upgrading using the following rake task.

lib/tasks/rails.rake
desc "Checks your app and gently warns you if you are using deprecated code." 
task :deprecated => :environment do
  deprecated = {
    '@params'    => 'Use params[] instead',
    '@session'   => 'Use session[] instead',
    '@flash'     => 'Use flash[] instead',
    '@request'   => 'Use request[] instead',
    '@env' => 'Use env[] instead',
    'find_all'   => 'Use find(:all) instead',
    'find_first' => 'Use find(:first) instead',
    'render_partial' => 'Use render :partial instead',
    'component'  => 'Use of components are frowned upon',
    'paginate'   => 'The default paginator is slow. Writing your own may be faster',
    'start_form_tag'   => 'Use form_for instead',
    'end_form_tag'   => 'Use form_for instead',
    ':post => true'   => 'Use :method => :post instead'
  }

  deprecated.each do |key, warning|
    puts '--> ' + key
    output = `cd '#{File.expand_path('app', RAILS_ROOT)}' && grep -n --exclude=*.svn* -r '#{key}' *`
    unless output =~ /^$/
      puts "  !! " + warning + " !!" 
      puts '  ' + '.' * (warning.length + 6)
      puts output
    else
      puts "  Clean! Cheers for you!" 
    end
    puts
  end
end

Use rake to execute the task.

rake deprecated

With any luck you won’t get many warnings; it should give you a rough estimate on how long your upgrade may take.

Getting Rails 2

First you need to get the RC2 (or newer) tagged Rails source using rake. This downloads the Rails framework to your appliction’s vender/rails directory.

rake rails:freeze:edge TAG=rel_2-0-0_RC2

Upgrade issues

After upgrading, it’s worth running your test suite looking for any problems. The following are issues I ran into to get trawlr.com working.

Singular resources now map to plural controllers – Override by using the :controller option in routes.rb

config/routes.rb
# Singleton reader resource
map.resource :reader, :controller => 'reader'

with_scope is now protected – Use .send(:with_scope) to call method

This caused an issue due to using the magic join model pattern, although the fix is relatively simple.

MyModel.send(:with_scope, args)

asset_packager plugin broken – Apply fix

The fabulous asset_packager plugin required a quick change to get working again (single-line change).

Index: vendor/plugins/asset_packager/lib/synthesis/asset_package_helper.rb
===================================================================
--- vendor/plugins/asset_packager/lib/synthesis/asset_package_helper.rb    (revision 86)
+++ vendor/plugins/asset_packager/lib/synthesis/asset_package_helper.rb    (working copy)
@@ -37,7 +37,7 @@
     private
       # rewrite compute_public_path to allow us to not include the query string timestamp
       # used by ActionView::Helpers::AssetTagHelper
-      def compute_public_path(source, dir, ext, add_asset_id=true)
+      def compute_public_path(source, dir, ext = nil, add_asset_id=true)
         source = source.dup
         source << ".#{ext}" if File.extname(source).blank?
         unless source =~ %r{^[-a-z]+://}

I still prefer this to the new Rails 2 asset merging as it also minifies JS and CSS files (including stripping comments) and also allows you to specify multiple asset groups (for example one grouping for the main site, another for an iPhone version). A word of caution if you use asset_packager you cannot take advantage of the new Rails 2 asset servers (see below).

# DOES NOT work with asset_packager
config.action_controller.asset_host = "http://asset%d.site.com" 

Nested route helpers changed – Must specifiy parent resource

If you have any nested routes it’s likely you will have to alter the named route helpers, for example child_path is now parent_child_path.

start_form_tag and end_form_tag have been deprecated – Quick fix is to replace with form_tag and </form> respectively.

<%= form_tag articles_path %> 
  <%= text_field :article, :title %> 
  <%= submit_tag "Save" %> 
</form>

render_without_layout has been deprecated – Use :layout => false instead.

render :layout => false

restful_authentication plugin is broken – Replace redirect_to_url (deprecated) in lib/authenticated_system.rb with to redirect_to.

Index: authenticated_system.rb
===================================================================
--- authenticated_system.rb    (revision 86)
+++ authenticated_system.rb    (working copy)
@@ -94,7 +94,7 @@
     # Redirect to the URI stored by the most recent store_location call or
     # to the passed default.
     def redirect_back_or_default(default)
-      session[:return_to] ? redirect_to_url(session[:return_to]) : redirect_to(default)
+      session[:return_to] ? redirect_to(session[:return_to]) : redirect_to(default)
       session[:return_to] = nil
     end

Optionally, rename your views

Rails 2 includes some changes to the way views are named. Previously you may have had show.rhtml and show.rjs which now become show.html.erb and show.js.rjs to indicate the mime type and template engines used. You don’t have to change your old views, but the following rake task should make it a quick change if you decide to. For trawlr.com I chose to use the new format for any new view templates but left the existing ones as they were (I’ll probably rename as changes are made on an individual basis).

lib/tasks/rails.rake
namespace 'views' do
  desc 'Renames all .rhtml views to .html.erb, .rjs to .js.rjs, .rxml to .xml.builder, and .haml to .html.haml'
  task 'rename' do
    Dir.glob('app/views/**/[^_]*.rhtml').each do |file|
      puts `svn mv #{file} #{file.gsub(/\.rhtml$/, '.html.erb')}`
    end

    Dir.glob('app/views/**/[^_]*.rxml').each do |file|
      puts `svn mv #{file} #{file.gsub(/\.rxml$/, '.xml.builder')}`
    end

    Dir.glob('app/views/**/[^_]*.rjs').each do |file|
      puts `svn mv #{file} #{file.gsub(/\.rjs$/, '.js.rjs')}`
    end
    Dir.glob('app/views/**/[^_]*.haml').each do |file|
      puts `svn mv #{file} #{file.gsub(/\.haml$/, '.html.haml')}`
    end
  end
end

Resources

For more information on Rails 2 refer to the preview release blog post. For $9 you can pick up a copy of Ryan Daigle’s worthwhile Rails 2 PDF book available via peepcode.

Comments

Leave a response

  1. Steve 1 day later:

    Thanks for the rake task. Very handy!

  2. Farzad FARID 4 days later:

    Hi, thanks for the task, but one rule seems to give some false warnings.

    • find_all()” is deprecated, but “find_all_by_FIELD()” is not, because “find_by_FIELD(:all, ...)” does not work in Rails 1.2.6 :)

    Using 'find_all\([^_]\|$\)' instead of 'find_all' seems to works.

    Cheers.

  3. Ed 6 days later:
    FWIW: i added this to the grep command to filter out other temp SVN files: --exclude=.#*
    so the whole command is:
    grep -n --exclude=*.svn* --exclude=.#* -r '#{key}' *

    handy task – thanks!
  4. Jesper Rønn-Jensen 8 days later:

    Thanks a lot for making this task. It runs directly on windows as well (at least on my laptop), where cygwin is installed.

  5. Jesper Rønn-Jensen 8 days later:

    One more thing:

    There are quite some code that is moved to plugins in Rails 2.0

    It would be good with a notification of these…. You already added pagination, and there is also builder(?), auto_complete, acts_as_tree just to mention some examples.

    Could the rake task be modified so that it automatically detects whether the plugin is included or not? It probably requires detection if a corresponding plugin name is loaded if a certain pattern is found.

    I can’t figure it out now, but if somebody would patch the code I think it would be of great help to everybody.

    Thanks! /Jesper

  6. psychic readings 9 days later:

    This is GREAT! Thanks for the upgrade info,

  7. Rob Sanheim 10 days later:

    You can also use multi_rails, which will let you test against 1.2.6 and 2.0.1 as you make the changes to get compatibile.

  8. Jon Maddox 10 days later:

    Thanks for the hot tips. Heres a version of the rake task for GIT

    namespace 'views' do
      desc 'Renames all .rhtml views to .html.erb, .rjs to .js.rjs, .rxml to .xml.builder, and .haml to .html.haml'
      task 'rename' do
        Dir.glob('app/views/**/[^_]*.rhtml').each do |file|
          puts `git mv #{file} #{file.gsub(/\.rhtml$/, '.html.erb')}`
        end
    
        Dir.glob('app/views/**/[^_]*.rxml').each do |file|
          puts `git mv #{file} #{file.gsub(/\.rxml$/, '.xml.builder')}`
        end
    
        Dir.glob('app/views/**/[^_]*.rjs').each do |file|
          puts `git mv #{file} #{file.gsub(/\.rjs$/, '.js.rjs')}`
        end
        Dir.glob('app/views/**/[^_]*.haml').each do |file|
          puts `git mv #{file} #{file.gsub(/\.haml$/, '.html.haml')}`
        end
      end
    end
    
    
  9. Scott Becker 15 days later:

    Asset Packager – the ext=nil fix you mention has been in svn trunk since 11/04/07. :) The asset host issue should be resolved soon. Thanks! Also, Asset Packager now has a tracker, you can find it here.

  10. div 16 days later:

    How to use render in new version instead of render(:partial=>‘list_categories’) in rails 1.2.5

  11. Paul Goscicki 22 days later:

    Any particular reason why helpers (like _form.rhtml) are not renamed to *.html.erb using the above script?

  12. Ryan Waldron 25 days later:

    Hey, you can eliminate false positives on find_all_by_* by changing this line:

    'find_all'   => 'Use find(:all) instead',

    to this

    'find_all[^_]'   => 'Use find(:all) instead',
  13. saurabh purnaye about 1 month later:

    still i m getting some errors while redirection NoMethodError (undefined method `redirect_to_url’ for #): /app/controllers/test_controller.rb:14:in `__instance_exec0’ c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/active_support/core_ext/object/extending.rb:52:in `send’ c:/ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/active_support/core_ext/object/extending.rb:52:in `instance_exec’ c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_view/helpers/prototype_helper.rb:581:in `initialize’ c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/base.rb:905:in `new’ c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/base.rb:905:in `render_with_no_layout’ c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/layout.rb:270:in `render_without_benchmark’ c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/benchmarking.rb:51:in `render’ c:/ruby/lib/ruby/1.8/benchmark.rb:293:in `measure’ c:/ruby/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/benchmarking.rb:51:in `render’

  14. Ben about 1 month later:

    redirect_to_url is deprecated in Rails 2; use redirect_to instead.

  15. Tom Harrison about 1 month later:

    One more thing for the list: object transactions (e.g. “Model.transaction(@foo) do…” is bad, “Model.transaction do …” is ok). I guess a regex like /\.transaction\(/ would catch most of them. This is a case that is documented in the code, but the deprecations page doesn’t list it.

  16. Ahsan about 1 month later:

    It also detects ‘find_all’ which is a valid method of the Enumerable mixin ;)

  17. Mog 2 months later:

    Thanks!

    ‘render_component’ is detected by ‘component’ => ‘Use of components are frowned upon’.

    Can we use ‘render_component’ in Rails 2 ?

  18. Felipe Giotto 2 months later:

    Thanks!

    Great post to help users to migrate to Rails 2 applications! This is just what I need to put my old restful_authentication back to work!

    Felipe Giotto ;-)

  19. Jesse 3 months later:

    I am also curious why you chose to skip renaming partials with your rake task…?

  20. sullivan 5 months later:

    Not bad code

Comments