One of the greatest features in Rails 2.3 is the Rails Metal piece. It’s part of the goodness that came out of the Rails/Merb merge.

Metal provides a layer of functionality that is executed before or in the place of your rails application. The common term for this type of software is middleware. There is a terrific presentation that was given at Mountain West RubyConf 2009. You can actually view the video of it here.

Here’s a simple example of how to use metal to require authentication before accessing pages on the site.

First, in your rails application’s root directory, generate some metal:

script/generate metal authentication

If you look in your app folder, you’ll see a new folder called metal. Inside that folder, you’ll see a file called authentication.rb.

cd app/metal
ls

Open authentication.rb and look inside:

vim authentication.rb

You’ll see contents like this:

1
2
3
4
5
6
7
8
9
10
11
12
# Allow the metal piece to run in isolation
require(File.dirname(__FILE__) + "/../../config/environment") unless defined?(Rails)
 
class Authentication
  def self.call(env)
    if env["PATH_INFO"] =~ /^\/authentication/
      [200, {"Content-Type" => "text/html"}, ["Hello, World!"]]
    else
      [404, {"Content-Type" => "text/html"}, ["Not Found"]]
    end
  end
end

The call method is what is executed before your rails application. In this case, if the user goes to http://yourrailsapp.com/authentication, it’ll return the text “Hello World!” Isn’t rails great!

It is, but “Hello World!” isn’t very impressive. So, let’s make it do something interesting. Let’s assume that your rails app is authenticated when there’s a user_id variable in your session. So, let’s access the session, and check if there’s a user_id in it.

First, let’s initialize the session variable.

1
2
3
  def initialize(env)
    @session = env['rack.session']
  end

Then we’ll update our call method to check the session.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
require(File.dirname(__FILE__) + "/../../config/environment") unless defined?(Rails)
 
class Authentication
  def initialize(env)
    @session = env['rack.session']
    @current_user = User.find_by_id(@session["user_id"]) if @session["user_id"]
  end
 
  def self.call(env)
    # Create a new instance, which initializes our session and current user and processes the call.
    Authentication.new(env).process_call
  end
 
  def process_call
    if @current_user
      # A 400 status (Not Found) passes the call on to the next piece in the stack,
      # which can be another metal piece, or your rails application.
      [404, {"Content-Type" => "text/html"}, ["Not Found!"]]
    else
      # Redirects to login if there is no logged in user
      [302, {'Location'=> '/login' }, []]
    end
  end
end

If you implement this metal, you’ll find a problem. If you’re not logged in, you’ll get caught in a loop where you’re being redirected login over and over. To fix this, you’ll have to add an exception for the login page.

1
2
3
4
5
6
7
8
9
10
  def process_call
    if @current_user || env["PATH_INFO"] == "/login"
      # A 400 status (Not Found) passes the call on to the next piece in the stack,
      # which can be another metal piece, or your rails application.
      [404, {"Content-Type" => "text/html"}, ["Not Found!"]]
    else
      # Redirects to login if there is no logged in user
      [302, {'Location'=> '/login' }, []]
    end
  end

I could go on, but I’ll leave it up to you to figure out how to only protect certain paths or to add role based permissions to your users. In any case, I’m certain you can see the power of Rails Metal. Overall, it allows you to implement simple functions and allow them to execute extremely quickly without loading all of the extra stuff that Ruby on Rails provides.

  • DZone
  • Twitter
  • Slashdot
  • Delicious
  • Digg
  • Technorati Favorites
  • Facebook
  • Reddit
  • StumbleUpon
  • LiveJournal
  • Squidoo
  • Google Bookmarks
  • LinkedIn
  • Share/Bookmark

1 Comment »

chris

May 2, 2009

Very interesting!

“… I could go on, but I’ll leave it up to you to figure out how to only protect certain paths or to add role based permissions to your users. …”
Please “go on” … authentication is something I would like to get right.
thanks

Leave a comment