ruby,在不污染全局命名空间的情况下为 DSL 创建类型
ruby, create types for DSL without polluting global namespace
我有一个使用自定义类型的 DSL(从基元继承的对象,即 String
),但我不想用它们污染全局命名空间,因为它们只在少数情况下使用地方,别无他处。
如果我尝试将类型包装在与 DSL 执行上下文相同的模块命名空间中,我会得到 NameError
、uninitialized 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 更符合我的感觉。
我有一个使用自定义类型的 DSL(从基元继承的对象,即 String
),但我不想用它们污染全局命名空间,因为它们只在少数情况下使用地方,别无他处。
如果我尝试将类型包装在与 DSL 执行上下文相同的模块命名空间中,我会得到 NameError
、uninitialized 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 更符合我的感觉。