Meta-programming with instance_exec

Rails makes heavy use of a declarative style around it’s codebase- for example the has_one and belongs_to declarations inside ActiveRecord (amongst others). These are just class methods defined on modules, letting Rails wire up relationships, but they read like fully-fledged statements within a mini-language:

class Student < ActiveRecord::Base  has_many :tutorials

We can take advantage of the same in our code- letting us write in a declarative-style (hopefully revealing stronger intent) and reduce the amount of code we write (by using declarations to meta-program for us). I posted a little while ago that we’d used such an approach for marking attributes as immutable.

Anyway, back to the title of the post- #instance_exec, it’s a method defined in Ruby Facets. Like it’s documentation says, it’s equivalent to instance_eval but also lets you pass parameters- roll on the meta magic.

Let’s say we’re writing a system to calculate the monthly salary payment for an employee. We want to be able to say what the payment is rather than how it’s calculated.

class FullTimeEmployee  include Employee  bill_at {|hours| 50.pounds_sterling * hours}

In the snippet above, we’re making a declaration - defining the relationship between an hourly rate and the number of hours the employee works. We can implement bill_at in Employee as follows:

module Employee  module ClassMethods    def bill_at &block      define_method(:calculate_bill) do |hours|        instance_exec hours, &block      end  ...

Our assertion could be:

assert_equal 500.pounds_sterling, employee.calculate_bill(5.hours)