使用块、绑定和评估填充项目的层次结构

Populating a hierarchy of items using blocks, bindings and eval

我正在尝试创建一个 'foo' 项目,其中包含一个名为 'bar' 的子项目。预期输出为:

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

如何获得上面的预期输出?

instance_eval解决方案

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

instance_eval with block is usually a better idea than 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 []]]