Rack RESTful Dispatcher
Quoting the authors,
Rack
provides a minimal, modular and adaptable interface for developing web applications in Ruby. By wrapping HTTP requests and responses in the simplest way possible, it unifies and distills the API for web servers, web frameworks, and software in between (the so-called middleware) into a single method call
.
To test the theory in practice, I put together a superficial interface for writing RESTful HTTP services by implementing any of the DELETE, GET, POST, PUT, etc HTTP verbs in Ruby classes.
require "rubygems" require "rack" module RestfulDispatcher def call(env) request = Rack::Request.new(env) dispatcher = dispatcher_class.new(request) body = dispatcher.send(request.request_method.downcase) [200, {'Content-Type' => dispatcher.content_type}, body] end def dispatcher_class @dispatcher ||= Class.new(self.class) do attr_accessor :content_type def initialize(request) @request, @content_type = request, 'text/xml' end end end module SingletonMethods def start(handler, host, port) handler.run Rack::Lint.new(self.new), :Host => host, :Port => port end end def self.included(receiver) receiver.extend SingletonMethods end end
Let's examine the example one method at a time.
All Rack applications must implement one method -
call
- which accepts one argument, the
environment,
which encapsulates data relevant to the HTTP roundtrip. The
call
method must return an array of 3 items: a greater than 100 integer representation of the response status, a hash holding name/value pairs representing the response's header entries and an array of strings - the body of the response.
In the case of the
RestfulDispatcher
module,
call
first wraps
env
in
Rack::Request
,
a convenient and stateless interface to the Rack environment. We then create a new instance of a dispatcher class passing it the Rack Request. We will revisit this in more detail when talking about the
dispatcher_class
method. Instantiating a new dispatcher to handle the request should keep things thread safe. We then call a method on the dispatcher instance corresponding to the HTTP verb included in the request. This call should return the response body. Finally, adhering to the Rack standard, we return an array containing the response status code, headers and body.
dispatcher_class
creates a new class by subclassing the service we will be defining and giving it a constructor that accepts a request object. We also expose the content_type field, in case we want to override it anywhere in our service's implementation.
Finally, we provide a
start
singleton method which we can call to start the service.
Mixing the
RestfulDispatcher
module in a class will effectively enable the class to act as a standalone RESTful service. All we need to do is implement instance methods that correspond to the HTTP verbs we want the service to respond to.
class FooService include RestfulDispatcher def get "<test></test>" end end require "thin" FooService.start Rack::Handler::Thin, '127.0.0.1', 2323
Rack::Request#GET conveniently returns the data received in the request query string (e.g
http://127.0.0.1/?key=hello
)
as a hash.
Thanks to Rack's modular nature, switching from Thin to Mongrel is as easy as replacing the last two lines of the code above with:
require "mongrel" FooService.start Rack::Handler::Mongrel, '127.0.0.1', 2323