While wrapping up the second week of Hacker School and finishing Constantin’s “Sinatra: Up and Running”, I decided to peer under the hood of Sinatra itself and investigate how the magic unfolded. I was quite pleased when I ran into one of my favourite Ruby black-magic methods: define_method.
Prima facie, it may rather deceptively appear that Sinatra’s basic HTTP route definitions (get ‘/’, post ‘/’, put ‘/’, delete ‘/’) are method definitions, but au contraire- they are method calls deep inside the Sinatra lair. Let’s take a peek.
1 2 3 |
|
Inside this ‘get’ method call, self
is actually an instance of the class “Sinatra::Application”, which means our routes are passing blocks to instances of “Sinatra::Application”.
But who, if we may entreat, is the parent of “Sinatra::Application”? Well, none other than “Sinatra::Base”.
And who is the parent of Base? Object! (::Cue spooky voice:: And who, pray tell, is the parent of Object? BasicObject! But you knew that already, didn’t you, good Rubyist?)
Methods like our ‘get’, ‘post’, ‘put’, and ‘delete’ are actually defined twice; first in the module “Sinatra::Delegator”, as a mixin extending Object. Because Object is extended, the methods are now readily available at the top level, throughout our entire application. The module Sinatra::Delegator sends the method calls to Sinatra::Application, which inherits them from Sinatra::Base.
And now for a crash course in Ruby metaprogramming! Look at the following example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
Pulling up Sinatra’s main.rb file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
‘main.rb’ simply creates the Sinatra module, which is just subclassed from Sinatra::Application from Sinatra::Base. By the way, isn’t it neat to see all the options available for our disposal (changing ports!!!)? Hm, see the include Sinatra::Delegator
mixin at the end? The Delegator mixin wields most of the magic in Sinatra…
And if we may allow ourselves to politely peek under the covers of Delegator:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
So, how does ROUTING essentially work?
When unsuspecting websurfer Rachel Rabblerouser visits our app at “http://our_ineffably_splendiferous_app.com/”, her browser (Omniweb, because she’s a l337 hacker too cool for school Firefox) issues an HTTP ‘GET’ request for the resource (our website), our instance of Sinatra will take heed of the HTTP verb + path + code block (ie. get '/' do; "buy me falafels, please"; end
), register this in Application, and accordingly- execute it..