I bought 6 screencast episodes of Ruby metaprogramming about 3 years ago and only recently have I watched them all. And I must say: it was quite worth it! Just a reminder that it’s never too late 🙂 See below for my notes (which have been approved by Dave Thomas himself).
Episode 1 – Objects and Classes
- Think in terms of object-oriented programming instead of class-oriented programming
- 2 ways to change self:
- by a method call with an explicit receiver
- by a class or module definition
- a class does not have a name until you assign it to a constant
- classes are first-class objects
- class methods don’t exist: they are actually singleton methods on class objects
- method calling always works the same way. The ruby interpreter:
- identifies the class of the receiver
- looks up the method in that receiver’s class
- if it does not find it, it goes up the hierarchy, and looks up the method in the parent of that class
- retries the last step until it finds it
Episode II – Sharing Behavior
- Object#clone() copies the singleton method too; Object#dup() does not
- with a prototype, you can clone both the behavior and the state of an object (which is not the case with class-based inheritance)
- one can subclass from any expression, e.g. class A 0.5 ? B : C)
- do something in the singleton class like so: class << self ; # … ; end
- modules have 3 distinct usages:
- namespace
- to create module methods
- to create instance methods
- module methods can still be modified even if they have been included in a class, by reopening the module
- a method of an included module will come before a method defined in a superclass, during Ruby’s method lookup
- extend(module_name) actually opens the receiver’s singleton class and sticks the content of module_name in it
- reason because Person < ActiveRecord::Base is conceptually wrong: the intention is not to build a hierarchy (a “is a” relationship), but rather to share behavior
Episode III – Dynamic Code
- several ways to create blocks. Worth mentioning:
- lambda cares about arity (just like a method), proc does not
- a “return” inside a proc exits surrounding context
- a “return” in a lambda exits the lambda
- a “return” inside a do/end block will exist the surrounding context
- lambda : like an anonomymous method
- Proc.new : like inline code
- a Kernel method is available everywhere in our program
- eval() takes a string and evaluates it as ruby code
- a binding encapsulates:
- self,
- local variables (including method params)
- any associated block
- return stack
- a proc object always has an associated binding
- nothing prevents you from defining a method within a method
- define_method() is only available in modules and classes
Episode IV – instance_eval() and class_eval()
- instance_eval() can be called on any object
- class_eval() is an alias to module_eval
- class_eval() can only be called on classes and modules
- a method definition in class_eval() evaluates in the receiver’s class (thus creating instance methods)
- a method definition in instance_eval() evaluates in the receiver’s singleton class (thus creating class methods)
- “self” in a block of class_eval() or instance_eval() is set to the receiver’s
- include() is a private method
- you can change the “self” of a block with instance_eval(&block), e.g.
Episode V – Nine examples
- memoization: @memory[skus] ||= expensive_calculation()
- interesting examples making use of several metaprogramming techniques seen in previous episodes. If there’s one episode to watch, make it that one!
Episode VI – Some hook methods
- Struct.new returns a subclass of Struct
- method-related hooks:
- method_missing
- method_added
- singleton_method_added
- method_removed
- singleton_method_removed
- method_undefined
- singleton_method_undefined
- Class and Module hooks:
- inherited
- append_features
- included
- extend_object
- extended
- initialize_copy
- const_missing
- Marshalling hooks:
- marshal_dump
- marshal_load
- Coercion hooks:
- coerce
- induced_from
- to_xxx , e.g. to_s, to_proc
- advice: don’t forget to call the original method when writin code in the hook method to preserve behavior
You just convinced me to buy them!
Thanks for this post.