There are multiple ways of defining methods in Ruby. The most popular is by using a block, via def and end. One cool thing about Ruby is its metaprogramming (being able to write code that writes code) capabilities. As an example, take a look at this Baby class definition.

A Baby example

class Baby
CRYING = 0
POOPING = 1
SLEEPING = 2
attr_writer :status
def initialize
@status = CRYING
end
def sleeping?
status == SLEEPING
end
def pooping?
status == POOPING
end
def crying?
status == CRYING
end
end
baby = Baby.new
baby.crying?
#=> true
baby.sleeping?
#=> false
baby.status = 2
baby.crying?
#=> false
baby.sleeping?
#=> true

The defined methods are all very similar, and it'd be great if we could DRY it up somehow. Luckily, we can! We can use metaprogramming to write some code that will generate our sleeping?, pooping?, and crying? methods.

dynamically defining methods

We can use define_method to dynamically define methods in Ruby. define_method is used by passing a name of the method we want to define, and a block that will be the body for that method. Our Baby class can be refactored into:

class Baby
CRYING = 0
POOPING = 1
SLEEPING = 2
attr_writer :status
def initialize
@status = CRYING
end
[:crying, :pooping, :sleeping].each do |status|
define_method "#{status}?" do
status == Babe.const_get(status.upcase)
end
end
end
baby = Baby.new
baby.crying?
#=> true
baby.sleeping?
#=> false

Isn't that much nicer? We got rid of redundancy and made it a lot easier to write more status methods, such as laughing? or hiccuping?.

being mindful

A downside to using define_method is that it creates a closure. Objects in the closure are not garbage collected, so be mindful when using dynamically creating methods using define_method and creating objects. For more on benchmarking, take a look at tenderlove's blog post here.

References