如何实现一个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].