从模块内访问包含命名空间的类
我正在开发一个模块,除其他外,该模块将为您将其混合到的类添加一些通用的“查找器”类型功能。问题:出于方便和美观的原因,我想在类之外包含一些功能,与类本身的范围相同。
例如:
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技术交流群](/public/img/jiaqun_03.jpg)
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我倾向于过于聪明。
即使有一个优雅的解决方案,在类内部包含一个在类外部创建类的模块似乎相当奇怪。
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.
这是邮件列表中有时会出现的问题。这也是 Rails 中出现的一个问题。正如您已经怀疑的那样,解决方案基本上是正则表达式修改。
然而,还有一个更根本的问题:在 Ruby 中,类没有名称!类只是一个像其他对象一样的对象。您可以将其分配给实例变量、局部变量、全局变量、常量,甚至根本不将其分配给任何东西。
Module#name
方法基本上只是一种方便的方法,其工作方式如下:它查找已定义常量的列表,直到找到指向接收者的常量。如果找到,则返回它能找到的第一个,否则返回nil。因此,这里有两种失败模式:
Module#name
只会返回它找到的第一个名称现在,如果有人尝试调用
As
来获取A
列表,他们会非常惊讶地发现该方法不存在,但他们可以调用Bs< /code> 来获得相同的结果。
这确实在现实中确实发生过。例如,在 MacRuby 中,
String.name
返回NSMutableString
,Hash.name
返回NSMutableDictionary
和Object。 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 returnsnil
.So, there's two failure modes here:
Module#name
will only return the first one it findsNow, if someone tries to call
As
to get a list ofA
s, they will be pretty surprised to find that that method doesn't exist, but that they can callBs
instead to get the same result.This does actually happen in reality. In MacRuby, for example
String.name
returnsNSMutableString
,Hash.name
returnsNSMutableDictionary
andObject.name
returnsNSObject
. 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 thatNSMutableString
gets inserted into the symbol table first, which means it gets found first byModule#name
.在您的示例中,
User
只是一个指向Class
对象的常量。当包含MyMagicMixin
时,您可以轻松创建另一个常量指针:当然,这并不能回答您是否应该做这样的事情。
In your example,
User
is just a constant that points to aClass
object. You can easily create another constant pointer whenMyMagicMixin
is included:Of course this doesn't answer whether you should do such a thing.