homeASCIIcasts

227: Upgrading to Rails 3 Part 3 

(view original Railscast)

Other translations: It Pt Es

In this episode we’re going to finish our series on upgrading an Rails 2 application to Rails 3. We’ve made good progress so far; we have all of the application’s tests passing and have used the Rails upgrade plugin to find and fix most of the areas that need upgrading to Rails 3. There are still things that need to be fixed in the view layer, however, and we’ll focus on those in this episode.

Removing Deprecation Warnings

Before we start fixing the view layer we’ll take another look at the tests. By the end of the last episode all of the application’s tests passed but there were a large number of deprecation warnings shown as they ran. We’ll run the tests again now to see if we can reduce the number of warnings.

$ rake spec
# large amount of output snipped.
.DEPRECATION WARNING: error_messages_for was removed from Rails and is now available as a plugin. Please install it with `rails plugin install git://github.com/rails/dynamic_form.git`. (called from _app_views_sponsors__form_html_erb___2363957037552137609_2171491000_4161310651677273387 at /Users/eifion/rails/apps_for_asciicasts/ep227/railscasts/app/views/sponsors/_form.html.erb:2)
...................................................
Finished in 3.24 seconds
152 examples, 0 failures

The tests still pass, but there are still a large number of warnings. Fortunately a large number of them are duplicates so it shouldn’t take a lot of work to remove them.

We’ve cut out most of the output from rake spec above but left the final warning in. The error_messages_for and error_messages_on methods have been deprecated in Rails 3 and we could replace them with custom code that loops through the model’s errors.full_messages to display the errors. The old methods are still available as a plugin, however, so we’ll take the easier option and use that instead. The instructions for installing it are shown in the deprecation warning above.

$ rails plugin install git://github.com/rails/dynamic_form.git
Initialized empty Git repository in /Users/eifion/rails/apps_for_asciicasts/ep227/railscasts/vendor/plugins/dynamic_form/.git/
remote: Counting objects: 22, done.
remote: Compressing objects: 100% (17/17), done.
remote: Total 22 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (22/22), done.
From git://github.com/rails/dynamic_form
 * branch            HEAD       -> FETCH_HEAD

With the plugin installed when we run rake spec again and the warning has gone. There are other errors, though, and one that appears frequently is this:

DEPRECATION WARNING: subclasses is deprecated and will be removed from Rails 3.0 (use descendants instead). (called from block (2 levels) in load_models 
at /Users/eifion/.rvm/gems/ruby-1.9.2-rc2/gems/thinking-sphinx-2.0.0.rc1/lib/thinking_sphinx/context.rb:58)

This error is thrown by the Thinking Sphinx library. If you’re getting errors from a plugin it’s worth checking the issue tracker for that plugin to see if there are any issues listed related to the error you’re seeing. If not then we can add one and maybe it will be fixed for the next release. (Alternatively you can always fork the code and fix the error yourself.)

In the case of Thinking Sphinx this problem has already been reported and fixed but hasn’t been included in the latest gem release at the time of writing. To remove this warning from our application we can tell bundler to get the Thinking Sphinx code from a git repository instead of from a gem.

So, in our Gemfile instead of including Thinking Sphinx this way:

/Gemfile

gem 'thinking-sphinx', '>=2.0.0.rc1', :require => 'thinking_sphinx'

we’ll include it like this:

/Gemfile

gem 'thinking-sphinx', :require => 'thinking_sphinx', :git => "git://github.com/freelancing-god/thinking-sphinx.git", :branch => "rails3"

We have to run bundle install again so that it downloads Thinking Sphinx from its git repository. Our application will now use this version instead of the latest gem. When we run our specs now they all pass without warnings.

$ rake spec
........................................................................................................................................................
Finished in 3.3 seconds
152 examples, 0 failures

Fixing The Views

With the deprecation warnings out of the way we can focus on the views. When we run the application the first error that stands out is that the sidebar is missing from the episodes page.

The episodes page with the missing sidebar.

A part of the page as large as this should be covered by a test, if only to test for its existence. If we were upgrading this application for production then we’d write a test to cover this functionality but we won’t do that here.

If we take a look inside the layout file, the code that generates the side bar is this:

/app/views/layouts/application.html.erb

<%= yield(:side) || render(:partial => 'shared/side') %>

This code will yield the side content and if that returns nil it will render the side partial instead. The problem here is that in earlier versions of Rails yield would return nil if the appropriate content_for call was missing on the page whereas in Rails 3 it will return an empty string so the second part of the code will never be called.

To fix this we can use the content_for? method which will return true if that content area has been defined for a page. If it has we’ll display that content, otherwise we’ll render the sidebar.

/app/views/layouts/application.html.erb

<%= content_for?(:side) ? yield(:side) : render(:partial=> 'shared/side') %>

When we reload the page now the sidebar is displayed.

The sidebar has been restored.

There are still other problems in the view to be fixed. If we view the page for a single episode we’ll see that the show notes part of the page is being escaped.

The escaped HTML on the episode page.

The content for that part of the page goes through a textilize method that is defined in the ApplicationHelper module.

/app/helpers/application_helper.rb

