Ninjas on a Penny Farthing

  posted  
Use Twitter? Follow on Twitter.

Rack::URLMap and kicking ass.


Rack come's with a lot of excellent middleware out of the box, making a lot of seriously cool things possible. One of the most important of these middleware's is URLMap - a little gem that lets you map different url paths to different rack applications. Using Rack::Builder / rackup syntax you can use the map method with a block to define a different urled application.

For example, we'll start by writing a little proc based app (thank's to the fact rack uses the call method) that will simple print the SCRIPT_NAME and PATH_INFO parts of the request.

our_test_app = lambda do |env| path = env['PATH_INFO'] script = env['SCRIPT_NAME'] out = "<strong>Path:</strong>#{path}<br /><strong>Script:</strong>#{script}" return [200, {"Content-Type" => "text/html"}, [out]] end

Next, we're going to map a few different url's to this application to see the effect each has. Using Rack::Builder / rackup format, we can simply write a standard application declaration (e.g. using use / run etc) under a block calling the map method. e.g.:

map "/" do use Rack::CommonLogger run our_test_app end

Or alternatively, for the hell of it, we decide we want to also mount Rack::Lobster under '/lobster'. Note here though as well that we might get exceptions so we also add in the Rack::ShowExceptions middleware.

map "/lobster" do use Rack::ShowExceptions run Rack::Lobster.new end

Now, finally, we're going to mount a couple more test app's. It's important to note Rack::URLMap will check longer urls first.

map "/ninjas" do run our_test_app end map "/aloha" do run our_test_app end map "/ninjas/second_app" do run our_test_app end map "/lobster/another-app" do run our_test_app end

Now that we've done that, we should have the following app's available:

  1. our test application mounted at "/ninjas", "/aloha", "/ninjas/second_app", "/lobster/another-app" and "/"
  2. Rack::Lobster mounded at "/lobster"

Now, we can combine all of this into one file that you'll need to save as a rackup file - in my case, "rack-urlmap-demo.ru" (it needs the .ru extension for rack up to work correctly). So you don't have to copy it all again, I've put it all below:

require 'rack/lobster' our_test_app = lambda do |env| path = env['PATH_INFO'] script = env['SCRIPT_NAME'] out = "<strong>Path:</strong>#{path}<br /><strong>Script:</strong>#{script}" return [200, {"Content-Type" => "text/html"}, [out]] end use Rack::Lint map "/" do use Rack::CommonLogger run our_test_app end map "/lobster" do use Rack::ShowExceptions run Rack::Lobster.new end map "/ninjas" do run our_test_app end map "/aloha" do run our_test_app end map "/ninjas/second_app" do run our_test_app end map "/lobster/another-app" do run our_test_app end

Observant readers will notice I also added in the Rack::Lint middleware - which it's important for you to note that this will be run for every application. Now, start the app by changing to where you saved the file in your terminal and type "rackup rack-urlmap-demo.ru". Now, I want you to hit each url we mapped (it should be running on localhost:9292), esp. the "/lobster" and "/lobster/another-app" AND "/lobsters". What do you so? If all goes well, you should have learnt the following:

  1. "/lobster/another-app" is longer than "/lobster" hence it over rides it.
  2. "/lobsters" is not the same as "/lobster".

And there you have it. Rack Middleware combined with Rackup makes it super easy to do cool stuff with your request e.g. multiple applications on one domain.