Skip to content

Commit

Permalink
(PDOC-226) Add Puppet Data Type Alias documentation
Browse files Browse the repository at this point in the history
Previously Puppet-Strings could not document Data Type Aliases. This commit:

* Updates the puppet parser to interpret TypeAlias statements.
* Adds a DataTypeAlias code object and handler to document the Type Alias
  statement correctly.
* Adds tests for puppet parsing to ensure the Yard Code Object is populated
  correctly
* Adds support for JSON, Markdown and HTML rendering. Note that JSON separates
  Data Types from Data Type Aliases whereas Markdown and HTML rendering lump
  them together.  This is because from a human documentation point of view (i.e
  Mardown or HTML) they are very similar things. Much like Puppet V3 vs V4
  functions.  However the JSON output can be used by other systems so it is
  important to diffentiate them as they are different from a code inspection
  point of view.
* Adds tests for JSON and Markdown rendering
  • Loading branch information
glennsarti committed Jul 17, 2019
1 parent 7493d88 commit 8a802f2
Show file tree
Hide file tree
Showing 29 changed files with 373 additions and 16 deletions.
39 changes: 27 additions & 12 deletions JSON.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,19 @@ puppet strings generate --format json
Document Schema
===============

At the top level, there are eight arrays in the JSON document:

| Document Key | Description |
| ---------------- | ----------------------------------------------------------------------------- |
| puppet_classes | The list of Puppet classes that were parsed. |
| data_types | The list of data types that were parsed. |
| defined_types | The list of defined types that were parsed. |
| resource_types | The list of resource types that were parsed. |
| providers | The list of resource providers that were parsed. |
| puppet_functions | The list of Puppet functions (4.x, 4.x and Puppet language) that were parsed. |
| puppet_tasks | The list of Puppet tasks that were parsed. |
| puppet_plans | The list of Puppet plans that were parsed. |
At the top level, there are nine arrays in the JSON document:

| Document Key | Description |
| ----------------- | ----------------------------------------------------------------------------- |
| puppet_classes | The list of Puppet classes that were parsed. |
| data_types | The list of data types that were parsed. |
| data_type_aliases | | The list of data types that were parsed. |
| defined_types | The list of defined types that were parsed. |
| resource_types | The list of resource types that were parsed. |
| providers | The list of resource providers that were parsed. |
| puppet_functions | The list of Puppet functions (4.x, 4.x and Puppet language) that were parsed. |
| puppet_tasks | The list of Puppet tasks that were parsed. |
| puppet_plans | The list of Puppet plans that were parsed. |

Puppet Classes
--------------
Expand Down Expand Up @@ -51,6 +52,20 @@ Each entry in the `data_types` list is an object with the following attributes:
| defaults | The map of parameter names to default values. |
| source | The ruby source code for the data type. (Not Implemented) |

Data Type Aliases
-----------------

Each entry in the `data_type_aliases` list is an object with the following attributes:

| Attribute Key | Description |
| ------------- | ----------------------------------------------------------------- |
| name | The name of the data type. |
| file | The file defining the data type. |
| line | The line where the data type is defined. |
| docstring | The *DocString* object for the data type (see below). |
| alias_of | The actual type this is an alias of. |
| source | The Puppet source code for the data type alias. (Not Implemented) |

Defined Types
-------------

