diff --git a/lib/puppet/face/strings.rb b/lib/puppet/face/strings.rb index 13e88df..697d4e1 100644 --- a/lib/puppet/face/strings.rb +++ b/lib/puppet/face/strings.rb @@ -1,5 +1,4 @@ require 'puppet/face' -require 'puppetx/puppetlabs/strings/actions' Puppet::Face.define(:strings, '0.0.1') do summary "Generate Puppet documentation with YARD." @@ -34,6 +33,7 @@ Puppet::Face.define(:strings, '0.0.1') do when_invoked do |*args| check_required_features + require 'puppetx/puppetlabs/strings/actions' 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| check_required_features + require 'puppetx/puppetlabs/strings/actions' server_actions = Puppetx::PuppetLabs::Strings::Actions.new(Puppet[:debug], Puppet[:trace]) diff --git a/lib/puppetx/puppetlabs/strings.rb b/lib/puppetx/puppetlabs/strings.rb index 73e63d3..2c9ab44 100644 --- a/lib/puppetx/puppetlabs/strings.rb +++ b/lib/puppetx/puppetlabs/strings.rb @@ -6,6 +6,11 @@ module Puppetx::PuppetLabs # This submodule contains bits that interface with the YARD plugin system. module YARD + module Handlers + end + + module CodeObjects + end end # This submodule contains bits that operate on the Pops module produced by diff --git a/lib/puppetx/puppetlabs/strings/pops/yard_statement.rb b/lib/puppetx/puppetlabs/strings/pops/yard_statement.rb index ac2fe4c..e879964 100644 --- a/lib/puppetx/puppetlabs/strings/pops/yard_statement.rb +++ b/lib/puppetx/puppetlabs/strings/pops/yard_statement.rb @@ -3,76 +3,73 @@ require 'puppet/pops' require 'puppetx/puppetlabs/strings' -module Puppetx::PuppetLabs::Strings::Pops - # An adapter class that conforms a Pops model instance + adapters to the - # interface expected by YARD handlers. - # - # 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 - # would be improved by having a concrete model. - class YARDStatement < OpenStruct - attr_reader :pops_obj, :comments +# An adapter class that conforms a Pops model instance + adapters to the +# interface expected by YARD handlers. +# +# 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 +# would be improved by having a concrete model. +class Puppetx::PuppetLabs::Strings::Pops::YARDStatement < OpenStruct + attr_reader :pops_obj, :comments - def initialize(pops_obj) - # Initialize OpenStruct - super({}) + def initialize(pops_obj) + # Initialize OpenStruct + super({}) - 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}" + 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}" + 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 - - @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 + # Stick everything back together. + comments.join end end diff --git a/lib/puppetx/puppetlabs/strings/pops/yard_transformer.rb b/lib/puppetx/puppetlabs/strings/pops/yard_transformer.rb index 3cb57cb..111ee27 100644 --- a/lib/puppetx/puppetlabs/strings/pops/yard_transformer.rb +++ b/lib/puppetx/puppetlabs/strings/pops/yard_transformer.rb @@ -2,52 +2,50 @@ require 'puppet/pops' require 'puppetx/puppetlabs/strings' require 'puppetx/puppetlabs/strings/pops/yard_statement' -module Puppetx::PuppetLabs::Strings::Pops - # 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 - # by YARD handlers. - # - # @note Currently, this class only extracts node, host class and type - # definitions. - class YARDTransformer - def initialize - @transform_visitor = Puppet::Pops::Visitor.new(self, 'transform') +# 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 +# by YARD handlers. +# +# @note Currently, this class only extracts node, host class and type +# definitions. +class Puppetx::PuppetLabs::Strings::Pops::YARDTransformer + def initialize + @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 - def transform(o) - @transform_visitor.visit(o) - end + obj + end - private + # Catch-all visitor. + def transform_Positioned(o) + Puppetx::PuppetLabs::Strings::Pops::YARDStatement.new(o) + end - 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 = 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 + # nil in... nil out! + def transform_NilClass(o) + nil end end diff --git a/lib/puppetx/puppetlabs/strings/yard/code_objects/defined_type_object.rb b/lib/puppetx/puppetlabs/strings/yard/code_objects/defined_type_object.rb index aaafc92..7465b80 100644 --- a/lib/puppetx/puppetlabs/strings/yard/code_objects/defined_type_object.rb +++ b/lib/puppetx/puppetlabs/strings/yard/code_objects/defined_type_object.rb @@ -2,10 +2,8 @@ require 'puppet/pops' require 'puppetx/puppetlabs/strings/yard/code_objects/puppet_namespace_object' -module Puppetx::PuppetLabs::Strings::YARD::CodeObjects - class DefinedTypeObject < PuppetNamespaceObject - # A list of parameters attached to this class. - # @return [Array] - attr_accessor :parameters - end +class Puppetx::PuppetLabs::Strings::YARD::CodeObjects::DefinedTypeObject < Puppetx::PuppetLabs::Strings::YARD::CodeObjects::PuppetNamespaceObject + # A list of parameters attached to this class. + # @return [Array] + attr_accessor :parameters end diff --git a/lib/puppetx/puppetlabs/strings/yard/code_objects/host_class_object.rb b/lib/puppetx/puppetlabs/strings/yard/code_objects/host_class_object.rb index 1cc5022..5ca5086 100644 --- a/lib/puppetx/puppetlabs/strings/yard/code_objects/host_class_object.rb +++ b/lib/puppetx/puppetlabs/strings/yard/code_objects/host_class_object.rb @@ -1,26 +1,24 @@ require 'puppetx/puppetlabs/strings/yard/code_objects/defined_type_object' -module Puppetx::PuppetLabs::Strings::YARD::CodeObjects - class HostClassObject < DefinedTypeObject - # The {HostClassObject} that this class inherits from, if any. - # @return [HostClassObject, Proxy, nil] - attr_accessor :parent_class +class Puppetx::PuppetLabs::Strings::YARD::CodeObjects::HostClassObject < Puppetx::PuppetLabs::Strings::YARD::CodeObjects::DefinedTypeObject + # The {HostClassObject} that this class inherits from, if any. + # @return [HostClassObject, Proxy, nil] + attr_accessor :parent_class - # 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. - def inheritance_tree(include_mods = false) - if parent_class.is_a?(HostClassObject) - # Cool. We got a host class. Return self + parent inheritance tree. - [self] + parent_class.inheritance_tree - elsif parent_class.is_a?(YARD::CodeObjects::Proxy) - # We have a reference to a parent that has not been created yet. Just - # return it. - [self, parent_class] - else - # Most likely no parent class. But also possibly an object that we - # shouldn't inherit from. Just return self. - [self] - end + # 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. + def inheritance_tree(include_mods = false) + if parent_class.is_a?(Puppetx::PuppetLabs::Strings::YARD::CodeObjects::HostClassObject) + # Cool. We got a host class. Return self + parent inheritance tree. + [self] + parent_class.inheritance_tree + elsif parent_class.is_a?(YARD::CodeObjects::Proxy) + # We have a reference to a parent that has not been created yet. Just + # return it. + [self, parent_class] + else + # Most likely no parent class. But also possibly an object that we + # shouldn't inherit from. Just return self. + [self] end end end diff --git a/lib/puppetx/puppetlabs/strings/yard/code_objects/puppet_namespace_object.rb b/lib/puppetx/puppetlabs/strings/yard/code_objects/puppet_namespace_object.rb index 4fd0f47..00146eb 100644 --- a/lib/puppetx/puppetlabs/strings/yard/code_objects/puppet_namespace_object.rb +++ b/lib/puppetx/puppetlabs/strings/yard/code_objects/puppet_namespace_object.rb @@ -1,35 +1,33 @@ require 'yard' require 'puppetx/puppetlabs/strings' -module Puppetx::PuppetLabs::Strings::YARD::CodeObjects - class PuppetNamespaceObject < YARD::CodeObjects::NamespaceObject - # NOTE: `YARD::Registry#resolve` requires a method with this signature to - # be present on all subclasses of `NamespaceObject`. - def inheritance_tree(include_mods = false) - [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) +class Puppetx::PuppetLabs::Strings::YARD::CodeObjects::PuppetNamespaceObject < YARD::CodeObjects::NamespaceObject + # NOTE: `YARD::Registry#resolve` requires a method with this signature to + # be present on all subclasses of `NamespaceObject`. + def inheritance_tree(include_mods = false) + [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 diff --git a/lib/puppetx/puppetlabs/strings/yard/handlers/base.rb b/lib/puppetx/puppetlabs/strings/yard/handlers/base.rb index 2b637da..36d1b79 100644 --- a/lib/puppetx/puppetlabs/strings/yard/handlers/base.rb +++ b/lib/puppetx/puppetlabs/strings/yard/handlers/base.rb @@ -4,16 +4,14 @@ require 'puppet/pops' require 'puppetx/puppetlabs/strings' require 'puppetx/puppetlabs/strings/yard/code_objects' -module Puppetx::PuppetLabs::Strings::YARD::Handlers - class Base < YARD::Handlers::Base - # Easy access to Pops model objects for handler matching. - include Puppet::Pops::Model - # Easy access to custom code objects from which documentation is generated. - include Puppetx::PuppetLabs::Strings::YARD::CodeObjects - - def self.handles?(statement) - handlers.any? {|h| h == statement.type} - end +class Puppetx::PuppetLabs::Strings::YARD::Handlers::Base < YARD::Handlers::Base + # Easy access to Pops model objects for handler matching. + include Puppet::Pops::Model + # Easy access to custom code objects from which documentation is generated. + include Puppetx::PuppetLabs::Strings::YARD::CodeObjects + def self.handles?(statement) + handlers.any? {|h| h == statement.type} end + end diff --git a/lib/puppetx/puppetlabs/strings/yard/handlers/defined_type_handler.rb b/lib/puppetx/puppetlabs/strings/yard/handlers/defined_type_handler.rb index 74297a7..309b6cc 100644 --- a/lib/puppetx/puppetlabs/strings/yard/handlers/defined_type_handler.rb +++ b/lib/puppetx/puppetlabs/strings/yard/handlers/defined_type_handler.rb @@ -1,18 +1,16 @@ require 'puppetx/puppetlabs/strings/yard/handlers/base' -module Puppetx::PuppetLabs::Strings::YARD::Handlers - class DefinedTypeHandler < Base - handles ResourceTypeDefinition +class Puppetx::PuppetLabs::Strings::YARD::Handlers::DefinedTypeHandler < Puppetx::PuppetLabs::Strings::YARD::Handlers:: Base + handles ResourceTypeDefinition - process do - obj = DefinedTypeObject.new(:root, statement.pops_obj.name) do |o| - o.parameters = statement.parameters.map do |a| - param_tuple = [a[0].pops_obj.name] - param_tuple << ( a[1].nil? ? nil : a[1].source ) - end + process do + obj = DefinedTypeObject.new(:root, statement.pops_obj.name) do |o| + o.parameters = statement.parameters.map do |a| + param_tuple = [a[0].pops_obj.name] + param_tuple << ( a[1].nil? ? nil : a[1].source ) end - - register obj end + + register obj end end diff --git a/lib/puppetx/puppetlabs/strings/yard/handlers/host_class_handler.rb b/lib/puppetx/puppetlabs/strings/yard/handlers/host_class_handler.rb index 405acee..519dc8b 100644 --- a/lib/puppetx/puppetlabs/strings/yard/handlers/host_class_handler.rb +++ b/lib/puppetx/puppetlabs/strings/yard/handlers/host_class_handler.rb @@ -1,24 +1,22 @@ require 'puppetx/puppetlabs/strings/yard/handlers/base' -module Puppetx::PuppetLabs::Strings::YARD::Handlers - class HostClassHandler < Base - handles HostClassDefinition +class Puppetx::PuppetLabs::Strings::YARD::Handlers::HostClassHandler < Puppetx::PuppetLabs::Strings::YARD::Handlers::Base + handles HostClassDefinition - process do - obj = HostClassObject.new(:root, statement.pops_obj.name) do |o| - o.parameters = statement.parameters.map do |a| - param_tuple = [a[0].pops_obj.name] - param_tuple << ( a[1].nil? ? nil : a[1].source ) - end + process do + obj = HostClassObject.new(:root, statement.pops_obj.name) do |o| + o.parameters = statement.parameters.map do |a| + param_tuple = [a[0].pops_obj.name] + param_tuple << ( a[1].nil? ? nil : a[1].source ) end - - statement.pops_obj.tap do |o| - if o.parent_class - obj.parent_class = P(:root, o.parent_class) - end - end - - register obj 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 diff --git a/lib/puppetx/puppetlabs/strings/yard/handlers/puppet_3x_function_handler.rb b/lib/puppetx/puppetlabs/strings/yard/handlers/puppet_3x_function_handler.rb index 8b4bfb3..6df0024 100644 --- a/lib/puppetx/puppetlabs/strings/yard/handlers/puppet_3x_function_handler.rb +++ b/lib/puppetx/puppetlabs/strings/yard/handlers/puppet_3x_function_handler.rb @@ -2,87 +2,86 @@ require 'puppet/util/docs' require 'puppetx/puppetlabs/strings/yard/code_objects' -module Puppetx::PuppetLabs::Strings::YARD::Handlers - class Puppet3xFunctionHandler < YARD::Handlers::Ruby::Base - include Puppetx::PuppetLabs::Strings::YARD::CodeObjects +class Puppetx::PuppetLabs::Strings::YARD::Handlers::Puppet3xFunctionHandler < YARD::Handlers::Ruby::Base + include Puppetx::PuppetLabs::Strings::YARD::CodeObjects - handles method_call(:newfunction) + handles method_call(:newfunction) - process do - name, options = process_parameters + process do + name, options = process_parameters - obj = MethodObject.new(function_namespace, name) + obj = MethodObject.new(function_namespace, name) - register obj - if options['doc'] - 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') + register obj + if options['doc'] + register_docstring(obj, options['doc'], nil) 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 - # 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') + # 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 - register namespace_obj - end + private - 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 - # NOTE: The following methods duplicate functionality from - # Puppet::Util::Reference and Puppet::Parser::Functions.functiondocs - # - # 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. + obj + end - # 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 + # NOTE: The following methods duplicate functionality from + # Puppet::Util::Reference and Puppet::Parser::Functions.functiondocs + # + # 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. - 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| - # 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 = process_element(name) - [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 - # Sometimes the YARD parser returns Heredoc strings that start with `<-` - # instead of `<<-`. - HEREDOC_START = /^ obj) end - class Puppet4xFunctionHandler < YARD::Handlers::Ruby::Base - include Puppetx::PuppetLabs::Strings::YARD::CodeObjects + private - 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 - 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) + register namespace_obj + # FIXME: The docstring has to be cleared. Otherwise, the namespace + # object will be registered using the docstring of the + # `create_function` call that is currently being processed. + # + # Figure out how to properly register the namespace without using the + # function handler object. + register_docstring(namespace_obj, '', nil) + namespace_obj.add_tag YARD::Tags::Tag.new(:api, 'public') end - private + obj + end - # 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') + # NOTE: The following methods duplicate functionality from + # Puppet::Util::Reference and Puppet::Parser::Functions.functiondocs + # + # 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. - register namespace_obj - # FIXME: The docstring has to be cleared. Otherwise, the namespace - # object will be registered using the docstring of the - # `create_function` call that is currently being processed. - # - # Figure out how to properly register the namespace without using the - # function handler object. - register_docstring(namespace_obj, '', nil) - namespace_obj.add_tag YARD::Tags::Tag.new(:api, 'public') - end + # 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, _ = statement.parameters(false).compact - obj - end + name = process_element(name) - # NOTE: The following methods duplicate functionality from - # Puppet::Util::Reference and Puppet::Parser::Functions.functiondocs - # - # 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. + name + end - # 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, _ = statement.parameters(false).compact - - name = process_element(name) - - name - end - - # Sometimes the YARD parser returns Heredoc strings that start with `<-` - # instead of `<<-`. - HEREDOC_START = /^