Como faço para usar define_method para criar métodos de classe?


108

Isso é útil se você está tentando criar métodos de classe metaprogramaticamente:

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

Minha resposta a seguir ...

Respostas:


196

Acho que em Ruby 1.9 você pode fazer isso:

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

A.loudly "my message"

# >> MY MESSAGE

4
tambémsingleton_class.define_method
Pyro

@Pyro Só para esclarecer, você poderia ir singleton_class.define_method :loudly do |message|etc.?
Joshua Pinter

25

Eu prefiro usar send para chamar define_method, e também gosto de criar um método metaclasse para acessar a metaclasse:

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
Obrigado! Definitivamente, existem maneiras de tornar isso mais agradável para você. Mas se você está trabalhando em um plug-in de código aberto, por exemplo, acho melhor não entupir o namespace metaclass, então é bom conhecer a abreviatura autônoma e fácil.
Chinasaur

Decidi ir com minha resposta original. Meu entendimento é que usar send () para acessar métodos privados está indo embora no Ruby 1.9, então isso não parece uma coisa boa de se usar. Além disso, se você estiver definindo mais de um método, instance_evaling um bloco é mais limpo.
Chinasaur

@Vincent Robert algum link que explicasse a magia do método metaclasse?
Amol Pujari

class << self; auto; fim; simplesmente reabre a classe de self (class << self) e, em seguida, retorna essa classe (self), retornando de fato a metaclasse de self.
Vincent Robert

10

Esta é a maneira mais simples em Ruby 1.8+:

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

1
Eu realmente gosto deste. Pequeno, elegante, tem uma boa leitura e é portátil. Claro, você pode perguntar o que estou fazendo usando ruby ​​1.8 em 2013 ...
A Fader Darkly

8

Derivado de: Jay e Why , que também fornecem maneiras de tornar isso mais bonito.

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

Atualização : da contribuição da VR abaixo; um método mais conciso (contanto que você defina apenas um método desta forma) que ainda é autônomo:

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

mas note que usar send () para acessar métodos privados como define_method () não é necessariamente uma boa ideia (meu entendimento é que isso vai desaparecer no Ruby 1.9).


Uma alternativa melhor (?) Pode ser colocar as coisas em um módulo e, em seguida, fazer com que seu create_class_method estenda o módulo para a classe ??? Veja: blog.jayfields.com/2008/07/ruby-underuse-of-modules.html
Chinasaur

6

Para ser usado no Rails se você quiser definir métodos de classe dinamicamente a partir de preocupação:

module Concerns::Testable
  extend ActiveSupport::Concern

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

-1

Você também pode fazer algo assim sem depender de 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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.