Close and Go BackBack to Viget

Rack Support in Rails: Why It Matters

Ben Scofield
Ben Scofield, Technology Director, May 19, 2009 2

As I’ve mentioned elsewhere, my RailsConf session on Rack support in Rails went very well. I’m not planning on giving this talk again, however, so I thought I’d write a quick summary of it for people who are interested in a little more detail than the slides can provide.

My main motivation in giving this talk was to call attention to what I feel has been the most important change in Rails over the past year – its move to become Rack-compatible. The initial work on this was done by Ezra almost a year ago, but it only made it into a stable release with 2.3. This delay meant that it stayed a bit under the radar, and even the addition of Rails Metal didn’t really pull it into the spotlight. Then, in December of 2008, the announcement of the Merb-Rails merger pretty well decided the story of the year for Rails.

So What?

So, why is Rack support so important? Why should it be recognized as more vital to the success of Rails than the merger of the two most popular Ruby web frameworks? Rack is just a common API for Ruby web frameworks, after all, and as such isn’t as flashy as, say, Merb’s router.

The answer is just that Rack is a common API for Ruby web frameworks. It’s essentially the Ruby version of Python’s WSGI, specifiying a simple interface for handling HTTP requests in Ruby. The simplest Rack-compatible web application would look something like this:

class SimpleRackApp
  def call(env)
    [
      
      status,  # 200
      headers, # {"Content-Type" => "text/html"}
 
      body     # ["..."]

    ]

  end
end

To be Rack-compatible, an application need only have a call method that accepts a hash of environment data (including, for instance, the HTTP headers included in the request) and returns an array of an HTTP status, a hash of headers, and a body that responds to .each.

Standardizing the interface for web applications means that you can link them together in interesting and useful ways – leading us to Rack middleware. Middlewares are just Rack-compatible applications that intercept and manipulate a request or response; a middleware could, for example, rescue any unhandled exceptions from your application and display a friendly error page.

Rack in Rails

As one of the results of the integration of Rack into Rails, many of the functions of ActionController have been refactored into middlewares, which means they can be required into non-Rails applications and used. As of today, these middlewares live in the ActionDispatch module, and include classes like ActionDispatch::Failsafe, ActionDispatch::ShowExceptions, and ActionDispatch::ParamsParser (among others). Normally, these will be automatically required when you run your Rails application through script/server or Passenger, but you can control their inclusion or replace them entirely with environment.rb; see the Rack guide for more details.

Rails Metal is another of the results of the move to Rack. Metal provides a way to bypass some of the normal overhead of the Rails stack, allowing a developer to specify certain routes and code to execute when those routes are hit. Metal actions avoid the entirety of ActionController, and so are somewhat faster than standard Rails actions (though this performance benefit may not always be significant).

Tools and Techniques

All of this is just background, though; the real argument in favor of the importance of Rack is found when you look at what you can now do. In the session at RailsConf, I presented two such examples: Rack::Bug and progressive caching. Rack::Bug is a middleware, and some great things have been written on it recently. Progressive caching is a technique that uses Rails Metal to respond to AJAX requests, making page caching a more generally useful strategy (it is also one of the other talks I give, and I’ve written on it in a couple of places).

During the final part of the section, I showed how to write a couple of middlewares that might be generally useful: Rack::Embiggener and Rack::Hoptoad. The latter is obviously a port of the hoptoad_notifier plugin to a Rack middleware (and indeed, this is a direction that the thoughtbot team is considering – something I’m really looking forward to!). The former, however, may be a more interesting example. Rack::Embiggener is a middleware that intercepts the response from a Rack application, detects any TinyURLs embedded in it, and replaces them with the expanded URL. An obvious use-case for this is Twitter, where tweets could be stored with TinyURLs in place (remaining under the 140 character limit), while they are displayed with the full URL visible regardless of the length1.

