Копировать метод из одного класса в другой - PullRequest
4 голосов
/ 15 февраля 2012

Я хочу написать небольшой класс с некоторыми методами, который на самом деле принадлежит другим классам, так как я могу определить методы в других классах, которые являются копиями существующих.Я полагаю, что это магия метапрограммирования, которого я не понимаю.

class Foo
  def initialize
    # with blocks, I would just pass block, but this is methods
    # so this won't work
    Bar.class_eval(perform)
    Bar.class_eval(process)
    Bar.class_eval(save)
  end

  def perform
    1+1
  end

  def process
    # some code
  end

  def save
    # some code
  end
end

class Bar; end

foo = Foo.new
foo.perform
#=> 2
Bar.test
#=> 1

Зачем мне это нужно?Я работаю над гемом, который берет урок всего тремя методами.При инициализации (которая будет скрыта в родительском классе) он передаст эти методы различным классам.Я могу сделать это с помощью блоков, но с помощью методов это немного чище.

PS : Это похоже на копирование методов из одного класса в другой

PSS : или ... как преобразовать метод в proc, чтобы я мог передать его class_eval

Ответы [ 2 ]

13 голосов
/ 15 февраля 2012

Чтобы преобразовать метод во что-то, что можно вызвать как Proc, используйте obj.method(:method_name). Это даст вам объект bound Method, который при call ed будет вызываться на obj. Если вы хотите вызвать его для другого объекта того же класса, вы можете вызвать method.unbind.bind(different_obj).

Это все еще не позволяет вам «копировать» методы из одного класса в другой. Если вы хотите разрешить пользователю передавать класс, который определяет 3 метода, а не пропускать 3 блока, это может сработать лучше, если вы храните ссылку на этот класс (или его экземпляр) внутри и вызываете методы для него по мере необходимости. , Вот что имел в виду человек, который прокомментировал «делегирование».

ИЛИ, вы можете позволить пользователю передать модуль и сделать свой собственный класс include или extend модулем (при необходимости).

0 голосов
/ 24 августа 2018

Можно копировать методы из одного класса в другой, но есть серьезное предостережение: целевой класс должен быть kind_of?исходный класс или источник должен быть модулем.Это ограничение частично задокументировано в документации для UnboundMethod # bind , но чтобы увидеть исключение модуля, вам нужно взглянуть на исходный код метода. Этот ответ содержит дополнительные обсуждения по теме.

Вот пример:

module A
  def say_hello
    puts "Hello"
  end
end

class B
  define_method(:say_hello, A.instance_method(:say_hello))
end

b = B.new
b.say_hello
=> Hello

Это также будет работать, если A был классом и Bбыл класс, унаследованный от A.Но в этом случае вы уже получили бы метод с помощью наследования, поэтому я лично не вижу его использования.

Единственная ситуация, в которой я нашел этот шаблон полезным, - это проектирование DSL с использованием объектов, которыенаследовать от BasicObject.Поскольку BasicObject не include Kernel, как Object, у вас нет простого доступа ко многим полезным методам, таким как #instance_variables или даже #class.Но вы можете скопировать их по отдельности из ядра в ваш класс:

class Foo < BasicObject
  define_method(:class, ::Kernel.instance_method(:class))
end

f = Foo.new
puts f.class
=> Foo
...