Ruby 中带有大写字母的方法的可选括号?
我刚刚开始在 .NET 应用程序中使用 IronRuby(但当我在普通 Ruby 中测试它时,其行为似乎是一致的)作为 DSL - 作为其中的一部分,我正在定义通过 Define_method 从 DSL 调用的方法。
但是,在调用以大写字母开头的方法时,我遇到了有关可选括号的问题。
给出以下程序:
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 `<main>'
我意识到 Ruby 约定是常量以大写字母开头,并且 Ruby 中方法的一般命名约定是小写字母。但括号目前确实扼杀了我的 DSL 语法。
有什么办法可以解决这个问题吗?
I just started out using IronRuby (but the behaviour seems consistent when I tested it in plain Ruby) for a DSL in my .NET application - and as part of this I'm defining methods to be called from the DSL via define_method.
However, I've run into an issue regarding optional parens when calling methods starting with an uppercase letter.
Given the following program:
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
Running this code in a console (using plain ruby) yields the following output:
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>'
I realize that the Ruby convention is that constants start with an uppercase letter and that the general naming convention for methods in Ruby is lowercase. But the parens are really killing my DSL syntax at the moment.
Is there any way around this issue?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这只是 Ruby 歧义解决的一部分。
在 Ruby 中,方法和变量位于不同的命名空间中,因此可以存在同名的方法和变量(或常量)。这意味着,在使用它们时,需要有某种方法来区分它们。一般来说,这不是问题:消息有接收者,而变量没有。消息有参数,而变量没有。变量被分配,消息则没有。
唯一的问题是当你没有接收者、没有参数、也没有分配时。然后,Ruby 无法区分不带参数的无接收者消息发送和变量之间的区别。因此,它必须制定一些任意规则,这些规则基本上是:
请注意,对于使用以下命令发送的消息参数(即使参数列表为空),没有歧义,这就是您的第三个示例有效的原因。
test()
:显然是一个消息发送,这里没有歧义test
:可能是一个消息发送或一个变量;解析规则说这是一个消息发送Test()
:显然是一个消息发送,这里没有歧义self.Test
:也显然是一个消息发送,这里没有歧义Test
:可能是发送的消息或常量;解析规则说它是一个常量请注意,这些规则有点微妙,例如:
这些规则说,是否将不明确的标记解释为变量或消息发送由解析器确定而不是口译员。因此,因为解析器已经看到
foo =whatever
,所以它将foo
标记为变量,即使代码永远不会被执行并且foo
将与 Ruby 中所有未初始化的变量一样,计算结果为nil
。TL;DR 摘要:你是 SOL。
您可以做的是覆盖
const_missing
以转换为消息发送。像这样的东西:除了这显然行不通,因为
const_missing
是在DemoClass
上定义的,因此,当const_missing
运行时,self
是DemoClass
,这意味着它在应该通过调用
。DemoClass#test
时尝试调用DemoClass.test
演示.测试我不知道如何轻松解决这个问题。
This is just part of Ruby's ambiguity resolution.
In Ruby, methods and variables live in different namespaces, therefore there can be methods and variables (or constants) with the same name. This means that, when using them, there needs to be some way to distinguish them. In general, that's not a problem: messages have receivers, variables don't. Messages have arguments, variables don't. Variables are assigned to, messages aren't.
The only problem is when you have no receiver, no argument and no assignment. Then, Ruby cannot tell the difference between a receiverless message send without arguments and a variable. So, it has to make up some arbitrary rules, and those rules are basically:
Note that for a message send with arguments (even if the argument list is empty), there is no ambiguity, which is why your third example works.
test()
: obviously a message send, no ambiguity heretest
: might be a message send or a variable; resolution rules say it is a message sendTest()
: obviously a message send, no ambiguity hereself.Test
: also obviously a message send, no ambiguity hereTest
: might be a message send or a constant; resolution rules say it is a constantNote that those rules are a little bit subtle, for example here:
The rules say that whether an ambiguous token gets interpreted as a variable or a message send is determined by the parser and not the interpreter. So, because the parser has seen
foo = whatever
, it tagsfoo
as a variable, even though the code will never get executed andfoo
will evaluate tonil
as all uninitialized variables in Ruby do.TL;DR summary: you're SOL.
What you could do is override
const_missing
to translate into a message send. Something like this:Except this obviously won't work, since
const_missing
is defined onDemoClass
and thus, whenconst_missing
is run,self
isDemoClass
which means that it tries to callDemoClass.test
when it should be callingDemoClass#test
viademo.test
.I don't know how to easily solve this.