module Rack
  class Embiggener
    def initialize(app)
      @app = app
    end
    
    def call(env)
      status, headers, body = @app.call(env)
      headers.delete('Content-Length')
    
      response = Rack::Response.new(
        Rack::Embiggener.embiggen_urls(body),
        status,
        headers
      )
    
      response.finish
      return response.to_a
    end
        
    def self.embiggen_urls(original_body)

     new_body = []
      
      original_body.each { |line|
        tinys = line.scan(/(http:\/\/(?:www\.)?tinyurl\.com\/(.{6}))/)
  
     new_body << tinys.uniq.inject(line) do |body, tiny|
    
     original_tiny = tiny[0]
    
     preview_url = "http://preview.tinyurl.com/#{tiny[1]}"
    
     response = Net::HTTP.get_response(URI.parse(preview_url))
    
     embiggened_url = response.body.match(/redirecturl" href="(.*)">/)[1]
    

    body.gsub(original_tiny, embiggened_url)
  
     end

     }

     
      new_body

   end
  end

end

Walking through the code:

  • #initialize stores the Rack application object in a variable for later use.
  • #call passes the request to the application stored in @app, and records its responses. It then deletes the Content-Length header (since we’ll be enlarging some URLs), and creates a new Rack response with the same information, passing the original body through the embiggen_urls method. The response.finish call ensures that the appropriate headers (including a new Content-Length) are set.
  • embiggen_urls accepts a body (that, per the Rack specification, responds to .each), and (inefficiently) scans over it to find and replace the TinyURLs2.

With this middleware, then, any Rack-compatible application – be it Twitter, some other Rails 2.3 application, a Sinatra microapp, or even something written in Camping – can replace TinyURLs with their expansions.

1 More interestingly, Twitter or another service might be able to replace the full ‘http:/tinyurl.com/[foo]’ with just ‘[foo]’ and a single-character flag of some sort, providing even more storage savings.

2 This code was just meant to inspire people by showing something that could be done, and isn’t meant to be particularly efficient or good. Feel free to fork and improve it!

Wrapup

One of my favorite sayings comes from Socrates: “A list is not a definition.” My presentation (and this post) argue for the importance of Rack in Rails, but all they really do is give a list of examples of useful tools and techniques. My final point, then, is this: Rack compliance makes Rails a first-class member of the community of Ruby web frameworks (contrast that with Django, which implements a non-standard version of WSGI). This means that work done for Rails, such as the refactoring of parts of ActionController into ActionDispatch’s middlewares, benefits both Rails developers and users of other frameworks, and vice versa. Rack unites the fragmented Ruby web framework ecosystem, which means that no matter the community in which we work, our efforts improve the whole rather than just a single piece. As we’ve seen in open source time and again, the more people working on a set of problems, the better.

Provisional and Repo Man: Automated Bootstrapping

Mark Cornick
Mark Cornick, Web Developer, May 13, 2009 0

If we’ve said it once, we’ve said it many times: Don’t Repeat Yourself.

Continue reading "Provisional and Repo Man: Automated Bootstrapping"

JSConf 2009 Recap: Javascript at the Edge

Brian Landau
Brian Landau, Web Developer, May 12, 2009 1

Two weeks ago, I spent an amazingly full two days at JSConf 2009 getting my mind blown by some JavaScript developers who are pushing the boundaries of the language. Thanks to having so many great people there, we all had a great time discussing ideas and learning from each other. Having a smaller number of attendees really enhanced this aspect of the conference and allowed people to discuss the ideas more deeply.

Beside the discussions of newer Javascript Frameworks (like Cappuccino/Objective-J and SproutCore), the main theme that I think threaded its way through a lot of the conference was using javascript outside the browser. Also, everyone I met was fired up about Javascript and exploring what new things we can do with it.

Continue reading "JSConf 2009 Recap: Javascript at the Edge"

RailsConf 2009 Wrapup

Ben Scofield
Ben Scofield, Technology Director, May 11, 2009 0

I've been back from RailsConf for a few days now, and I've had a chance to reflect on the things I learned while in Las Vegas for what turned out to be my favorite RailsConf yet. Elsewhere, I wrote about my overall impressions, but for this post I want to focus on several of the sessions I attended and participated in.

Teaching Rails BoF

RailsConf for me really started Monday night, with the Teaching Rails Birds of a Feather session that I moderated. We had a great turnout (somewhere in the neighborhood of 30 people), and the attendees ranged across a number of different disciplines — some people were teaching in a university setting, some were writing books, and others were interested primarily in building the community through educating new developers. Regardless of their backgrounds, though, everyone had good ideas and experiences to share, and I learned about a number of exciting new efforts (including the various components of the RailsBridge effort and the upcoming Rails Tutorials project).

The Even-Darker Art of Rails Engines

Tuesday, I attended James Adam's session on Rails Engines. I heard great things about his presentation on plugins from the 2008 RailsConf, so I had high hopes about this one. As it turned out, the presentation itself was decent, but the content was a little too basic for me — we've looked fairly carefully at the engines support in Rails as the next step for our resourceful plugins, so most of what James talked about I'd already seen. I do have to say that the DHH-headed cherub slide was a real winner, though.

Continue reading "RailsConf 2009 Wrapup"

Backup your Database in Git

David Eisinger
David Eisinger, Web Developer, May 08, 2009 20

Short version: dump your production database into a git repository for an instant backup solution.

Long version: keeping backups of production data is fundamental for a well-run web application, but it’s tricky to maintain history while keeping disk usage at a reasonable level. You could continually overwrite the backup with the latest data, but you risk automatically replacing good data with bad. You could save each version in a separate, timestamped file, but since most of the data is static, you would end up wasting a lot of disk space.

Continue reading "Backup your Database in Git"

We're the Developers

at Viget Labs. We write about web development trends, tips, best practices, industry events, and our projects — all with an emphasis on Ruby on Rails.

Recent Comments

For translating strings you can use Rails I18n backend instead of using inflectors.

The `typus_human_name` is a patch to fix a problem in `human_name` [1].

[1] https://rails.lighthouseapp.com/projects/8994/tickets/2120-humanize-and-human_name-dont-separate-words

Contact Us

Have any questions, comments, ideas, or secrets to share? Let us know.


Sorry, you need to have Javascript enabled to use this form. (Don't blame us, blame the spammers!) If you'd like to contact us, please visit our Contact page.