Как использовать define_method для создания методов класса?

108

Это полезно, если вы пытаетесь создать методы класса метапрограммно:

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

Мой ответ, чтобы следовать ...

Китайзавр
источник

Ответы:

196

Я думаю, что в Ruby 1.9 вы можете сделать это:

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

A.loudly "my message"

# >> MY MESSAGE
fguillen
источник
4
такжеsingleton_class.define_method
Pyro
@Pyro Просто чтобы уточнить, не могли бы вы пойти singleton_class.define_method :loudly do |message|и т. Д.?
Джошуа Пинтер
25

Я предпочитаю использовать send для вызова define_method, и мне также нравится создавать метод метакласса для доступа к метаклассу:

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
Спасибо! Определенно есть способы сделать это лучше для себя. Но если вы, например, работаете над плагином с открытым исходным кодом, я думаю, что лучше не засорять пространство имен metaclass, поэтому хорошо знать простую, автономную стенографию.
Chinasaur
Я решил оставить свой первоначальный ответ. Насколько я понимаю, использование send () для доступа к частным методам, если оно исчезнет в Ruby 1.9, так что это не похоже на хорошее использование. Кроме того, если вы определяете более одного метода, instance_evaling блока будет чище.
Chinasaur
@Vincent Robert любая ссылка, которая объяснила бы магию метода метакласса?
Амол Пуджари,
class << self; самостоятельно; конец; просто повторно открывает класс self (class << self), а затем возвращает этот класс (self), фактически возвращая метакласс self.
Винсент Роберт
10

Это самый простой способ в Ruby 1.8+:

class A
  class << self
    def method_name
      ...
    end
  end
end
Джозеф Рэйвенвулф
источник
1
Мне это очень нравится. Маленький, аккуратный, хорошо читается и портативный. Конечно, вы можете спросить, что я делаю, используя Ruby 1.8 в 2013 году ...
A Fader Darkly
8

Произведено от: Jay and Why , которые также предлагают способы сделать это красивее.

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

Обновление : из вклада VR ниже; более сжатый метод (пока вы определяете только один метод таким образом), который все еще является автономным:

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

но обратите внимание, что использование send () для доступа к частным методам, таким как define_method (), не обязательно является хорошей идеей (я понимаю, что это исчезнет в Ruby 1.9).

Китайзавр
источник
Лучшая (?) Альтернатива может заключаться в том, чтобы поместить вещи в модуль, а затем ваш create_class_method расширил модуль на класс ??? См .: blog.jayfields.com/2008/07/ruby-underuse-of-modules.html
Chinasaur,
6

Для использования в Rails, если вы хотите динамически определять методы класса из соображений безопасности:

module Concerns::Testable
  extend ActiveSupport::Concern

  included do 
    singleton_class.instance_eval do
      define_method(:test) do
        puts 'test'
      end
    end
  end
end
Артур Беляев
источник
-1

Вы также можете сделать что-то подобное, не полагаясь на 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
Биджан
источник