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

Ras*_*sKL 2 ruby ironruby

我刚开始使用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
Run Code Online (Sandbox Code Playgroud)

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

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 `<main>'
Run Code Online (Sandbox Code Playgroud)

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

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

Jör*_*tag 7

这只是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
Run Code Online (Sandbox Code Playgroud)

规则表明,模糊标记是否被解释为变量或消息发送是由解析器而不是解释器确定的.因此,因为解析器已经看到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
Run Code Online (Sandbox Code Playgroud)

除了这显然是行不通的,因为const_missing定义上DemoClass,因此,当const_missing运行时,selfDemoClass这意味着它试图调用DemoClass.test的时候,应该调用DemoClass#test通过demo.test.

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