diff --git a/lib/puppet-strings/yard.rb b/lib/puppet-strings/yard.rb
index 90d0be0..2a03364 100644
--- a/lib/puppet-strings/yard.rb
+++ b/lib/puppet-strings/yard.rb
@@ -15,6 +15,9 @@ module PuppetStrings::Yard
# Register the Puppet parser
YARD::Parser::SourceParser.register_parser_type(:puppet, PuppetStrings::Yard::Parsers::Puppet::Parser, ['pp'])
+
+ # Register our handlers
+ YARD::Handlers::Processor.register_handler_namespace(:puppet, PuppetStrings::Yard::Handlers::Puppet)
end
end
@@ -25,7 +28,8 @@ class YARD::CLI::Yardoc
YARD::Registry.all(
:root,
:module,
- :class
+ :class,
+ :puppet_class,
)
end
end
@@ -34,6 +38,10 @@ end
# This is the recommended way to add custom stats.
# @private
class YARD::CLI::Stats
+ def stats_for_puppet_classes
+ output 'Puppet Classes', *type_statistics_all(:puppet_class)
+ end
+
def output(name, data, undoc = nil)
# Monkey patch output to accommodate our larger header widths
@total += data if data.is_a?(Integer) && undoc
diff --git a/lib/puppet-strings/yard/code_objects.rb b/lib/puppet-strings/yard/code_objects.rb
index d346ef7..93a5a0f 100644
--- a/lib/puppet-strings/yard/code_objects.rb
+++ b/lib/puppet-strings/yard/code_objects.rb
@@ -1,3 +1,4 @@
# The module for custom YARD code objects.
module PuppetStrings::Yard::CodeObjects
+ require 'puppet-strings/yard/code_objects/class'
end
diff --git a/lib/puppet-strings/yard/code_objects/base.rb b/lib/puppet-strings/yard/code_objects/base.rb
new file mode 100644
index 0000000..ba9840e
--- /dev/null
+++ b/lib/puppet-strings/yard/code_objects/base.rb
@@ -0,0 +1,14 @@
+# Implements the base code object.
+class PuppetStrings::Yard::CodeObjects::Base < YARD::CodeObjects::NamespaceObject
+ # Allocates a new code object.
+ # @param [Array] args The arguments to initialize the code object with.
+ # @return Returns the code object.
+ def self.new(*args)
+ # Skip the super class' implementation because it detects :: in names and this will cause namespaces in the output we don't want
+ object = Object.class.instance_method(:new).bind(self).call(*args)
+ existing = YARD::Registry.at(object.path)
+ object = existing if existing && existing.class == self
+ yield(object) if block_given?
+ object
+ end
+end
diff --git a/lib/puppet-strings/yard/code_objects/class.rb b/lib/puppet-strings/yard/code_objects/class.rb
new file mode 100644
index 0000000..71461c5
--- /dev/null
+++ b/lib/puppet-strings/yard/code_objects/class.rb
@@ -0,0 +1,44 @@
+require 'puppet-strings/yard/code_objects/group'
+
+# Implements the group for Puppet classes.
+class PuppetStrings::Yard::CodeObjects::Classes < PuppetStrings::Yard::CodeObjects::Group
+ # Gets the singleton instance of the group.
+ # @return Returns the singleton instance of the group.
+ def self.instance
+ super(:puppet_classes)
+ end
+
+ # Gets the display name of the group.
+ # @param [Boolean] prefix whether to show a prefix. Ignored for Puppet group namespaces.
+ # @return [String] Returns the display name of the group.
+ def name(prefix = false)
+ 'Puppet Classes'
+ end
+end
+
+# Implements the Puppet class code object.
+class PuppetStrings::Yard::CodeObjects::Class < PuppetStrings::Yard::CodeObjects::Base
+ attr_reader :statement
+ attr_reader :parameters
+
+ # Initializes a Puppet class code object.
+ # @param [PuppetStrings::Parsers::ClassStatement] statement The class statement that was parsed.
+ # @return [void]
+ def initialize(statement)
+ @statement = statement
+ @parameters = statement.parameters.map { |p| [p.name, p.value] }
+ super(PuppetStrings::Yard::CodeObjects::Classes.instance, statement.name)
+ end
+
+ # Gets the type of the code object.
+ # @return Returns the type of the code object.
+ def type
+ :puppet_class
+ end
+
+ # Gets the source of the code object.
+ # @return Returns the source of the code object.
+ def source
+ @statement.source
+ end
+end
diff --git a/lib/puppet-strings/yard/code_objects/group.rb b/lib/puppet-strings/yard/code_objects/group.rb
new file mode 100644
index 0000000..f3becac
--- /dev/null
+++ b/lib/puppet-strings/yard/code_objects/group.rb
@@ -0,0 +1,30 @@
+require 'puppet-strings/yard/code_objects/base'
+
+# Implements the base class for "groups".
+#
+# A group behaves like a YARD namespace object, but displays differently in the HTML output.
+class PuppetStrings::Yard::CodeObjects::Group < PuppetStrings::Yard::CodeObjects::Base
+ # Gets the singleton instance of the group.
+ # @param [Symbol] key The key to lookup the group for.
+ # @return Returns the singleton instance of the group.
+ def self.instance(key)
+ instance = P(:root, key)
+ return instance unless instance.is_a?(YARD::CodeObjects::Proxy)
+ instance = self.new(:root, key)
+ instance.visibility = :hidden
+ P(:root).children << instance
+ instance
+ end
+
+ # Gets the path to the group.
+ # @return [String] Returns the path to the group.
+ def path
+ @name.to_s
+ end
+
+ # Gets the type of the group.
+ # @return [Symbol] Returns the type of the group.
+ def type
+ @name
+ end
+end
diff --git a/lib/puppet-strings/yard/handlers.rb b/lib/puppet-strings/yard/handlers.rb
index e502343..df90be1 100644
--- a/lib/puppet-strings/yard/handlers.rb
+++ b/lib/puppet-strings/yard/handlers.rb
@@ -1,3 +1,7 @@
# The module for custom YARD handlers.
module PuppetStrings::Yard::Handlers
+ # The module for custom Puppet YARD handlers.
+ module Puppet
+ require 'puppet-strings/yard/handlers/puppet/class_handler'
+ end
end
diff --git a/lib/puppet-strings/yard/handlers/puppet/base.rb b/lib/puppet-strings/yard/handlers/puppet/base.rb
new file mode 100644
index 0000000..0315b3c
--- /dev/null
+++ b/lib/puppet-strings/yard/handlers/puppet/base.rb
@@ -0,0 +1,46 @@
+# Implements the base handler for Puppet language handlers.
+class PuppetStrings::Yard::Handlers::Puppet::Base < YARD::Handlers::Base
+ # Determine sif the handler handles the given statement.
+ # @param statement The statement that was parsed.
+ # @return [Boolean] Returns true if the statement is handled by this handler or false if not.
+ def self.handles?(statement)
+ handlers.any? {|handler| statement.is_a?(handler)}
+ end
+
+ protected
+ # Sets the parameter tag types for the given code object.
+ # This also performs some validation on the parameter tags.
+ # @param object The code object to set the parameter tag types for.
+ # @return [void]
+ def set_parameter_types(object)
+ # Ensure there is an actual parameter for each parameter tag
+ tags = object.tags(:param)
+ tags.each do |tag|
+ next if statement.parameters.find { |p| tag.name == p.name }
+ log.warn "The @param tag for parameter '#{tag.name}' has no matching parameter at #{statement.file}:#{statement.line}."
+ end
+
+ # Assign the types for the parameter
+ statement.parameters.each do |parameter|
+ tag = tags.find { |t| t.name == parameter.name }
+
+ unless tag
+ log.warn "Missing @param tag for parameter '#{parameter.name}' near #{statement.file}:#{statement.line}." unless object.docstring.empty?
+
+ # Add a tag with an empty docstring
+ object.add_tag YARD::Tags::Tag.new(:param, '', [parameter.type || 'Any'], parameter.name)
+ object.parameters << [parameter.name, parameter.value]
+ next
+ end
+
+ # Warn if the parameter is typed and the tag also has a type
+ log.warn "The @param tag for parameter '#{parameter.name}' should not contain a type specification near #{statement.file}:#{statement.line}: ignoring in favor of parameter type information." if parameter.type && tag.types && !tag.types.empty?
+
+ if parameter.type
+ tag.types = [parameter.type]
+ elsif !tag.types
+ tag.types = ['Any']
+ end
+ end
+ end
+end
diff --git a/lib/puppet-strings/yard/handlers/puppet/class_handler.rb b/lib/puppet-strings/yard/handlers/puppet/class_handler.rb
new file mode 100644
index 0000000..9415bc5
--- /dev/null
+++ b/lib/puppet-strings/yard/handlers/puppet/class_handler.rb
@@ -0,0 +1,23 @@
+require 'puppet-strings/yard/handlers/puppet/base'
+require 'puppet-strings/yard/parsers'
+require 'puppet-strings/yard/code_objects'
+
+# Implements the handler for Puppet classes.
+class PuppetStrings::Yard::Handlers::Puppet::ClassHandler < PuppetStrings::Yard::Handlers::Puppet::Base
+ handles PuppetStrings::Yard::Parsers::Puppet::ClassStatement
+
+ process do
+ # Register the object
+ object = PuppetStrings::Yard::CodeObjects::Class.new(statement)
+ register object
+
+ # Log a warning if missing documentation
+ log.warn "Missing documentation for Puppet class '#{object.name}' at #{statement.file}:#{statement.line}." if object.docstring.empty?
+
+ # Set the parameter types
+ set_parameter_types(object)
+
+ # Mark the class as public if it doesn't already have an api tag
+ object.add_tag YARD::Tags::Tag.new(:api, 'public') unless object.has_tag? :api
+ end
+end
diff --git a/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_class.erb b/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_class.erb
new file mode 100644
index 0000000..095188f
--- /dev/null
+++ b/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_class.erb
@@ -0,0 +1,9 @@
+<% even = false %>
+<% @items.each do |item| %>
+
+
+ <%= linkify item, h(item.name(true)) %>
+
+
+ <% even = !even %>
+<% end %>
diff --git a/lib/puppet-strings/yard/templates/default/fulldoc/html/setup.rb b/lib/puppet-strings/yard/templates/default/fulldoc/html/setup.rb
new file mode 100644
index 0000000..0c58c0f
--- /dev/null
+++ b/lib/puppet-strings/yard/templates/default/fulldoc/html/setup.rb
@@ -0,0 +1,28 @@
+# Generates the searchable Puppet class list.
+# @return [void]
+def generate_puppet_class_list
+ @items = Registry.all(:puppet_class).sort_by { |c| c.name.to_s }
+ @list_title = 'Puppet Class List'
+ @list_type = 'puppet_class'
+ generate_list_contents
+end
+
+# Generates the searchable Ruby method list.
+# @return [void]
+def generate_method_list
+ @items = prune_method_listing(Registry.all(:method), false)
+ @items = @items.reject {|m| m.name.to_s =~ /=$/ && m.is_attribute? }
+ @items = @items.sort_by {|m| m.name.to_s }
+ @list_title = 'Ruby Method List'
+ @list_type = 'method'
+ generate_list_contents
+end
+
+# Generate a searchable Ruby class list in the output.
+# @return [void]
+def generate_class_list
+ @items = options.objects if options.objects
+ @list_title = 'Ruby Class List'
+ @list_type = 'class'
+ generate_list_contents
+end
diff --git a/lib/puppet-strings/yard/templates/default/layout/html/objects.erb b/lib/puppet-strings/yard/templates/default/layout/html/objects.erb
new file mode 100644
index 0000000..9395340
--- /dev/null
+++ b/lib/puppet-strings/yard/templates/default/layout/html/objects.erb
@@ -0,0 +1,31 @@
+<% unless @objects_by_letter.empty? %>
+<%= @title %>
+
+ <% i = 0 %>
+
+
+
+ <% @objects_by_letter.sort_by {|l,o| l.to_s }.each do |letter, objects| %>
+ <% if (i += 1) % 8 == 0 %>
+ |
+
+ <% i = 0 %>
+ <% end %>
+
+ - <%= letter %>
+
+ <% objects.each do |obj| %>
+ -
+ <%= linkify obj, obj.name %>
+ <% if (obj.type == :module || obj.type == :class) && !obj.namespace.root? %>
+ (<%= obj.namespace.path %>)
+ <% end %>
+
+ <% end %>
+
+
+ <% end %>
+ |
+
+
+<% end %>
diff --git a/lib/puppet-strings/yard/templates/default/layout/html/setup.rb b/lib/puppet-strings/yard/templates/default/layout/html/setup.rb
new file mode 100644
index 0000000..bd5a48e
--- /dev/null
+++ b/lib/puppet-strings/yard/templates/default/layout/html/setup.rb
@@ -0,0 +1,104 @@
+# Initializes the template.
+# @return [void]
+def init
+ case object
+ when '_index.html'
+ @page_title = options.title
+ sections :layout, [:index, [:listing, [:classes, :files, :objects]]]
+ else
+ super
+ end
+end
+
+# Renders the layout section.
+# @return [String] Returns the rendered section.
+def layout
+ @nav_url = url_for_list(!@file || options.index ? menu_lists.first[:type] : 'file')
+
+ case object
+ when nil, String
+ @path = nil
+ when @file
+ @path = @file.path
+ when !object.is_a?(YARD::CodeObjects::NamespaceObject)
+ @path = object.parent.path
+ @nav_url = url_for_list('class')
+ when YARD::CodeObjects::ClassObject
+ @path = object.path
+ @nav_url = url_for_list('class')
+ when PuppetStrings::Yard::CodeObjects::Class
+ @nav_url = url_for_list('puppet_class')
+ @page_title = "Puppet Class: #{object.name}"
+ @path = object.path
+ else
+ @path = object.path
+ end
+
+ erb(:layout)
+end
+
+# Creates the dynamic menu lists.
+# @return [Array] Returns the dynamic menu list.
+def create_menu_lists
+ menu_lists = [
+ {
+ type: 'puppet_class',
+ title: 'Puppet Classes',
+ search_title: 'Puppet Classes'
+ },
+ {
+ type: 'class',
+ title: 'Ruby Classes',
+ search_title: 'Class List'
+ },
+ {
+ type: 'method',
+ title: 'Ruby Methods',
+ search_title: 'Method List'
+ },
+ ]
+
+ menu_lists.delete_if { |e| YARD::Registry.all(e[:type].intern).empty? }
+
+ # We must always return at least one group, so always keep the files list
+ menu_lists << {
+ type: 'file',
+ title: 'Files',
+ search_title: 'File List'
+ } if menu_lists.empty? || !YARD::Registry.all(:file).empty?
+
+ menu_lists
+end
+
+# Gets the menu lists to use.
+# @return [Array
+ <% if object.statement.parent_class %>
+
+ - Inherits:
+ - <%= linkify(Registry["puppet_classes::#{object.statement.parent_class}"], object.statement.parent_class.dup) %>
+
+ <% end %>
+ <% if @subclasses && !@subclasses.empty? %>
+
+ - Inherited by:
+ -
+ <% @subclasses.each do |subclass| %>
+ <%= linkify(subclass, subclass.name.to_s) %>
+ <% end %>
+
+
+ <% end %>
+
+ - Defined in:
+ -
+ <%= object.file %><% if object.files.size > 1 %>,
+ <%= object.files[1..-1].map {|f| f.first }.join(",
") %>
+ <% end %>
+
+
+
diff --git a/lib/puppet-strings/yard/templates/default/puppet_class/html/header.erb b/lib/puppet-strings/yard/templates/default/puppet_class/html/header.erb
new file mode 100644
index 0000000..a7e847c
--- /dev/null
+++ b/lib/puppet-strings/yard/templates/default/puppet_class/html/header.erb
@@ -0,0 +1 @@
+Puppet Class: <%= object.name %>
diff --git a/lib/puppet-strings/yard/templates/default/puppet_class/html/overview.erb b/lib/puppet-strings/yard/templates/default/puppet_class/html/overview.erb
new file mode 100644
index 0000000..a5b527a
--- /dev/null
+++ b/lib/puppet-strings/yard/templates/default/puppet_class/html/overview.erb
@@ -0,0 +1,6 @@
+Overview
+
+
+ <%= htmlify(object.docstring) %>
+
+
diff --git a/lib/puppet-strings/yard/templates/default/puppet_class/html/setup.rb b/lib/puppet-strings/yard/templates/default/puppet_class/html/setup.rb
new file mode 100644
index 0000000..2250d2f
--- /dev/null
+++ b/lib/puppet-strings/yard/templates/default/puppet_class/html/setup.rb
@@ -0,0 +1,14 @@
+# Initializes the template.
+# @return [void]
+def init
+ sections :header, :box_info, :overview, T('tags'), :source
+end
+
+# Renders the box_info section.
+# @return [String] Returns the rendered section.
+def box_info
+ @subclasses = Registry.all(:puppet_class).find_all { |c|
+ c.statement.parent_class == object.name.to_s
+ }
+ erb(:box_info)
+end
diff --git a/lib/puppet-strings/yard/templates/default/puppet_class/html/source.erb b/lib/puppet-strings/yard/templates/default/puppet_class/html/source.erb
new file mode 100644
index 0000000..0fd3c5e
--- /dev/null
+++ b/lib/puppet-strings/yard/templates/default/puppet_class/html/source.erb
@@ -0,0 +1,12 @@
+
+
+
+
+ <%= "\n\n\n" %><%= h format_lines(object) %>
+ |
+
+ # File '<%= h object.file %>'<% if object.line %>, line <%= object.line %><% end %><%= "\n\n" %><%= html_syntax_highlight object.source %>
+ |
+
+
+
diff --git a/lib/puppet-strings/yard/templates/default/tags/setup.rb b/lib/puppet-strings/yard/templates/default/tags/setup.rb
new file mode 100644
index 0000000..be92cf0
--- /dev/null
+++ b/lib/puppet-strings/yard/templates/default/tags/setup.rb
@@ -0,0 +1,8 @@
+# Called to return parameter tags.
+# @return [Array] Returns the parameter tags if the object should have parameters.
+def param
+ tag(:param) if
+ object.type == :method ||
+ object.type == :puppet_class
+end
+