开发者

Exposing any Ruby object over the Web

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开发者_如何学C, 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.


Not sure how familiar you are with ruby - let me know what doesn't make sense.

Lines 1 and 2 import the libraries needed to start a webserver.

Then on line 4, the base class Object is reopened to add a new method to it - you can do this anywhere you like in ruby. All classes inherit from Object, so whatever methods you define here can be called on any object.

Line 5 begins defining a new method for all objects, called webapp.

Line 6 and 7 define a method call for each instance of Object that is instantiated. I think this is basically equivalent to using self.call to define the method.

Lines 8 takes the relative part of the URL (eg push/1 or to_a) and splits it into two parts, (ie func and a variable length string of attrs).

Line 9 then returns a raw HTTP response, comprised of the status code (200), headers (empty in this case: {}), and body ([send(func, *attrs).to_s])

The body is comprised of the response of the object to the method func, with the arguments attrs. So if the URL requested was push/1, the method push would be called with the argument 1. This is equivalent to [push(1).to_s]. The to_s converts the value that is returned by the method to a string.

Finally, on line 16, a webserver is started on port 9292. The object that the webapp method is called on is an empty array - you could use a String (ie ''), or a Hash (ie {}) or a Proc (ie Proc.new) or any ruby class you like.

You can then manipulate the object by hitting URLs of the form func/attribute1/attribute2/attribute3..., and the object will call the method func with the string of arguments attribute1, attribute2, attribute3....

Interesting code!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