Ruby:如果找不到类方法,则在Module中引发错误

时间:2011-07-29 16:56:36

标签: ruby module include metaprogramming

我想在模块中放入一些代码,如果没有定义某个方法,则抛出错误。 此模块依赖于此方法的外部定义,因为此方法的实现对所有类都不同。这段代码可以帮助开发人员早日知道他们忘记实现该方法,而不是在他们尝试使用模块的功能时。

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 个答案:

答案 0 :(得分:6)

听起来您想要使用method_missingdefine_method

如果您使用method_missing,请不要忘记:

  • 针对未处理的案件致电super
  • 还实施了respond_to?方法

查看this question,加上thisthat

更新:

听起来我的目标是像Java或c ++那样进行静态方法检查。这在红宝石中并没有多大意义: - (

因为在红宝石中:

  • 对象的每个实例都有自己的特征类。给定对象可能在运行时混合了必要的方法。因为Foo在类加载时没有方法是没有意义的。
  • 像RoR这样的框架挂钩method_missing并动态创建数据库查询方法所需的方法,因此在需要时可以存在(或不存在)方法。

关于“on class on load”:真正执行了类定义。试试这个:

class Foo 
  p "Hi"
end

第一次使用Foo时,您将看到“Hi”。这就是devise之类的东西可以用来做他们的魔法。

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方法调用?

我理解这个意图,但似乎正在与ruby的设计方式作斗争。

作为一个主要是Java的人,我很欣赏这种挫败感。让我猜一下:重复的代码案例被推到生产中,缺少方法? :-P

<强> UPDATE2:

wrt onload在红宝石中禁止使用冻结类,一直定义新的方法。 (或者一个实例可以获得仅为该实例定义的新方法。)因此检查方法的不存在只是一个快照检查,而不是像静态语言带来的那样确定的检查。这是ruby自己的Halting problem

答案 1 :(得分:4)

如何使用该名称声明一个只会引发错误的方法,以确保用户重新定义该方法?

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


class MyClass 
  include MyModule
  def my_method
    # do something
  end
end

答案 2 :(得分:2)

假设您的程序在启动时需要所有文件而不使用任何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

但是,我个人总是使用Dogbert的解决方案。