将Ruby常量转换为字符串

3
我想将常量FOO::BAR转换为字符串"BAR"
这与constantize的作用相反,非常类似于demodulize,但我希望得到字符串而不是实际的模块引用。
我想自己制作一个助手来完成这个任务,但我无法获得FOO::BAR的“字符串化”版本。
最好能避免使用任何第三方gems。
示例:
class FOO
  BAR = {}
end

# this works
FOO            #=> FOO
FOO.name       #=> "FOO"    

# this doesn't
FOO::BAR       #=> {} 
FOO::BAR.name  #=> NoMethodError: undefined method `name' for {}:Hash

你想做的事情没有意义。为什么要获取常量名称的“名称”?这样做有什么用途? - Simone Carletti
常量名称也是我正在实现的服务的一个值。这使我不必执行 BAR = { name: 'BAR', other_field: other_value }。如果我可以使用变形来将常量名称转换为字符串,那么我就能保持DRY。 - doremi
1
你的问题不够清晰:在标题中,你说你想要一个模块的名称,在第一段中,你说你想要一个模块或类的名称,但是在你的代码示例中,你实际上有一个哈希(显然没有名称)。 - Jörg W Mittag
2个回答

5
您无法传递常量,只能传递对象。如果您将 FOO::BAR 传递给一个方法,您传递的不是常量,而是已分配给 FOO::BAR 的对象,即哈希表。

要从被分配对象中检索出已分配的常量名称,对象必须以某种方式存储该名称。

模块确实会存储它们所分配的常量名称(当模块第一次分配到常量时,Ruby 会设置名称)。并且因为 FOO 是一个模块(类也是模块),您可以调用 FOO.name 并返回 "FOO"。但这仅在对象“知道”自己的名称时才有效。

在内置对象中,只有 Module(因此包括 Class)具有按此方式工作的 name 方法。

可以向哈希实例添加一个 name 方法,该实例指向 FOO::BAR,但这可能不是您想要的:

def (FOO::BAR).name
  'FOO::BAR'
end

FOO::BAR.name #=> "FOO::BAR"

另一种方法是将常量(实际上是对象)和它所在的模块一起传递给一个方法:

def find_const(mod, obj)
  mod.constants.find { |c| mod.const_get(c).equal?(obj) }
end

find_const(FOO, FOO::BAR) #=> :BAR

该方法遍历模块的常量并返回引用传递对象的(第一个)常量。

0
class FOO
  class BAR           
  end
end

FOO::BAR.to_s.split('::').last
  #=> "BAR"

请注意,方法Module#to_s“返回表示此模块或类的字符串。对于基本类和模块,这是名称”。Module#name可以替代to_s

这样做不起作用,因为它会在常量值上调用.to_s而不是常量名称本身。 - doremi
我不理解你的评论。难道你不想要一个接受嵌套类(例如 FOO::BAR)作为参数并返回最内层类名字符串(这里是 "BAR")的方法吗?这个方法就是实现了这个功能。 - Cary Swoveland
我想将一个指向类常量 FOO::BAR 的引用传递给一个方法,该方法将转换并返回常量名称 BAR 为字符串 "BAR"。您的方法应该返回 FOO::BAR 的值,并在常量值上调用 .to_s,而不是常量名称本身。 - doremi
我仍然不理解你的观点,但我已经添加了一些内容来澄清。这可能也有所帮助:VAL = 6; VAL.to_s #=> "6",而FOO.to_s #=> "FOO"VALFOO都是常量,但FOO也是一个类名。 - Cary Swoveland
问题在于BAR不是一个类,而是Foo类上的一个常量。看看这如何改变你的答案。 - doremi
我真的很感激你的努力,很抱歉你不理解。Stefan明白了,我会接受他的答案。 - doremi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接