更改 ruby 块内的上下文/绑定
Posted
技术标签:
【中文标题】更改 ruby 块内的上下文/绑定【英文标题】:Change the context/binding inside a block in ruby 【发布时间】:2011-08-16 14:32:36 【问题描述】:我在 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
块内代码的上下文/绑定更改为不同于默认值。
关于这是否可能以及我如何做到这一点的任何想法?
【问题讨论】:
【参考方案1】:粘贴此代码:
def evaluate(&block)
@self_before_instance_eval = eval "self", block.binding
instance_eval &block
end
def method_missing(method, *args, &block)
@self_before_instance_eval.send method, *args, &block
end
更多信息,请参考这篇非常好的文章here
【讨论】:
评估特别吗?链接的文章没有这样指出。我的代码在command
的定义中执行yield
。你是说我应该把 &block 放在我的方法 sig 中,然后 instance_eval 那个块而不是 yield? (使用此信息更新问题)【参考方案2】:
class CommandDSL
def self.call(&blk)
# Create a new CommandDSL instance, and instance_eval the block to it
instance = new
instance.instance_eval(&blk)
# Now return all of the set instance variables as a Hash
instance.instance_variables.inject() |result_hash, instance_variable|
result_hash[instance_variable] = instance.instance_variable_get(instance_variable)
result_hash # Gotta have the block return the result_hash
end
def desc(str); @desc = str; end
def switch(sym); @switch = sym; end
def action(&blk); @action = blk; end
end
def command(name, &blk)
values_set_within_dsl = CommandDSL.call(&blk)
# INSERT CODE HERE
p name
p values_set_within_dsl
end
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
将打印:
:list
:@desc=>"show todos in long form", :@switch=>:l, :@action=>#<Proc:0x2392830@C:/Users/Ryguy/Desktop/tesdt.rb:38>
【讨论】:
【参考方案3】:也许
def command(*names, &blk)
command = make_command_object(..)
command.instance_eval(&blk)
end
可以在命令对象的上下文中评估块。
【讨论】:
【参考方案4】:我编写了一个类来处理这个确切的问题,并处理诸如@instance_variable 访问、嵌套等问题。这是另一个问题的文章:
Block call in Ruby on Rails
【讨论】:
以上是关于更改 ruby 块内的上下文/绑定的主要内容,如果未能解决你的问题,请参考以下文章