123456789_123456789_123456789_123456789_123456789_

Class: YARD::Handlers::Ruby::Legacy::Base Abstract

Overview

This class is abstract.

See ::YARD::Handlers::Base for subclassing information.

This is the base handler for the legacy parser. To implement a legacy handler, subclass this class.

Constant Summary

::YARD::CodeObjects - Included

BUILTIN_ALL, BUILTIN_CLASSES, BUILTIN_EXCEPTIONS, BUILTIN_EXCEPTIONS_HASH, BUILTIN_MODULES, CONSTANTMATCH, CONSTANTSTART, CSEP, CSEPQ, ISEP, ISEPQ, METHODMATCH, METHODNAMEMATCH, NAMESPACEMATCH, NSEP, NSEPQ, PROXY_MATCH

::YARD::Parser::Ruby::Legacy::RubyToken - Included

EXPR_ARG, EXPR_BEG, EXPR_CLASS, EXPR_DOT, EXPR_END, EXPR_FNAME, EXPR_MID, NEWLINE_TOKEN, TkReading2Token, TkSymbol2Token, TokenDefinitions

Class Attribute Summary

::YARD::Handlers::Base - Inherited

.namespace_only

Declares that the handler should only be called when inside a ::YARD::CodeObjects::NamespaceObject, not a method body.

.namespace_only?

Class Method Summary

::YARD::Handlers::Base - Inherited

.clear_subclasses

Clear all registered subclasses.

.handlers,
.handles

Declares the statement type which will be processed by this handler.

.handles?

This class is implemented by ::YARD::Handlers::Ruby::Base and Base.

.in_file

Declares that a handler should only be called when inside a filename by its basename or a regex match for the full path.

.inherited, .matches_file?, .new,
.process

Generates a process method, equivalent to +def process; ...

.subclasses

Returns all registered handler subclasses.

Instance Attribute Summary

::YARD::Handlers::Base - Inherited

#extra_state

Share state across different handlers inside of a file.

#globals

::YARD::Handlers can share state for the entire post processing stage through this attribute.

#namespace, #namespace=, #owner, #owner=, #parser, #scope, #scope=, #statement, #visibility, #visibility=

Instance Method Summary

::YARD::Parser::Ruby::Legacy::RubyToken - Included

::YARD::Handlers::Base - Inherited

#abort!

Aborts a handler by raising ::YARD::Handlers::HandlerAborted.

#call_params, #caller_method,
#ensure_loaded!

Ensures that a specific object has been parsed and loaded into the registry.

#parse_block

Parses the semantic "block" contained in the statement node.

#process

The main handler method called by the parser on a statement that matches the handles declaration.

#push_state

Executes a given block with specific state values for #owner, #namespace and #scope.

#register

Do some post processing on a list of code objects.

#register_docstring

Registers any docstring found for the object and expands macros.

#register_dynamic

Registers the object as dynamic if the object is defined inside a method or block (owner != namespace).

#register_ensure_loaded

Ensures that the object's namespace is loaded before attaching it to the namespace.

#register_file_info

Registers the file/line of the declaration with the object.

#register_group

Registers the object as being inside a specific group.

#register_module_function

Registers the same method information on the module function, if the object was defined as a module function.

#register_source,
#register_transitive_tags

Registers any transitive tags from the namespace on the object.

#register_visibility

Registers visibility on a method object.

Constructor Details

This class inherits a constructor from YARD::Handlers::Base

Class Method Details

.handles?(stmt) ⇒ Boolean

Returns:

[ GitHub ]

  
# File 'lib/yard/handlers/ruby/legacy/base.rb', line 15

def self.handles?(stmt)
  handlers.any? do |a_handler|
    case a_handler
    when String
      stmt.tokens.first.text == a_handler
    when Regexp
      stmt.tokens.to_s =~ a_handler
    else
      a_handler == stmt.tokens.first.class
    end
  end
end

Instance Method Details

#call_params

[ GitHub ]

  
# File 'lib/yard/handlers/ruby/legacy/base.rb', line 44

def call_params
  if statement.tokens.first.is_a?(TkDEF)
    extract_method_details.last.map(&:first)
  else
    tokens = statement.tokens[1..-1]
    tokval_list(tokens, :attr, :identifier, TkId).map(&:to_s)
  end
