Отличный вопрос. Я полностью понимаю вашу мотивацию. Позвольте мне вначале отметить, что существуют определенные виды специальных объектов, которые при определенных обстоятельствах знают переменную, которой они были назначены. Эти специальные объекты, например. 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
К счастью, Я написал камень y_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 .