(PDOC-3) Switch to one line class declarations

Prior to this commit, many of the classes in this project were declared
in two lines. The first line would put them in the context of the module
they belonged to, and the second line would declare the class and deal with
inheritance. In order to make the code more readable and to allow
require statements to be moved to the top level, turn all class declarations
into one line statements.
This commit is contained in:
Hailee Kenney 2014-09-29 15:49:05 -07:00
parent df648a246f
commit f0da72b2b7
13 changed files with 376 additions and 393 deletions

View File

@ -1,5 +1,4 @@
require 'puppet/face' require 'puppet/face'
require 'puppetx/puppetlabs/strings/actions'
Puppet::Face.define(:strings, '0.0.1') do Puppet::Face.define(:strings, '0.0.1') do
summary "Generate Puppet documentation with YARD." summary "Generate Puppet documentation with YARD."
@ -34,6 +33,7 @@ Puppet::Face.define(:strings, '0.0.1') do
when_invoked do |*args| when_invoked do |*args|
check_required_features check_required_features
require 'puppetx/puppetlabs/strings/actions'
yardoc_actions = Puppetx::PuppetLabs::Strings::Actions.new(Puppet[:debug], Puppet[:trace]) yardoc_actions = Puppetx::PuppetLabs::Strings::Actions.new(Puppet[:debug], Puppet[:trace])
@ -63,6 +63,7 @@ Puppet::Face.define(:strings, '0.0.1') do
when_invoked do |*args| when_invoked do |*args|
check_required_features check_required_features
require 'puppetx/puppetlabs/strings/actions'
server_actions = Puppetx::PuppetLabs::Strings::Actions.new(Puppet[:debug], Puppet[:trace]) server_actions = Puppetx::PuppetLabs::Strings::Actions.new(Puppet[:debug], Puppet[:trace])

View File

@ -6,6 +6,11 @@ module Puppetx::PuppetLabs
# This submodule contains bits that interface with the YARD plugin system. # This submodule contains bits that interface with the YARD plugin system.
module YARD module YARD
module Handlers
end
module CodeObjects
end
end end
# This submodule contains bits that operate on the Pops module produced by # This submodule contains bits that operate on the Pops module produced by

View File

