Advanced Rails - Building Industrial-Strength Web Apps in Record Time

(Tuis.) #1
Metaprogramming Techniques | 23

A classic example of closures is themake_counterfunction, which returns a counter
function (aProc) that, when executed, increments and returns its counter. In Ruby,
make_counter can be implemented like this:


def make_counter(i=0)
lambda { i += 1 }
end

x = make_counter
x.call # => 1
x.call # => 2

y = make_counter
y.call # => 1
y.call # => 2

Thelambdafunction creates a closure that closes over the current value of the local
variablei. Not only can the variable be accessed, but its value can be modified. Each
closure gets a separate instance of the variable (because it is a variable local to a par-
ticular instantiation ofmake_counter). Sincexandycontain references to different
instances of the local variablei, they have different state.


Metaprogramming Techniques


Now that we’ve covered the fundamentals of Ruby, we can examine some of the
common metaprogramming techniques that are used in Rails.


Although we write examples in Ruby, most of these techniques are applicable to any
dynamic programming language. In fact, many of Ruby’s metaprogramming idioms
are shamelessly stolen from either Lisp, Smalltalk, or Perl.


Delaying Method Lookup Until Runtime


Often we want to create an interface whose methods vary depending on some piece of
runtime data. The most prominent example of this in Rails is ActiveRecord’s attribute
accessor methods. Method calls on an ActiveRecord object (likeperson.name) are trans-
lated at runtime to attribute accesses. At the class-method level, ActiveRecord offers
extreme flexibility: Person.find_all_by_user_id_and_active(42, true)is translated
into the appropriate SQL query, raising the standardNoMethodErrorexception should
those attributes not exist.


The magic behind this is Ruby’smethod_missingmethod. When a nonexistent method
is called on an object, Ruby first checks that object’s class for amethod_missing
method before raising aNoMethodError.method_missing’s first argument is the name of
the method called; the remainder of the arguments correspond to the arguments passed
to the method. Any block passed to the method is passed through tomethod_missing.
So, a complete method signature is:

Free download pdf