(PDOC-63) Implement a Puppet class YARD handler.
This commit implements a YARD handler for Puppet classes and the associated code object and templates.
This commit is contained in:
parent
f2e41a9c23
commit
7c3cd5463c
|
@ -15,6 +15,9 @@ module PuppetStrings::Yard
|
||||||
|
|
||||||
# Register the Puppet parser
|
# Register the Puppet parser
|
||||||
YARD::Parser::SourceParser.register_parser_type(:puppet, PuppetStrings::Yard::Parsers::Puppet::Parser, ['pp'])
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -25,7 +28,8 @@ class YARD::CLI::Yardoc
|
||||||
YARD::Registry.all(
|
YARD::Registry.all(
|
||||||
:root,
|
:root,
|
||||||
:module,
|
:module,
|
||||||
:class
|
:class,
|
||||||
|
:puppet_class,
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -34,6 +38,10 @@ end
|
||||||
# This is the recommended way to add custom stats.
|
# This is the recommended way to add custom stats.
|
||||||
# @private
|
# @private
|
||||||
class YARD::CLI::Stats
|
class YARD::CLI::Stats
|
||||||
|
def stats_for_puppet_classes
|
||||||
|
output 'Puppet Classes', *type_statistics_all(:puppet_class)
|
||||||
|
end
|
||||||
|
|
||||||
def output(name, data, undoc = nil)
|
def output(name, data, undoc = nil)
|
||||||
# Monkey patch output to accommodate our larger header widths
|
# Monkey patch output to accommodate our larger header widths
|
||||||
@total += data if data.is_a?(Integer) && undoc
|
@total += data if data.is_a?(Integer) && undoc
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
# The module for custom YARD code objects.
|
# The module for custom YARD code objects.
|
||||||
module PuppetStrings::Yard::CodeObjects
|
module PuppetStrings::Yard::CodeObjects
|
||||||
|
require 'puppet-strings/yard/code_objects/class'
|
||||||
end
|
end
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -1,3 +1,7 @@
|
||||||
# The module for custom YARD handlers.
|
# The module for custom YARD handlers.
|
||||||
module PuppetStrings::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
|
end
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,9 @@
|
||||||
|
<% even = false %>
|
||||||
|
<% @items.each do |item| %>
|
||||||
|
<li id="object_<%=item.path%>" class="<%= even ? 'even' : 'odd' %>">
|
||||||
|
<div class="item">
|
||||||
|
<%= linkify item, h(item.name(true)) %>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<% even = !even %>
|
||||||
|
<% end %>
|
|
@ -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
|
|
@ -0,0 +1,31 @@
|
||||||
|
<% unless @objects_by_letter.empty? %>
|
||||||
|
<h2><%= @title %></h2>
|
||||||
|
|
||||||
|
<% i = 0 %>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td valign='top' width="33%">
|
||||||
|
<% @objects_by_letter.sort_by {|l,o| l.to_s }.each do |letter, objects| %>
|
||||||
|
<% if (i += 1) % 8 == 0 %>
|
||||||
|
</td>
|
||||||
|
<td valign='top' width="33%">
|
||||||
|
<% i = 0 %>
|
||||||
|
<% end %>
|
||||||
|
<ul id="alpha_<%= letter %>" class="alpha">
|
||||||
|
<li class="letter"><%= letter %></li>
|
||||||
|
<ul>
|
||||||
|
<% objects.each do |obj| %>
|
||||||
|
<li>
|
||||||
|
<%= linkify obj, obj.name %>
|
||||||
|
<% if (obj.type == :module || obj.type == :class) && !obj.namespace.root? %>
|
||||||
|
<small>(<%= obj.namespace.path %>)</small>
|
||||||
|
<% end %>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</ul>
|
||||||
|
<% end %>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<% end %>
|
|
@ -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<Hash>] 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<Hash] Returns the menu lists to use.
|
||||||
|
def menu_lists
|
||||||
|
@@lists ||= create_menu_lists.freeze
|
||||||
|
end
|
||||||
|
|
||||||
|
# Builds a list of objects by letter.
|
||||||
|
# @param [Array] types The types of objects to find.
|
||||||
|
# @return [Hash] Returns a hash of first letter of the object name to list of objects.
|
||||||
|
def objects_by_letter(*types)
|
||||||
|
hash = {}
|
||||||
|
objects = Registry.all(*types).sort_by {|o| o.name.to_s }
|
||||||
|
objects = run_verifier(objects)
|
||||||
|
objects.each {|o| (hash[o.name.to_s[0,1].upcase] ||= []) << o }
|
||||||
|
hash
|
||||||
|
end
|
||||||
|
|
||||||
|
# Renders the classes section.
|
||||||
|
# @return [String] Returns the rendered section.
|
||||||
|
def classes
|
||||||
|
@title = 'Puppet Class Listing A-Z'
|
||||||
|
@objects_by_letter = objects_by_letter(:puppet_class)
|
||||||
|
erb(:objects)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Renders the objects section.
|
||||||
|
# @return [String] Returns the rendered section.
|
||||||
|
def objects
|
||||||
|
@title = 'Ruby Namespace Listing A-Z'
|
||||||
|
@objects_by_letter = objects_by_letter(:class, :module)
|
||||||
|
erb(:objects)
|
||||||
|
end
|
|
@ -0,0 +1,26 @@
|
||||||
|
<div class="box_info">
|
||||||
|
<% if object.statement.parent_class %>
|
||||||
|
<dl>
|
||||||
|
<dt>Inherits:</dt>
|
||||||
|
<dd><%= linkify(Registry["puppet_classes::#{object.statement.parent_class}"], object.statement.parent_class.dup) %></dd>
|
||||||
|
</dl>
|
||||||
|
<% end %>
|
||||||
|
<% if @subclasses && !@subclasses.empty? %>
|
||||||
|
<dl>
|
||||||
|
<dt>Inherited by:</dt>
|
||||||
|
<dd>
|
||||||
|
<% @subclasses.each do |subclass| %>
|
||||||
|
<%= linkify(subclass, subclass.name.to_s) %><br/>
|
||||||
|
<% end %>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
<% end %>
|
||||||
|
<dl>
|
||||||
|
<dt>Defined in:</dt>
|
||||||
|
<dd>
|
||||||
|
<%= object.file %><% if object.files.size > 1 %><span class="defines">,<br />
|
||||||
|
<%= object.files[1..-1].map {|f| f.first }.join(",<br /> ") %></div>
|
||||||
|
<% end %>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
|
@ -0,0 +1 @@
|
||||||
|
<h1>Puppet Class: <%= object.name %></h1>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<h2>Overview</h2>
|
||||||
|
<div class="docstring">
|
||||||
|
<div class="discussion">
|
||||||
|
<%= htmlify(object.docstring) %>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -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
|
|
@ -0,0 +1,12 @@
|
||||||
|
<div class="method_details_list">
|
||||||
|
<table class="source_code">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<pre class="lines"><%= "\n\n\n" %><%= h format_lines(object) %></pre>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<pre class="code"><span class="info file"># File '<%= h object.file %>'<% if object.line %>, line <%= object.line %><% end %></span><%= "\n\n" %><%= html_syntax_highlight object.source %></pre>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Called to return parameter tags.
|
||||||
|
# @return [Array<YARD::Tag>] Returns the parameter tags if the object should have parameters.
|
||||||
|
def param
|
||||||
|
tag(:param) if
|
||||||
|
object.type == :method ||
|
||||||
|
object.type == :puppet_class
|
||||||
|
end
|
||||||
|
|
Loading…
Reference in New Issue