Ruby method_added обратный вызов не запускается, включая модули - PullRequest
5 голосов
/ 22 февраля 2012

Я хотел написать небольшую библиотеку "Deprecate-It" и часто использовал обратный вызов "method_added".Но теперь я заметил, что этот обратный вызов не запускается при включении модуля.

Есть ли какие-либо обратные вызовы или обходные пути, чтобы проинформировать класс "Foobar", когда что-то включено в него?

SmallДемонстрация для демонстрации:

# Including Moduls won't trigger method_added callback

module InvisibleMethod
  def invisible
    "You won't get a callback from me"
  end
end

class Foobar
  def self.method_added(m)
    puts "InstanceMethod: '#{m}' added to '#{self}'"
  end

  def visible
    "You will get a callback from me"
  end

  include InvisibleMethod
end

[:invisible, :visible, :wont_exist].each do |meth|
  puts "#{meth}: #{Foobar.public_method_defined? meth}"
end

Вот результат:

InstanceMethod: 'visible' added to 'Foobar'
invisible: true
visible: true
wont_exist: false

Дополнительная информация:

Мне действительно нужно использовать хук, как method_added.

ActiveModel добавляет public_instance_methods в класс во время выполнения через анонимные модули.

Ответы [ 3 ]

8 голосов
/ 02 марта 2012

Проблема в том, что включение модулей не добавляет методы в классы - оно только меняет цепочку вызовов методов.Эта цепочка определяет, какие классы / модули будут искать для метода, который не определен для рассматриваемого класса.То, что происходит, когда вы включаете модуль, является добавлением записи в этой цепочке.

Это то же самое, что и при добавлении метода в суперкласс - это не вызывает method_added, так как он не определен в суперклассе.Было бы очень странно, если бы подкласс мог изменить поведение суперкласса.

Это можно исправить, вручную вызвав метод, добавленный для включенного модуля, переопределив include для вашего класса:

class Foobar
  def self.include(included_module)
    included_module.instance_methods.each{|m| self.method_added(m)}
    super
  end
end

И это намного безопаснее, чем переопределение метода included в Module - изменение сужается только для классов, которые вы определили сами.

3 голосов
/ 29 февраля 2012

Как подсказывает один из комментариев, вы можете использовать другой хук, чтобы получить желаемое поведение.Например, попробуйте добавить это в начале вашего кода:

class Module
  def included(klass)
    if klass.respond_to?(:method_added)
      self.instance_methods.each do |method|
        klass.method_added(method)
      end
    end
  end
end

Всякий раз, когда модуль включен в класс, все методы экземпляра этого модуля будут уведомлены классу, если он определяетметод method_added.Запустив ваш код с указанным выше изменением, я получаю следующий результат:

InstanceMethod: 'visible' added to 'Foobar'
InstanceMethod: 'invisible' added to 'Foobar'
invisible: true
visible: true
wont_exist: false

Какое поведение я считаю нужным.

1 голос
/ 03 марта 2012

Я думаю, deprecation не большая проблема, чтобы требовать библиотеку.это реализовано так в datamappermethod_added крюк;он работает как ожидалось, потому что методы уже добавлены в module, а не class.Только вы можете получить ожидаемый результат обезьяны исправления included крючок.

# got from https://github.com/datamapper/dm-core/blob/master/lib/dm-core/support/deprecate.rb
module Deprecate
  def deprecate(old_method, new_method)
    class_eval <<-RUBY, __FILE__, __LINE__ + 1
      def #{old_method}(*args, &block)
        warn "\#{self.class}##{old_method} is deprecated, use \#{self.class}##{new_method} instead (\#{caller.first})"
        send(#{new_method.inspect}, *args, &block)
      end
    RUBY
  end
end # module Deprecate

class MyClass
  extend Deprecate

  def old_method
    p "I am old"
  end
  deprecate :old_method, :new_method

  def new_method
    p "I am new"
  end
end

m = MyClass.new
m.old_method

# MyClass#old_method is deprecated, use MyClass#new_method instead (pinger.rb:27:in `<main>')
# "I am new"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...