从模块中访问包含类的命名空间

时间:2010-03-20 03:22:27

标签: ruby namespaces module metaprogramming mixins

我正在研究一个模块,除其他外,它将为你混合的类添加一些通用的'finder'类型功能。问题:出于方便和美观的原因,我想在类外部包含一些功能,与类本身在同一范围内。

例如:

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方法使我可以访问该类,但不能访问其包含的范围。没有简单的“父”类型方法,我可以在类和模块上看到。 (对于名称空间,我的意思是,不是超类,也不是嵌套模块。)

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

有没有人有想法?或者我只是为了自己的利益而过于聪明?

3 个答案:

答案 0 :(得分:3)

我倾向于过于聪明。

即使有一个优雅的解决方案,在一个类内部中创建一个类类之外的类似乎很奇怪。

答案 1 :(得分:2)

这是邮件列表中有时出现的问题。这也是Rails中出现的问题。正如您所怀疑的那样,解决方案基本上是Regexp munging。

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

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

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

现在,如果有人试图调用As来获取A的列表,他们会惊讶地发现该方法不存在,但他们可以调用{{1而不是得到相同的结果。

实际上是在现实中发生的。在MacRuby中,例如Bs返回String.nameNSMutableString返回Hash.nameNSMutableDictionary返回Object.name。原因是MacRuby将Ruby运行时和Objective-C运行时集成为一个,并且由于Objective-C可变字符串的语义与Ruby字符串相同,所以Ruby的字符串类的整个实现基本上是一行:NSObject。由于MacRuby位于Objective-C之上,这意味着Objective-C首先启动,这意味着String = NSMutableString首先被插入到符号表中,这意味着它首先被找到 NSMutableString

答案 2 :(得分: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

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