更改 ruby 中块内的上下文/绑定
我在 Ruby 中有一个 DSL,其工作方式如下:
desc 'list all todos'
command :list do |c|
c.desc 'show todos in long form'
c.switch :l
c.action do |global,option,args|
# some code that's not relevant to this question
end
end
desc 'make a new todo'
command :new do |c|
# etc.
end
一位开发人员建议我增强 DSL,使其不需要将 c
传递到 command
块,因此不需要 c.
对于所有 里面的方法;据推测,他暗示我可以让以下代码以同样的方式工作:
desc 'list all todos'
command :list do
desc 'show todos in long form'
switch :l
action do |global,option,args|
# some code that's not relevant to this question
end
end
desc 'make a new todo'
command :new do
# etc.
end
command
的代码看起来像是
def command(*names)
command = make_command_object(..)
yield command
end
我尝试了几件事但无法让它工作;我不知道如何将 command
块内代码的上下文/绑定更改为与默认值不同。
关于这是否可能以及我该如何做有什么想法吗?
I have a DSL in Ruby that works like so:
desc 'list all todos'
command :list do |c|
c.desc 'show todos in long form'
c.switch :l
c.action do |global,option,args|
# some code that's not relevant to this question
end
end
desc 'make a new todo'
command :new do |c|
# etc.
end
A fellow developer suggested I enhance my DSL to not require passing c
to the command
block, and thus not require the c.
for all
the methods inside; presumably, he implied I could make the following code work the same:
desc 'list all todos'
command :list do
desc 'show todos in long form'
switch :l
action do |global,option,args|
# some code that's not relevant to this question
end
end
desc 'make a new todo'
command :new do
# etc.
end
The code for command
looks something like
def command(*names)
command = make_command_object(..)
yield command
end
I tried several things and was unable to get it to work; I couldn't figure out how to change the context/binding of the code inside the command
block to be different than the default.
Any ideas on if this is possible and how I might do it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
粘贴此代码:
有关更多信息,请参阅这篇非常好的文章 此处
Paste this code:
For more information, refer to this really good article here
也许
可以在命令对象的上下文中评估该块。
Maybe
can evaluate the block in the context of command object.
将打印:
Will print:
我编写了一个类来处理这个确切的问题,并处理 @instance_variable 访问、嵌套等问题。这是另一个问题的文章:
Block call in Ruby on Rails< /a>
I wrote a class that handles this exact issue, and deals with things like @instance_variable access, nesting, and so forth. Here's the write-up from another question:
Block call in Ruby on Rails
@Jatin Ganhotra 的答案似乎更准确,但需要根据问题进行调整并提供更多信息。
下面是一个改编的解决方案
,有两个主要修改和一个改进:(
nil
method_missing的默认行为当我们不在
应该占上风evaluate
调用的上下文中(也就是我们调用super
的地方)时,instance_exec
允许传递参数到块
。这样我们就可以使用块接收的参数调用评估,最终用户可能仍然想使用它们(并且为了与现有定义向后兼容)。在问题的场景中,我们假设有一个类
Command
。您可以在其中包含此模块:然后,顶级
command
方法将定义如下:command
作为参数传递给块。与以前的定义向后兼容,其中命令
明确被称为块的参数。您为
command
创建的新块将隐式引用您的Command
对象的方法,从而使以下内容按您的预期工作: 。
必须注意的是,当使用
evaluate
时,带有missing_method
钩子的方法将引用回原始调用者 这意味着在command
块中,您应该能够引用该块的原始上下文中可用的方法(即argument
)。但是,如果该方法也存在于您的Command
对象中,则会改为调用它:command
将通过正确调用(从主上下文) missing_method
,此方法将无法将最后一个desc
描述链接到嵌套的command
,因为desc
作为存在Command
方法(以及command
对象是其块内的self
)。在此更改之前,上述情况不会发生。但我猜这是使用嵌套 DSL 引用冲突方法(在本例中为
desc
)的常见问题。解决方法
您可以使用
command
方法的命名参数来解决此问题:还有其他替代方法,但这里不是主题。
@Jatin Ganhotra answer seems the more accurate one, yet needs to be adapted to the question and provide some more info.
The below is an adapted solution
With two main amendments and one improvement:
nil
once the block has been evaluatedmethod_missing
should prevail when we are not in the context of anevaluate
call (that's where we callsuper
)instance_exec
allows to pass parameters to theblock
. This way we can callevaluate
with params that would be received by the block, may the end user would still want to use them (and for backwards compatibility with existing definitions).In the question's scenario, let's suppose there is a class
Command
. You would include this module in it:Then, the top level
command
method would be defined like this:command
as a parameter to the block. Backwards compatible with previous definitions wherecommand
is explicitly referred to as as an argument of the block.New blocks you create for
command
will implicitly refer to methods of yourCommand
object, making the below to work as you expected:Cross-context clashed methods
It must be noted, that the approach with
missing_method
hook will refer back to the original caller, whenevaluate
has been used. This means that within thecommand
block you are supposed to be able to refer to methods that were available in the original context of the block (i.e.argument
). However, if that method also exists in yourCommand
object, it will be called instead:command
will be correctly called (from the main context) viamissing_method
, this approach will fail to link the lastdesc
description to the nestedcommand
, becausedesc
exists as aCommand
method (and thecommand
object isself
within its block).rpec
does.Before this change, the above would not happen. But I guess that's a normal problem of using nested DSLs that refer to methods that clash (
desc
in this case).Work Around
You could though work this around with named parameters to the
command
method:There are other alternatives, but it is off topic here.