Expand Down
1 change: 1 addition & 0 deletions lib/puppet-strings/json.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ def self.render(file = nil)
document = {
puppet_classes: YARD::Registry.all(:puppet_class).sort_by!(&:name).map!(&:to_hash),
data_types: YARD::Registry.all(:puppet_data_type).sort_by!(&:name).map!(&:to_hash),
data_type_aliases: YARD::Registry.all(:puppet_data_type_alias).sort_by!(&:name).map!(&:to_hash),
defined_types: YARD::Registry.all(:puppet_defined_type).sort_by!(&:name).map!(&:to_hash),
resource_types: YARD::Registry.all(:puppet_type).sort_by!(&:name).map!(&:to_hash),
providers: YARD::Registry.all(:puppet_provider).sort_by!(&:name).map!(&:to_hash),
Expand Down
4 changes: 4 additions & 0 deletions lib/puppet-strings/markdown/data_type.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
require 'puppet-strings/markdown/base'

module PuppetStrings::Markdown
# This class encapsualtes ruby data types and puppet type aliases
class DataType < Base
attr_reader :alias_of

def initialize(registry)
@template = 'data_type.erb'
super(registry, 'data type')
@alias_of = registry[:alias_of] unless registry[:alias_of].nil?
end

def render
Expand Down
5 changes: 4 additions & 1 deletion lib/puppet-strings/markdown/data_types.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ module DataTypes

# @return [Array] list of data types
def self.in_dtypes
arr = YARD::Registry.all(:puppet_data_type).sort_by!(&:name).map!(&:to_hash)
arr = YARD::Registry.all(:puppet_data_type).map!(&:to_hash)
arr.concat(YARD::Registry.all(:puppet_data_type_alias).map!(&:to_hash))

arr.sort! { |a,b| a[:name] <=> b[:name] }
arr.map! { |a| PuppetStrings::Markdown::DataType.new(a) }
end

Expand Down
4 changes: 4 additions & 0 deletions lib/puppet-strings/markdown/templates/data_type.erb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
```

<% end -%>
<% end -%>
<% if alias_of -%>
Alias of `<%= alias_of %>`

<% end -%>
<% if params -%>
#### Parameters
Expand Down
5 changes: 5 additions & 0 deletions lib/puppet-strings/yard.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def all_objects
:class,
:puppet_class,
:puppet_data_type,
:puppet_data_type_alias,
:puppet_defined_type,
:puppet_type,
:puppet_provider,
Expand All @@ -69,6 +70,10 @@ def stats_for_puppet_data_types
output 'Puppet Data Types', *type_statistics_all(:puppet_data_type)
end

def stats_for_puppet_data_type_aliases
output 'Puppet Data Type Aliases', *type_statistics_all(:puppet_data_type_alias)
end

def stats_for_puppet_defined_types
output 'Puppet Defined Types', *type_statistics_all(:puppet_defined_type)
end
Expand Down
1 change: 1 addition & 0 deletions lib/puppet-strings/yard/code_objects.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
module PuppetStrings::Yard::CodeObjects
require 'puppet-strings/yard/code_objects/class'
require 'puppet-strings/yard/code_objects/data_type'
require 'puppet-strings/yard/code_objects/data_type_alias'
require 'puppet-strings/yard/code_objects/defined_type'
require 'puppet-strings/yard/code_objects/type'
require 'puppet-strings/yard/code_objects/provider'
Expand Down
58 changes: 58 additions & 0 deletions lib/puppet-strings/yard/code_objects/data_type_alias.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
require 'puppet-strings/yard/code_objects/group'
require 'puppet-strings/yard/util'

# Implements the group for Puppet DataTypeAliases.
class PuppetStrings::Yard::CodeObjects::DataTypeAliases < PuppetStrings::Yard::CodeObjects::Group
# Gets the singleton instance of the group.
# @return Returns the singleton instance of the group.
def self.instance
super(:puppet_data_type_aliases)
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 Data Type Aliases'
end
end

# Implements the Puppet DataTypeAlias code object.
class PuppetStrings::Yard::CodeObjects::DataTypeAlias < PuppetStrings::Yard::CodeObjects::Base
attr_reader :statement
attr_accessor :alias_of

# Initializes a Puppet data type alias code object.
# @param [PuppetStrings::Parsers::DataTypeAliasStatement] statement The data type alias statement that was parsed.
# @return [void]
def initialize(statement)
@statement = statement
@alias_of = statement.alias_of
super(PuppetStrings::Yard::CodeObjects::DataTypeAliases.instance, statement.name)
end

# Gets the type of the code object.
# @return Returns the type of the code object.
def type
:puppet_data_type_alias
end

# Gets the source of the code object.
# @return Returns the source of the code object.
def source
# Not implemented, but would be nice!
nil
end

# Converts the code object to a hash representation.
# @return [Hash] Returns a hash representation of the code object.
def to_hash
hash = {}
hash[:name] = name
hash[:file] = file
hash[:line] = line
hash[:docstring] = PuppetStrings::Yard::Util.docstring_to_hash(docstring)
hash[:alias_of] = alias_of
hash
end
end
1 change: 1 addition & 0 deletions lib/puppet-strings/yard/handlers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ module JSON
# The module for custom Puppet YARD handlers.
module Puppet
require 'puppet-strings/yard/handlers/puppet/class_handler'
require 'puppet-strings/yard/handlers/puppet/data_type_alias_handler'
require 'puppet-strings/yard/handlers/puppet/defined_type_handler'
require 'puppet-strings/yard/handlers/puppet/function_handler'
require 'puppet-strings/yard/handlers/puppet/plan_handler'
Expand Down
24 changes: 24 additions & 0 deletions lib/puppet-strings/yard/handlers/puppet/data_type_alias_handler.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
require 'puppet-strings/yard/handlers/helpers'
require 'puppet-strings/yard/handlers/puppet/base'
require 'puppet-strings/yard/parsers'
require 'puppet-strings/yard/code_objects'

# Implements the handler for Puppet Data Type Alias.
class PuppetStrings::Yard::Handlers::Puppet::DataTypeAliasHandler < PuppetStrings::Yard::Handlers::Puppet::Base
handles PuppetStrings::Yard::Parsers::Puppet::DataTypeAliasStatement

process do
# Register the object
object = PuppetStrings::Yard::CodeObjects::DataTypeAlias.new(statement)
register object

# Log a warning if missing documentation
log.warn "Missing documentation for Puppet type alias '#{object.name}' at #{statement.file}:#{statement.line}." if object.docstring.empty? && object.tags.empty?

# 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

# Warn if a summary longer than 140 characters was provided
PuppetStrings::Yard::Handlers::Helpers.validate_summary_tag(object) if object.has_tag? :summary
end
end
6 changes: 6 additions & 0 deletions lib/puppet-strings/yard/parsers/puppet/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ def transform_PlanDefinition(o) # rubocop:disable Naming/UncommunicativeMethodPa
statement
end

def transform_TypeAlias(o) # rubocop:disable Naming/UncommunicativeMethodParamName
statement = PuppetStrings::Yard::Parsers::Puppet::DataTypeAliasStatement.new(o, @file)
statement.extract_docstring(@lines)
statement
end

def transform_Object(o) # rubocop:disable Naming/UncommunicativeMethodParamName
# Ignore anything else (will be compacted out of the resulting array)
end
Expand Down
25 changes: 25 additions & 0 deletions lib/puppet-strings/yard/parsers/puppet/statement.rb
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,29 @@ def initialize(object, file)
end
end

# Implements the Puppet data type alias statement.
class DataTypeAliasStatement < Statement
attr_reader :name
attr_reader :alias_of

# Initializes the Puppet data type alias statement.
# @param [Puppet::Pops::Model::TypeAlias] object The model object for the type statement.
# @param [String] file The file containing the statement.
def initialize(object, file)
super(object, file)

type_expr = object.type_expr
case type_expr
when Puppet::Pops::Model::AccessExpression
# TODO: I don't like rebuilding the source from the AST, but AccessExpressions don't expose the original source
@alias_of = ::Puppet::Pops::Adapters::SourcePosAdapter.adapt(type_expr.left_expr).extract_text + '['
@alias_of << type_expr.keys.map { |key| ::Puppet::Pops::Adapters::SourcePosAdapter.adapt(key).extract_text }.join(', ')
@alias_of << ']'
else
adapter = ::Puppet::Pops::Adapters::SourcePosAdapter.adapt(type_expr)
@alias_of = adapter.extract_text
end
@name = object.name
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<li id="object_<%=item.path%>" class="<%= even ? 'even' : 'odd' %>">
<div class="item">
<%= linkify item, h(item.name(false)) %>
<% if item.type == :puppet_data_type_alias %><small>Alias</small><% end %>
</div>
</li>
<% even = !even %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def generate_puppet_class_list
# Generates the searchable Puppet data type list.
# @return [void]
def generate_puppet_data_type_list
@items = Registry.all(:puppet_data_type).sort_by {|dt| dt.name.to_s }
@items = Registry.all(:puppet_data_type, :puppet_data_type_alias).sort_by {|dt| dt.name.to_s }
@list_title = 'Data Type List'
@list_type = 'puppet_data_type'
generate_list_contents
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
<small>(Resource type: <%= obj.type_name %>)</small>
<% elsif obj.type == :puppet_function %>
<small>(<%= obj.function_type %>)</small>
<% elsif obj.type == :puppet_data_type_alias %>
<small>(Alias)</small>
<% end %>
</li>
<% end %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def layout
@nav_url = url_for_list('puppet_class')
@page_title = "Puppet Class: #{object.name}"
@path = object.path
when PuppetStrings::Yard::CodeObjects::DataType
when PuppetStrings::Yard::CodeObjects::DataType, PuppetStrings::Yard::CodeObjects::DataTypeAlias
@nav_url = url_for_list('puppet_data_type')
@page_title = "Data Type: #{object.name}"
@path = object.path
Expand Down Expand Up @@ -168,7 +168,7 @@ def classes
# @return [String] Returns the rendered section.
def data_types
@title = 'Data Type Listing A-Z'
@objects_by_letter = objects_by_letter(:puppet_data_type)
@objects_by_letter = objects_by_letter(:puppet_data_type, :puppet_data_type_alias)
erb(:objects)
end

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<% if @alias_of && !@alias_of.empty? %>
<div class="tags">
<p class="tag_title"><%= @tag_title %></p>
<div class="docstring">
<div class="discussion">
<pre class="code"><span class="info"><%= @alias_of %></span></pre>
</div>
</div>
</div>
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<div class="box_info">
<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>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<h1>Puppet Data Type Alias: <%= object.name %></h1>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<% object.tags(:note).each do |tag| %>
<div class="note notetag">
<strong>Note:</strong>
<%= htmlify_line tag.text %>
</div>
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<h2>Overview</h2>
<div class="docstring">
<div class="discussion">
<%= htmlify(object.docstring) %>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Initializes the template.
# @return [void]
def init
sections :header, :box_info, :summary, :overview, :alias_of, :note, :todo, T('tags'), :source
end

# Renders the alias_of section.
# @return [String] Returns the rendered section.
def alias_of
# Properties are the same thing as parameters (from the documentation standpoint),
# so reuse the same template but with a different title and data source.
#@parameters = object.properties || []
#@parameters.sort_by! { |p| p.name }
@tag_title = 'Alias of'
@alias_of = object.alias_of
erb(:alias_of)
end
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<% if object.docstring.has_tag?(:summary) %>
<h2>Summary</h2>
<%= object.docstring.tag(:summary).text %>
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<% object.tags(:todo).each do |tag| %>
<div class="note todo">
<strong>TODO:</strong>
<%= htmlify_line tag.text %>
</div>
<% end %>
Loading

0 comments on commit 8a802f2

Please sign in to comment.