I <3 Rack
Rack is great for thin webservices or simple sites, it even has simple support for dealing with sessions, authentication, OpenID and more.
Where Rack really starts to break down is when you want to do any kindof non-trivial routing.
So, what are some ways we can add routing to our Rack apps?
case / when
The simplest routing, ofcourse, is via simple case statements. Any Rack app is likely to start out with some kindof simple routing, like this:
1 #! /usr/bin/env ruby 2 # 3 # example of case/when style rack routing 4 # 5 %w( rubygems thin ).each {|lib| require lib } 6 Rack::Handler::Thin.run lambda { |env| 7 path = env['PATH_INFO'] 8 case path 9 10 when '' 11 when '/' 12 [200, {}, "routed #{path} to index"] 13 14 when /chunky/i 15 [200, {}, "bacon!"] 16 17 else 18 [200, {}, "route not found for #{path}"] 19 20 end 21 }
Rack + Merb Router == DIY DSL
Simple case/when routing logic is great. But what if we want to do more complicated routing logic?
The Merb Router comes out of the box with all kinds of cool functionality. Did you know that you can even perform authentication in the Merb Router? And you can return directly from it without calling any controllers or anything?
Watch some of the MerbCamp Videos to see the awesomeness of the Merb Router.
So, what if you want to delegate routes to classes and methods easily? Or build your own web framework / DSL? The Merb Router can be used outside of Merb to help you do this. Essentially, all the Merb router really does it help you may URIs to hashes and then you can do whatever you want to with the hash.
While this example is obviously much longer than the simple case/when, you can see how much more powerful this can be. Being able to easily map URIs to hashes and then … do whatever!
1 #! /usr/bin/env ruby 2 # 3 # example of rack routing using the merb router 4 # 5 %w( rubygems thin merb-core/dispatch/router extlib ).each {|lib| require lib } 6 7 # we need to call *something* to render our routes, so let's make some little classes 8 class Actions 9 def self.index 10 "hello from index!" 11 end 12 end 13 class MoreActions 14 def self.default 15 "route not found" 16 end 17 def self.chunky 18 "bacon!" 19 end 20 end 21 22 Rack::Handler::Thin.run lambda { |env| 23 24 # setup some routes 25 Merb::Router.prepare do 26 match('/').to :class => 'Actions', :method => 'index' 27 match(/\/chunky/).to :class => 'MoreActions', :method => 'chunky' 28 end 29 30 # execute the merb router, getting back a hash for the found route 31 request = Struct.new(:path, :method).new(env['PATH_INFO'], :get) 32 route = Merb::Router.match(request).last 33 34 # ok, we got a hash back for our path ... do something with it! 35 if route.empty? 36 response = MoreActions.default 37 else 38 response = Kernel.const_get(route[:class]).send route[:method] 39 end 40 41 [200, {}, response] 42 }
If you’re anything like me, you look at the above code and think “man, it sure would be easy to make a nice purty DSL like Sinatra’s this way!” and you would be right.
Who needs Metal and all that jazz? Rack’s awesome all by itself :)
PS. thanks to Marc Chung for the idea of using the Merb router outside of Merb