How to dynamically create instance methods at runt

2019-02-23 01:31发布

问题:

[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

回答1:

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


回答2:

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.



回答3:

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