attr_accessor :status
[:planned, :pre_sale, :on_sale].each do |method_name|
define_method :"?" do
status == method_name
end
end
end
The above code works, but I find it painful to maintain. When I begin to read
[:planned, :pre_sale, :on_sale].each do |method_name| I'm not expecting to find methods being defined. Often, I define class methods that give me a more descriptive way to generate the similar methods. In this case I would consider creating a create_status_boolean_methods method. If I went down that path the code could look like the example found below.
attr_accessor :status
methods.each do |method_name|
define_method :"?" do
status == method_name
end
end
end
create_status_boolean_methods :planned, :pre_sale, :on_sale
end
This solution is decent, but there are a few things I don't like about it. Firstly, it's usually not important to understand how the
create_status_boolean_methods method does it's job. Second, the definition of create_status_boolean_methods is often not near the actual usage of create_status_boolean_methods; therefore, on the rare occasion that you do need to understand how the methods are being defined you'll have to do a little searching within the class. Third, and perhaps the most obvious, it would take less code to define each method on it's own.The above option is a good solution in some cases; however, Neal and I were looking for something simpler that didn't suffer from the above list of limitations. We came up with the following generalized solution to defining similar methods.
method_names.each do |method_name|
define_method method_name do
instance_exec method_name, &block
end
end
end
endGiven the
def_each method defined on Class the Ticket class becomes easier to read in my opinion. The reason it's easier (in my opinion) is because when I'm scanning the file and I see def_each :planned?, :pre_sale?, :on_sale? do |method_name| I know that similar methods are being defined. Below is the full code now required to define the Ticket class.
attr_accessor :status
def_each :planned?, :pre_sale?, :on_sale? do |method_name|
:"?" == method_name
end
endYou may have noticed the
instance_exec method call in the implementation of the def_each method. I've previously written about instance_exec, but I'll include the implementation here for completeness.
end
end