Есть ли элегантный способ проверить, является ли один метод экземпляра псевдонимом для другого?

В модульном тесте мне нужно проверить, правильно ли определены методы псевдонимов, определенные alias_method. Я мог бы просто использовать те же тесты для псевдонимов, которые использовались для их оригиналов, но мне интересно, есть ли более определенное или эффективное решение. Например, есть ли способ 1) разыменовать псевдоним метода и вернуть имя его оригинала, 2) получить и сравнить какой-либо базовый идентификатор или адрес метода или 3) получить и сравнить определения метода? Например:

class MyClass
  def foo
    # do something
  end

  alias_method :bar, :foo
end

describe MyClass do
  it "method bar should be an alias for method foo" do
    m = MyClass.new
    # ??? identity(m.bar).should == identity(m.foo) ???
  end
end

Предложения?

3 ответа

Решение

Согласно документации для метода,

Два объекта метода равны, если они связаны с одним и тем же объектом и содержат одно и то же тело.

призвание Object#method и сравнивая Method объекты, которые он возвращает, проверят, что методы эквивалентны:

m.method(:bar) == m.method(:foo)

Метод bk1e работает большую часть времени, но я случайно натолкнулся на случай, когда он не работает:

class Stream
  class << self
    alias_method :open, :new
  end
end

open = Stream.method(:open)
new = Stream.method(:new)
p open, new                   # => #<Method: Stream.new>, #<Method: Class#new>
p open.receiver, new.receiver # => Stream, Stream
p open == new                 # => false

Вывод производится в Ruby 1.9, не уверен, что это ошибка или нет, поскольку Ruby 1.8 производит true для последней строки. Поэтому, если вы используете 1.9, будьте осторожны, если вы используете псевдоним метода класса (например, Class#new), эти два метода связаны с одним и тем же объектом (объектом класса). Stream), но они считаются не эквивалентными по Ruby 1.9.

Мой обходной путь прост - снова создайте псевдоним исходного метода и проверьте равенство двух псевдонимов:

class << Stream; alias_method :alias_test_open, :new; end
open = Stream.method(:open)
alias_test_open = Stream.method(:alias_test_open)
p open, alias_test_open                   # => #<Method: Stream.new>, #<Method: Stream.new>
p open.receiver, alias_test_open.receiver # => Stream, Stream
p open == alias_test_open                 # => true

Надеюсь это поможет.

ОБНОВИТЬ:

Смотрите http://bugs.ruby-lang.org/issues/7613

Так Method#== должен вернуть false в этом случае, так как super вызов будет вызывать разные методы; это не ошибка.

Призвание MyClass.instance_method(:foo) приведет к экземпляру UnboundMethod, который имеет eql? метод.

Итак, ответ:

describe MyClass do
  subject { described_class }

  specify do
    expect(subject.instance_method(:foo)).to be_eql(subject.instance_method(:bar))
  end
end
Другие вопросы по тегам