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
end
Given 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
end
You 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
This article is a keeper. Great summary.
ReplyDeleteFor the particular example, I agree that the last is nicest.
Jay, some of the underscores seem to be escaping from your code when being formatted for colour in the article.
ReplyDeleteYou'll have to tighten up security :)
My favorite part of this idea is that it would make it easy to solve a really tricky problem: having such methods show up in rdocs. It wouldn't be too hard to modify rdoc to recognize the def_each method and, in many of the cases where it would be used, have the methods show up properly in rdocs. For example, Rails could have proper documentation of the get, post, put, delete, etc. methods in ActionController::TestProcess.
ReplyDelete