Jak używać define_method do tworzenia metod klas?


108

Jest to przydatne, jeśli próbujesz utworzyć metody klas metaprogramowo:

def self.create_methods(method_name)
    # To create instance methods:
    define_method method_name do
      ...
    end

    # To create class methods that refer to the args on create_methods:
    ???
end

Moja odpowiedź do naśladowania ...

Odpowiedzi:


196

Myślę, że w Rubim 1.9 możesz to zrobić:

class A
  define_singleton_method :loudly do |message|
    puts message.upcase
  end
end

A.loudly "my message"

# >> MY MESSAGE

4
takżesingleton_class.define_method
Pyro,

@Pyro Dla wyjaśnienia, czy po prostu pójdziesz singleton_class.define_method :loudly do |message|itd.?
Joshua Pinter

25

Wolę używać send to call define_method, a także lubię tworzyć metodę metaklasy, aby uzyskać dostęp do metaklasy:

class Object
  def metaclass
    class << self
      self
    end
  end
end

class MyClass
  # Defines MyClass.my_method
  self.metaclass.send(:define_method, :my_method) do
    ...
  end
end

2
Dzięki! Zdecydowanie są sposoby, aby uczynić to dla siebie przyjemniejszym. Ale jeśli pracujesz na przykład nad wtyczką open source, myślę, że przyjemniej jest nie zatykać przestrzeni nazw metaclass, więc dobrze jest znać proste, samodzielne skróty.
Chinasaur

Postanowiłem pójść z moją pierwotną odpowiedzią. Rozumiem, że używanie send () do uzyskiwania dostępu do metod prywatnych, jeśli odchodzisz w Ruby 1.9, więc nie wydawało się to dobrym rozwiązaniem. Dodatkowo, jeśli definiujesz więcej niż jedną metodę, instance_evaling a blok jest czystszy.
Chinasaur

@Vincent Robert jakikolwiek link wyjaśniający magię metody metaklasy?
Amol Pujari

class << self; samego siebie; koniec; po prostu ponownie otwiera klasę siebie (class << self), a następnie zwraca tę klasę (self), więc w rzeczywistości zwraca metaklasę siebie.
Vincent Robert

10

Oto najprostszy sposób w Rubim 1.8+:

class A
  class << self
    def method_name
      ...
    end
  end
end

1
Bardzo mi się to podoba. Mały, zgrabny, dobrze się czyta i jest przenośny. Oczywiście, możesz zapytać, co robię używając Ruby 1.8 w 2013 ...
A Fader Darkly

8

Pochodzi z: Jay and Why , którzy również zapewniają sposoby na uczynienie tego ładniejszym.

self.create_class_method(method_name)
  (class << self; self; end).instance_eval do
    define_method method_name do
      ...
    end
  end
end

Aktualizacja : z wkładu VR poniżej; bardziej zwięzła metoda (o ile w ten sposób definiujesz tylko jedną metodę), która jest nadal samodzielna:

self.create_class_method(method_name)
  (class << self; self; end).send(:define_method, method_name) do
    ...
  end
end

ale zauważ, że używanie send () do uzyskiwania dostępu do prywatnych metod, takich jak define_method (), niekoniecznie jest dobrym pomysłem (rozumiem, że w Rubim 1.9 nie ma go).


Lepszą (?) Alternatywą może być umieszczenie rzeczy w module, a następnie utworzenie przez create_class_method rozszerzenia modułu na klasę ??? Zobacz: blog.jayfields.com/2008/07/ruby-underuse-of-modules.html
Chinasaur

6

Do użycia w Railsach, jeśli chcesz dynamicznie definiować metody klas na podstawie problemów:

module Concerns::Testable
  extend ActiveSupport::Concern

  included do 
    singleton_class.instance_eval do
      define_method(:test) do
        puts 'test'
      end
    end
  end
end

-1

Możesz także zrobić coś takiego bez polegania na define_method:

A.class_eval do
  def self.class_method_name(param)
    puts param
  end
end

A.class_method_name("hello") # outputs "hello" and returns nil
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.