Как динамически создавать методы экземпляра во время выполнения? - PullRequest
6 голосов
/ 28 июля 2011

[ruby 1.8]

Предположим, у меня есть:

dummy "string" do
    puts "thing" 
end

Теперь это вызов метода, который имеет в качестве входных аргументов одну строку и одинблок.Хорошо.

Теперь предположим, что у меня может быть много похожих вызовов (разные имена методов, одни и те же аргументы).Пример:

otherdummy "string" do
    puts "thing"
end

Теперь, поскольку они делают одно и то же, а их могут быть сотни, я не хочу создавать метод экземпляра для каждого в требуемом классе.Я хотел бы найти умный способ динамического определения метода во время выполнения на основе общего правила.

Возможно ли это?Какие методы обычно используются?

Спасибо

Ответы [ 3 ]

8 голосов
/ 28 июля 2011

Мне особенно нравится использовать method_missing, особенно когда код, который вы хотите использовать, очень похож на различные вызовы методов.Вот пример с этого сайта - всякий раз, когда кто-то вызывает x.boo и boo не существует, method_missing вызывается с boo, аргументами boo и (необязательно) блоком:

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 также похоже, что это будет работать для вас, но у меня меньше опыта с этим, чем method_missing.Вот пример по той же ссылке:

%w(user email food).each do |meth|
  define_method(meth) { @data[meth.to_sym] }
end
7 голосов
/ 28 июля 2011

Да, есть несколько вариантов.

Первый - method_missing. Его первый аргумент - это символ, который является вызванным методом, а остальные аргументы - это использованные аргументы.

class MyClass
  def method_missing(meth, *args, &block)
    # handle the method dispatch as you want;
    # call super if you cannot resolve it
  end
end

Другой вариант - динамическое создание методов экземпляра во время выполнения, если вы заранее знаете, какие методы понадобятся. Это должно быть сделано в классе, и один пример такой:

class MyClass
  1.upto(1000) do |n|
    define_method :"method_#{n}" do
      puts "I am method #{n}!"
    end
  end
end

Это распространенный шаблон для вызова define_method в методе класса, который должен создавать новые методы экземпляра во время выполнения.

3 голосов
/ 08 июня 2015

использовать 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

Вывод:

content goes here
...