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)