3

我正在开发一个模块,除其他外,它将向您将其混入的类添加一些通用的“查找器”类型功能。问题:出于方便和美观的原因,我想在类之外包含一些功能,与类本身在同一范围内。

例如:

class User
  include MyMagicMixin
end

# Should automagically enable:

User.name('Bob')   # Returns first user named Bob
Users.name('Bob')  # Returns ALL users named Bob 
User(5)            # Returns the user with an ID of 5
Users              # Returns all users

我可以这些方法中完成功能,没问题。案例 1 ( User.name('Bob')) 很简单。然而,案例 2-4 需要能够在User. 该Module.included方法使我可以访问该类,但不能访问其包含范围。我在 Class 或 Module 上看不到简单的“父”类型方法。(对于命名空间,我的意思是,不是超类也不是嵌套模块。)

我能想到的最好方法是对类进行一些字符串解析#name以打破其命名空间,然后将字符串转换回常量。但这似乎很笨拙,鉴于这是 Ruby,我觉得应该有一种更优雅的方式。

有人有想法吗?或者我只是为了自己的利益太聪明了?

4

3 回答 3

3

我倾向于过于聪明。

即使有一个优雅的解决方案,在类中包含一个在类创建类的模块似乎也很奇怪。

于 2010-03-20T04:02:50.663 回答
2

这是邮件列表中有时会出现的问题。这也是 Rails 中出现的一个问题。正如您已经怀疑的那样,解决方案基本上是正则表达式。

但是,还有一个更根本的问题:在 Ruby 中,类没有名称!类只是一个对象,就像任何其他对象一样。您可以将其分配给实例变量、局部变量、全局变量、常量,甚至根本不将其分配给任何东西。该Module#name方法基本上只是一种方便的方法,其工作原理如下:它查看已定义常量的列表,直到找到指向接收者的常量。如果它找到一个,它返回它可以找到的第一个,否则它返回nil

所以,这里有两种失败模式:

a = Class.new
a.name # => nil
B = a
B.name # => "B"
A = B
A.name # => "B"
  • 一个类可能根本没有名字
  • 一个类可能有多个名称,但Module#name只会返回它找到的第一个

现在,如果有人尝试调用As以获取As 列表,他们会非常惊讶地发现该方法不存在,但他们可以调用Bs来获得相同的结果。

确实在现实中发生。在 MacRuby 中,例如String.name返回NSMutableStringHash.name返回NSMutableDictionaryObject.name返回NSObject。原因是 MacRuby 将 Ruby 运行时和 Objective-C 运行时集成为一体,而且由于 Objective-C 可变字符串的语义与 Ruby 字符串相同,因此 Ruby 的 string 类的整个实现本质上是一行: String = NSMutableString. 而且由于MacRuby位于Objective-C之上,这意味着Objective-C首先开始,这意味着它首先NSMutableString被插入到符号表中,这意味着它首先Module#name.

于 2010-03-20T07:51:14.153 回答
1

在您的示例中,User它只是一个指向Class对象的常量。当包含时,您可以轻松地创建另一个常量指针MyMagicMixin

module MyMagicMixin
  class <<self
    def self.included(klass)
      base.extend MyMagicMixin::ClassMethods
      create_pluralized_alias(klass)
    end

    private

    def create_pluralized_alias(klass)
      fq_name = klass.to_s
      class_name = fq_name.demodulize
      including_module = fq_name.sub(Regexp.new("::#{class_name}$", ''))
      including_module = including_module.blank? ? Object : including_module.constantize
      including_module.const_set class_name.pluralize, klass
    end
  end

  module ClassMethods
    # cass methods here
  end
end

当然,这并不能回答你是否应该做这样的事情。

于 2010-03-20T15:20:43.987 回答