4.7. Metaprogramming

Metaprogramming is defined as the act of programming code that is able to manipulate itself (or other code). A practical example of this showed up a few sections ago when you used methods such as attr_accessor to dynamically obtain getter and setter methods for instance variables. The ability to reopen classes, define classes and methods conditionally, and call methods that get defined when first invoked, are all good examples of Ruby's metaprogramming abilities.

Metaprogramming is generally encouraged within the Ruby community partially because it's particularly useful when defining Domain Specific Languages (DSLs). Most Ruby developers would agree that powerful metaprogramming techniques should not be abused when writing code that may end up becoming "too clever for its own good," but that a decent mastery of the subject can really separate a beginner from a pro Ruby programmer.

Rails makes extensive use of metaprogramming in its implementation, and you are encouraged to explore the topic further on your own once you've gained more confidence with Ruby and Rails. Even as a Rails developer (as opposed to a Rails implementer), metaprogramming can be very useful, particularly when you want to start creating and publishing your own plugins. It's important to provide you with one quick example though, in order to understand how ActiveRecord's dynamic finders work. If you have a User model with a user_name and a password attribute, in ActiveRecord you could issue the following:

@user = User.find_by_user_name_and_password("acangiano", "secret")

No matter where you look in ActiveRecord's code you won't find a find_by_user_name_and_password method. This method gets created on the spot, the first time that it's invoked. This is mainly possible thanks to two metaprogramming features of Ruby: method_missing and the ability to dynamically define methods (this can be accomplished in more than one way).

method_missing is a special method that gets invoked whenever a requested method cannot be found by the receiver. By default method_missing will just raise a NoMethodError error, but it can be overwritten and customized to implement your own logic. The following example verifies that method_missing gets invoked when you send an undefined method name to the receiver:

class String
  def method_missing(method_id, *args)
    puts "Don't know how to handle #{method_id}."
  end
end

"A string".matz

The "A string" object doesn't know how to handle the method matz, so method_missing (the overwritten version) is invoked. This is the output of the preceding snippet:

Don't know how to handle matz.

The first argument of method_missing is a symbol that represents the name of the unknown method, and the second argument stores a variable number of arguments in the args variable (here they are called method_id and args, but they are just parameters; you can call them however you please). If you wanted to handle a possible block passed to the method, you could use the usual third &block parameter.

This example kept things simple and printed a message, but within that method_missing method you could write elaborate logic to handle situations where a method is unknown to its receiver. Behind the scenes that's exactly what ActiveRecord does with dynamic finders, and it employs class_eval, a method used to dynamically add methods to classes.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.176.166