是否有可能在非全局环境中评估Ruby DSL?

时间:2010-02-22 14:34:10

标签: ruby metaprogramming dsl

我正在使用Blockenspiel创建一个使用Ruby的DSL。它工作得很好 解决了很多我的问题,但我遇到了以下问题 这与Blockenspiel没有严格的关系。

假设我的DSL看起来像这样:

dish do
  name = 'Pizza'
  ingredients = ...
  nutrition_facts = ...
end

dish do
  name = 'Doner'
  ingredients = ...
  nutrition_facts = ...
end

现在我有了一个菜单编译器,它可以将菜肴编译进去 一份菜单。编译器现在应该能够编译多个菜单文件, 所以它已经设置并清除了全局背景。这应该是优选的 并行发生。

我发现sinatra使用了类变量,但是它有 结果是它只能进行顺序处理而且你 当你想编译一个新的时,必须清除类变量 菜单。另一种方法是使用全局变量。

我更愿意评估范围内的DSL方法 对象,所以没有全局上下文,我可以编译 菜单并行,但上次我试过这个,我遇到了一些 在菜单文件中声明(辅助)方法时出现问题。

哪种方法可行?建议的方法是什么?

2 个答案:

答案 0 :(得分:2)

基本上有两种方法可以达到你想要的效果。

选项a:使用setter-methods产生一个对象:

Dish = Struct.new(:name, :ingredients, :nutrition_facts)
def dish
  d = Dish.new
  yield d
  d
end

dish do |d|
  d.name = 'Pizza'
  d.ingredients = ...
  d.nutrition_facts = ...
end

选项b:您使用实例变量和instance_eval

class Dish
  attr_accessor :name, :ingredients, :nutrition_facts
end
def dish(&blk)
  d = Dish.new
  d.instance_eval(&blk)
  d
end

dish do
  @name = 'Doner'
  @ingredients = ...
  @nutrition_facts = ...
end

在这两种情况下,碟形方法都会返回一个Dish实例,您可以在其中调用例如name访问块中设置的名称(对dish的多次调用将返回独立对象)。请注意,使用instance_eval,用户还可以在块中调用Dish类的私有方法,并且拼写错误的变量名称不会导致错误。

答案 1 :(得分:2)

我见过的许多图书馆都利用instance_eval来做这类事情。

只要表现不是一个大问题,你可以做以下事情:

class Menu
  def initialize file
    instance_eval File.read(file),file,1
  end

  def dish &block
    Dish.new &block
  end
  #....
end

class Dish
  def name(n=nil)
    @name = n if n
    @name
  end
  def ingredients(igrd=nil)
    @ingredients= igrd if igrd
    @ingredients
  end
end
#....

Menu.new'menus / pizza_joint'

<强>菜单/ pizza_joint

dish do
  name 'Cheese Pizza'
  ingredients ['Cheese','Dough','Sauce']
end

实际上有DSL库可以添加#name#ingredients等访问器,因此您无需手动构建它们。例如dslify