以任何方式捕获声明中常量的名称?

我上课了。 我们称之为SomeClass:

class SomeClass end 

我不想用常规方法定义这个类的实例,而是使用常量来定义它们:

 MyConstant = SomeClass.new 

我希望能够捕获某些类设置的常量的名称,这与标准ruby类使用.class方法的方式非常相似。

 MyConstant.name #-> "MyConstant" 

我希望能够这样做以从某些类的所有实例呈现更好的错误消息,如下所示:

 class SomeClass def display_error_message "Error, some class #{self.name} has a problem" end end MyConstant.display_error_message #-> "Error, some class MyConstant has a problem" 

有没有办法实现这个目标?

编辑


这是一个澄清我正在拍摄的例子。

(Enum是我正在创建的类的名称,它的作用类似于Swifts的Enum’类型。基本上它设置了一个预定义的选项列表(:pepperoni,:sausage,:mushroom)和raw_value(“Pepperoni” “,”“香肠”,“蘑菇”。)显然在这个例子中,用于将符号转换为UpperCamel案例字符串的哈希或简单算法可以工作,但实际上enum类会做更多,但是这个例子显示了它的要点。

 class Pizza attr_reader :topping Toppings = Enum.new do option(:pepperoni).set("Pepperoni") option(:sausage).set("Sausage") option(:mushrooms).set("Mushrooms") end def set_topping(symbol) @topping = Toppings[symbol] end end pizza = Pizza.new ### Happy Case pizza.set_topping(:pepperoni) ### Sad Case (Error message shown below is what I'm trying to figure out) pizza.set_topping(:spinach) #-> Error. enum Toppings has no option spinach 

变量只是引用对象的一种方式,变量的名称是无关紧要的。 如果你说X = YY恰好是一个类,那么Y类已经有了名字"Y" ,所以你不能改变它。

就Ruby而言, XY是难以区分的。

如果要更改名称,即使该子类不执行任何不同操作,也可以创建子类:

 X = Class.new(Y) X.name # => "X" Z = X Z.name # => "X" 

这样可以正确保留名称,但仅限于初始化的上下文中。 我认为Ruby做了一些偷偷摸摸的事情,如果将一个新类分配给一个常量,它会分配一个名称,但对于普通变量,它不会:

 x = Class.new(Y) x.name # => nil 

所以这是一个特例。

这里的关键是,一个确实影响名称的子类和一个没有影响名称的变量引用之间存在巨大差异。

还有其他一些奇怪的东西在这里发生,因为看起来这个类以某种方式“知道”什么时候它被分配给某个东西而且如果那个东西是一个常量它会窃取常量的名字:

 z = Class.new z.name # => nil Z = z z.name # => "Z" 

正如他们在编程中所说:“Wat?”

您的Enum类看起来像这样:

 class Enum def initialize(name, &blk) @defined_options = {} @name = name.freeze instance_eval(&blk) @defined_options.freeze end def [](key) if @defined_options.key? key @defined_options[key].value else unfound_option = Option.new(@name, key) raise "Option #{unfound_option} not found." end end def to_s "#{@name}" end def inspect keys = @defined_options.keys.join(',') "#<#{self}::{#{keys}}>" end class Option attr_reader :value def initialize(enum_name, key) @value_initialized = false @enum_name = enum_name @key = key end def set(value) if @value_initialized raise "Value for #{self} can't be set to #{value} " + "because it is already initialized to #{@value}" else @value_initialized = true @value = value.freeze end end def to_s "#{@enum_name}::#{@key}" end def inspect "#<#{self}>" end end private def option(sym) unless @defined_options.key? sym option = Option.new(@name, sym) @defined_options[sym] = option end @defined_options[sym] end end 

现在,您几乎可以保留问题中的语法,并执行以下操作:

 class Pizza attr_reader :topping # I had to add the name in the initializer for better error reporting. Toppings = Enum.new('Toppings') do option(:pepperoni).set("Pepperoni") option(:sausage).set("Sausage") option(:mushrooms).set("Mushrooms") end def set_topping(symbol) @topping = Toppings[symbol] end end pizza = Pizza.new ### Happy Case pizza.set_topping(:pepperoni) #=> "Pepperoni" ### Sad Case (Error message shown below is what I'm trying to figure out) pizza.set_topping(:spinach) #=> RuntimeError: Option Toppings::spinach not found. 

这可能会让您了解如何解决此问题。 这只是课程的粗略草图,可能会根据您的需要进行调整。