Can someone explain how the following Ruby code works? (taken from gist: 675667)
require 'rubygems'
require 'rack'
class Object
def webapp
class << self
define_method :call do |env|
func, *attrs = env['PATH_INFO'].split('/').reject(&:empty?)
[200, {}, [send(func, *attrs).to_s]]
end
end
self
end
end
Rack::Handler::Mongrel.run [].webapp, :Port => 9292
# ^^^^^^^^^^^
# | (x)
# ROFLSCALE DB ---/
#
If we run it, we can access it over the Web:
GET http://localhost:9292/push/1 -> 1
GET http://localhost:9292/push/2 -> 12
GET http://localhost:9292/push/3 -> 123
GET http://localhost:9292/to_a -> 123
GET http://localhost:9292/pop -> 3
GET http://localhost:9292/shift -> 1
Of course, we can run something like:
GET http://localhost:9292/instance_eval/exec("rm -rf /")
Anyways... how does it work? Can you walk me through the code step-by-step?
The class Object
is the base class for all objects in Ruby. A new method webapp
is defined on this, which makes it callable for all objects.
When calling webapp
, the method self.define_method
is called on the objects class (But only for that particular object - That's called a meta-class, by the way). This defines a new method call
for its instance (Eg. the object).
This new call
method takes env
as argument and splits PATH_INFO
by forward-slashes and stores in an array. The first element is then assigned to a variable func
, and the remainder to a variable attrs
. Then the magic method send
is invoked, which basically calls a method by the name of the variable func
. It then returns an array, consisting of a status-code (200), an empty hash and the output of the method-call.
On the final line, a new array instance is created ([]
is shorthand for Array.new
). Then the method webapp
is called on it, which enriches it with the call
method, as explained above. webapp
conveniently returns self
. So you can pass it directly to Rack::Handler::Mongrel.run
, which will launch a web server (Mongrel is the web server - Rack is an abstraction-layer that gives different web servers a uniform interface). The server will pass requests to the call
method, and interpret the return value to send a response back.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With