Rails 3: First Impressions from a Veteran Ruby on Rails Guy

This article is based on my recent experiences getting up to speed on Rails 3, starting about four weeks before the final release. While I have used Rails in my consulting business for several years and my last book had two Rails examples, I explored Rails 3 with a fresh perspective because I have spent five months away from Rails development (working full time on statistical natural language processing and text mining).

I divided the article into two parts:

Getting set up for Rails 3 development A quick overview of new features and how to use them

I cover only the Rails 3 aspects that I have been experimenting with during the past several weeks, so this is not an exhaustive overview by any means. However, you hopefully will have fun with the material and find it a useful jumping off point for learning Rails 3.

Note: The ZIP file containing examples for this article has top-level directories with the same names as the Rails project names that I will create.

I am not converting my old Rails projects to Rails 3, but all of my new projects will use Rails 3. I might decide to update in the future, but I'm not eager to change code that works. I have tried Rails 3 using Ruby 1.9.2, 1.8.7, and Rubinius. I use Ruby 1.9.2 for the examples in this article and that is probably the best choice for you too. That said, I find myself using Rubinius for a lot of my day-to-day Ruby development, so I have also favored it for experimenting with Rails 3.

If you are already a Rails developer, there are very few new things that you need to learn before creating new Rails 3 projects. The rails command is used for all command-line actions. Default routes are not set up for you, but so far I have adjusted quickly to the new system, which definitely is an improvement. Most new features are about control over customization and you can get into customizing your Rails applications as needed over a period of time. This is the approach that I use.

Getting Set Up for Ruby on Rails 3 Development

I assume that you have Ruby 1.9.2 and Rails 3 installed either from the Rails download page instructions or have installed the Ruby Version Manager (RVM) and installed Ruby 1.9.2 and Rails 3:

bash < <( curl http://rvm.beginrescueend.com/releases/rvm-install-head )rvm 1.9.2rvm list # optional to show installed Ruby versions and which is the version being usedgem install rails

I will not recommend one editor over another, but I use TextMate on OS X, GEdit on Linux, and the e editor on Windows 7.

What's New in Rails 3?

The following sections highlight the most notable new features or updates in Rails 3.

New Router

As powerful as the new routing system is, you can also use it simply for common use cases. For example, if you create a scaffold like this:

rails generate scaffold Post title:string content:stringrake db:migraterm public/index.html

you can set your routes simply by adding this to your routes.rb file (which is generated empty):

resources :posts

In this simplest form, Rails 3 automatically maps HTTP verbs like GET and PUSH to controller actions like edit and list. You will see another simple routing setup later in the first example. If you are not familiar with Rails routing, here is a quick overview. Consider the URL http://localhost:3000/posts/4 as an example. Then if you define in your routes.rb file:

match "/posts/:id" => "posts#show"

The method show in the PostsController gets called, and the parameter hash params can be checked for the database row ID for the table post (for example, params[:id]> ). For the example class Post, you can also generate the proper URI from links using posts_path(@post.id) where I assume that you defined @post using something like @post = Post.find(4) .

New Bundler for Storing Dependencies Inside Rails 3 Applications

Before Rails 3 you had several options for managing gems and plugins and they all had problems. Yehuda Katz and Carl Lerche wrote the gem bundler to make the dependencies for each of your Rails applications more transparent and easier to manage.

When you use rails new my_new_project_name to create a new application, the rails command-line utility is run from your Ruby's bin directory. The rails command-line script knows the current Rails version and adds a dependency to the generated GemFile. By default only the rails and sqlite3-ruby gems are configured, but many comments are generated showing other common gems that you might want to add. Ignoring the comments, the generated GemFile will look something like this:

source 'http://rubygems.org'gem 'rails', '3.0.0'gem 'sqlite3-ruby', :require => 'sqlite3'

As you will see in later examples, running the command bundle install will ensure that the required gems are installed.

By using the source method, you can choose one or more repositories to use.

All View Output Is Escaped By Default

This is one change I really like. You no longer have to use the utility Erb method h to escape HTML generated from user input; this is now the default. You can override this default behavior with <%=raw don't escape this text - insecure!%> .

ActionController

Instead of using all of ActionController::Base you can use just the base class ActionController::Metal and add in just the modules you need. When the Merb and Rails projects merged, one of the main goals was to make Rails more modular like Merb. As you would expect from its name, ActionController::Metal is a "bare metal" controller that lets you use just what you need in your application.

For better efficiency, let's start with an example that uses only rendering. This example is initialized using:

rails new meta_render_only --skip-prototype --skip-active-record --skip-test-unitcd meta_render_onlyrails generate controller rubyinform public/index.htmlrails server

Generators have been rewritten for Rails 3. Also notice that you no longer run Rails utilities out of the local script directory. Rather, you pass arguments to the rails command-line script that was installed and placed on your PATH when you installed Rails 3.

So we have something to render, you can create the file app/views/rubyinfo/index.rhtml and add the following text:

<p>You are running Ruby version <%= ENV['RUBY_VERSION'] %> </p>

If you look at the config/routes.rb file that Rails 3 generated for you, you will notice that there are no routes set, so add this line:

root :to => "rubyinfo#index"

This makes the default "landing page" for the Web application call method index on the RubyInfo controller. Now if you hit http://localhost:3000 you will see the rendered page. However, we are still bringing in all controller modules, even the old Rails 2.x compatibility support. Change the Rubyinfo controller to use ActionController::Metal and for now, the Web app will be broken:

class RubyinfoController < ActionController::Metalend

A suggestion you may want to follow at a later time:

One of the great things about Rails is that it is quite possible to read through the implementation code. Admittedly, this is not as easy to do as it was 3 or 4 years ago. Still, I would urge you to at least understand where the Rails-specific gems are kept on your file system and get comfortable looking at some of the code. To get started, find where your ruby executable is kept and use the following pattern to find the gems: markw$ which ruby/Users/markw/.rvm/rubies/ruby-1.9.2-head/bin/rubyls /Users/markw/.rvm/rubies/ruby-1.9.2-head/lib/ruby/gems/1.9/gemsmate /Users/markw/.rvm/rubies/ruby-1.9.2-head/lib/ruby/gems/1.9/gems/*3.0.0* Figure 1 shows the TextMate editor open to my gem installation directory with Rails 3 gems.

Figure 1. My Gem Installation Directory with Rails 3 Gems (on TextMate) The Rails source code is well documented and the README files in top-level Module directories are useful and interesting. In my recent experiments with Rails 3 I found it useful to spend some time looking at specific code like the base controller code.

So, that is my suggestion, now back to exploring a bare-metal Web application. As I mentioned, after editing the RubyinfoController class to inherit from ActionController::Metal, this example is broken. We can fix it by editing the controller again to make it look like this:

class RubyinfoController < ActionController::Metal def index self.response_body = "Test rubyinfo bare-metal controller" endend

OK, that works, but is it perhaps too close to the metal? Let's add just enough to use RHTML templates. Here is the new controller code followed by the new contents of app/views/rubyinfo/index.rhtml:

class RubyinfoController <ActionController::Metal include ActionController::Rendering append_view_path "#{Rails.root}/app/views" def index @time = Time.now render endend<p>You are running Ruby version <%= ENV['RUBY_VERSION'] %> </p><p><%= @time %>

Since I am also an enthusiastic user of Sinatra and I used to use Merb for projects, the ability to customize Rails 3 -- especially to use only the things specifically required for a project -- is one of my favorite new features of Rails 3. That said, you might want to use the full ActionController::Base that loads everything (even Rails 2.x compatibility code) when you first get started.

Page 1 of 2