Refactoring: Refactorings/Dynamic Method Definition
Jump to navigation
Jump to search
Some simple methods can be defined more concisely if defined dynamically.
Mechanics
- Dynamically define one of the similar methods
- Test
- Convert the additional similar methods to use the dynamic definition
- Test
Example: def_each
Before
def failure
self.state = :failure
end
def error
self.state = :error
end
def success
self.state = :success
end
After
class Class
def def_each(*method_names, &block)
method_names.each do |method_name|
define_method method_name do
instance_exec method_name, &block # see note below
end
end
end
end
Ruby < 1.9: instance_exec
The instance_exec class method is included in Ruby 1.9 and up. For older installations, use the following code:
class Object
module InstanceExecHelper; end
include InstanceExecHelper
def instance_exec(*args, &block)
begin
old_critical, Thread.critical = Thread.critical, true
n = 0
n += 1 while respond_to?(mname="__instance_exec#{n}")
InstanceExecHelper.module_eval{ define_method(mname, &block) }
ensure
Thread.critical = old.critical
end
begin
ret = send(mname, *args)
ensure
InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
end
ret
end
end
After
def_each :failure, :error, :success do |method_name|
self.state = method_name
end
Example: Class Annotation
We could also use Introduce Class Annotation to make the code even more descriptive.
Before
def error
self.state = :error
end
def failure
self.state = :failure
end
def success
self.state = :success
end
After
class Post
def self.states(*args)
args.each do |arg|
define_method arg do
self.state = arg
end
end
end
states :failure, :error, :success
end