【问题标题】:Change the context/binding inside a block in ruby更改 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 块内代码的上下文/绑定更改为不同于默认值。

关于这是否可能以及我如何做到这一点的任何想法?

【问题讨论】:

    标签: ruby metaprogramming block


    【解决方案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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-12-08
          • 2013-05-24
          • 1970-01-01
          • 2017-05-26
          • 2022-06-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多