@ -3,76 +3,73 @@ require 'puppet/pops'
require 'puppetx/puppetlabs/strings' require 'puppetx/puppetlabs/strings'
module Puppetx::PuppetLabs::Strings::Pops # An adapter class that conforms a Pops model instance + adapters to the
# An adapter class that conforms a Pops model instance + adapters to the # interface expected by YARD handlers.
# interface expected by YARD handlers. #
# # FIXME: Inhertiting from OpenStruct is a bit of a hack. It allows attributes
# FIXME: Inhertiting from OpenStruct is a bit of a hack. It allows attributes # to be declared as needed but in the long run understandibility of the code
# to be declared as needed but in the long run understandibility of the code # would be improved by having a concrete model.
# would be improved by having a concrete model. class Puppetx::PuppetLabs::Strings::Pops::YARDStatement < OpenStruct
class YARDStatement < OpenStruct attr_reader :pops_obj, :comments
attr_reader :pops_obj, :comments
def initialize(pops_obj) def initialize(pops_obj)
# Initialize OpenStruct # Initialize OpenStruct
super({}) super({})
unless pops_obj.is_a? Puppet::Pops::Model::PopsObject unless pops_obj.is_a? Puppet::Pops::Model::PopsObject
raise ArgumentError, "A YARDStatement can only be initialized from a PopsObject. Got a: #{pops_obj.class}" raise ArgumentError, "A YARDStatement can only be initialized from a PopsObject. Got a: #{pops_obj.class}"
end
@pops_obj = pops_obj
@pos_adapter = Puppet::Pops::Adapters::SourcePosAdapter.adapt(@pops_obj)
# FIXME: Perhaps this should be a seperate adapter?
@comments = extract_comments
end
def type
pops_obj.class
end
def line
@line ||= @pos_adapter.line
end
def source
@source ||= @pos_adapter.extract_text
end
# FIXME: I don't know quite what these are supposed to do, but they show up
# quite often in the YARD handler code. Figure out whether they are
# necessary.
alias_method :show, :source
def comments_hash_flag; nil end
def comments_range; nil end
private
# TODO: This stuff should probably be part of a separate class/adapter.
COMMENT_PATTERN = /^\s*#.*\n/
def extract_comments
comments = []
program = pops_obj.eAllContainers.find {|c| c.is_a?(Puppet::Pops::Model::Program) }
# FIXME: Horribly inefficient. Multiple copies. Generator pattern would
# be much better.
source_text = program.source_text.lines.to_a
source_text.slice(0, line-1).reverse.each do |line|
if COMMENT_PATTERN.match(line)
# FIXME: The gsub trims comments, but is extremely optimistic: It
# assumes only one space separates the comment body from the
# comment character.
comments.unshift line.gsub(/^\s*#\s/, '')
else
# No comment found on this line. We must be done piecing together a
# comment block.
break
end end
@pops_obj = pops_obj
@pos_adapter = Puppet::Pops::Adapters::SourcePosAdapter.adapt(@pops_obj)
# FIXME: Perhaps this should be a seperate adapter?
@comments = extract_comments
end
def type
pops_obj.class
end
def line
@line ||= @pos_adapter.line
end
def source
@source ||= @pos_adapter.extract_text
end
# FIXME: I don't know quite what these are supposed to do, but they show up
# quite often in the YARD handler code. Figure out whether they are
# necessary.
alias_method :show, :source
def comments_hash_flag; nil end
def comments_range; nil end
private
# TODO: This stuff should probably be part of a separate class/adapter.
COMMENT_PATTERN = /^\s*#.*\n/
def extract_comments
comments = []
program = pops_obj.eAllContainers.find {|c| c.is_a?(Puppet::Pops::Model::Program) }
# FIXME: Horribly inefficient. Multiple copies. Generator pattern would
# be much better.
source_text = program.source_text.lines.to_a
source_text.slice(0, line-1).reverse.each do |line|
if COMMENT_PATTERN.match(line)
# FIXME: The gsub trims comments, but is extremely optimistic: It
# assumes only one space separates the comment body from the
# comment character.
comments.unshift line.gsub(/^\s*#\s/, '')
else
# No comment found on this line. We must be done piecing together a
# comment block.
break
end
end
# Stick everything back together.
comments.join
end end
# Stick everything back together.
comments.join
end end
end end

View File

@ -2,52 +2,50 @@ require 'puppet/pops'
require 'puppetx/puppetlabs/strings' require 'puppetx/puppetlabs/strings'
require 'puppetx/puppetlabs/strings/pops/yard_statement' require 'puppetx/puppetlabs/strings/pops/yard_statement'
module Puppetx::PuppetLabs::Strings::Pops # Loosely based on the TreeDumper classes in Pops::Model. The responsibility of
# Loosely based on the TreeDumper classes in Pops::Model. The responsibility of # this class is to walk a Pops::Model and output objects that can be consumed
# this class is to walk a Pops::Model and output objects that can be consumed # by YARD handlers.
# by YARD handlers. #
# # @note Currently, this class only extracts node, host class and type
# @note Currently, this class only extracts node, host class and type # definitions.
# definitions. class Puppetx::PuppetLabs::Strings::Pops::YARDTransformer
class YARDTransformer def initialize
def initialize @transform_visitor = Puppet::Pops::Visitor.new(self, 'transform')
@transform_visitor = Puppet::Pops::Visitor.new(self, 'transform') end
def transform(o)
@transform_visitor.visit(o)
end
private
def transform_Factory(o)
transform(o.current)
end
def transform_Program(o)
o.definitions.map{|d| transform(d)}
end
# Extract comments from type definitions and class definitions. Wrap them
# into YARDStatement objects that provide an interface for YARD handlers.
def transform_NamedDefinition(o)
obj = Puppetx::PuppetLabs::Strings::Pops::YARDStatement.new(o)
obj.parameters = o.parameters.map do |p|
param_tuple = [transform(p)]
param_tuple << ( p.value.nil? ? nil : transform(p.value) )
end end
def transform(o) obj
@transform_visitor.visit(o) end
end
private # Catch-all visitor.
def transform_Positioned(o)
Puppetx::PuppetLabs::Strings::Pops::YARDStatement.new(o)
end
def transform_Factory(o) # nil in... nil out!
transform(o.current) def transform_NilClass(o)
end nil
def transform_Program(o)
o.definitions.map{|d| transform(d)}
end
# Extract comments from type definitions and class definitions. Wrap them
# into YARDStatement objects that provide an interface for YARD handlers.
def transform_NamedDefinition(o)
obj = YARDStatement.new(o)
obj.parameters = o.parameters.map do |p|
param_tuple = [transform(p)]
param_tuple << ( p.value.nil? ? nil : transform(p.value) )
end
obj
end
# Catch-all visitor.
def transform_Positioned(o)
YARDStatement.new(o)
end
# nil in... nil out!
def transform_NilClass(o)
nil
end
end end
end end

View File

@ -2,10 +2,8 @@ require 'puppet/pops'
require 'puppetx/puppetlabs/strings/yard/code_objects/puppet_namespace_object' require 'puppetx/puppetlabs/strings/yard/code_objects/puppet_namespace_object'
module Puppetx::PuppetLabs::Strings::YARD::CodeObjects class Puppetx::PuppetLabs::Strings::YARD::CodeObjects::DefinedTypeObject < Puppetx::PuppetLabs::Strings::YARD::CodeObjects::PuppetNamespaceObject
class DefinedTypeObject < PuppetNamespaceObject # A list of parameters attached to this class.
# A list of parameters attached to this class. # @return [Array<Array(String, String)>]
# @return [Array<Array(String, String)>] attr_accessor :parameters
attr_accessor :parameters
end
end end

View File

@ -1,26 +1,24 @@
require 'puppetx/puppetlabs/strings/yard/code_objects/defined_type_object' require 'puppetx/puppetlabs/strings/yard/code_objects/defined_type_object'
module Puppetx::PuppetLabs::Strings::YARD::CodeObjects class Puppetx::PuppetLabs::Strings::YARD::CodeObjects::HostClassObject < Puppetx::PuppetLabs::Strings::YARD::CodeObjects::DefinedTypeObject
class HostClassObject < DefinedTypeObject # The {HostClassObject} that this class inherits from, if any.
# The {HostClassObject} that this class inherits from, if any. # @return [HostClassObject, Proxy, nil]
# @return [HostClassObject, Proxy, nil] attr_accessor :parent_class
attr_accessor :parent_class
# NOTE: `include_mods` is never used as it makes no sense for Puppet, but # NOTE: `include_mods` is never used as it makes no sense for Puppet, but
# this is called by `YARD::Registry` and it will pass a parameter. # this is called by `YARD::Registry` and it will pass a parameter.
def inheritance_tree(include_mods = false) def inheritance_tree(include_mods = false)
if parent_class.is_a?(HostClassObject) if parent_class.is_a?(Puppetx::PuppetLabs::Strings::YARD::CodeObjects::HostClassObject)
# Cool. We got a host class. Return self + parent inheritance tree. # Cool. We got a host class. Return self + parent inheritance tree.
[self] + parent_class.inheritance_tree [self] + parent_class.inheritance_tree
elsif parent_class.is_a?(YARD::CodeObjects::Proxy) elsif parent_class.is_a?(YARD::CodeObjects::Proxy)
# We have a reference to a parent that has not been created yet. Just # We have a reference to a parent that has not been created yet. Just
# return it. # return it.
[self, parent_class] [self, parent_class]
else else
# Most likely no parent class. But also possibly an object that we # Most likely no parent class. But also possibly an object that we
# shouldn't inherit from. Just return self. # shouldn't inherit from. Just return self.
[self] [self]
end
end end
end end
end end

View File

@ -1,35 +1,33 @@
require 'yard' require 'yard'
require 'puppetx/puppetlabs/strings' require 'puppetx/puppetlabs/strings'
module Puppetx::PuppetLabs::Strings::YARD::CodeObjects class Puppetx::PuppetLabs::Strings::YARD::CodeObjects::PuppetNamespaceObject < YARD::CodeObjects::NamespaceObject
class PuppetNamespaceObject < YARD::CodeObjects::NamespaceObject # NOTE: `YARD::Registry#resolve` requires a method with this signature to
# NOTE: `YARD::Registry#resolve` requires a method with this signature to # be present on all subclasses of `NamespaceObject`.
# be present on all subclasses of `NamespaceObject`. def inheritance_tree(include_mods = false)
def inheritance_tree(include_mods = false) [self]
[self]
end
# FIXME: We used to override `self.new` to ensure no YARD proxies were
# created for namespaces segments that did not map to a host class or
# defined type. Fighting the system in this way turned out to be
# counter-productive.
#
# However, if a proxy is left in, YARD will drop back to namspace-mangling
# heuristics that are very specific to Ruby and which produce ugly paths in
# the resulting output. Need to find a way to address this.
#
# Tried:
#
# - Overriding self.new in the code object. Failed because self.new
# overrides are gross and difficult to pull off. Especially when
# replacing an existing override.
#
# - Adding functionality to the base handler to ensure something other
# than a proxy occupies each namespace segment. Failed because once a
# code object is created with a namespace, it will never update.
# Unless that namespace is set to a Proxy.
#
# def self.new(namespace, name, *args, &block)
end end
# FIXME: We used to override `self.new` to ensure no YARD proxies were
# created for namespaces segments that did not map to a host class or
# defined type. Fighting the system in this way turned out to be
# counter-productive.
#
# However, if a proxy is left in, YARD will drop back to namspace-mangling
# heuristics that are very specific to Ruby and which produce ugly paths in
# the resulting output. Need to find a way to address this.
#
# Tried:
#
# - Overriding self.new in the code object. Failed because self.new
# overrides are gross and difficult to pull off. Especially when
# replacing an existing override.
#
# - Adding functionality to the base handler to ensure something other
# than a proxy occupies each namespace segment. Failed because once a
# code object is created with a namespace, it will never update.
# Unless that namespace is set to a Proxy.
#
# def self.new(namespace, name, *args, &block)
end end

View File

@ -4,16 +4,14 @@ require 'puppet/pops'
require 'puppetx/puppetlabs/strings' require 'puppetx/puppetlabs/strings'
require 'puppetx/puppetlabs/strings/yard/code_objects' require 'puppetx/puppetlabs/strings/yard/code_objects'
module Puppetx::PuppetLabs::Strings::YARD::Handlers class Puppetx::PuppetLabs::Strings::YARD::Handlers::Base < YARD::Handlers::Base
class Base < YARD::Handlers::Base # Easy access to Pops model objects for handler matching.
# Easy access to Pops model objects for handler matching. include Puppet::Pops::Model
include Puppet::Pops::Model # Easy access to custom code objects from which documentation is generated.
# Easy access to custom code objects from which documentation is generated. include Puppetx::PuppetLabs::Strings::YARD::CodeObjects
include Puppetx::PuppetLabs::Strings::YARD::CodeObjects
def self.handles?(statement)
handlers.any? {|h| h == statement.type}
end
def self.handles?(statement)
handlers.any? {|h| h == statement.type}
end end
end end

View File

@ -1,18 +1,16 @@
require 'puppetx/puppetlabs/strings/yard/handlers/base' require 'puppetx/puppetlabs/strings/yard/handlers/base'
module Puppetx::PuppetLabs::Strings::YARD::Handlers class Puppetx::PuppetLabs::Strings::YARD::Handlers::DefinedTypeHandler < Puppetx::PuppetLabs::Strings::YARD::Handlers:: Base
class DefinedTypeHandler < Base handles ResourceTypeDefinition
handles ResourceTypeDefinition
process do process do
obj = DefinedTypeObject.new(:root, statement.pops_obj.name) do |o| obj = DefinedTypeObject.new(:root, statement.pops_obj.name) do |o|
o.parameters = statement.parameters.map do |a| o.parameters = statement.parameters.map do |a|
param_tuple = [a[0].pops_obj.name] param_tuple = [a[0].pops_obj.name]
param_tuple << ( a[1].nil? ? nil : a[1].source ) param_tuple << ( a[1].nil? ? nil : a[1].source )
end
end end
register obj
end end
register obj
end end
end end

View File

@ -1,24 +1,22 @@
require 'puppetx/puppetlabs/strings/yard/handlers/base' require 'puppetx/puppetlabs/strings/yard/handlers/base'
module Puppetx::PuppetLabs::Strings::YARD::Handlers class Puppetx::PuppetLabs::Strings::YARD::Handlers::HostClassHandler < Puppetx::PuppetLabs::Strings::YARD::Handlers::Base
class HostClassHandler < Base handles HostClassDefinition
handles HostClassDefinition
process do process do
obj = HostClassObject.new(:root, statement.pops_obj.name) do |o| obj = HostClassObject.new(:root, statement.pops_obj.name) do |o|
o.parameters = statement.parameters.map do |a| o.parameters = statement.parameters.map do |a|
param_tuple = [a[0].pops_obj.name] param_tuple = [a[0].pops_obj.name]
param_tuple << ( a[1].nil? ? nil : a[1].source ) param_tuple << ( a[1].nil? ? nil : a[1].source )
end
end end
statement.pops_obj.tap do |o|
if o.parent_class
obj.parent_class = P(:root, o.parent_class)
end
end
register obj
end end
statement.pops_obj.tap do |o|
if o.parent_class
obj.parent_class = P(:root, o.parent_class)
end
end
register obj
end end
end end

View File

@ -2,87 +2,86 @@
require 'puppet/util/docs' require 'puppet/util/docs'
require 'puppetx/puppetlabs/strings/yard/code_objects' require 'puppetx/puppetlabs/strings/yard/code_objects'
module Puppetx::PuppetLabs::Strings::YARD::Handlers class Puppetx::PuppetLabs::Strings::YARD::Handlers::Puppet3xFunctionHandler < YARD::Handlers::Ruby::Base
class Puppet3xFunctionHandler < YARD::Handlers::Ruby::Base include Puppetx::PuppetLabs::Strings::YARD::CodeObjects
include Puppetx::PuppetLabs::Strings::YARD::CodeObjects
handles method_call(:newfunction) handles method_call(:newfunction)
process do process do
name, options = process_parameters name, options = process_parameters
obj = MethodObject.new(function_namespace, name) obj = MethodObject.new(function_namespace, name)
register obj register obj
if options['doc'] if options['doc']
register_docstring(obj, options['doc'], nil) register_docstring(obj, options['doc'], nil)
end
# This has to be done _after_ register_docstring as all tags on the
# object are overwritten by tags parsed out of the docstring.
return_type = options['type']
return_type ||= 'statement' # Default for newfunction
obj.add_tag YARD::Tags::Tag.new(:return, '', return_type)
# FIXME: This is a hack that allows us to document the Puppet Core which
# uses `--no-transitive-tag api` and then only shows things explicitly
# tagged with `public` or `private` api. This is kind of insane and
# should be fixed upstream.
obj.add_tag YARD::Tags::Tag.new(:api, 'public')
end end
private # This has to be done _after_ register_docstring as all tags on the
# object are overwritten by tags parsed out of the docstring.
return_type = options['type']
return_type ||= 'statement' # Default for newfunction
obj.add_tag YARD::Tags::Tag.new(:return, '', return_type)
# Returns a {PuppetNamespaceObject} for holding functions. Creates this # FIXME: This is a hack that allows us to document the Puppet Core which
# object if necessary. # uses `--no-transitive-tag api` and then only shows things explicitly
# # tagged with `public` or `private` api. This is kind of insane and
# @return [PuppetNamespaceObject] # should be fixed upstream.
def function_namespace obj.add_tag YARD::Tags::Tag.new(:api, 'public')
# NOTE: This tricky. If there is ever a Ruby class or module with the end
# name ::Puppet3xFunctions, then there will be a clash. Hopefully the name
# is sufficiently uncommon.
obj = P(:root, 'Puppet3xFunctions')
if obj.is_a? Proxy
namespace_obj = PuppetNamespaceObject.new(:root, 'Puppet3xFunctions')
namespace_obj.add_tag YARD::Tags::Tag.new(:api, 'public')
register namespace_obj private
end
obj # Returns a {PuppetNamespaceObject} for holding functions. Creates this
# object if necessary.
#
# @return [PuppetNamespaceObject]
def function_namespace
# NOTE: This tricky. If there is ever a Ruby class or module with the
# name ::Puppet3xFunctions, then there will be a clash. Hopefully the name
# is sufficiently uncommon.
obj = P(:root, 'Puppet3xFunctions')
if obj.is_a? Proxy
namespace_obj = PuppetNamespaceObject.new(:root, 'Puppet3xFunctions')
namespace_obj.add_tag YARD::Tags::Tag.new(:api, 'public')
register namespace_obj
end end
# NOTE: The following methods duplicate functionality from obj
# Puppet::Util::Reference and Puppet::Parser::Functions.functiondocs end
#
# However, implementing this natively in YARD is a good test for the
# feasibility of extracting custom Ruby documentation. In the end, the
# existing approach taken by Puppet::Util::Reference may be the best due to
# the heavy use of metaprogramming in Types and Providers.
# Extracts the Puppet function name and options hash from the parsed # NOTE: The following methods duplicate functionality from
# definition. # Puppet::Util::Reference and Puppet::Parser::Functions.functiondocs
# #
# @return [(String, Hash{String => String})] # However, implementing this natively in YARD is a good test for the
def process_parameters # feasibility of extracting custom Ruby documentation. In the end, the
# Passing `false` to prameters excludes the block param from the returned # existing approach taken by Puppet::Util::Reference may be the best due to
# list. # the heavy use of metaprogramming in Types and Providers.
name, opts = statement.parameters(false).compact
name = process_element(name) # Extracts the Puppet function name and options hash from the parsed
# definition.
#
# @return [(String, Hash{String => String})]
def process_parameters
# Passing `false` to prameters excludes the block param from the returned
# list.
name, opts = statement.parameters(false).compact
opts = opts.map do |tuple| name = process_element(name)
# Jump down into the S-Expression that represents a hashrocket, `=>`,
# and the values on either side of it.
tuple.jump(:assoc).map{|e| process_element(e)}
end
[name, Hash[opts]] opts = opts.map do |tuple|
# Jump down into the S-Expression that represents a hashrocket, `=>`,
# and the values on either side of it.
tuple.jump(:assoc).map{|e| process_element(e)}
end end
# Sometimes the YARD parser returns Heredoc strings that start with `<-` [name, Hash[opts]]
# instead of `<<-`. end
HEREDOC_START = /^<?<-/
# Sometimes the YARD parser returns Heredoc strings that start with `<-`
# instead of `<<-`.
HEREDOC_START = /^<?<-/
# Turns an entry in the method parameter list into a string. # Turns an entry in the method parameter list into a string.
# #
@ -104,20 +103,19 @@ module Puppetx::PuppetLabs::Strings::YARD::Handlers
end end
end end
# Cleans up and formats Heredoc contents parsed by YARD. # Cleans up and formats Heredoc contents parsed by YARD.
# #
# @param source [String] # @param source [String]
# @return [String] # @return [String]
def process_heredoc(source) def process_heredoc(source)
source = source.lines.to_a source = source.lines.to_a
# YARD adds a line of source context on either side of the Heredoc # YARD adds a line of source context on either side of the Heredoc
# contents. # contents.
source.shift source.shift
source.pop source.pop
# This utility method normalizes indentation and trims whitespace. # This utility method normalizes indentation and trims whitespace.
Puppet::Util::Docs.scrub(source.join) Puppet::Util::Docs.scrub(source.join)
end
end end
end end

View File

@ -1,98 +1,97 @@
require 'puppetx/puppetlabs/strings/yard/code_objects' require 'puppetx/puppetlabs/strings/yard/code_objects'
module Puppetx::PuppetLabs::Strings::YARD::Handlers # Handles `dispatch` calls within a future parser function declaration. For
# Handles `dispatch` calls within a future parser function declaration. For # now, it just treats any docstring as an `@overlaod` tag and attaches the
# now, it just treats any docstring as an `@overlaod` tag and attaches the # overload to the parent function.
# overload to the parent function. class Puppetx::PuppetLabs::Strings::YARD::Handlers::Puppet4xFunctionHandler < YARD::Handlers::Ruby::Base
class Puppet4xFunctionHandler < YARD::Handlers::Ruby::Base include Puppetx::PuppetLabs::Strings::YARD::CodeObjects
include Puppetx::PuppetLabs::Strings::YARD::CodeObjects
handles method_call(:dispatch) handles method_call(:dispatch)
process do process do
return unless owner.is_a?(MethodObject) && owner['puppet_4x_function'] return unless owner.is_a?(MethodObject) && owner['puppet_4x_function']
return unless statement.docstring return unless statement.docstring
docstring = ::YARD::Docstring.new(statement.docstring, nil) docstring = ::YARD::Docstring.new(statement.docstring, nil)
# FIXME: This does a wholesale copy of all possible tags. But, we're only # FIXME: This does a wholesale copy of all possible tags. But, we're only
# interested in the @overload tag. # interested in the @overload tag.
owner.add_tag *docstring.tags owner.add_tag *docstring.tags
end end
end
class Puppet4xFunctionHandler < YARD::Handlers::Ruby::Base
include Puppetx::PuppetLabs::Strings::YARD::CodeObjects
handles method_call(:create_function)
process do
name = process_parameters
obj = MethodObject.new(function_namespace, name)
obj['puppet_4x_function'] = true
register obj
obj.add_tag YARD::Tags::Tag.new(:api, 'public')
blk = statement.block.children.first
parse_block(blk, :owner => obj)
end end
class Puppet4xFunctionHandler < YARD::Handlers::Ruby::Base private
include Puppetx::PuppetLabs::Strings::YARD::CodeObjects
handles method_call(:create_function) # Returns a {PuppetNamespaceObject} for holding functions. Creates this
# object if necessary.
#
# @return [PuppetNamespaceObject]
def function_namespace
# NOTE: This tricky. If there is ever a Ruby class or module with the
# name ::Puppet4xFunctions, then there will be a clash. Hopefully the name
# is sufficiently uncommon.
obj = P(:root, 'Puppet4xFunctions')
if obj.is_a? Proxy
namespace_obj = PuppetNamespaceObject.new(:root, 'Puppet4xFunctions')
process do register namespace_obj
name = process_parameters # FIXME: The docstring has to be cleared. Otherwise, the namespace
# object will be registered using the docstring of the
obj = MethodObject.new(function_namespace, name) # `create_function` call that is currently being processed.
obj['puppet_4x_function'] = true #
# Figure out how to properly register the namespace without using the
register obj # function handler object.
register_docstring(namespace_obj, '', nil)
obj.add_tag YARD::Tags::Tag.new(:api, 'public') namespace_obj.add_tag YARD::Tags::Tag.new(:api, 'public')
blk = statement.block.children.first
parse_block(blk, :owner => obj)
end end
private obj
end
# Returns a {PuppetNamespaceObject} for holding functions. Creates this # NOTE: The following methods duplicate functionality from
# object if necessary. # Puppet::Util::Reference and Puppet::Parser::Functions.functiondocs
# #
# @return [PuppetNamespaceObject] # However, implementing this natively in YARD is a good test for the
def function_namespace # feasibility of extracting custom Ruby documentation. In the end, the
# NOTE: This tricky. If there is ever a Ruby class or module with the # existing approach taken by Puppet::Util::Reference may be the best due to
# name ::Puppet4xFunctions, then there will be a clash. Hopefully the name # the heavy use of metaprogramming in Types and Providers.
# is sufficiently uncommon.
obj = P(:root, 'Puppet4xFunctions')
if obj.is_a? Proxy
namespace_obj = PuppetNamespaceObject.new(:root, 'Puppet4xFunctions')
register namespace_obj # Extracts the Puppet function name and options hash from the parsed
# FIXME: The docstring has to be cleared. Otherwise, the namespace # definition.
# object will be registered using the docstring of the #
# `create_function` call that is currently being processed. # @return [(String, Hash{String => String})]
# def process_parameters
# Figure out how to properly register the namespace without using the # Passing `false` to prameters excludes the block param from the returned
# function handler object. # list.
register_docstring(namespace_obj, '', nil) name, _ = statement.parameters(false).compact
namespace_obj.add_tag YARD::Tags::Tag.new(:api, 'public')
end
obj name = process_element(name)
end
# NOTE: The following methods duplicate functionality from name
# Puppet::Util::Reference and Puppet::Parser::Functions.functiondocs end
#
# However, implementing this natively in YARD is a good test for the
# feasibility of extracting custom Ruby documentation. In the end, the
# existing approach taken by Puppet::Util::Reference may be the best due to
# the heavy use of metaprogramming in Types and Providers.
# Extracts the Puppet function name and options hash from the parsed # Sometimes the YARD parser returns Heredoc strings that start with `<-`
# definition. # instead of `<<-`.
# HEREDOC_START = /^<?<-/
# @return [(String, Hash{String => String})]
def process_parameters
# Passing `false` to prameters excludes the block param from the returned
# list.
name, _ = statement.parameters(false).compact
name = process_element(name)
name
end
# Sometimes the YARD parser returns Heredoc strings that start with `<-`
# instead of `<<-`.
HEREDOC_START = /^<?<-/
# Turns an entry in the method parameter list into a string. # Turns an entry in the method parameter list into a string.
# #
@ -114,20 +113,19 @@ module Puppetx::PuppetLabs::Strings::YARD::Handlers
end end
end end
# Cleans up and formats Heredoc contents parsed by YARD. # Cleans up and formats Heredoc contents parsed by YARD.
# #
# @param source [String] # @param source [String]
# @return [String] # @return [String]
def process_heredoc(source) def process_heredoc(source)
source = source.lines.to_a source = source.lines.to_a
# YARD adds a line of source context on either side of the Heredoc # YARD adds a line of source context on either side of the Heredoc
# contents. # contents.
source.shift source.shift
source.pop source.pop
# This utility method normalizes indentation and trims whitespace. # This utility method normalizes indentation and trims whitespace.
Puppet::Util::Docs.scrub(source.join) Puppet::Util::Docs.scrub(source.join)
end
end end
end end

View File

@ -4,29 +4,27 @@ require 'puppet/pops'
require 'puppetx/puppetlabs/strings' require 'puppetx/puppetlabs/strings'
require 'puppetx/puppetlabs/strings//pops/yard_transformer' require 'puppetx/puppetlabs/strings//pops/yard_transformer'
module Puppetx::PuppetLabs::Strings::YARD class Puppetx::PuppetLabs::Strings::YARD::PuppetParser < YARD::Parser::Base
class PuppetParser < YARD::Parser::Base attr_reader :file, :source
attr_reader :file, :source
def initialize(source, filename) def initialize(source, filename)
@source = source @source = source
@file = filename @file = filename
@parser = Puppet::Pops::Parser::Parser.new()
@transformer = Puppetx::PuppetLabs::Strings::Pops::YARDTransformer.new()
end
def parse
@parse_result ||= @parser.parse_string(source)
self
end
def enumerator
statements = @transformer.transform(@parse_result)
# Ensure an array is returned and prune any nil values.
Array(statements).compact
end
@parser = Puppet::Pops::Parser::Parser.new()
@transformer = Puppetx::PuppetLabs::Strings::Pops::YARDTransformer.new()
end end
def parse
@parse_result ||= @parser.parse_string(source)
self
end
def enumerator
statements = @transformer.transform(@parse_result)
# Ensure an array is returned and prune any nil values.
Array(statements).compact
end
end end