123456789_123456789_123456789_123456789_123456789_

Class: YARD::Tags::MacroDirective

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, Directive
Instance Chain:
self, Directive
Inherits: YARD::Tags::Directive
Defined in: lib/yard/tags/directives.rb

Overview

Defines a block of text to be expanded whenever the macro is called by name in subsequent docstrings. The macro data can be any arbitrary text data, be it regular documentation, meta-data tags or directives.

Defining a Macro

A macro must first be defined in order to be used. Note that a macro is also expanded upon definition if it defined on an object (the docstring of a method, class, module or constant object as opposed to a free standing comment). To define a macro, use the "new" or "attach" identifier in the types specifier list. A macro will also automatically be created if an indented macro data block is given, so the keywords are not strictly needed.

Anonymous Macros

In addition to standard named macros, macros can be defined anonymously if no name is given. In this case, they can not be re-used in future docstrings, but they will expand in the first definition. This is useful when needing to take advantage of the macro expansion variables (described below).

Using a Macro

To re-use a macro in another docstring after it is defined, simply use @!macro the_name with no indented block of macro data. The resulting data will be expanded in place.

Attaching a Macro to a DSL Method

Macros can be defined to auto-expand on DSL-style class method calls. To define a macro to be auto expanded in this way, use the "attach" keyword in the type specifier list ("new" is implied).

Attached macros can also be attached directly on the class method declaration that provides the DSL method to its subclasses. The syntax in either case is the same.

Macro Expansion Variables

In the case of using macros on DSL-style method calls, a number of expansion variables can be used for interpolation inside of the macro data. The variables, similar in syntax to Ruby's global variables, are as follows:

  • $0 - the method name being called
  • $1, $2, $3, ... - the Nth argument in the method call
  • $& - the full source line

The following example shows what the expansion variables might hold for a given DSL method call:

property :foo, :a, :b, :c, String # $0 => "property" # $1 => "foo" # $2 => "a" # $& => "property :foo, :a, :b, :c, String"

Ranges

Ranges are also acceptable with the syntax ${N-M}. Negative values on either N or M are valid, and refer to indexes from the end of the list. Consider a DSL method that creates a method using the first argument with argument names following, ending with the return type of the method. This could be documented as:

# @!macro dsl_method
#   @!method $1(${2--2})
#   @return [${-1}] the return value of $0
create_method_with_args :foo, :a, :b, :c, String

As described, the method is using the signature foo(a, b, c) and the return type from the last argument, ::String. When using ranges, tokens are joined with commas. Note that this includes using $0:

$0-1 # => Interpolates to "create_method_with_args, foo"

If you want to separate them with spaces, use $1 $2 $3 $4 .... Note that if the token cannot be expanded, it will return the empty string (not an error), so it would be safe to list $1 $2 ... $10, for example.

Escaping Interpolation

Interpolation can be escaped by prefixing the $ with \, like so:

# @!macro foo
#   I have \$2.00 USD.

Examples:

Defining a simple macro

# @!macro [new] returnself
#   @return [self] returns itself

Using a simple macro in multiple docstrings

# Documentation for map
# ...
# @macro returnself
def map; end

# Documentation for filter
# ...
# @macro returnself
def filter; end

Attaching a macro to a class method (for DSL usage)

class Resource
  # Defines a new property
  # @param [String] name the property name
  # @param [Class] type the property's type
  # @!macro [attach] property
  #   @return [$2] the $1 property
  def self.property(name, type) end
end

class Post < Resource
  property :title, String
  property :view_count, Integer
end

Attaching a macro directly to a DSL method

class Post < Resource
  # @!macro [attach] property
  #   @return [$2] the $1 property
  property :title, String

  # Macro will expand on this definition too
  property :view_count, Integer
end

Since:

  • 0.7.0

Parser callbacks

Class Method Summary

Directive - Inherited

Instance Attribute Summary

Directive - Inherited

#expanded_text

Set this field to replace the directive definition inside of a docstring with arbitrary text.

#handler, #object, #tag

Instance Method Summary

Directive - Inherited

#after_parse

Called after parsing all directives and tags in the docstring.

#call

Called when processing the directive.

Constructor Details

This class inherits a constructor from YARD::Tags::Directive

Instance Attribute Details

#anonymous?Boolean (readonly, private)

Since:

  • 0.7.0

[ GitHub ]

  
# File 'lib/yard/tags/directives.rb', line 288

def anonymous?
  tag.name.nil? || tag.name.empty?
end

#attach?Boolean (readonly, private)

Since:

  • 0.7.0

[ GitHub ]

  
# File 'lib/yard/tags/directives.rb', line 277

def attach?
  new? && # must have data or there is nothing to attach
    class_method? || # always attach to class methods
    (tag.types && tag.types.include?('attach'))
end

#class_method?Boolean (readonly, private)

Since:

  • 0.7.0

[ GitHub ]

  
# File 'lib/yard/tags/directives.rb', line 283

def class_method?
  object && object.is_a?(CodeObjects::MethodObject) &&
    object.scope == :class
end

#new?Boolean (readonly, private)

Since:

  • 0.7.0

[ GitHub ]

  
# File 'lib/yard/tags/directives.rb', line 272

def new?
  (tag.types && tag.types.include?('new')) ||
    (tag.text && !tag.text.strip.empty?)
end

Instance Method Details

#call

Raises:

Since:

  • 0.7.0

[ GitHub ]

  
# File 'lib/yard/tags/directives.rb', line 259

def call
  raise TagFormatError if tag.name.nil? && tag.text.to_s.empty?
  macro_data = find_or_create
  unless macro_data
    warn
    return
  end

  self.expanded_text = expand(macro_data)
end

#expand(macro_data) (private)

Since:

  • 0.7.0

[ GitHub ]

  
# File 'lib/yard/tags/directives.rb', line 292

def expand(macro_data)
  return if attach? && class_method?
  return if !anonymous? && new? &&
            (!handler || handler.statement.source.empty?)
  call_params = []
  caller_method = nil
  full_source = ''
  if handler
    call_params = handler.call_params
    caller_method = handler.caller_method
    full_source = handler.statement.source
  end
  all_params = ([caller_method] + call_params).compact
  CodeObjects::MacroObject.expand(macro_data, all_params, full_source)
end

#find_or_create (private)

Since:

  • 0.7.0

[ GitHub ]

  
# File 'lib/yard/tags/directives.rb', line 308

def find_or_create
  if new? || attach?
    if handler && attach?
      if object && object.is_a?(CodeObjects::NamespaceObject)
        log.warn "Attaching macros to non-methods is unsupported, ignoring: " \
                 "#{object.path} (#{handler.parser.file}:#{handler.statement.line})"
        obj = nil
      else
        obj = object ? object :
          P("#{handler.namespace}.#{handler.caller_method}")
      end
    else
      obj = nil
    end

    return tag.text || "" if anonymous? # anonymous macro
    macro = CodeObjects::MacroObject.create(tag.name, tag.text, obj)
  else
    macro = CodeObjects::MacroObject.find(tag.name)
  end

  macro ? macro.macro_data : nil
end

#warn (private)

Since:

  • 0.7.0

[ GitHub ]

  
# File 'lib/yard/tags/directives.rb', line 332

def warn
  if object && handler
    log.warn "Invalid/missing macro name for " \
             "#{object.path} (#{handler.parser.file}:#{handler.statement.line})"
  end
end