使用块,绑定和eval填充项的层次结构

时间:2017-01-31 11:02:55

标签: ruby dependency-injection eval metaprogramming

我正在尝试使用名为“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

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

1 个答案:

答案 0 :(得分:2)

instance_eval解决方案

这是达到你想要的一种方式。

带有阻止的

instance_eval通常比eval更好。

item方法并不复杂:

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

调试模式

这里有与调试信息相同的代码:

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