Як я можу використовувати 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

4
такожsingleton_class.define_method
Піро

@Pyro Просто для уточнення, ти б просто пішов singleton_class.define_method :loudly do |message|тощо?
Джошуа Пінтер

25

Я вважаю за краще використовувати send to call 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

@ Вінсент Роберт будь-яке посилання, яке б пояснило магію методу метакласу?
Амол Пуджарі

клас << самостійна; самості; кінець; просто знову відкриває клас "Я" (клас << Я "), а потім повертає цей клас (" Я "), таким чином фактично повертаючи метаклас" Я ".
Вінсент Роберт

10

Це найпростіший спосіб у Ruby 1.8+:

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

1
Мені дуже подобається цей. Невеликий, акуратний, добре читає і портативний. Звичайно, ви можете запитати, що я роблю, використовуючи рубін 1,8 в 2013 році ...
A Fader Darkly

8

Похідне від: Джей і чому , які також пропонують способи зробити це гарнішим.

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.