I'm probably missing something painfully obvious here, but I can't seem to find an answer, or work it out myself. In Sinatra, they have a self.get
method, which captures blocks, when a block is called, you're able to use the request
variable inside, how is this possible?
Sinatra
module Sinatra
class Base
class Request < Rack::Request
end
attr_accessor :request
def call!(env)
@request = Request.new(env)
end
class << self
def get(path, opts = {}, &block)
...
end
end
end
end
App
class App < Sinatra::Base
get '/' do
puts request
end
end
Wow. You piqued my curiousity, and sure enough, researching this was facinating. The magic starts in the
compile!
method defined at: https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L1529Notice we turn the block passed to
get
(or any route function) into an unbound method via generate_method (defined a few lines above). We then store a proc which takes two parameters, an object to bind the method to, and a list of arguments, which the method is called with.Skip ahead to
process_route
: https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L971Theres a lot going on here, but the key is:
This calls the stored block above with self, and the appropriate arguments. Thus the unbound method is bound to whatever
process_route
is bound to( the currentself
inprocess_route
). And what isprocess_route
bound to? An instance ofSinatra::Base
, which as we know has an attribute accessorrequest
that can now be reached in your original block. Tada!