Delegating methods using extend
A change from Rails today, we’re dipping into Sinatra to look at how we can use extend to implement a Delegator.
A delegator is an object which delegates responsibility to another object. Sinatra uses it to delegate methods called on the global scope to another object which can handle them.
Sinatra
Sinatra allows us to write code like this:
get "/hello"
return "Hello There!"
end
The get method appears to be defined in the global scope. In fact, the methods are namespaced nicely in a class called Application. The global scope is delegating method calls to the Application class.
The Delegator forwards method calls to another class
If we look inside Sinatra/Base we’ll find this dandy little action ninja:
module Delegator #:nodoc:
def self.delegate(*methods)
methods.each do |method_name|
define_method(method_name) do |*args, &block|
return super(*args, &block) if respond_to? method_name
Delegator.target.send(method_name, *args, &block)
end
private method_name
end
end
delegate :get, :patch, :put, :post, :delete, :head, :options, :link, :unlink,
:template, :layout, :before, :after, :error, :not_found, :configure,
:set, :mime_type, :enable, :disable, :use, :development?, :test?,
:production?, :helpers, :settings, :register
class << self
attr_accessor :target
end
self.target = Application
end
This module defines a whole load of methods using define_method. If you look though, all of these methods send a call to the Delegator.target which, which in this case is the Application class.
Rocking it in on the global scope
Now Sinatra needs to mix these methods in to the global scope, so they are available everywhere. If you remember from last time we add methods to an object (rather than to a class) using extend.
From the global scope we can now simply do this:
extend Sinatra::Delegator
This will extend the global scope object (pointed to by self) with the methods in the mixin. We can now call all of these methods on the global scope, and the delegator will forward them nicely onto the Application class.