我正在研究一个模块,除其他外,它将为你混合的类添加一些通用的'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,我觉得应该有更优雅的方式。
有没有人有想法?或者我只是为了自己的利益而过于聪明?
答案 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.name
,NSMutableString
返回Hash.name
,NSMutableDictionary
返回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
当然,这不能回答你应该做这样的事情。