Wie verwende ich define_method, um Klassenmethoden zu erstellen?

108

Dies ist nützlich, wenn Sie versuchen, Klassenmethoden metaprogrammatisch zu erstellen:

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

Meine Antwort folgt ...

Chinasaurier
quelle

Antworten:

196

Ich denke, in Ruby 1.9 können Sie dies tun:

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

A.loudly "my message"

# >> MY MESSAGE
fguillen
quelle
4
auchsingleton_class.define_method
Pyro
@ Pyro Nur um zu verdeutlichen, würden Sie einfach gehen singleton_class.define_method :loudly do |message|usw.?
Joshua Pinter
25

Ich bevorzuge send, um define_method aufzurufen, und ich möchte auch eine Metaklassenmethode erstellen, um auf die Metaklasse zuzugreifen:

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
Vincent Robert
quelle
2
Vielen Dank! Auf jeden Fall gibt es Möglichkeiten, dies für sich selbst schöner zu machen. Wenn Sie beispielsweise an einem Open-Source-Plugin arbeiten, ist es meiner Meinung nach besser, den Namespace nicht zu verstopfen. Daher metaclassist es schön, die einfache, eigenständige Kurzform zu kennen.
Chinasaurier
Ich entschied mich für meine ursprüngliche Antwort. Meines Wissens nach ist die Verwendung von send () für den Zugriff auf private Methoden in Ruby 1.9 nicht sinnvoll. Wenn Sie mehr als eine Methode definieren, ist die Instanzbewertung eines Blocks sauberer.
Chinasaur
@ Vincent Robert irgendein Link, der die Magie der Metaklassenmethode erklären würde?
Amol Pujari
Klasse << Selbst; selbst; Ende; öffnet einfach die Klasse des Selbst (Klasse << Selbst) wieder und gibt dann diese Klasse (Selbst) zurück, so dass tatsächlich die Metaklasse des Selbst zurückgegeben wird.
Vincent Robert
10

Dies ist der einfachste Weg in Ruby 1.8+:

class A
  class << self
    def method_name
      ...
    end
  end
end
Joseph Ravenwolfe
quelle
1
Ich mag diesen wirklich. Klein, ordentlich, liest sich gut und ist tragbar. Natürlich könnte man fragen, was ich 2013 mit Ruby 1.8 mache ...
A Fader Darkly
8

Abgeleitet von: Jay und Why , die auch Möglichkeiten bieten, dies schöner zu machen.

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

Update : aus dem Beitrag von VR unten; Eine präzisere Methode (solange Sie nur eine Methode auf diese Weise definieren), die immer noch eigenständig ist:

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

Beachten Sie jedoch, dass die Verwendung von send () für den Zugriff auf private Methoden wie define_method () nicht unbedingt eine gute Idee ist (nach meinem Verständnis wird sie in Ruby 1.9 nicht mehr verwendet).

Chinasaurier
quelle
Eine bessere (?) Alternative könnte sein, Dinge in ein Modul zu setzen und dann Ihre create_class_method das Modul auf die Klasse erweitern zu lassen ??? Siehe: blog.jayfields.com/2008/07/ruby-underuse-of-modules.html
Chinasaur
6

Zur Verwendung in Rails, wenn Sie Klassenmethoden dynamisch aus Bedenken definieren möchten:

module Concerns::Testable
  extend ActiveSupport::Concern

  included do 
    singleton_class.instance_eval do
      define_method(:test) do
        puts 'test'
      end
    end
  end
end
Artur Beljajev
quelle
-1

Sie können auch so etwas tun, ohne sich auf define_method zu verlassen:

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

A.class_method_name("hello") # outputs "hello" and returns nil
Bijan
quelle