[rubin 1.8]

Załóżmy, że mam:

dummy "string" do
    puts "thing" 
end

Teraz jest to wywołanie metody, która ma jako argumenty wejściowe jeden ciąg i jeden blok. Miły.

Załóżmy teraz, że mogę mieć wiele podobnych wywołań (różne nazwy metod, te same argumenty). Przykład:

otherdummy "string" do
    puts "thing"
end

Teraz, ponieważ robią to samo, a mogą być setki, nie chcę tworzyć metody instancji dla każdego z nich w poszukiwanej klasie. Wolałbym znaleźć sprytny sposób na dynamiczne definiowanie metody w czasie wykonywania w oparciu o ogólną zasadę.

Czy to jest możliwe? Jakie techniki są powszechnie stosowane?

Dzięki

11
Emiliano Poggi 28 lipiec 2011, 19:53

3 odpowiedzi

Najlepsza odpowiedź

Szczególnie lubię używać method_missing, zwłaszcza gdy kod, którego chcesz użyć, jest bardzo podobny w różnych wywołaniach metod. Oto przykład z tej strony - za każdym razem, gdy ktoś wywoła x.boo i boo nie istnieje, method_missing jest wywoływana z boo, argumentami do boo i (opcjonalnie) blokiem:

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 również wygląda na to, że będzie działać w Twoim przypadku, ale mam z tym mniej doświadczenia niż method_missing. Oto przykład z tego samego linku:

%w(user email food).each do |meth|
  define_method(meth) { @data[meth.to_sym] }
end
8
Chris Bunch 28 lipiec 2011, 20:00
Dzięki za przydatny przykład (muszę też w ten sposób zarządzać nazwami metod). Zdecydowanie pójdę z method_missing będącymi metodami, których nie znamy a priori.
 – 
Emiliano Poggi
28 lipiec 2011, 23:02

Tak, jest kilka opcji.

Pierwszy to method_missing. Jej pierwszym argumentem jest symbol będący wywołaną metodą, a pozostałe argumenty to argumenty, które zostały użyte.

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

Inną opcją jest dynamiczne tworzenie metod instancji w czasie wykonywania, jeśli wiesz z góry, które metody będą potrzebne. Należy to zrobić w klasie, a jeden przykład jest taki:

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

Powszechnym wzorcem jest wywoływanie define_method w metodzie klasy, która musi utworzyć nowe metody instancji w czasie wykonywania.

8
Jeremy Roman 28 lipiec 2011, 20:04
Myślę, że byłoby to puts "I am method #{n}!". W każdym razie dobra odpowiedź!
 – 
Sony Santos
28 lipiec 2011, 20:08

Użyj metody define_:

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

Wynik:

content goes here
3
ksht 8 czerwiec 2015, 22:00