Подтвердить что ты не робот

Method_missing gotchas в Ruby

Есть ли какие-нибудь вопросы, которые следует учитывать при определении метода method_missing в Ruby? Мне интересно, есть ли какие-то не столь очевидные взаимодействия от наследования, выброса исключений, производительности или чего-либо еще.

4b9b3361

Ответ 1

Несколько очевидный: всегда переопределяйте respond_to?, если вы переопределите method_missing. Если method_missing(:sym) работает, respond_to?(:sym) всегда должен возвращать true. На это много библиотек.

Далее:

Пример:

# Wrap a Foo; don't expose the internal guts.
# Pass any method that starts with 'a' on to the
# Foo.
class FooWrapper
  def initialize(foo)
    @foo = foo
  end
  def some_method_that_doesnt_start_with_a
    'bar'
  end
  def a_method_that_does_start_with_a
    'baz'
  end
  def respond_to?(sym, include_private = false)
    pass_sym_to_foo?(sym) || super(sym, include_private)
  end
  def method_missing(sym, *args, &block)
    return foo.call(sym, *args, &block) if pass_sym_to_foo?(sym)
    super(sym, *args, &block)
  end
  private
  def pass_sym_to_foo?(sym)
    sym.to_s =~ /^a/ && @foo.respond_to?(sym)
  end
end

class Foo
  def argh
    'argh'
  end
  def blech
    'blech'
  end
end

w = FooWrapper.new(Foo.new)

w.respond_to?(:some_method_that_doesnt_start_with_a)
# => true
w.some_method_that_doesnt_start_with_a
# => 'bar'

w.respond_to?(:a_method_that_does_start_with_a)
# => true
w.a_method_that_does_start_with_a
# => 'baz'

w.respond_to?(:argh)
# => true
w.argh
# => 'argh'

w.respond_to?(:blech)
# => false
w.blech
# NoMethodError

w.respond_to?(:glem!)
# => false
w.glem!
# NoMethodError

w.respond_to?(:apples?)
w.apples?
# NoMethodError

Ответ 2

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

Ответ 3

Если вы можете предвидеть имена методов, лучше динамически объявлять их, чем полагаться на method_missing, потому что method_missing несет штраф за производительность. Например, предположим, что вы хотите расширить дескриптор базы данных, чтобы иметь возможность обращаться к представлениям базы данных с помощью этого синтаксиса:

selected_view_rows = @dbh.viewname( :column => value, ... )

Вместо того, чтобы полагаться на method_missing на дескрипторе базы данных и отправлять имя метода в базу данных в качестве имени представления, вы могли бы заранее определить все представления в базе данных, а затем перебрать их для создания методов "viewname" на @dbh.

Ответ 4

На основе Точка пистолета: method_missing по крайней мере на порядок медленнее обычного метода, вызывающего все реализации Ruby, которые я пробовал. Он прав, когда можно избежать звонков на method_missing.

Если вы чувствуете себя авантюрно, ознакомьтесь с малоизвестным рубином class.

Ответ 5

Другая информация:

method_missing ведет себя по-разному между obj.call_method и obj.send(:call_method). По сути, первый из них пропускает все частные и неопределенные методы, а позже не пропускает частные методы.

Итак, вы method_missing никогда не поймаете вызов, когда кто-то вызовет ваш частный метод через send.