Ruby中的可选parens用于带大写起始字母的方法?

我刚刚开始使用IronRuby(但是当我在普通的Ruby中测试它时,行为似乎是一致的)我的.NET应用程序中的DSL – 作为其中的一部分,我定义了通过define_method从DSL调用的方法。

但是,在调用以大写字母开头的方法时,我遇到了关于可选parens的问题。

鉴于以下计划:

class DemoClass define_method :test do puts "output from test" end define_method :Test do puts "output from Test" end def run puts "Calling 'test'" test() puts "Calling 'test'" test puts "Calling 'Test()'" Test() puts "Calling 'Test'" Test end end demo = DemoClass.new demo.run 

在控制台中运行此代码(使用普通ruby)会产生以下输出:

 ruby .\test.rb Calling 'test' output from test Calling 'test' output from test Calling 'Test()' output from Test Calling 'Test' ./test.rb:13:in `run': uninitialized constant DemoClass::Test (NameError) from ./test.rb:19:in `' 

我意识到Ruby约定是常量以大写字母开头,并且Ruby中方法的一般命名约定是小写的。 但是parens目前正在杀死我的DSL语法。

有没有解决这个问题的方法?

这只是Ruby暧昧解决方案的一部分。

在Ruby中,方法和变量存在于不同的名称空间中,因此可以存在具有相同名称的方法和变量(或常量)。 这意味着,在使用它们时,需要有一些方法来区分它们。 一般来说,这不是问题:消息有接收器,变量没有。 消息有参数,变量没有。 变量分配给,消息不分配。

唯一的问题是你没有接收器,没有参数和没有任务。 然后,Ruby无法区分无参数的无接收消息与变量之间的区别。 所以,它必须构成一些任意规则,这些规则基本上是:

  • 对于以小写字母开头的模糊标记,更喜欢将其解释为消息发送, 除非您肯定知道它是变量(即解析器 (不是(!)解释器)之前已经看过一个赋值)
  • 对于以大写字母开头的模糊标记,更喜欢将其解释为常量

请注意,对于带参数的消息发送(即使参数列表为空),也没有歧义,这就是您的第三个示例有效的原因。

  • test() :显然是一条消息发送,这里没有歧义
  • test :可能是消息发送或变量; 解决规则说它是一个消息发送
  • Test() :显然是一条消息发送,这里没有歧义
  • self.Test :显然也是一个消息发送,这里没有歧义
  • Test :可能是消息发送或常量; 决议规则说这是一个常数

请注意,这些规则有点微妙,例如:

 if false foo = 'This will never get executed' end foo # still this will get interpreted as a variable 

规则表明,模糊标记是否被解释为变量或消息发送是由解析器而不是解释器确定的。 因此,因为解析器已经看到foo = whatever ,所以它将foo标记为变量,即使代码永远不会执行,并且foo将评估为nil因为Ruby中的所有未初始化变量都是如此。

TL; DR摘要:你是SOL。

可以做的是覆盖const_missing以转换为消息发送。 像这样的东西:

 class DemoClass def test; puts "output from test" end def Test; puts "output from Test" end def run puts "Calling 'test'" test() puts "Calling 'test'" test puts "Calling 'Test()'" Test() puts "Calling 'Test'" Test end def self.const_missing(const) send const.downcase end end demo = DemoClass.new demo.run 

除了这显然不起作用,因为const_missing是在const_missing上定义的,因此,当运行const_missing时, selfconst_missing ,这意味着当它应该通过demo.test调用demo.test时它会尝试调用demo.test

我不知道如何轻松解决这个问题。