Ruby Object Model and Metaprogramming Screencasts by Dave Thomas

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.
[gist https://gist.github.com/cawel/95f6ff6630560869c9b3]

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

One comment

  1. You just convinced me to buy them!
    Thanks for this post.

Got a comment?