Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exposing any Ruby object over the Web

Tags:

ruby

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?

like image 617
Rubyist Avatar asked Dec 22 '22 21:12

Rubyist


1 Answers

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.

like image 182
troelskn Avatar answered Mar 08 '23 08:03

troelskn