ruby,在不污染全局命名空间的情况下为 DSL 创建类型

ruby, create types for DSL without polluting global namespace

我有一个使用自定义类型的 DSL(从基元继承的对象,即 String),但我不想用它们污染全局命名空间,因为它们只在少数情况下使用地方,别无他处。

如果我尝试将类型包装在与 DSL 执行上下文相同的模块命名空间中,我会得到 NameErroruninitialized constant。我也试过包括包装类型的模块,甚至对 Object class 进行了改进,但没有任何效果。

由于问题的(相对)复杂性,我不确定这是否是您需要帮助我的,所以我猜测。

module Forms
  class Define
    class << self
      attr_accessor :forms
      def draw name, &script
        @forms[name] = new(&script)
      end
    end

    def initialize &description
      @meta = {}
      @section_counter = 0
      self.tree = Tree.new
      instance_eval &description
    end
  end
end

并像这样使用:

Forms::Define.draw 'Register' do
  #some dsl code...
end

编辑:谢谢,无论如何都没注意到错别字。

既然大家都不明白"I have a DSL that uses custom types"和"I don't want to pollute the global namespace with them"我就把问题说得更具体些。

我是说我有这样的类型:

class Some_Custom_Type < String
end

但是这样做会污染全局命名空间,所以如果我像这样命名类型:

module Forms
  class Some_Custom_Type < String
  end
end

然后尝试使用它:

Forms::Define.draw 'Register' do
  some_code_using Some_Custom_Type
  #some dsl code...
end

会报错uninitialized constant Some_Custom_Type (NameError)

所以我再次问得更清楚一点,我如何确保 Some_Custom_Type 在 DSL 的执行上下文中可用而不污染全局命名空间?

我不确定我是否正确理解了这个问题,但类似的内容可能会有所帮助:

@forms[name] = const_set(
  name, 
  Class.new(Object) do |c|
    c.send :define_method, :initialize, *args, &script
  end
)

调用 Forms::Define.draw 'Register' 后,您将有一个 class Forms::Define::Register,可在任何地方使用。

我通过创建接口解决了这个问题。

从 OP 中的示例给出:

module Forms
  module Types
    class Some_Custom_Type < String
    end
  end
end

我补充了:

module Forms
  module Types_Wrapper
    def some_custom_type
      Some_Custom_Type
    end
  end

  def self.included(child)
    child.include Types_Wrapper
  end
end

是这样添加的:

module Forms
  class Define
    include Types
  end
end

并且可以这样使用:

Forms::Define.draw 'Register' do
  some_code_using some_custom_type
  #some dsl code...
end

超过:

Forms::Define.draw 'Register' do
  some_code_using Some_Custom_Type
  #some dsl code...
end

差别很小,但现在可以用了,而且 DSL 更符合我的感觉。