[ruby 1.8]
Assume I have:
dummy "string" do
puts "thing"
end
Now, this is a call to a method which has as input arguments one string and one block. Nice.
Now assume I can have a lot of similar calls (different method names, same arguments). Example:
otherdummy "string" do
puts "thing"
end
Now because they do the same thing, and they can be hundreds, I don't want create an instance method for each one in the wanted class. I would like rather find a smart way to define the method dynamically at runtime based on a general rule.
Is that possible? Which techniques are commonly used?
Thanks
I'm particularly fond of using method_missing
, especially when the code you want to use is very similar across the various method calls. Here's an example from this site - whenever somebody calls x.boo
and boo
doesn't exist, method_missing is called with boo
, the arguments to boo
, and (optionally) a block:
class ActiveRecord::Base
def method_missing(meth, *args, &block)
if meth.to_s =~ /^find_by_(.+)$/
run_find_by_method($1, *args, &block)
else
super # You *must* call super if you don't handle the
# method, otherwise you'll mess up Ruby's method
# lookup.
end
end
def run_find_by_method(attrs, *args, &block)
# Make an array of attribute names
attrs = attrs.split('_and_')
# #transpose will zip the two arrays together like so:
# [[:a, :b, :c], [1, 2, 3]].transpose
# # => [[:a, 1], [:b, 2], [:c, 3]]
attrs_with_args = [attrs, args].transpose
# Hash[] will take the passed associative array and turn it
# into a hash like so:
# Hash[[[:a, 2], [:b, 4]]] # => { :a => 2, :b => 4 }
conditions = Hash[attrs_with_args]
# #where and #all are new AREL goodness that will find all
# records matching our conditions
where(conditions).all
end
end
define_method
also looks like it would work for you, but I have less experience with it than method_missing
. Here's the example from the same link:
%w(user email food).each do |meth|
define_method(meth) { @data[meth.to_sym] }
end
Yes, there are a few options.
The first is method_missing
. Its first argument is a symbol which is the method that was called, and the remaining arguments are the arguments that were used.
class MyClass
def method_missing(meth, *args, &block)
# handle the method dispatch as you want;
# call super if you cannot resolve it
end
end
The other option is dynamically creating the instance methods at runtime, if you know in advance which methods will be needed. This should be done in the class, and one example is like this:
class MyClass
1.upto(1000) do |n|
define_method :"method_#{n}" do
puts "I am method #{n}!"
end
end
end
It is a common pattern to have define_method
called in a class method which needs to create new instance methods at runtime.
use define_method:
class Bar
end
bar_obj = Bar.new
class << bar_obj
define_method :new_dynamic_method do
puts "content goes here"
end
end
bar_obj.new_dynamic_method
Output:
content goes here