Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
可以在非全局上下文中评估Ruby DSL吗?_Ruby_Metaprogramming_Dsl - Fatal编程技术网

可以在非全局上下文中评估Ruby DSL吗?

可以在非全局上下文中评估Ruby DSL吗?,ruby,metaprogramming,dsl,Ruby,Metaprogramming,Dsl,我正在使用Ruby创建一个DSL。它工作得很好,而且 解决了我的很多问题,但我遇到了以下问题 这与Blockenspiel没有严格的关系 假设我有一个DSL,看起来像这样: dish do name = 'Pizza' ingredients = ... nutrition_facts = ... end dish do name = 'Doner' ingredients = ... nutrition_facts = ... end 现在,我有了一个菜单编译器,它

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

假设我有一个DSL,看起来像这样:

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

dish do
  name = 'Doner'
  ingredients = ...
  nutrition_facts = ...
end
现在,我有了一个菜单编译器,它可以将菜肴编译成 菜单。编译器现在应该能够编译多个菜单文件, 因此,它设置并清除了一个全局上下文。这应该是最好的 同时发生

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

我更愿意在 对象,这样就没有全局上下文,我可以编译 菜单是并行的,但上次我尝试这个,我遇到了一些问题 在菜单文件中声明(helper-)方法时出现问题


哪些方法是可行的?推荐的方法是什么?

基本上有两种方法来归档您想要的内容

选项a:使用setter方法生成对象:

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:使用实例变量和实例求值

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方法都将返回dish实例,您可以在其上调用,例如
name
,以访问块中设置的名称(对dish的多次调用将返回独立对象)。请注意,使用instance_eval,用户还可以调用块中Dish类的私有方法,拼写错误的变量名不会导致错误。

我所看到的许多库所做的就是利用
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
#....
菜单。新的“菜单/比萨店”

菜单/比萨店

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

实际上,有些DSL库添加了诸如
#name
#components
之类的访问器,因此您不必手动构建它们。例如

如果你用
self.foo=
替换
@foo=
,你就不必担心拼写错误。但我仍然需要一个全局菜单,这是我想要避免的。你可以创建一个
菜单(name='default')do。。。将
组件结束到DSL。
dish
方法只能在
菜单中使用。菜单文件中的helper方法到底有什么问题?我希望菜单名取自我编译的菜单文件。我想在没有全局状态或上下文的情况下并行编译所有菜单。上下文只能是菜单文件的本地上下文。我还可以声明新函数并在计算菜单时在菜单中使用它们吗?可以
instance_eval
,就像一个块一样,允许您在其中定义类、模块和方法。