module ApplicationHelper
  def textilize(text)
    Textilizer.new(text).to_html unless text.blank?
  end
end

This method returns some HTML as a string. In Rails 3 if you’re going to pass a string of HTML to a view to be displayed the string will need to be marked as HTML-safe, otherwise it will be automatically escaped. To mark the string as safe we just need to call the html_safe method on it.

/app/helpers/application_helper.rb

module ApplicationHelper
  def textilize(text)
    Textilizer.new(text).to_html.html_safe unless text.blank?
  end
end

Now the string has been marked as safe it will no longer be escaped. This is covered in more detail in episode 204 [watch, read]. When we reload the page again the show notes are rendered correctly.

The show notes are now shown correctly.

There’s still a small problem left on this page: the titlebar should include the name of the episode. This problem is specific to the way that titles are handled in this application; a title helper method is used which sets a instance variable rather than using content_for to set the title.

/app/helpers/layout_helper.rb

module LayoutHelper
  def title(page_title, show_title = true)
    @content_for_title = page_title.to_s
    @show_title = show_title
  end
end

To fix this we should set the content for the title using content_for, like this:

/app/helpers/layout_helper.rb

module LayoutHelper
  def title(page_title, show_title = true)
    content_for(:title, page_title.to_s)
    @show_title = show_title
  end
end

Now when we call yield(:title) in the layout the title will be set properly. We can pass the content itself in as a block or, as we have here, as a second argument.

Fixing Destroy Links

The next error is in the admin section of the application. On the page below is a list of model objects, each with an “edit” and “destroy” link.

The Spam Questions page.

The problem here lies with the “destroy” links. When we click one of them we’ll be taken to the show action for the question rather than destroy. The reason that the delete action isn’t being called is that the JavaScript used for destroy links has been made unobtrusive in Rails 3. If we look at the HTML for one of the questions we’ll see that the JavaScript is no longer embedded in the delete link’s tag. Instead there are two custom HTML 5 attributes that begin with data- and there should be some JavaScript that will detect these attributes and change the request to a DELETE request.

<tr>
  <td>What does the M stand for in MVC?</td>
  <td>model</td>
  <td><a href="/spam_questions/1/edit">Edit</a></td>
  <td><a href="/spam_questions/1" data-confirm="Are you sure?" data-method="delete" rel="nofollow">Destroy</a></td>
</tr>

As this application uses jQuery we need to download a custom rails.js file from the jquery-ujs project. If we were using Prototype then we wouldn’t need to do this as the correct file is already included in the /public/javascripts directory. When we’ve downloaded the file and copied it into the javascripts directory we’ll need to add a reference to it in the head section of our application’s layout file. We also need to add a csrf meta tag to prevent cross-site request forgeries.

/application/layouts/application.html.erb

<%= javascript_include_tag 'jquery', 'rails', 'application' %>
<%= csrf_meta_tag %>

The use of unobtrusive JavaScript in Rails 3 was covered in episode 205 [watch, read].

While we’re in the layout file we’ll update the application to use HTML 5 by changing the DOCTYPE and the opening html tag.

/application/layouts/application.html.erb

<!DOCTYPE html>
<html>

There will be other things that we need to do to make the application fully HTML 5 valid and we can run the code through a validator to find out what has to be changed.

With the new rails.js file in place when we reload the spam questions page and try to delete one of the questions the JavaScript confirmation will be shown and the question will be deleted if we click OK.

The destroy links now work.

The final change we can make to the view code is to remove any calls to the h method. In Rails 2 views any output that we wanted to be HTML-escaped had to be wrapped in the h method. In Rails 3 all output is escaped by default so we can tidy up the views by going through them and removing those h methods. This was covered in episode 204 [watch, read]. Leaving the method in won’t cause any problems but the view code will look cleaner without it.

Removing Obsolete Files

We’re coming to the end of the upgrade now; everything is working but we need to clean up some of the files that we no longer need. When we generated the new Rails 3 application it will have created a new welcome page and this will need to be removed.

$ rm public/index.html

The same applies to the Rails image file that’s shown on the welcome page.

$ rm public/images/rails.png

There are also files in the /script directory that are no longer used by Rails 3 and which can be removed.

$ ls script
about		destroy		process	setup
autospec	generate		rails		setup_test
console	performance	runner	spec
dbconsole	plugin		server	spec_server

Only the rails script is used by Rails 3 and so we can remove the others, such as console, server and so on.

That’s it for this series on upgrading a Rails 2 application to Rails 3. Keep in mind that a lot of what we’ve covered is specific to the Railscasts application and there’s no way that we could cover everything that might need to be done to upgrade an application to Rails 3. If you see deprecation warnings or error messages then the chances are that someone else has already had the same problem and a Google search will bring a solution. It’s also worth checking out the other episodes that cover Rails 3.

A large number of the problems that you’ll come across when upgrading an application will be caused by plugins. the Rails Plugins9 site has a great list of plugins and lists whether they are compatible with Rails 3 or not. If you do have trouble with a specific plug in it’s also worth checking it’s Github repository to see if a Rails 3 compatible version is available.