代码之家  ›  专栏  ›  技术社区  ›  user141335

是否可以在非全局上下文中评估RubyDSL?

  •  3
  • user141335  · 技术社区  · 15 年前

    我在用 Blockenspiel 用Ruby创建DSL。它很管用,而且 解决了很多问题,但我遇到了以下问题 这与Blockenspiel无关。

    假设我有一个这样的DSL:

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

    现在我有了一个菜单编译程序,它把盘子编译成 菜单。编译器现在应该能够编译多个菜单文件, 因此,它已经设置并清除了一个全局上下文。最好是这样 并行发生。

    我发现Sinatra使用类变量,但这有 其结果是它只能进行顺序处理, 要编译新的 菜单。另一种选择是使用全局变量。

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

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

    2 回复  |  直到 15 年前
        1
  •  2
  •   BaroqueBobcat    15 年前

    我见过的许多图书馆都在利用 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“菜单/比萨店”

    菜单/比萨店

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

    实际上,有些DSL库添加了类似这样的访问器 #name #ingredients 所以你不必手工建造它们。如 dslify

        2
  •  2
  •   sepp2k    15 年前

    基本上有两种方法可以实现你想要的。

    选项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的多次调用将返回独立的对象)。请注意,对于实例eval,用户还可以调用块中dish类的私有方法,拼写错误的变量名不会导致错误。