Ruby: выдает ошибку в модуле, если метод класса не найден - PullRequest
4 голосов
/ 29 июля 2011

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

module MyModule
  def self.included(klass)
    raise "MyModule: please `def my_method` on #{klass}" unless klass.respond_to?(:my_method)
  end
end 

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

class MyClass
  include MyModule
  def self.my_method
    # ...
  end
end

Это все равно вызовет ошибку: (

Возможно ли вызвать ошибку, только если метод действительно не определен в определении класса? Почти нужен обратный вызов class.onloadЕсли нет, есть ли другие идеи о том, как уменьшить возможности, которые программист может включить в наш модуль, не определяя этот необходимый метод?

Ответы [ 3 ]

6 голосов
/ 29 июля 2011

Похоже, что вы хотите использовать method_missing и define_method.

Если вы используете method_missing, не забудьте:

  • вызов super для необработанных дел.
  • также реализует respond_to? метод

посмотрите на этот вопрос , плюс , и , .

Обновление:

Похоже, цель состоит в том, чтобы выполнять статическую проверку методов, как это делают Java или c ++. Это не очень важно в ruby: - ​​(

Так как в рубине:

  • Каждый экземпляр объекта имеет свой собственный класс. У данного объекта могут быть необходимые методы, смешанные во время выполнения. Так что просто потому, что Foo не имеет метода во время загрузки класса, бессмысленно.
  • Фреймворки, такие как RoR, перехватывают method_missing и динамически создают методы, необходимые для методов запроса к базе данных, поэтому метод может существовать (или не существовать), когда это необходимо.

Что касается «класса под нагрузкой»: определение класса действительно выполняется. Попробуйте это:

class Foo 
  p "Hi"
end

Вы увидите «Привет» в первый и только первый раз, когда Foo используется. Вот как такие вещи, как изобретают , пытаются творить свою магию.

class User < ActiveRecord::Base
  # **CALL 'devise' method**
  devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable

  # **CALL attr_accessible method**
  attr_accessible :email, :password, :password_confirmation
end

Так может быть по частному соглашению разработчики добавили вызов метода check_class в конец рассматриваемых классов?

Я понимаю намерение, но похоже, что я сражаюсь так, как работает рубин.

Как человек, в основном Java, я ценю разочарование. Позвольте мне угадать: повторные случаи, когда код был запущен в производство с отсутствующими методами? : -Р

Update2:

wrt onload При запрете использования ruby ​​класса класс постоянно получает новые методы. (Или экземпляр может получить новые методы, определенные только для этого экземпляра.) Поэтому проверка на несуществование метода является только проверкой моментального снимка, а не такой окончательной проверкой, которую статический язык вносит в таблицу. Это собственная проблема Рубина Остановка

4 голосов
/ 29 июля 2011

Как насчет объявления метода с таким именем, который просто вызывает ошибку, чтобы убедиться, что пользователь переопределяет метод?

module MyModule
  def my_method
    raise "Please implement me"
  end
end


class MyClass 
  include MyModule
  def my_method
    # do something
  end
end
2 голосов
/ 29 июля 2011

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

classes_to_check = Object.constants.find_all do |const|
  klass = Object.const_get(c)
  klass.ancestors.include?(MyModule) if klass.kind_of?(Module)
end

classes_to_check.each do |klass|
  raise "MyModule: please `def my_method` on #{klass}" \
    unless klass.respond_to?(:my_method)
end

Однако я лично всегда использую решение Догберта.

...