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

发布于 2024-08-26 02:16:01 字数 750 浏览 6 评论 0原文

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

例如:

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,我觉得应该有一种更优雅的方式。

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

I'm working on a module that, among other things, will add some generic 'finder' type functionality to the class you mix it into. The problem: for reasons of convenience and aesthetics, I want to include some functionality outside the class, in the same scope as the class itself.

For example:

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

I can do the functionality within these methods, no problem. And case 1 (User.name('Bob')) is easy. Cases 2–4, however, require being able to create new classes and methods outside User. The Module.included method gives me access to the class, but not to its containing scope. There is no simple "parent" type method that I can see on Class nor Module. (For namespace, I mean, not superclass nor nested modules.)

The best way I can think to do this is with some string parsing on the class's #name to break out its namespace, and then turn the string back into a constant. But that seems clumsy, and given that this is Ruby, I feel like there should be a more elegant way.

Does anyone have ideas? Or am I just being too clever for my own good?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

或十年 2024-09-02 02:16:01

我倾向于过于聪明。

即使有一个优雅的解决方案,在类内部包含一个在类外部创建类的模块似乎相当奇怪。

I'd lean toward being too clever.

Even if there was an elegant solution, it seems rather odd to be including a module inside a class which creates classes outside the class.

太阳男子 2024-09-02 02:16:01

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

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

因此,这里有两种失败模式:

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

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

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

This is a problem that comes up sometimes on the mailinglists. It's also a problem that comes up in Rails. The solution is, as you already suspected, basically Regexp munging.

However, there is a more fundamental problem: in Ruby, classes do not have a name! A class is just an object like any other. You can assign it to an instance variable, to a local variable, to a global variable, to a constant or even not assign it to anything at all. The Module#name method is basically just a convenience method that works like this: it looks through the list of defined constants until it finds one that points to the receiver. If it finds one, it returns the first one it can find, otherwise it returns nil.

So, there's two failure modes here:

a = Class.new
a.name # => nil
B = a
B.name # => "B"
A = B
A.name # => "B"
  • a class might not have a name at all
  • a class might have more than one name, but Module#name will only return the first one it finds

Now, if someone tries to call As to get a list of As, they will be pretty surprised to find that that method doesn't exist, but that they can call Bs instead to get the same result.

This does actually happen in reality. In MacRuby, for example String.name returns NSMutableString, Hash.name returns NSMutableDictionary and Object.name returns NSObject. The reason for this is that MacRuby integrates the Ruby runtime and the Objective-C runtime into one, and since the semantics of an Objective-C mutable string are identical to a Ruby string, the entire implementation of Ruby's string class is essentially a single line: String = NSMutableString. And since MacRuby sits on top of Objective-C, that means that Objective-C starts first, which means that NSMutableString gets inserted into the symbol table first, which means it gets found first by Module#name.

难以启齿的温柔 2024-09-02 02:16:01

在您的示例中,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

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

In your example, User is just a constant that points to a Class object. You can easily create another constant pointer when MyMagicMixin is included:

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

Of course this doesn't answer whether you should do such a thing.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文