【问题标题】:Populating a hierarchy of items using blocks, bindings and eval使用块、绑定和评估填充项目的层次结构
【发布时间】:2017-01-31 11:02:55
【问题描述】:

我正在尝试使用名为“bar”的子项创建一个“foo”项。预期的输出是:

foo_item = Item @name="foo", @children=[<...>]
foo_item children = [Item @name="bar", @children=[]]

我正在使用块、绑定和评估。这是我的代码:

class Item
  attr_accessor :name, :children
  def initialize name
    @name = name
    @children = []
  end
end

def item(item_name)
  @item = Item.new(item_name)
  if @context
    @context.eval('@item.children') << @item
  end

  if block_given? 
    old_context = @context if @context
    @context = binding
    yield
    if old_context
      @context = old_context 
    else
      @context = nil
    end
  end
  @item
end

foo_item = item('foo') do 
  item('bar')
end

puts "foo_item = #{foo_item.inspect}"
puts "foo_item children = #{foo_item.children.inspect}"

在下面的实际输出中,foo_item 包含 bar 项,其子项也是 bar 项:

foo_item = Item @name="bar", @children=[<...>]
foo_item children = [Item @name="bar", @children=[<...>]]

给定相同的输入:

foo_item = item('foo') do 
  item('bar')
end

如何获得上述预期输出?

【问题讨论】:

    标签: ruby dependency-injection eval metaprogramming


    【解决方案1】:

    instance_eval 解决方案

    这是实现您想要的一种方法。

    instance_eval with block 通常比eval 更好。

    item 方法不太复杂:

    • 它首先使用item_name 创建一个项目
    • 如果有一个块,它会在item 的上下文中执行它。这意味着在此块中执行的代码将知道@name@children
    • 如果定义了children,则表示当前item方法已在另一个item的块内被调用。当前的item 应该添加到父itemchildren

    class Item
      attr_accessor :name, :children
      def initialize(name)
        @name = name
        @children = []
      end
    
      def inspect
        "#{name} #{children}"
      end
    end
    
    def item(item_name, &block)
      item = Item.new(item_name)
      item.instance_eval(&block) if block
      children << item if defined?(children)
      item
    end
    
    foo_item = item('foo') do
      item('bar') do
        item('biz')
        item('boz')
      end
      item('baz')
    end
    
    p foo_item
    #=> foo [bar [biz [], boz []], baz []
    

    调试模式

    这是带有调试信息的相同代码:

    class Item
      attr_accessor :name, :children
      def initialize(name, indent = "")
        @name = name
        @children = []
        @indent = indent
      end
    
      def inspect
        "#{name} #{children}"
      end
    end
    
    @indent = ""
    def item(name, &block)
      puts "#{@indent}Creating item #{name}"
      item = Item.new(name, @indent + "  ")
      item.instance_eval do
        puts "#{@indent}Inside item #{@name}"
      end
      if block
        puts "#{@indent}  Block is here. Executing it in item #{item.name}"
        item.instance_eval(&block)
      end
      if defined?(children)
        puts "#{@indent}Inside item #{@name}! Adding item #{item.name} to #{@children}"
        children << item 
      end
      item
    end
    

    它输出:

    Creating item foo
      Inside item foo
      Block is here. Executing it in item foo
      Creating item bar
        Inside item bar
        Block is here. Executing it in item bar
        Creating item biz
          Inside item biz
        Inside item bar! Adding item biz to []
        Creating item boz
          Inside item boz
        Inside item bar! Adding item boz to [biz []]
      Inside item foo! Adding item bar to []
      Creating item baz
        Inside item baz
      Inside item foo! Adding item baz to [bar [biz [], boz []]]
    

    【讨论】:

    • 我看到这行得通,但不明白如何。你能解释一下发生了什么吗?
    猜你喜欢
    • 2015-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多