好问题。我完全理解你的动机。让我首先指出,有某些特殊对象,在某些情况下,知道它们被分配到的变量。这些特殊对象例如。Module
实例,Class
实例和Struct
实例:
Dog = Class.new
Dog.name # Dog
问题是,这仅在执行赋值的变量是常量时才有效。(我们都知道 Ruby 常量只不过是情绪敏感的变量。)因此:
x = Module.new # creating an anonymous module
x.name #=> nil # the module does not know that it has been assigned to x
Animal = x # but will notice once we assign it to a constant
x.name #=> "Animal"
对象知道它们被分配了哪些变量的这种行为通常被称为常量魔法(因为它仅限于常量)。但是这种非常受欢迎的常量魔法只适用于某些对象:
Rover = Dog.new
Rover.name #=> raises NoMethodError
幸运的是,我写了一个 gemy_support/name_magic
,它会为你解决这个问题:
# first, gem install y_support
require 'y_support/name_magic'
class Cat
include NameMagic
end
事实上,这只适用于常量(即以大写字母开头的变量)并不是一个很大的限制。事实上,它让您可以随意命名或不命名您的对象:
tmp = Cat.new # nameless kitty
tmp.name #=> nil
Josie = tmp # by assigning to a constant, we name the kitty Josie
tmp.name #=> :Josie
不幸的是,这不适用于数组文字,因为它们是在内部构造的,没有使用依赖的#new
方法。NameMagic
因此,要实现您想要的,您将必须继承子类Array
:
require 'y_support/name_magic'
class MyArr < Array
include NameMagic
end
foo = MyArr.new ["goo", "baz"] # not named yet
foo.name #=> nil
Foo = foo # but assignment to a constant is noticed
foo.name #=> :Foo
# You can even list the instances
MyArr.instances #=> [["goo", "baz"]]
MyArr.instance_names #=> [:Foo]
# Get an instance by name:
MyArr.instance "Foo" #=> ["goo", "baz"]
MyArr.instance :Foo #=> ["goo", "baz"]
# Rename it:
Foo.name = "Quux"
Foo.name #=> :Quux
# Or forget the name again:
MyArr.forget :Quux
Foo.name #=> nil
# In addition, you can name the object upon creation even without assignment
u = MyArr.new [1, 2], name: :Pair
u.name #=> :Pair
v = MyArr.new [1, 2, 3], ɴ: :Trinity
v.name #=> :Trinity
我通过搜索当前 Ruby 对象空间的所有命名空间中的所有常量来实现常量魔术模仿行为。这浪费了几分之一秒,但由于只执行一次搜索,一旦对象找出它的名称,就没有性能损失。未来,Ruby 核心团队已经承诺const_assigned
hook。