我正在使用Blockenspiel创建一个使用Ruby的DSL。它工作得很好 解决了很多我的问题,但我遇到了以下问题 这与Blockenspiel没有严格的关系。
假设我的DSL看起来像这样:
dish do
name = 'Pizza'
ingredients = ...
nutrition_facts = ...
end
dish do
name = 'Doner'
ingredients = ...
nutrition_facts = ...
end
现在我有了一个菜单编译器,它可以将菜肴编译进去 一份菜单。编译器现在应该能够编译多个菜单文件, 所以它已经设置并清除了全局背景。这应该是优选的 并行发生。
我发现sinatra使用了类变量,但是它有 结果是它只能进行顺序处理而且你 当你想编译一个新的时,必须清除类变量 菜单。另一种方法是使用全局变量。
我更愿意评估范围内的DSL方法 对象,所以没有全局上下文,我可以编译 菜单并行,但上次我试过这个,我遇到了一些 在菜单文件中声明(辅助)方法时出现问题。
哪种方法可行?建议的方法是什么?
答案 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