Channels ▼

Nick Plante

Dr. Dobb's Bloggers

Roll Your Own Web Framework

March 18, 2008

In my last post we talked about a variety of Ruby web app frameworks. If you're disgusted by the recent proliferation of options (and who doesn't hate options?), you can blame Rack, as it provides the foundation for many of these upstarts.

Rack is to Ruby as WSGI is to Python; a clean, minimalist interface between web frameworks and web app servers. What this means to you, Joe Average Ruby Developer, is that you can use Rack to build your own frameworks and tools with ease, and then deploy them without writing server specific interfaces for Mongrel, WEBrick, or FastCGI (or Thin, Ebb, and Fuzed!). It'll just work.

Why would you want to build your own web framework? That's certainly a good question. There are lots of choices out there now, and there's a good chance that one of them fits your needs. On the other hand, perhaps you have a specific requirement or an idea for a wholly different approach, or maybe you just want a better understanding of how the existing solutions work. You can also use Rack to implement fast lightweight request processing tools, for when you don't need the overhead of a more full-featured framework. So it's definitely worth learning about.

Anyway, the beauty of Rack is that it basically distills the interface between web frameworks and servers down to a single method, #call. This method must be implemented in any web framework or application you want to build on Rack. The #call method always receives a hash of environment details as an argument and needs to return an array containing a status, headers, and a response body. Rack makes this even easier on us by providing convenient helpers like Rack::Request and Rack::Response.

The following (rack_test_app.rb) is a super-simple Rack application. To execute it, you'll need to have both Rack and Mongrel installed, of course (gem install rack mongrel).

{geshibot lang="rack"}require 'rack'

class RackTestApp
  def call(env)
    @request = Rack::Request.new(env)
    @response = Rack::Response.new

    #answer(@request.path_info) # look up the route and process the request

    @response.write("Hello World! My path is #{@request.path_info}")
    @response.finish
  end
end

Rack::Handler::Mongrel.run(RackTestApp.new, {:Host => "127.0.0.1", :Port => 8080}){/geshibot}

This quick script kicks off a Mongrel process listening to port 8080 on localhost and will hand all requests that it receives to the class we've created. Rack makes sure that each request triggers the #call method, and we use the request and response helpers to wrap that request (we don't have to do this, but they're awfully nice conveniences). We can use @response to iteratively generate the response body, which is buffered until @response.finish is called, at which point the response is delivered to the client.

Run this code and visit http://127.0.0.1:8080/foo/bar in a browser, and you should see a simple "Hello World" message followed by the path you used to access it (just to show how the @request object can be used). So, although we're not going to be giving Merb or Rails a run for their money just yet, this is certainly a good starting point for further exploration.

If you want to build something that resembles a reusable framework, your next thought might turn to routing, and how you could direct different requests to different methods for handling. For a first attempt, you might choose to define your routing endpoints inline with the responses using a syntax like

answer "/foo/bar" { @response.write("This is the foo bar page") }  

in your framework client code. The framework class could read this and use it to dynamically define responder methods and add route/method pairs to a routing Hash for later retrieval. Then, when a URL is requested, the corresponding method could be looked up in a Hash and invoked to handle the request. You could parse URLs into component parts as well, locating controller names, action names, resource identifiers, and so on.

In any case, these are just a few ideas to get you started thinking about the sort of things you can build with Rack. If you're interested in further examples, you can check out DialTone, a simple "microframework" I threw together in just a couple hours a few nights ago.

At a whopping 70 lines of code, DialTone isn't anything you'll want to use for serious work (heh), but it's useful as an easily-grokked educational tool. If nothing else, it should demonstrate exactly how much you can do with so little by leveraging a common HTTP interface like Rack.

The source is available at http://github.com/zapnap/dialtone.

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 


Dr. Dobb's TV