Come si usa define_method per creare metodi di classe?


108

Questo è utile se stai cercando di creare metodi di classe metaprogrammaticamente:

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

La mia risposta da seguire ...

Risposte:


196

Penso che in Ruby 1.9 tu possa fare questo:

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

A.loudly "my message"

# >> MY MESSAGE

4
anchesingleton_class.define_method
Pyro

@Pyro Solo per chiarire, andresti e così singleton_class.define_method :loudly do |message|via?
Joshua Pinter

25

Preferisco usare send to call define_method e mi piace anche creare un metodo metaclass per accedere alla 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

2
Grazie! Sicuramente ci sono modi per renderlo più bello per te stesso. Ma se stai lavorando su un plugin open source, ad esempio, penso che sia meglio non intasare lo spazio dei nomi metaclass, quindi è bello conoscere la facile scorciatoia standalone.
Chinasaur

Ho deciso di seguire la mia risposta originale. La mia comprensione è che l'uso di send () per accedere a metodi privati ​​se ne andava in Ruby 1.9, quindi non sembrava una buona cosa da usare. Inoltre, se stai definendo più di un metodo, instance_evaling a block è più pulito.
Chinasaur

@ Vincent Robert qualsiasi collegamento che spiegherebbe la magia del metodo metaclasse?
Amol Pujari

class << self; se stesso; fine; riapre semplicemente la classe di sé (classe << sé) e quindi restituisce quella classe (sé), quindi restituisce effettivamente la metaclasse di sé.
Vincent Robert

10

Questo è il modo più semplice in Ruby 1.8+:

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

1
Mi piace molto questo. Piccolo, pulito, si legge bene ed è portatile. Certo, potresti chiedermi cosa sto facendo usando ruby ​​1.8 nel 2013 ...
A Fader Darkly

8

Derivato da: Jay e Why , che forniscono anche modi per renderlo più carino.

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

Aggiornamento : dal contributo di VR di seguito; un metodo più conciso (a patto che tu definisca solo un metodo in questo modo) che è ancora autonomo:

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

ma si noti che l'uso di send () per accedere a metodi privati ​​come define_method () non è necessariamente una buona idea (la mia comprensione è che non sarà più disponibile in Ruby 1.9).


L'alternativa migliore (?) Potrebbe essere quella di mettere le cose in un modulo e poi fare in modo che il metodo create_class_method estenda il modulo alla classe ??? Vedi: blog.jayfields.com/2008/07/ruby-underuse-of-modules.html
Chinasaur

6

Da usare in Rails se vuoi definire dinamicamente i metodi di classe dalla preoccupazione:

module Concerns::Testable
  extend ActiveSupport::Concern

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

-1

Puoi anche fare qualcosa di simile senza fare affidamento su 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.