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