如何实现一个dsl模块?
How to implement a dsl module?
我是 ruby 的新手,刚开始学习这种语言的元编程技术。现在我正在尝试编写一个 DSL 模块,以便能够像在 RoR 中那样注释 类。不幸的是,我仍然没有得到关于这个主题的一些东西,这在实现中造成了一些麻烦。
这是一个 tag 注释的代码示例:
module Annotations
def self.included(host_class)
host_class.extend(ClassMethods)
end
def tags(*values)
if values.empty?
if defined? @tags
@tags
else
self.class.tags(values)
end
else
@tags = []
values.map { |value| validate_tag(value) }.each { |tag| @tags << tag}
end
end
module ClassMethods
def tags(*values)
unless defined? @tags
superclass_tags = self.superclass.tags if self.superclass.respond_to?(:tags)
@tags = superclass_tags&.any? ? superclass_tags : []
end
if values.empty?
@tags
else
if self.superclass.respond_to?(:tags)
values.map { |value| validate_tag(value) }.each { |tag| @tags << tag}
else
@tags = values.map { |value| validate_tag(value) }
end
@tags
end
end
alias_method :tag, :tags
def validate_tag(tag)
raise 'Tag should be less than 15 chars.' if tag.to_s.length > 15
end
end
end
class Foo
include Annotations
tags :x1, :x2
end
当我尝试执行它时,它总是相同的错误输出。
Foo.tags
=> [nil, nil]
Foo.tag 'c'
Foo.tags
=> [nil]
foo2 = Foo.new
foo2.tags :c5, :c9
foo2.tags
=> [nil, nil]
# etc...
你能帮我改进吗code/make我明白我错在哪里了?
唯一的问题是您的 validate_tag
方法。守卫正在检查作为字符串的标记的长度是否大于 15,如果发生这种情况则引发异常,但是当这种情况没有发生时没有明确添加值。在那种情况下 Ruby returns nil.
如果 tag.to_s.length > 15
returns false:
,您可以尝试将其更新为 return 标签
def validate_tag(tag)
raise 'Tag should be less than 15 chars.' if tag.to_s.length > 15
tag
end
您的 validate_tag
方法没有 return tag
,但您正在映射 tags
中的 values
参数并调用 validate_tag(value)
, 将 [:foo, :bar]
之类的数组转换为 [nil, nil]
.
我是 ruby 的新手,刚开始学习这种语言的元编程技术。现在我正在尝试编写一个 DSL 模块,以便能够像在 RoR 中那样注释 类。不幸的是,我仍然没有得到关于这个主题的一些东西,这在实现中造成了一些麻烦。
这是一个 tag 注释的代码示例:
module Annotations
def self.included(host_class)
host_class.extend(ClassMethods)
end
def tags(*values)
if values.empty?
if defined? @tags
@tags
else
self.class.tags(values)
end
else
@tags = []
values.map { |value| validate_tag(value) }.each { |tag| @tags << tag}
end
end
module ClassMethods
def tags(*values)
unless defined? @tags
superclass_tags = self.superclass.tags if self.superclass.respond_to?(:tags)
@tags = superclass_tags&.any? ? superclass_tags : []
end
if values.empty?
@tags
else
if self.superclass.respond_to?(:tags)
values.map { |value| validate_tag(value) }.each { |tag| @tags << tag}
else
@tags = values.map { |value| validate_tag(value) }
end
@tags
end
end
alias_method :tag, :tags
def validate_tag(tag)
raise 'Tag should be less than 15 chars.' if tag.to_s.length > 15
end
end
end
class Foo
include Annotations
tags :x1, :x2
end
当我尝试执行它时,它总是相同的错误输出。
Foo.tags
=> [nil, nil]
Foo.tag 'c'
Foo.tags
=> [nil]
foo2 = Foo.new
foo2.tags :c5, :c9
foo2.tags
=> [nil, nil]
# etc...
你能帮我改进吗code/make我明白我错在哪里了?
唯一的问题是您的 validate_tag
方法。守卫正在检查作为字符串的标记的长度是否大于 15,如果发生这种情况则引发异常,但是当这种情况没有发生时没有明确添加值。在那种情况下 Ruby returns nil.
如果 tag.to_s.length > 15
returns false:
def validate_tag(tag)
raise 'Tag should be less than 15 chars.' if tag.to_s.length > 15
tag
end
您的 validate_tag
方法没有 return tag
,但您正在映射 tags
中的 values
参数并调用 validate_tag(value)
, 将 [:foo, :bar]
之类的数组转换为 [nil, nil]
.