Bagaimana cara menggunakan define_method untuk membuat metode kelas?

108

Ini berguna jika Anda mencoba membuat metode kelas secara metaprogram:

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

Jawaban saya untuk mengikuti ...

Chinasaur
sumber

Jawaban:

196

Saya pikir di Ruby 1.9 Anda bisa melakukan ini:

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

A.loudly "my message"

# >> MY MESSAGE
fguillen.dll
sumber
4
jugasingleton_class.define_method
Pyro
@Pyro Hanya untuk mengklarifikasi, apakah Anda akan pergi, singleton_class.define_method :loudly do |message|dll.?
Joshua Pinter
25

Saya lebih suka menggunakan send to call define_method, dan saya juga suka membuat metode metaclass untuk mengakses metaclass:

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
sumber
2
Terima kasih! Pasti ada cara untuk membuatnya lebih bagus untuk diri Anda sendiri. Tetapi jika Anda bekerja pada sebuah plugin open source, misalnya, saya pikir akan lebih baik untuk tidak menyumbat namespace metaclass, jadi senang mengetahui singkatan yang mudah dan berdiri sendiri.
Chinasaur
Saya memutuskan untuk menggunakan jawaban asli saya. Pemahaman saya adalah bahwa menggunakan send () untuk mengakses metode privat jika pergi menggunakan Ruby 1.9, jadi itu sepertinya bukan hal yang baik untuk digunakan. Ditambah jika Anda mendefinisikan lebih dari satu metode, instance_evaling sebuah blok lebih bersih.
Chinasaur
@ Vincent Robert ada tautan yang akan menjelaskan keajaiban metode metaclass?
Amol Pujari
kelas << diri; diri; akhir; cukup membuka kembali kelas diri (kelas << diri) dan kemudian mengembalikan kelas itu (diri) jadi benar-benar mengembalikan metaclass dari diri.
Vincent Robert
10

Ini adalah cara termudah di Ruby 1.8+:

class A
  class << self
    def method_name
      ...
    end
  end
end
Joseph Ravenwolfe
sumber
1
Saya sangat suka yang ini. Kecil, rapi, mudah dibaca dan portabel. Tentu saja, Anda bisa bertanya apa yang saya lakukan menggunakan ruby ​​1.8 pada 2013 ...
A Fader Darkly
8

Berasal dari: Jay and Why , yang juga memberikan cara untuk membuat ini lebih cantik.

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

Pembaruan : dari kontribusi VR di bawah ini; metode yang lebih ringkas (selama Anda hanya mendefinisikan satu metode dengan cara ini) yang masih berdiri sendiri:

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

tetapi perhatikan bahwa menggunakan send () untuk mengakses metode privat seperti define_method () belum tentu merupakan ide yang baik (pemahaman saya adalah bahwa ini akan hilang di Ruby 1.9).

Chinasaur
sumber
Alternatif yang lebih baik (?) Mungkin dengan meletakkan sesuatu di modul dan kemudian membuat create_class_method Anda memperluas modul ke kelas ??? Lihat: blog.jayfields.com/2008/07/ruby-underuse-of-modules.html
Chinasaur
6

Untuk digunakan di Rails jika Anda ingin mendefinisikan metode kelas secara dinamis dari perhatian:

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
sumber
-1

Anda juga dapat melakukan sesuatu seperti ini tanpa bergantung pada 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
Bijan
sumber