end

#caller_method

[ GitHub ]

  
# File 'lib/yard/handlers/ruby/legacy/base.rb', line 53

def caller_method
  if statement.tokens.first.is_a?(TkIDENTIFIER)
    statement.tokens.first.text
  elsif statement.tokens.first.is_a?(TkDEF)
    extract_method_details.first
  end
end

#extract_method_detailsArray<String,Array<Array<String>>> (private)

TODO:

This is a duplicate implementation of MethodHandler. Refactor.

Extracts method information for macro expansion only

Returns:

[ GitHub ]

  
# File 'lib/yard/handlers/ruby/legacy/base.rb', line 68

def extract_method_details
  if statement.tokens.to_s =~ /^def\s+(#{METHODMATCH})(?:(?:\s+|\s*\()(.*)(?:\)\s*$)?)?/m
    meth = $1
    args = $2
    meth.gsub!(/\s+/, '')
    args = tokval_list(Parser::Ruby::Legacy::TokenList.new(args), :all)
    args.map! {|a| k, v = *a.split('=', 2); [k.strip, (v ? v.strip : nil)] } if args
    meth = $` if meth =~ /(?:#{NSEPQ}|#{CSEPQ})([^#{NSEP}#{CSEPQ}]+)$/
    [meth, args]
  end
end

#parse_block(opts = {})

Parses a statement's block with a set of state values. If the statement has no block, nothing happens. A description of state values can be found at Base#push_state

Parameters:

  • opts (Hash) (defaults to: {})

    State options

Options Hash (opts):

  • :namespace (CodeObjects::NamespaceObject) — default: value of #namespace

    the namespace object that #namespace will be equal to for the duration of the block.

  • :scope (Symbol) — default: :instance

    the scope for the duration of the block.

  • :owner (CodeObjects::Base) — default: value of #owner

    the owner object (method) for the duration of the block

See Also:

[ GitHub ]

  
# File 'lib/yard/handlers/ruby/legacy/base.rb', line 35

def parse_block(opts = {})
  push_state(opts) do
    if statement.block
      blk = Parser::Ruby::Legacy::StatementList.new(statement.block)
      parser.process(blk)
    end
  end
end

#tokval(token, *accepted_types) ⇒ Object? (private)

The string value of a token. For example, the return value for the symbol :sym would be :sym. The return value for a string +"foo #{ bar}"+ would be the literal +"foo #{ bar}"+ without any interpolation. The return value of the identifier 'test' would be the same value: 'test'. Here is a list of common types and their return values:

Examples:

tokval(TokenList.new('"foo"').first) => "foo"
tokval(TokenList.new(':foo').first) => :foo
tokval(TokenList.new('CONSTANT').first, RubyToken::TkId) => "CONSTANT"
tokval(TokenList.new('identifier').first, RubyToken::TkId) => "identifier"
tokval(TokenList.new('3.25').first) => 3.25
tokval(TokenList.new('/xyz/i').first) => /xyz/i

Parameters:

  • token (Token)

    The token of the class

  • accepted_types (Array<Class<Token>>, Symbol)

    The allowed token types that this token can be. Defaults to [TkVal]. A list of types would be, for example, [+TkSTRING+, +TkSYMBOL+], to return the token's value if it is either of those types. If TkVal is accepted, TkNode is also accepted.

    Certain symbol keys are allowed to specify multiple types in one fell swoop. These symbols are: :string => TkSTRING, TkDSTRING, TkDXSTRING and TkXSTRING :attr => TkSYMBOL and TkSTRING :identifier => +TkIDENTIFIER, TkFID and TkGVAR. :number => TkFLOAT, TkINTEGER

Returns:

  • (Object)

    if the token is one of the accepted types, in its real value form. It should be noted that identifiers and constants are kept in ::String form.

  • (nil)

    if the token is not any of the specified accepted types

[ GitHub ]

  
# File 'lib/yard/handlers/ruby/legacy/base.rb', line 112

def tokval(token, *accepted_types)
  accepted_types = [TkVal] if accepted_types.empty?
  accepted_types.push(TkNode) if accepted_types.include? TkVal

  if accepted_types.include?(:attr)
    accepted_types.push(TkSTRING, TkSYMBOL)
  end

  if accepted_types.include?(:string)
    accepted_types.push(TkSTRING, TkDSTRING, TkXSTRING, TkDXSTRING)
  end

  if accepted_types.include?(:identifier)
    accepted_types.push(TkIDENTIFIER, TkFID, TkGVAR)
  end

  if accepted_types.include?(:number)
    accepted_types.push(TkFLOAT, TkINTEGER)
  end

  return unless accepted_types.any? {|t| t === token }

  case token
  when TkSTRING, TkDSTRING, TkXSTRING, TkDXSTRING
    token.text[1..-2]
  when TkSYMBOL
    token.text[1..-1].to_sym
  when TkFLOAT
    token.text.to_f
  when TkINTEGER
    token.text.to_i
  when TkREGEXP
    token.text =~ %r{\A/(.+)/([^/])\Z}
    Regexp.new($1, $2)
  when TkTRUE
    true
  when TkFALSE
    false
  when TkNIL
    nil
  else
    token.text
  end
end

#tokval_list(tokenlist, *accepted_types) ⇒ Array<String>, Array<EMPTY> (private)

Returns a list of symbols or string values from a statement. The list must be a valid comma delimited list, and values will only be returned to the end of the list only.

Example: attr_accessor :a, 'b', :c, :d => ['a', 'b', 'c', 'd'] attr_accessor 'a', UNACCEPTED_TYPE, 'c' => ['a', 'c']

The tokval list of a ::YARD::Parser::Ruby::Legacy::TokenList of the above code would be the #tokval value of :a, 'b', :c and :d.

It should also be noted that this function stops immediately at any ruby keyword encountered: "attr_accessor :a, :b, :c if x == 5" => ['a', 'b', 'c']

Parameters:

  • tokenlist (TokenList)

    The list of tokens to process.

  • accepted_types (Array<Class<Token>>)

    passed to #tokval

Returns:

  • (Array<String>)

    the list of tokvalues in the list.

  • (Array<EMPTY>)

    if there are no symbols or Strings in the list

See Also:

[ GitHub ]

  
# File 'lib/yard/handlers/ruby/legacy/base.rb', line 178

def tokval_list(tokenlist, *accepted_types)
  return [] unless tokenlist
  out = [[]]
  parencount = 0
  beforeparen = 0
  needcomma = false
  seen_comma = true
  tokenlist.each do |token|
    tokval = accepted_types == [:all] ? token.text : tokval(token, *accepted_types)
    parencond = !out.last.empty? && !tokval.nil?
    # puts "#{seen_comma.inspect} #{parencount} #{token.class.class_name} #{out.inspect}"
    case token
    when TkCOMMA
      if parencount == 0
        out << [] unless out.last.empty?
        needcomma = false
        seen_comma = true
      elsif parencond
        out.last << token.text
      end
    when TkLPAREN
      if seen_comma
        beforeparen += 1
      else
        parencount += 1
        out.last << token.text if parencond
      end
    when TkRPAREN
      if beforeparen > 0
        beforeparen -= 1
      else
        out.last << token.text if parencount > 0 && !tokval.nil?
        parencount -= 1
      end
    when TkLBRACE, TkLBRACK, TkDO
      parencount += 1
      out.last << token.text unless tokval.nil?
    when TkRBRACE, TkRBRACK, TkEND
      out.last << token.text unless tokval.nil?
      parencount -= 1
    else
      break if TkKW === token && ![TkTRUE, TkFALSE, TkSUPER, TkSELF, TkNIL].include?(token.class)

      seen_comma = false unless TkWhitespace === token
      if parencount == 0
        next if needcomma
        next if TkWhitespace === token
        if !tokval.nil?
          out.last << tokval
        else
          out.last.clear
          needcomma = true
        end
      elsif parencond
        needcomma = true
        out.last << token.text
      end
    end

    break if beforeparen == 0 && parencount < 0
  end
  # Flatten any single element lists
  out.map {|e| e.empty? ? nil : (e.size == 1 ? e.pop : e.flatten.join) }.compact
end