Merge pull request #98 from peterhuene/rewrite

(PDOC-63) Code refactoring, fix up, and lots of new functionality.
This commit is contained in:
Will Hopper 2016-09-16 09:57:10 -07:00 committed by GitHub
commit 7ae8e74ec5
182 changed files with 5530 additions and 4075 deletions

11
.gitignore vendored
View File

@ -12,7 +12,7 @@ tmtags
## VIM
*.swp
tags
/tags
## BUNDLER
.bundle
@ -24,3 +24,12 @@ Gemfile.lock
## MODULE BUILDS
**/pkg
## RubyMine
/.idea/
## rvm/rbenv
/.ruby-version
## YARD output
/doc/

View File

@ -2,7 +2,7 @@ AllCops:
Exclude:
# Ignore HTML related things
- '**/*.erb'
- 'lib/puppetx/puppetlabs/strings/yard/templates/**/*'
- 'lib/puppet-strings/yard/templates/**/*'
Lint/ConditionPosition:
Enabled: true

View File

@ -1,2 +1,2 @@
--exclude lib/puppetx/puppetlabs/strings/yard/templates/
--exclude lib/puppetx/puppetlabs/strings/yard/code_objects/host_class_object.rb
--exclude lib/puppet-strings/yard/templates/
--no-private

View File

@ -9,8 +9,8 @@ All related tickets can be found under the [PDOC][PDOC JIRA] JIRA project with t
###Features
- Support for JSON output **(PDOC-23)**
- Strings now has the ability to produce a JSON representation of a given puppet module
- The details of the JSON schema can be found [here](https://github.com/puppetlabs/puppetlabs-strings/blob/master/json_dom.md)
- For details on how to generate JSON, see the [README](https://github.com/puppetlabs/puppetlabs-strings/blob/master/README.md#running-puppet-strings)
- The details of the JSON schema can be found [here](https://github.com/puppetlabs/puppet-strings/blob/master/json_dom.md)
- For details on how to generate JSON, see the [README](https://github.com/puppetlabs/puppet-strings/blob/master/README.md#running-puppet-strings)
- Migrate to ruby gems as a distribution method **(PDOC-28)**
- This is the last release of strings that will be available as a puppet module
- The 0.4.0 release will be released concurrently as a ruby gem

View File

@ -53,9 +53,9 @@ merged.
**security** - Where critical security fixes are merged. These change sets
will then be merged into release branches independently from one another. (i.e.
no merging up). Please do not submit pull requests against the security branch
and instead report all security related issues to security@puppetlabs.com as
and instead report all security related issues to security@puppet.com as
per our security policy published at
[https://puppetlabs.com/security/](https://puppetlabs.com/security/).
[https://puppet.com/security/](https://puppet.com/security/).
Committer Guide
====
@ -84,8 +84,8 @@ applied to earlier minor releases of a major release, but the patches should
first be merged into the `security` branch. Security patches should be merged
by Puppet Labs staff members. Pull requests should not be submitted with the
security branch as the base branch. Please send all security related
information or patches to security@puppetlabs.com as per our [Security
Policy](https://puppetlabs.com/security/).
information or patches to security@puppet.com as per our [Security
Policy](https://puppet.com/security/).
The CI systems are configured to run against `master`. Over time, this branch
will refer to different versions, but its name will remain fixed to avoid having
@ -118,7 +118,7 @@ This section aims to provide guidelines for being a good commit citizen by
paying attention to our automated build tools.
* Dont push on a broken build. (A broken build is defined as a failing job
in the [Puppet FOSS](https://jenkins.puppetlabs.com/view/Puppet%20FOSS/)
in [Puppet Strings](https://jenkins.puppetlabs.com/job/platform_puppet-strings_unit-ruby_master/)
page.)
* Watch the build until your changes have gone through green
* Update the ticket status and target version. The target version field in
@ -143,7 +143,7 @@ First, the committer pulls down the branch using the `hub` gem. This tool
automates the process of adding the remote repository and creating a local
branch to track the remote branch.
$ hub checkout https://github.com/puppetlabs/puppetlabs-strings/pull/123
$ hub checkout https://github.com/puppetlabs/puppet-strings/pull/123
Branch jeffmccune-pdoc-34_fix_foo_error set up to track remote branch pdoc-34-fix_foo_error from jeffmccune.
Switched to a new branch 'jeffmccune-pdoc-34_fix_foo_error'
@ -178,7 +178,7 @@ Please note, the checklist should be complete at this point. It's helpful to mak
sure your local branches are up to date to avoid one of the branches failing to fast
forward while the other succeeds.
$ git push puppetlabs master:master
$ git push origin master:master
That's it! The committer then updates the pull request, updates the issue in
our issue tracker, and keeps an eye on the [build

View File

@ -68,7 +68,7 @@ a ticket number.
## Submitting Changes
* Sign the [Contributor License Agreement](http://links.puppetlabs.com/cla).
* Sign the [Contributor License Agreement](http://links.puppet.com/cla).
* Push your changes to a topic branch in your fork of the repository.
* Submit a pull request to the repository in the puppetlabs organization.
* Update your Jira ticket to mark that you have submitted code and are ready for it to be reviewed (Status: Ready for Merge).
@ -81,9 +81,9 @@ a ticket number.
# Additional Resources
* [More information on contributing](http://links.puppetlabs.com/contribute-to-puppet)
* [More information on contributing](http://links.puppet.com/contribute-to-puppet)
* [Bug tracker (Jira)](http://tickets.puppetlabs.com)
* [Contributor License Agreement](http://links.puppetlabs.com/cla)
* [Contributor License Agreement](http://links.puppet.com/cla)
* [General GitHub documentation](http://help.github.com/)
* [GitHub pull request documentation](http://help.github.com/send-pull-requests/)
* #puppet-dev IRC channel on freenode.org

View File

@ -4,7 +4,7 @@ gemspec
gem 'rgen'
gem 'redcarpet'
gem "yard", "~> 0.8.7"
gem 'yard', '~> 0.9.5'
puppetversion = ENV['PUPPET_VERSION']

511
JSON.md Normal file
View File

@ -0,0 +1,511 @@
Puppet Strings JSON Data
========================
Puppet Strings has two flags to the `generate` action that can be used to emit JSON data:
* `--emit-json <file>`: Emits the JSON data to the given file.
* `--emit-json-stdout`: Emits the JSON data to STDOUT.
Document Schema
===============
At the top level, there are five arrays in the JSON document:
| Document Key | Description |
| ---------------- | ----------------------------------------------------------------------------- |
| puppet_classes | The list of Puppet classes 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 (3.x, 4.x and Puppet language) that were parsed. |
Puppet Classes
--------------
Each entry in the `puppet_classes` list is an object with the following attributes:
| Attribute Key | Description |
| ------------- | ----------------------------------------------------- |
| name | The name of the Puppet class. |
| file | The file defining the Puppet class. |
| line | The line where the Puppet class is defined. |
| inherits | The name of the Puppet class the class inherits from. |
| docstring | The *DocString* object for the class (see below). |
| defaults | The map of parameter names to default values. |
| source | The Puppet source code for the class. |
Defined Types
-------------
Each entry in the `defined_types` list is an object with the following attributes:
| Attribute Key | Description |
| ------------- | -------------------------------------------------------- |
| name | The name of the defined type. |
| file | The file defining the defined type. |
| line | The line where the defined type is defined. |
| docstring | The *DocString* object for the defined type (see below). |
| defaults | The map of parameter names to default values. |
| source | The Puppet source code for the defined type. |
Resource Types
--------------
Each entry in the `resource_types` list is an object with the following attributes:
| Attribute Key | Description |
| ------------- | --------------------------------------------------------- |
| name | The name of the resource type. |
| file | The file defining the resource type. |
| line | The line where the resource type is defined. |
| docstring | The *DocString* object for the resource type (see below). |
| properties | The list of properties for the resource type (see below). |
| parameters | The list of parameters for the resource type (see below). |
| features | The list of features for the resource type (see below). |
Each entry in the `properties` list is an object with the following attributes:
| Attribute Key | Description |
| ------------- | ------------------------------------------------------- |
| name | The name of the property. |
| description | The description of the property. |
| values | The array of acceptable string values for the property. |
| aliases | The map of new values aliased to existing values. |
| isnamevar | True if the property is a namevar or false if not. |
| default | The default value for the property. |
Each entry in the `parameters` list is an object with the following attributes:
| Attribute Key | Description |
| ------------- | -------------------------------------------------------- |
| name | The name of the parameter. |
| description | The description of the parameter. |
| values | The array of acceptable string values for the parameter. |
| aliases | The map of new values aliased to existing values. |
| isnamevar | True if the parameter is a namevar or false if not. |
| default | The default value for the parameter. |
Each entry in the `features` list is an object with the following attributes:
| Attribute Key | Description |
| ------------- | ------------------------------- |
| name | The name of the feature. |
| description | The description of the feature. |
Providers
---------
Each entry in the `providers` list is an object with the following attributes:
| Attribute Key | Description |
| ------------- | ---------------------------------------------------- |
| name | The name of the provider. |
| type_name | The name of the resource type of the provider. |
| file | The file defining the provider. |
| line | The line where the provider is defined. |
| docstring | The *DocString* object for the provider (see below). |
| confines | The string map of confines for the provider. |
| features | The list of features implemented by the provider. |
| defaults | The string map of "default for" for the provider. |
| commands | The string map of commands for the provider. |
Puppet Functions
----------------
Each entry in the `puppet_functions` list is an object with the following attributes:
| Attribute Key | Description |
| ------------- | ---------------------------------------------------- |
| name | The name of the function. |
| file | The file defining the provider. |
| line | The line where the provider is defined. |
| type | The function type (e.g. ruby3x, ruby4x, puppet). |
| signature | The Puppet signature of the function (no overloads). |
| docstring | The *DocString* object for the function (see below). |
| defaults | The map of parameter names to default values. |
| source | The source code for the function. |
DocString Objects
-----------------
For the above types, their docstrings are represented as an object with the following attributes:
| Attribute Key | Description DocString |
| ------------- | --------------------------------------------------- |
| text | The textual part of the DocString. |
| tags | The array of tag objects, if any are present. |
Each entry in the `tags` list is an object with the following properties:
| Attribute Key | Description |
| ------------- | ------------------------------------------------------- |
| tag_name | The name of the tag (e.g. param, return, etc.). |
| text | The descriptive text of the tag. |
| types | The array of types associated with the tag. |
| name | The name associated with the tag (e.g. parameter name). |
For Puppet 4.x functions with overloads, `overload` tags will contain three additional attributes:
| Attribute Key | Description |
| ------------- | ----------------------------------------------- |
| signature | The Puppet signature of the overload. |
| docstring | The *DocString* object describing the overload. |
| defaults | The map of parameter names to default values. |
Example JSON Document
---------------------
An example JSON document describing a Puppet class, defined type, resource type, provider, and Puppet functions:
```json
{
"puppet_classes": [
{
"name": "foo",
"file": "site.pp",
"line": 5,
"inherits": "foo::bar",
"docstring": {
"text": "A simple class.",
"tags": [
{
"tag_name": "param",
"text": "First param.",
"types": [
"Integer"
],
"name": "param1"
},
{
"tag_name": "param",
"text": "Second param.",
"types": [
"Any"
],
"name": "param2"
},
{
"tag_name": "param",
"text": "Third param.",
"types": [
"String"
],
"name": "param3"
}
]
},
"defaults": {
"param3": "hi"
},
"source": "class foo(Integer $param1, $param2, String $param3 = hi) inherits foo::bar {\n}"
}
],
"defined_types": [
{
"name": "dt",
"file": "site.pp",
"line": 12,
"docstring": {
"text": "A simple defined type.",
"tags": [
{
"tag_name": "param",
"text": "First param.",
"types": [
"Integer"
],
"name": "param1"
},
{
"tag_name": "param",
"text": "Second param.",
"types": [
"Any"
],
"name": "param2"
},
{
"tag_name": "param",
"text": "Third param.",
"types": [
"String"
],
"name": "param3"
}
]
},
"defaults": {
"param3": "hi"
},
"source": "define dt(Integer $param1, $param2, String $param3 = hi) {\n}"
}
],
"resource_types": [
{
"name": "database",
"file": "database.rb",
"line": 43,
"docstring": {
"text": "An example database server resource type."
},
"properties": [
{
"name": "ensure",
"description": "What state the database should be in.",
"values": [
"present",
"absent",
"up",
"down"
],
"aliases": {
"up": "present",
"down": "absent"
},
"default": "up"
},
{
"name": "file",
"description": "The database file to use."
},
{
"name": "log_level",
"description": "The log level to use.",
"values": [
"debug",
"warn",
"error"
],
"default": "warn"
}
],
"parameters": [
{
"name": "address",
"description": "The database server name.",
"isnamevar": true
},
{
"name": "encryption_key",
"description": "The encryption key to use."
},
{
"name": "encrypt",
"description": "Whether or not to encrypt the database.",
"values": [
"true",
"false",
"yes",
"no"
],
"default": "false"
}
],
"features": [
{
"name": "encryption",
"description": "The provider supports encryption."
}
]
}
],
"providers": [
{
"name": "linux",
"type_name": "database",
"file": "linux.rb",
"line": 33,
"docstring": {
"text": "An example provider on Linux."
},
"confines": {
"kernel": "Linux",
"osfamily": "RedHat"
},
"features": [
"implements_some_feature",
"some_other_feature"
],
"defaults": {
"kernel": "Linux"
},
"commands": {
"foo": "/usr/bin/foo"
}
}
],
"puppet_functions": [
{
"name": "func",
"file": "site.pp",
"line": 20,
"type": "puppet",
"signature": "func(Integer $param1, Any $param2, String $param3 = hi)",
"docstring": {
"text": "A simple function.",
"tags": [
{
"tag_name": "param",
"text": "First param.",
"types": [
"Integer"
],
"name": "param1"
},
{
"tag_name": "param",
"text": "Second param.",
"types": [
"Any"
],
"name": "param2"
},
{
"tag_name": "param",
"text": "Third param.",
"types": [
"String"
],
"name": "param3"
},
{
"tag_name": "return",
"text": "Returns nothing.",
"types": [
"Undef"
]
}
]
},
"defaults": {
"param3": "hi"
},
"source": "function func(Integer $param1, $param2, String $param3 = hi) {\n}"
},
{
"name": "func3x",
"file": "func3x.rb",
"line": 1,
"type": "ruby3x",
"signature": "func3x(String $first, Any $second)",
"docstring": {
"text": "An example 3.x function.",
"tags": [
{
"tag_name": "param",
"text": "The first parameter.",
"types": [
"String"
],
"name": "first"
},
{
"tag_name": "param",
"text": "The second parameter.",
"types": [
"Any"
],
"name": "second"
},
{
"tag_name": "return",
"text": "Returns nothing.",
"types": [
"Undef"
]
}
]
},
"source": "Puppet::Parser::Functions.newfunction(:func3x, doc: <<-DOC\nAn example 3.x function.\n@param [String] first The first parameter.\n@param second The second parameter.\n@return [Undef] Returns nothing.\nDOC\n) do |*args|\nend"
},
{
"name": "func4x",
"file": "func4x.rb",
"line": 11,
"type": "ruby4x",
"docstring": {
"text": "An example 4.x function.",
"tags": [
{
"tag_name": "overload",
"signature": "func4x(Integer $param1, Any $param2, Optional[Array[String]] $param3)",
"docstring": {
"text": "The first overload.",
"tags": [
{
"tag_name": "param",
"text": "The first parameter.",
"types": [
"Integer"
],
"name": "param1"
},
{
"tag_name": "param",
"text": "The second parameter.",
"types": [
"Any"
],
"name": "param2"
},
{
"tag_name": "param",
"text": "The third parameter.",
"types": [
"Optional[Array[String]]"
],
"name": "param3"
},
{
"tag_name": "return",
"text": "Returns nothing.",
"types": [
"Undef"
]
}
]
},
"name": "func4x"
},
{
"tag_name": "overload",
"signature": "func4x(Boolean $param, Callable &$block)",
"docstring": {
"text": "The second overload.",
"tags": [
{
"tag_name": "param",
"text": "The first parameter.",
"types": [
"Boolean"
],
"name": "param"
},
{
"tag_name": "param",
"text": "The block parameter.",
"types": [
"Callable"
],
"name": "&block"
},
{
"tag_name": "return",
"text": "Returns a string.",
"types": [
"String"
]
}
]
},
"name": "func4x"
}
]
},
"source": "Puppet::Functions.create_function(:func4x) do\n # The first overload.\n # @param param1 The first parameter.\n # @param param2 The second parameter.\n # @param param3 The third parameter.\n # @return [Undef] Returns nothing.\n dispatch :foo do\n param 'Integer', :param1\n param 'Any', :param2\n optional_param 'Array[String]', :param3\n end\n\n # The second overload.\n # @param param The first parameter.\n # @param block The block parameter.\n # @return [String] Returns a string.\n dispatch :other do\n param 'Boolean', :param\n block_param\n end\nend"
}
]
}
```

401
README.md
View File

@ -1,38 +1,41 @@
Puppet Strings
=============
[![Build Status](https://travis-ci.org/puppetlabs/puppetlabs-strings.png?branch=master)](https://travis-ci.org/puppetlabs/puppetlabs-strings) [![Gem Version](https://badge.fury.io/rb/puppet-strings.svg)](https://badge.fury.io/rb/puppet-strings)
==============
[![Build Status](https://travis-ci.org/puppetlabs/puppet-strings.png?branch=master)](https://travis-ci.org/puppetlabs/puppet-strings) [![Gem Version](https://badge.fury.io/rb/puppet-strings.svg)](https://badge.fury.io/rb/puppet-strings)
A Puppet Face and plugin built on the [YARD Documentation Tool](http://yardoc.org/) and the Puppet 4 Parser. It is uses YARD and the Puppet Parser to generate HTML documentation about Puppet code and Puppet extensions written in Ruby. It will eventually replace the `puppet doc` command once feature parity has been achieved.
A Puppet command built on [YARD](http://yardoc.org/).
Puppet Strings generates HTML documentation for Puppet extensions written in Puppet and Ruby.
| | |
| -------------- |------------------------------------------------------------ |
| *Code* | [GitHub][repo] |
| *Issues* | [Puppet Labs' JIRA Tracker][JIRA] |
| *License* | [Apache 2.0][LICENSE] |
| *Change log* | [CHANGELOG.md][changelog] |
| *Contributing* | [CONTRIBUTING.md][contributing] and [COMMITTERS.md][committers]|
This tool will eventually place the existing `puppet doc` command once feature parity has been achieved.
| | |
| -------------- |---------------------------------------------------------------- |
| *Code* | [GitHub][repo] |
| *Issues* | [Puppet JIRA Tracker][JIRA] |
| *License* | [Apache 2.0][LICENSE] |
| *Change log* | [CHANGELOG.md][changelog] |
| *Contributing* | [CONTRIBUTING.md][contributing] and [COMMITTERS.md][committers] |
[repo]: https://github.com/puppetlabs/puppetlabs-strings
[repo]: https://github.com/puppetlabs/puppet-strings
[JIRA]: https://tickets.puppetlabs.com/browse/PDOC
[LICENSE]: https://github.com/puppetlabs/puppetlabs-strings/blob/master/LICENSE
[changelog]: https://github.com/puppetlabs/puppetlabs-strings/blob/master/CHANGELOG.md
[contributing]: https://github.com/puppetlabs/puppetlabs-strings/blob/master/CONTRIBUTING.md
[committers]: https://github.com/puppetlabs/puppetlabs-strings/blob/master/COMMITTERS.md
[LICENSE]: https://github.com/puppetlabs/puppet-strings/blob/master/LICENSE
[changelog]: https://github.com/puppetlabs/puppet-strings/blob/master/CHANGELOG.md
[contributing]: https://github.com/puppetlabs/puppet-strings/blob/master/CONTRIBUTING.md
[committers]: https://github.com/puppetlabs/puppet-strings/blob/master/COMMITTERS.md
Installation
Requirements
------------
In order to run strings you need to have the following software installed:
* Ruby 1.9.3 or newer
* Puppet 3.7 or newer
* The YARD RubyGem
* The `yard` Ruby gem
Installing the YARD Gem
-----------------------
**Installing the YARD Gem with Puppet**
The easiest way to install the YARD gem is with Puppet itself.
The easiest way to install the `yard` gem is with Puppet itself:
For Puppet 4.x:
```
@ -44,11 +47,12 @@ For Puppet 3.x:
$ puppet resource package yard provider=gem
```
Installing Strings Itself
Installing Puppet Strings
-------------------------
**PLEASE NOTE** that Strings was previously distributed via the puppetlabs-strings module. This is no longer the preferred method of installation as the module will not longer receive updates. So even though there is still a module on the Puppet Forge, please use the RubyGem.
Strings can be installed using the [puppet-strings RubyGem](https://rubygems.org/gems/puppet-strings). To ensure it is installed in right place, it is best to install it using Puppet.
Strings can be installed using the [puppet-strings](https://rubygems.org/gems/puppet-strings) gem.
To ensure it is installed in right place, it is best to install it using Puppet:
For Puppet 4.x:
```
@ -70,30 +74,34 @@ $ cd /path/to/module
$ puppet strings
```
This processes `README` and all puppet and ruby files under `manifests/`
and `lib/`.
This processes `README` and all Puppet and Ruby source files under the `./manifests/`, `./functions/`, and `./lib/`
directories by default and creates HTML documentation under the `./doc/` directory.
To document specific files:
```
$ puppet strings some_manifest.pp [another_if_you_feel_like_it.rb]
$ puppet strings generate first.pp second.pp ...
```
Strings can also emit the generated documentation as JSON:
To document specific directories:
```
$ puppet strings yardoc some_manifest.pp --emit-json documentation.json
$ puppet strings 'modules/foo/lib/**/*.rb' 'modules/foo/manifests/**/*.pp' 'modules/foo/functions/**/*.pp' ...
```
Strings can emit JSON documenting the Puppet extensions:
```
$ puppet strings generate --emit-json documentation.json
```
It can also print the JSON to stdout:
```
$ puppet strings yardoc some_manifest.pp --emit-json-stdout
$ puppet strings generate --emit-json-stdout
```
The schema for the JSON which Strings emits is [well documented](https://github.com/puppetlabs/puppetlabs-strings/blob/master/json_dom.md).
Processing is delegated to the `yardoc` tool so some options listed in `yard help doc` are available. However, Puppet Faces do not support passing arbitrary options through a face so these options must be specified in a `.yardopts` file.
The schema for the JSON output is [documented here](https://github.com/puppetlabs/puppet-strings/blob/master/JSON.md).
In addition to generating a directory full of HTML, you can also serve up documentation for all your modules using the `server` action:
@ -101,157 +109,290 @@ In addition to generating a directory full of HTML, you can also serve up docume
$ puppet strings server
```
Writing Compatible Documentation
--------------------------------
YARD Options
------------
Since the strings module is built around YARD, a few different comment formats can be used. YARD can work with RDoc, meaning it is backwards compatible with previously documented modules. Feel free to try out strings with RDoc, but we are planning to move to Markdown as the standard. You can configure which you would like YARD to use by adding a `.yardopts` file to the root of your module directory which specifies the desired format:
YARD options (see `yard help doc`) are supported in a `.yardopts` file in the same directory where `puppet strings` is run.
```
--markup markdown
```
Puppet Strings automatically sets the `markup` option to `markdown`, allowing your documentation strings to be in Markdown format.
While we have yet to decide exactly how documentation should work in the future, here are some very basic examples to get you started using the strings module. These are very much subject to change as we continue to work out a style guide.
Documenting Puppet Extensions
-----------------------------
### Functions
Here's an example of how you might document a 4x function:
### Puppet Classes / Defined Types
```ruby
# When given two numbers, returns the one that is larger.
# You could have a several line description here if you wanted,
# but I don't have much to say about this function.
#
# @example using two integers
# $bigger_int = max(int_one, int_two)
#
# @return [Integer] the larger of the two parameters
#
# @param num_a [Integer] the first number to be compared
# @param num_b [Integer] the second number to be compared
Puppet::Functions.create_function(:max) do
def max(num_a, num_b)
num_a >= num_b ? num_a : num_b
end
end
```
### Classes / Defined Types
Here's an example of how you might document a class:
To document Puppet classes and defined types, use a YARD docstring before the class or defined type definition:
```puppet
# This class is meant to serve as an example of how one might
# want to document a manifest in a way that is compatible.
# with the strings module
# An example class.
#
# @example when declaring the example class
# This is an example of how to document a Puppet class
#
# @example Declaring the class
# include example
#
# @param first_arg The first parameter for this class
# @param second_arg The second paramter for this class
class example (
$first_arg = $example::params::first_arg,
$second_arg = $exampe::params::second_arg,
) { }
# @param first The first parameter for this class
# @param second The second paramter for this class
class example_class(
String $first = $example::params::first_arg,
Integer $second = $example::params::second_arg,
) inherits example::params {
# ...
}
# An example defined type.
#
# This is an example of how to document a defined type.
# @param ports The array of port numbers to use.
define example_type(
Array[Integer] $ports = []
) {
# ...
}
```
### Types and Providers
Strings will automatically extract the `@doc` provider docstring and any `desc` parameter/property docstrings.
***Note: unlike Ruby, Puppet is a typed language; Puppet Strings will automatically use the parameter type information to
document the parameters. A warning will be emitted if you document parameter types.***
Sometimes however, Puppet types use metaprogramming to create parameters and methods automatically. In those cases Strings will not be able to document them automatically (Strings doesn't execute the code that would generate those parameters), so you will need to provide hints on how to document your code. To document a parameter which is automatically created you must use the special directive `@!puppet.type.param` which may take types, the parameter name, and a description.
### Resource Types
To document custom resource types and their parameters/properties, use the `desc` method or assign a value to the `doc` attribute:
```ruby
# @!puppet.type.param my_parameter This parameter needs to be explicitly
# documented as it is generated by mk_resource_methods
Puppet::Type.newtype(:minifile) do
@doc = "Manages files, including their content, ownership, and permissions.
The provider can manage symbolic links."
# This function does some metaprogramming on the new type.
mk_resource_methods
newparam(:path) do
desc <<-'EOT'
The path to the file to manage. Must be fully qualified.
EOT
# ... do stuff here
Puppet::Type.newtype(:example) do
desc <<-DESC
An example resource type.
@example Using the type.
example { foo:
param => 'hi'
}
DESC
newparam(:param) do
desc 'An example parameter.'
# ...
end
newproperty(:prop) do
desc 'An example property.'
#...
end
# ...
end
```
Puppet Strings documents this way to preserve backwards compatibility with `puppet doc` and existing resource types.
***Note: Puppet Strings does not evaluate your Ruby code, so only certain static expressions are supported.***
To document parameters and properties that are dynamically created, use the `#@!puppet.type.param` and `#@!puppet.type.property`
directives before the `newtype` call:
```ruby
# @!puppet.type.param [value1, value2, value3] my_param Documentation for a dynamic parameter.
# @!puppet.type.property [foo, bar, baz] my_prop Documentation for a dynamic property.
Puppet::Type.newtype(:example) do
#...
end
```
### Providers
To document providers, use the `desc` method or assign a value to the `doc` attribute:
```ruby
Puppet::Type.type(:example).provide :platform do
desc 'An example provider.'
# ...
end
```
Puppet Strings documents this way to preserve backwards compatibility with `puppet doc` and existing resource types.
***Note: Puppet Strings does not evaluate your Ruby code, so only certain static expressions are supported.***
### Functions
Puppet Strings supports three different ways of defining a function in Puppet: with the Puppet 3.x API, Puppet 4.X API,
and in the Puppet language itself.
## Puppet 3.x API
To document a function in the Puppet 3.x API, use the `doc` option to `newfunction`:
```ruby
Puppet::Parser::Functions.newfunction(:example, doc: <<-DOC
Documentation for an example 3.x function.
@param [String] param1 The first parameter.
@param [Integer] param2 The second parameter.
@return [Undef]
@example Calling the function.
example('hi', 10)
DOC
) do |*args|
#...
end
```
***Note: if parameter types are omitted, a default of the `Any` Puppet type will be used.***
## Puppet 4.x API
To document a function in the Puppet 4.x API, use a YARD docstring before the `create_function` call and any `dispatch`
calls:
```ruby
# An example 4.x function.
Puppet::Functions.create_function(:example) do
# @param first The first parameter.
# @param second The second parameter.
# @return [String] Returns a string.
# @example Calling the function
# example('hi', 10)
dispatch :example do
param 'String', :first
param 'Integer', :second
end
# ...
end
```
***Note: Puppet Strings will automatically use the parameter type information from the `dispatch` block to document
the parameters. Only document your parameter types when the Puppet 4.x function contains no `dispatch` calls.***
If the Puppet 4.x function contains multiple `dispatch` calls, Puppet Strings will automatically create `overload` tags
to describe the function's overloads:
```ruby
# An example 4.x function.
Puppet::Functions.create_function(:example) do
# Overload by string.
# @param first The first parameter.
# @return [String] Returns a string.
# @example Calling the function
# example('hi')
dispatch :example_string do
param 'String', :first
end
# Overload by integer.
# @param first The first parameter.
# @return [Integer] Returns an integer.
# @example Calling the function
# example(10)
dispatch :example_integer do
param 'Integer', :first
end
# ...
```
The resulting HTML for this example function will document both `example(String $first)` and `example(Integer $first)`.
## Puppet Language
To document Puppet functions written in the Puppet language, use a YARD docstring before the function definition:
```puppet
# An example function written in Pupppet.
# @param name The name to say hello to.
# @return [String] Returns a string.
# @example Calling the function
# example('world')
function example(String $name) {
"hello $name"
}
```
***Note: Puppet Strings will automatically use the parameter type information from the function's parameters to document
the parameters.***
Additional Resources
--------------------
Here are a few other good resources for getting started with documentation:
* [Module README Template](https://docs.puppetlabs.com/puppet/latest/reference/modules_documentation.html)
* [Module README Template](https://docs.puppet.com/puppet/latest/reference/modules_documentation.html)
* [YARD Getting Started Guide](http://www.rubydoc.info/gems/yard/file/docs/GettingStarted.md)
* [YARD Tags Overview](http://www.rubydoc.info/gems/yard/file/docs/Tags.md)
Rake Tasks
-----
----------
This module is also available as a Gem and makes three rake tasks (`strings:generate`, `strings:serve`, and `strings:gh_pages`) available in `puppet-strings/rake_tasks`. To add this to your module's CI workflow, be sure to add this module to your `Gemfile`:
Puppet Strings comes with two rake tasks: `strings:generate` and `strings:gh_pages:update` available in `puppet-strings/tasks`.
In addition to generating the usual 'doc' directory of HTML documentation, the `strings:generate` rake task will also drop a strings.json file containing a JSON representation of the module into the directory the rake task was run from.
Add the following to your Gemfile to use `puppet-strings`:
```ruby
gem 'puppet-strings', :git => 'https://github.com/puppetlabs/puppetlabs-strings.git'
gem 'puppet-strings', :git => 'https://github.com/puppetlabs/puppet-strings.git'
```
To use the rake tasks, `require puppet-strings/rake_tasks` in your `Rakefile`:
In your `Rakefile`, add the following to use the `puppet-strings` tasks:
```ruby
require 'puppet-strings/rake_tasks'
require 'puppet-strings/tasks'
```
The task `strings:generate` which is provided by including `puppet-strings/rake_tasks` will scan the manifests and lib directory from your single module. If you need to document a complete, or part of a, puppet tree, you can use the `PuppetStrings::RakeTasks::Generate` task. This rake task will by default overwrite strings:generate unless you specify a custom name. See the example below on how you can use it and which options it supports.
The `strings:generate` task can be used to generate documentation:
```ruby
require 'puppet-strings/rake_tasks/generate'
PuppetStrings::RakeTasks::Generate.new(:documentation) do |task|
task.paths = ['site/roles','site/profiles','modules/internal']
task.excludes = ['/vendor/','/example/']
task.options = {} # disables the strings.json output
# module_resourcefiles are the patterns of included files. Below is the default.
# task.module_resourcefiles = ['manifests/**/*.pp', 'lib/**/*.rb']
end
```
$ rake strings:generate
```
The `strings:gh_pages` task will generate your Strings documentation to be made available via [GitHub Pages](https://pages.github.com/). It will:
The task accepts the following parameters:
1. Create a `doc` directory in the root of your project
2. Check out the `gh-pages` branch of the current repository in the `doc` directory (it will create a branch if one does not already exist)
3. Generate strings documentation using the `strings:generate` task
4. Commit the changes and push them to the `gh-pages` branch **with the `-f` flag**
* `patterns`: the search patterns to use for finding files to document (defaults to `manifests/**/*.pp functions/**/*.pp types/**/*.pp lib/**/*.rb`).
* `debug`: enables debug output when set to `true`.
* `backtrace`: enables backtraces for errors when set to `true`.
* `markup`: the markup language to use (defaults to `markdown`).
* `yard_args`: additional arguments to pass to YARD.
This task aims to keep the `gh-pages` branch up to date with the current code and uses the `-f` flag when pushing to the `gh-pages` branch. Please keep this in mind as it **will be destructive** if not used properly.
An example of passing arguments to the `strings:generate` Rake task:
```
$ rake strings:generate\['**/*.pp **/*.rb, true, true, markdown, --readme README.md']
```
The `strings:gh_pages:update` task will generate your Puppet Strings documentation to be made available via [GitHub Pages](https://pages.github.com/). It will:
1. Create a `doc` directory in the root of your project
2. Check out the `gh-pages` branch of the current repository in the `doc` directory (it will create a branch if one does not already exist)
3. Generate strings documentation using the `strings:generate` task
4. Commit the changes and push them to the `gh-pages` branch **with the `--force` flag**
This task aims to keep the `gh-pages` branch up to date with the current code and uses the `-f` flag when pushing to the `gh-pages` branch.
***Please note this operation will be destructive if not used properly.***
Developing and Contributing
-----
---------------------------
We love contributions from the community! If you'd like to contribute to the strings module, check out [CONTRIBUTING.md](https://github.com/puppetlabs/puppetlabs-strings/blob/master/CONTRIBUTING.md) to get information on the contribution process.
We love contributions from the community!
If you'd like to contribute to the strings module, check out [CONTRIBUTING.md](https://github.com/puppetlabs/puppet-strings/blob/master/CONTRIBUTING.md) to get information on the contribution process.
Running Specs
-----
-------------
If you're going to be doing any development with puppet strings, it's essential that you can run the spec tests. You should simply have to do the following:
If you plan on developing features or fixing bugs in Puppet Strings, it is essential that you run specs before opening a pull request.
To run specs, simply execute the `spec` rake task:
$ bundle install --path .bundle/gems
$ bundle exec rake spec
Support
-----
Please log tickets and issues at our [JIRA tracker][JIRA]. A [mailing list](https://groups.google.com/forum/?fromgroups#!forum/puppet-users) is available for asking questions and getting help from others. In addition there is an active #puppet channel on Freenode.
We use semantic version numbers for our releases, and recommend that users stay as up-to-date as possible by upgrading to patch releases and minor releases as they become available.
Bugfixes and ongoing development will occur in minor releases for the current major version. Security fixes will be backported to a previous major version on a best-effort basis, until the previous major version is no longer maintained.
Caveats
-------
- Documentation blocks must immediately precede the documented code with no whitespace. This is because the comment extractor possesses the elegance and intelligence of a bag of hammers.
Please log tickets and issues at our [JIRA tracker][JIRA]. A [mailing list](https://groups.google.com/forum/?fromgroups#!forum/puppet-users)
is available for asking questions and getting help from others.
- This project is very much a work in progress and may very well have undiscovered bugs and pitfalls. If you discover any of these, [please file a ticket](https://tickets.puppetlabs.com/browse/PDOC).
There is also an active #puppet channel on the Freenode IRC network.
We use semantic version numbers for our releases, and recommend that users stay as up-to-date as possible by upgrading to
patch releases and minor releases as they become available.
Bug fixes and ongoing development will occur in minor releases for the current major version.
Security fixes will be ported to a previous major version on a best-effort basis, until the previous major version is no longer maintained.

View File

@ -1,18 +1,19 @@
require 'rubygems'
require 'puppetlabs_spec_helper/rake_tasks'
require 'puppet-lint/tasks/puppet-lint'
PuppetLint.configuration.send('disable_80chars')
PuppetLint.configuration.ignore_paths = ["spec/**/*.pp", "pkg/**/*.pp"]
desc "Validate manifests, templates, and ruby files"
# Add our own tasks
require 'puppet-strings/tasks'
PuppetLint.configuration.send('disable_80chars')
PuppetLint.configuration.ignore_paths = %w(acceptance/**/*.pp spec/**/*.pp pkg/**/*.pp)
desc 'Validate Ruby source files and ERB templates.'
task :validate do
Dir['manifests/**/*.pp'].each do |manifest|
sh "puppet parser validate --noop #{manifest}"
end
Dir['spec/**/*.rb','lib/**/*.rb'].each do |ruby_file|
sh "ruby -c #{ruby_file}" unless ruby_file =~ /spec\/fixtures/
end
Dir['templates/**/*.erb'].each do |template|
Dir['lib/puppet-strings/yard/templates/**/*.erb'].each do |template|
sh "erb -P -x -T '-' #{template} | ruby -c"
end
end
@ -28,16 +29,16 @@ task :acceptance do
end
cli = BeakerHostGenerator::CLI.new([target])
nodeset_dir = "spec/acceptance/nodesets"
nodeset_dir = 'acceptance/nodesets'
nodeset = "#{nodeset_dir}/#{target}.yml"
FileUtils.mkdir_p(nodeset_dir)
File.open(nodeset, 'w') do |fh|
fh.print(cli.execute)
end
puts nodeset
sh "gem build puppet-strings.gemspec"
sh "puppet module build spec/unit/puppet/examples/test"
sh "BEAKER_set=#{ENV["platform"]} rspec spec/acceptance/*.rb"
sh 'gem build puppet-strings.gemspec'
sh 'puppet module build acceptance/fixtures/modules/test'
sh "BEAKER_set=#{ENV['platform']} rspec acceptance/*.rb"
end
task(:rubocop) do

View File

@ -0,0 +1,28 @@
require 'spec_helper_acceptance'
require 'json'
describe 'Generating module documentation using generate action' do
def read_file_on(host, filename)
on(host, "cat #{filename}").stdout
end
before :all do
modules = JSON.parse(on(master, puppet('module', 'list', '--render-as', 'json')).stdout)
test_module_info = modules['modules_by_path'].values.flatten.find { |mod_info| mod_info =~ /Module test/ }
test_module_path = test_module_info.match(/\(([^)]*)\)/)[1]
on master, puppet('strings', 'generate', "#{test_module_path}/**/*.{rb,pp}")
end
it 'should generate documentation for manifests' do
expect(read_file_on(master, '/root/doc/puppet_classes/test.html')).to include('Class: test')
end
it 'should generate documentation for 3x functions' do
expect(read_file_on(master, '/root/doc/puppet_functions_ruby3x/function3x.html')).to include('This is the function documentation for function3x')
end
it 'should generate documentation for 4x functions' do
expect(read_file_on(master, '/root/doc/puppet_functions_ruby4x/function4x.html')).to include('This is a function which is used to test puppet strings')
end
end

View File

@ -1,121 +0,0 @@
The Strings JSON Interchange Schema
===================================
Strings has two flags used to emit json.
* `--emit-json $FILE` Saves json to a file.
* `--emit-json-stdout` Prints json on stdout.
Top Level Structure
-------------------
The json outputted by strings is a single object which has 4 keys representing
the different types of Puppet code and extension functions Strings reads. The
value for each key is a list of json objects representing each puppet class,
function, etc.
Here is an example of the top level structure:
```json
{
"defined_types": [...],
"puppet_classes": [...],
"puppet_functions": [...],
"puppet_types": [...],
"puppet_providers": [...]
}
```
Defined Types
-------------
Each defined type or puppet class object has the following properties and values:
* `name`: A string representing the name of the object.
* `file`: The file the object came from. A string.
* `line`: The line in the file the object came from. A number.
* `docstring`: A string. The docstring describing the object.
* `examples`: A list of strings representing the content of the examples in the
docstring.
* `signatures`: A list of function signatures which may be supported by the
object. Each function signature is a json object whose keys are the
parameter names, and whose values are the types those parameters may take.
This is extracted from the code itself.
* `parameters`: An object whose keys are the parameter names and whose values
are the parameter's types or null if it has no types. This is extracted from
the docstring.
Puppet Functions
----------------
Both puppet 4x and 3x functions are represented as json objects kept in the
`puppet_functions` list. Puppet 4x functions have every property that 3x
functions have, as well as a few extras.
Puppet 3x functions have:
* `name`: A string representing the name of the
* `file`: The file the object came from. A string.
* `line`: The line in the file the object came from. A number.
* `docstring`: A string. The docstring describing our object.
* `function_api_version`: the number 3.
* `documented_params`: A object whose keys are the parameters which were
* documented and whose values are the types they may take, or null.
* `examples`: A list of strings representing the content of the examples in the
docstring.
Puppet 4x functions have everything 3x functions do as well as:
* The `function_api_version` is the number 4, not 3 (surprise!).
* `signatures`: A list of function signatures which may be supported by the
object. Each function signature is a json object whose keys are the parameter
names, and whose values are the types those parameters may take. This is
extracted from the code itself.
Puppet Types
------------
Each puppet type object has the following properties and values:
* `name`: A string representing the name of the object
* `file`: The file the object came from. A string.
* `line`: The line in the file the object came from. A number.
* `docstring`: A string. The docstring describing our object.
* `examples`: A list of strings representing the content of the examples in the
docstring.
* `parameters`: A list of objects with the following shape:
* `allowed_vales`: a list of strings representing the allowed values.
* `default`: a string or null.
* `docstring`: The docstring.
* `name`: the parameter name.
* `properties`: A list of objects with a shape very similar to parameters but
also including:
* `namevar`: A boolean.
* `features`: A list of objects representing possible features. They have the
following shape:
* `docstring`: The description of the feature.
* `methods`: null or a list of the available methods as strings.
* `name`: The feature's name.
Puppet Providers
----------------
Each puppet provider object has the following properties and values:
* `name`: A string representing the name of the object
* `file`: The file the object came from. A string.
* `line`: The line in the file the object came from. A number.
* `docstring`: A string. The docstring describing the object.
* `examples`: A list of strings representing the content of the examples in the
docstring.
* `commands`: A list of the names of the commands available.
* `confines`: An object whose keys are the confine keys and whose values are
the confine values.
* `defaults`: Similar to above.
* `features`: A list of strings representing the features this provider
supports.
* `type_name`: The type this provider accompanies.

63
lib/puppet-strings.rb Normal file
View File

@ -0,0 +1,63 @@
# The root module for Puppet Strings.
module PuppetStrings
# The glob patterns used to search for files to document.
DEFAULT_SEARCH_PATTERNS = %w(
manifests/**/*.pp
functions/**/*.pp
types/**/*.pp
lib/**/*.rb
).freeze
# Generates documentation.
# @param [Array<String>] search_patterns The search patterns (e.g. manifests/**/*.pp) to look for files.
# @param [Hash] options The options hash.
# @option options [Boolean] :debug Enable YARD debug output.
# @option options [Boolean] :backtrace Enable YARD backtraces.
# @option options [String] :markup The YARD markup format to use (defaults to 'markdown').
# @option options [String] :json Enables JSON output to the given file. If the file is nil, STDOUT is used.
# @option options [Array<String>] :yard_args The arguments to pass to yard.
# @return [void]
def self.generate(search_patterns = DEFAULT_SEARCH_PATTERNS, options = {})
require 'puppet-strings/yard'
PuppetStrings::Yard.setup!
# Format the arguments to YARD
args = ['doc']
args << '--debug' if options[:debug]
args << '--backtrace' if options[:backtrace]
args << "-m#{options[:markup] || 'markdown'}"
render_as_json = options.key? :json
json_file = nil
if render_as_json
json_file = options[:json]
# Disable output and prevent stats/progress when writing to STDOUT
args << '-n'
args << '-q' unless json_file
args << '--no-stats' unless json_file
args << '--no-progress' unless json_file
end
yard_args = options[:yard_args]
args += yard_args if yard_args
args += search_patterns
# Run YARD
YARD::CLI::Yardoc.run(*args)
# If outputting JSON, render the output
if render_as_json
require 'puppet-strings/json'
PuppetStrings::Json.render(json_file)
end
end
# Runs the YARD documentation server.
# @param [Array<String>] args The arguments to YARD.
def self.run_server(*args)
require 'puppet-strings/yard'
PuppetStrings::Yard.setup!
YARD::CLI::Server.run(*args)
end
end

View File

@ -0,0 +1,49 @@
require 'json'
# The module for JSON related functionality.
module PuppetStrings::Json
# Renders the current YARD registry as JSON to the given file (or STDOUT if nil).
# @param [String] file The path to the output file to render the registry to. If nil, output will be to STDOUT.
# @return [void]
def self.render(file = nil)
document = {
puppet_classes: YARD::Registry.all(:puppet_class).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),
puppet_functions: YARD::Registry.all(:puppet_function).sort_by!(&:name).map!(&:to_hash),
# TODO: Need Ruby documentation?
}
if file
File.open(file, 'w') do |f|
f.write(JSON.pretty_generate(document))
f.write("\n")
end
else
puts JSON.pretty_generate(document)
end
end
# Converts a YARD::Docstring (or String) to a docstring hash for JSON output.
# @param [YARD::Docstring, String] docstring The docstring to convert to a hash.
# @return [Hash] Returns a hash representation of the given docstring.
def self.docstring_to_hash(docstring)
hash = {}
hash[:text] = docstring
if docstring.is_a? YARD::Docstring
# Skip over the API tags that are public
tags = docstring.tags.select { |t| t.tag_name != 'api' || t.text != 'public' }.map do |t|
next t.to_hash if t.respond_to?(:to_hash)
tag = { tag_name: t.tag_name }
tag[:text] = t.text if t.text
tag[:types] = t.types if t.types
tag[:name] = t.name if t.name
tag
end
hash[:tags] = tags unless tags.empty?
end
hash
end
end

View File

@ -1,88 +0,0 @@
require 'rake'
require 'rake/tasklib'
require 'puppet_x/puppetlabs/strings/util'
module PuppetStrings
module RakeTasks
# A configurable rake task to generate documentation using puppet-strings.
#
# @attr [String] name the name of the rake task.
# @attr [Array<String>] module_resourcefiles globs used to specify which files to document.
# Defaults to {PuppetX::PuppetLabs::Strings::Util::MODULE_SOURCEFILES}
# @attr [Array<String>] excludes a list of paths or patterns of files and directories to ignore.
# @attr [Array<String>, nil] paths list of paths to generate documentation for.
# If this value is nil, uses the default paths for puppet strings.
# @attr [Hash] options a hash with options passed through to yardoc.
class Generate < ::Rake::TaskLib
attr_accessor :name
attr_accessor :module_resourcefiles
attr_accessor :paths
attr_accessor :excludes
attr_accessor :options
# Creates a new instance of the Generate Rake task.
# Defaults the name to 'strings:generate which overrides
# the namespaced generates task. Also default other attributes to
# mimic the current default behaviour.
def initialize(*args, &task_block)
@name = args.shift || 'strings:generate'
@module_sourcefiles = PuppetX::PuppetLabs::Strings::Util::MODULE_SOURCEFILES
@paths = nil
@options = {emit_json: 'strings.json'}
@excludes = []
define(args, &task_block)
end
# Creates the actual rake task after calling the task_block.
#
# @param [Array<String>] args arguments passed to the rake task.
# @param [Proc] task_block block to configure the task.
# @yield [self, args] configure this rake task.
def define(args, &task_block)
Rake::Task[@name].clear if Rake::Task.task_defined?(@name)
yield(*[self, args].slice(0, task_block.arity)) if task_block
desc 'Generate Puppet documentation with YARD.' unless ::Rake.application.last_description
task @name do
execute_task(generate_task_args)
end
end
private
# Converts all attributes and options to an arguments array that can be passed
# through to {PuppetX::PuppetLabs::Strings::Util #generate}.
#
# If paths is not nil, we expand them with the module_sourcefiles patterns.
def generate_task_args
@paths = [*@paths] unless @paths.nil?
@module_sourcefiles = [*@module_sourcefiles]
@excludes = [*@excludes]
exclude_args = @excludes.map {|x| ["--exclude", x]}.flatten
pattern_args = @paths.nil? ? [] : expand_paths(@paths, @module_sourcefiles)
exclude_args + pattern_args + [@options]
end
# Combine each prefix_path with each pattern with '/**/' glue.
#
# @example
# expand_paths(['a','b'], ['*.rb','*.pp'])
# => ["a/**/*.rb", "a/**/*.pp", "b/**/*.rb", "b/**/*.pp"]
#
# @param [Array<String>] prefix_paths an array with paths
# @param [Array<String>] patterns an array with patterns.
def expand_paths(prefix_paths, patterns)
prefix_paths.map {|path| patterns.map {|p| "#{path}/**/#{p}" } }.flatten
end
# call {PuppetX::PuppetLabs::Strings::Util #generate}
# @param [Array<String, Hash>] args Arguments. Last element should be a Hash.
def execute_task(args)
PuppetX::PuppetLabs::Strings::Util.generate(args)
end
end
end
end

View File

@ -0,0 +1,10 @@
require 'rake'
require 'rake/tasklib'
module PuppetStrings
# The module for Puppet Strings rake tasks.
module Tasks
require 'puppet-strings/tasks/generate.rb'
require 'puppet-strings/tasks/gh_pages.rb'
end
end

View File

@ -0,0 +1,23 @@
require 'puppet-strings'
# Implements the strings:generate task.
namespace :strings do
desc 'Generate Puppet documentation with YARD.'
task :generate, :patterns, :debug, :backtrace, :markup, :json, :yard_args do |t, args|
patterns = args[:patterns]
patterns = patterns.split if patterns
patterns ||= PuppetStrings::DEFAULT_SEARCH_PATTERNS
options = {
debug: args[:debug] == 'true',
backtrace: args[:backtrace] == 'true',
markup: args[:markup] || 'markdown',
}
options[:json] = args[:json] if args.key? :json
options[:yard_args] = args[:yard_args].split if args.key? :yard_args
PuppetStrings.generate(patterns, options)
end
end

View File

@ -1,45 +1,30 @@
require 'rake'
require 'rake/tasklib'
require 'puppet/face'
require 'puppet_x/puppetlabs/strings/util'
require 'puppet-strings/tasks'
namespace :strings do
desc 'Generate Puppet documentation with YARD.'
task :generate do
PuppetX::PuppetLabs::Strings::Util.generate([
{emit_json: 'strings.json'}
])
end
desc 'Serve YARD documentation for modules.'
task :serve do
PuppetX::PuppetLabs::Strings::Util.serve
end
namespace :gh_pages do
git_uri = `git config --get remote.origin.url`.strip
desc "Checkout the gh-pages branch for doc generation."
desc 'Checkout the gh-pages branch for doc generation.'
task :checkout do
if Dir.exist?('doc')
fail "The 'doc' directory (#{File.expand_path('doc')}) is not a Git repository! Remove it and run the Rake task again." unless Dir.exist?('doc/.git')
Dir.chdir('doc') do
system 'git checkout gh-pages'
system 'git reset --hard origin/gh-pages'
system 'git pull origin gh-pages'
system 'git pull --rebase origin gh-pages'
end
else
git_uri = `git config --get remote.origin.url`.strip
fail "Could not determine the remote URL for origin: ensure the current directory is a Git repro with a remote named 'origin'." unless $?.success?
Dir.mkdir('doc')
Dir.chdir('doc') do
system 'git init'
system "git remote add origin #{git_uri}"
system 'git pull'
system 'git pull origin gh-pages'
system 'git checkout -b gh-pages'
end
end
end
desc "Push new docs to GitHub."
desc 'Push new docs to GitHub.'
task :push do
Dir.chdir('doc') do
system 'git add .'
@ -48,7 +33,7 @@ namespace :strings do
end
end
desc "Run checkout, generate, and push tasks."
desc 'Run checkout, generate, and push tasks.'
task :update => [
:checkout,
:'strings:generate',

View File

@ -0,0 +1,96 @@
require 'yard'
# Module for YARD related functionality.
module PuppetStrings::Yard
require 'puppet-strings/yard/code_objects'
require 'puppet-strings/yard/handlers'
require 'puppet-strings/yard/tags'
require 'puppet-strings/yard/parsers'
# Sets up YARD for use with puppet-strings.
# @return [void]
def self.setup!
# Register the template path
YARD::Templates::Engine.register_template_path(File.join(File.dirname(__FILE__), 'yard', 'templates'))
# 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)
YARD::Handlers::Processor.register_handler_namespace(:puppet_ruby, PuppetStrings::Yard::Handlers::Ruby)
# Register the tag directives
PuppetStrings::Yard::Tags::ParameterDirective.register!
PuppetStrings::Yard::Tags::PropertyDirective.register!
# Ignore documentation on Puppet DSL calls
# This prevents the YARD DSL parser from emitting warnings for Puppet's Ruby DSL
YARD::Handlers::Ruby::DSLHandlerMethods::IGNORE_METHODS['create_function'] = true
YARD::Handlers::Ruby::DSLHandlerMethods::IGNORE_METHODS['newtype'] = true
end
end
# Monkey patch YARD::CLI::Yardoc#all_objects to return our custom code objects.
# @private
class YARD::CLI::Yardoc
def all_objects
YARD::Registry.all(
:root,
:module,
:class,
:puppet_class,
:puppet_defined_type,
:puppet_type,
:puppet_provider,
:puppet_function
)
end
end
# Monkey patch the stats object to return statistics for our objects.
# 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 stats_for_puppet_defined_types
output 'Puppet Defined Types', *type_statistics_all(:puppet_defined_type)
end
def stats_for_puppet_types
output 'Puppet Types', *type_statistics_all(:puppet_type)
end
def stats_for_puppet_providers
output 'Puppet Providers', *type_statistics_all(:puppet_provider)
end
def stats_for_puppet_functions
output 'Puppet Functions', *type_statistics_all(:puppet_function)
end
def output(name, data, undoc = nil)
# Monkey patch output to accommodate our larger header widths
@total += data if data.is_a?(Integer) && undoc
@undocumented += undoc if undoc.is_a?(Integer)
data =
if undoc
('%5s (% 5d undocumented)' % [data, undoc])
else
'%5s' % data
end
log.puts('%-21s %s' % [name + ':', data])
end
# This differs from the YARD implementation in that it considers
# a docstring without text but with tags to be undocumented.
def type_statistics_all(type)
objs = all_objects.select {|m| m.type == type }
undoc = objs.find_all {|m| m.docstring.all.empty? }
@undoc_list |= undoc if @undoc_list
[objs.size, undoc.size]
end
end

View File

@ -0,0 +1,8 @@
# The module for custom YARD code objects.
module PuppetStrings::Yard::CodeObjects
require 'puppet-strings/yard/code_objects/class'
require 'puppet-strings/yard/code_objects/defined_type'
require 'puppet-strings/yard/code_objects/type'
require 'puppet-strings/yard/code_objects/provider'
require 'puppet-strings/yard/code_objects/function'
end

View File

@ -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

View File

@ -0,0 +1,59 @@
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
# 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[:inherits] = statement.parent_class if statement.parent_class
hash[:docstring] = PuppetStrings::Json.docstring_to_hash(docstring)
defaults = Hash[*parameters.select{ |p| !p[1].nil? }.flatten]
hash[:defaults] = defaults unless defaults.empty?
hash[:source] = source unless source && source.empty?
hash
end
end

View File

@ -0,0 +1,58 @@
require 'puppet-strings/yard/code_objects/group'
# Implements the group for Puppet defined types.
class PuppetStrings::Yard::CodeObjects::DefinedTypes < PuppetStrings::Yard::CodeObjects::Group
# Gets the singleton instance of the group.
# @return Returns the singleton instance of the group.
def self.instance
super(:puppet_defined_types)
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)
'Defined Types'
end
end
# Implements the Puppet defined type code object.
class PuppetStrings::Yard::CodeObjects::DefinedType < PuppetStrings::Yard::CodeObjects::Base
attr_reader :statement
attr_reader :parameters
# Initializes a Puppet defined type code object.
# @param [PuppetStrings::Parsers::DefinedTypeStatement] statement The defined type statement that was parsed.
# @return [void]
def initialize(statement)
@statement = statement
@parameters = statement.parameters.map { |p| [p.name, p.value] }
super(PuppetStrings::Yard::CodeObjects::DefinedTypes.instance, statement.name)
end
# Gets the type of the code object.
# @return Returns the type of the code object.
def type
:puppet_defined_type
end
# Gets the source of the code object.
# @return Returns the source of the code object.
def source
@statement.source
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::Json.docstring_to_hash(docstring)
defaults = Hash[*parameters.select{ |p| !p[1].nil? }.flatten]
hash[:defaults] = defaults unless defaults.empty?
hash[:source] = source unless source && source.empty?
hash
end
end

View File

@ -0,0 +1,93 @@
require 'puppet-strings/yard/code_objects/group'
# Implements the group for Puppet functions.
class PuppetStrings::Yard::CodeObjects::Functions < PuppetStrings::Yard::CodeObjects::Group
# Gets the singleton instance of the group.
# @param [Symbol] type The function type to get the group for.
# @return Returns the singleton instance of the group.
def self.instance(type)
super("puppet_functions_#{type}".intern)
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 Functions'
end
end
# Implements the Puppet function code object.
class PuppetStrings::Yard::CodeObjects::Function < PuppetStrings::Yard::CodeObjects::Base
# Identifier for 3.x Ruby API functions
RUBY_3X = :ruby3x
# Identifier for 4.x Ruby API functions
RUBY_4X = :ruby4x
# Identifier for Puppet language functions
PUPPET = :puppet
attr_accessor :parameters
# Initializes a Puppet function code object.
# @param [String] name The name of the function.
# @param [Symbol] function_type The type of function (e.g. :ruby3x, :ruby4x, :puppet)
# @return [void]
def initialize(name, function_type)
super(PuppetStrings::Yard::CodeObjects::Functions.instance(function_type), name)
@parameters = []
@function_type = function_type
end
# Gets the type of the code object.
# @return Returns the type of the code object.
def type
:puppet_function
end
# Gets the function type display string.
# @return Returns the function type display string.
def function_type
case @function_type
when RUBY_3X
'Ruby 3.x API'
when RUBY_4X
'Ruby 4.x API'
else
'Puppet Language'
end
end
# Gets the Puppet signature of the function (single overload only).
# @return [String] Returns the Puppet signature of the function.
def signature
return '' if self.has_tag? :overload
tags = self.tags(:param)
args = @parameters.map do |parameter|
name, default = parameter
tag = tags.find { |tag| tag.name == name } if tags
type = tag && tag.types ? "#{tag.type} " : 'Any '
prefix = "#{name[0]}" if name.start_with?('*', '&')
name = name[1..-1] if prefix
default = " = #{default}" if default
"#{type}#{prefix}$#{name}#{default}"
end.join(', ')
@name.to_s + '(' + args + ')'
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[:type] = @function_type.to_s
signature = self.signature
hash[:signature] = signature unless signature.empty?
hash[:docstring] = PuppetStrings::Json.docstring_to_hash(docstring)
defaults = Hash[*parameters.select{ |p| !p[1].nil? }.flatten]
hash[:defaults] = defaults unless defaults.empty?
hash[:source] = source unless source && source.empty?
hash
end
end

View File

@ -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

View File

@ -0,0 +1,93 @@
require 'puppet-strings/yard/code_objects/group'
# Implements the group for Puppet providers.
class PuppetStrings::Yard::CodeObjects::Providers < PuppetStrings::Yard::CodeObjects::Group
# Gets the singleton instance of the group.
# @param [String] type The resource type name for the provider.
# @return Returns the singleton instance of the group.
def self.instance(type)
super("puppet_providers_#{type}".intern)
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)
'Providers'
end
end
# Implements the Puppet provider code object.
class PuppetStrings::Yard::CodeObjects::Provider < PuppetStrings::Yard::CodeObjects::Base
attr_reader :type_name, :confines, :features, :defaults, :commands
# Initializes a Puppet provider code object.
# @param [String] type_name The resource type name for the provider.
# @param [String] name The name of the provider.s
# @return [void]
def initialize(type_name, name)
@type_name = type_name
super(PuppetStrings::Yard::CodeObjects::Providers.instance(type_name), name)
end
# Gets the type of the code object.
# @return Returns the type of the code object.
def type
:puppet_provider
end
# Adds a confine to the provider.
# @param [String] key The confine's key.
# @param [String] value The confine's value.
# @return [void]
def add_confine(key, value)
return unless key && value
@confines ||= {}
@confines[key] = value
end
# Adds a feature to the provider.
# @param [String] feature The feature to add to the provider.
# @return [void]
def add_feature(feature)
return unless feature
@features ||= []
@features << feature
end
# Adds a default to the provider.
# @param [String] key The default's key.
# @param [String] value The default's value.
# @return [void]
def add_default(key, value)
return unless key && value
@defaults ||= {}
@defaults[key] = value
end
# Adds a command to the provider.
# @param [String] key The command's key.
# @param [String] value The command's value.
# @return [void]
def add_command(key, value)
return unless key && value
@commands ||= {}
@commands[key] = value
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[:type_name] = type_name
hash[:file] = file
hash[:line] = line
hash[:docstring] = PuppetStrings::Json.docstring_to_hash(docstring)
hash[:confines] = confines if confines && !confines.empty?
hash[:features] = features if features && !features.empty?
hash[:defaults] = defaults if defaults && !defaults.empty?
hash[:commands] = commands if commands && !commands.empty?
hash
end
end

View File

@ -0,0 +1,146 @@
require 'puppet-strings/yard/code_objects/group'
# Implements the group for Puppet resource types.
class PuppetStrings::Yard::CodeObjects::Types < PuppetStrings::Yard::CodeObjects::Group
# Gets the singleton instance of the group.
# @return Returns the singleton instance of the group.
def self.instance
super(:puppet_types)
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)
'Resource Types'
end
end
# Implements the Puppet resource type code object.
class PuppetStrings::Yard::CodeObjects::Type < PuppetStrings::Yard::CodeObjects::Base
# Represents a resource type parameter.
class Parameter
attr_reader :name, :values, :aliases
attr_accessor :docstring, :isnamevar, :default
# Initializes a resource type parameter or property.
# @param [String] name The name of the parameter or property.
# @param [String] docstring The docstring for the parameter or property.s
def initialize(name, docstring = nil)
@name = name
@docstring = docstring || ''
@values = []
@aliases = {}
@isnamevar = false
@default = nil
end
# Adds a value to the parameter or property.
# @param [String] value The value to add.
# @return [void]
def add(value)
@values << value
end
# Aliases a value to another value.
# @param [String] new The new (alias) value.
# @param [String] old The old (existing) value.
# @return [void]
def alias(new, old)
@values << new unless @values.include? new
@aliases[new] = old
end
# Converts the parameter to a hash representation.
# @return [Hash] Returns a hash representation of the parameter.
def to_hash
hash = {}
hash[:name] = name
hash[:description] = docstring unless docstring.empty?
hash[:values] = values unless values.empty?
hash[:aliases] = aliases unless aliases.empty?
hash[:isnamevar] = true if isnamevar
hash[:default] = default if default
hash
end
end
# Represents a resource type property (same attributes as a parameter).
class Property < Parameter
end
# Represents a resource type feature.
class Feature
attr_reader :name, :docstring
# Initializes a new feature.
# @param [String] name The name of the feature.
# @param [String] docstring The docstring of the feature.
def initialize(name, docstring)
@name = name
@docstring = docstring
end
# Converts the feature to a hash representation.
# @return [Hash] Returns a hash representation of the feature.
def to_hash
hash = {}
hash[:name] = name
hash[:description] = docstring unless docstring.empty?
hash
end
end
attr_reader :properties, :parameters, :features
# Initializes a new resource type.
# @param [String] name The resource type name.
# @return [void]
def initialize(name)
super(PuppetStrings::Yard::CodeObjects::Types.instance, name)
end
# Gets the type of the code object.
# @return Returns the type of the code object.
def type
:puppet_type
end
# Adds a parameter to the resource type
# @param [PuppetStrings::Yard::CodeObjects::Type::Parameter] parameter The parameter to add.
# @return [void]
def add_parameter(parameter)
@parameters ||= []
@parameters << parameter
end
# Adds a property to the resource type
# @param [PuppetStrings::Yard::CodeObjects::Type::Property] property The property to add.
# @return [void]
def add_property(property)
@properties ||= []
@properties << property
end
# Adds a feature to the resource type.
# @param [PuppetStrings::Yard::CodeObjects::Type::Feature] feature The feature to add.
# @return [void]
def add_feature(feature)
@features ||= []
@features << feature
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::Json.docstring_to_hash(docstring)
hash[:properties] = properties.map(&:to_hash) if properties && !properties.empty?
hash[:parameters] = parameters.map(&:to_hash) if parameters && !parameters.empty?
hash[:features] = features.map(&:to_hash) if features && !features.empty?
hash
end
end

View File

@ -0,0 +1,16 @@
# The module for custom YARD handlers.
module PuppetStrings::Yard::Handlers
# The module for custom Ruby YARD handlers.
module Ruby
require 'puppet-strings/yard/handlers/ruby/type_handler'
require 'puppet-strings/yard/handlers/ruby/provider_handler'
require 'puppet-strings/yard/handlers/ruby/function_handler'
end
# The module for custom Puppet YARD handlers.
module Puppet
require 'puppet-strings/yard/handlers/puppet/class_handler'
require 'puppet-strings/yard/handlers/puppet/defined_type_handler'
require 'puppet-strings/yard/handlers/puppet/function_handler'
end
end

View File

@ -0,0 +1,44 @@
# 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)
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

View File

@ -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

View File

@ -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 defined types.
class PuppetStrings::Yard::Handlers::Puppet::DefinedTypeHandler < PuppetStrings::Yard::Handlers::Puppet::Base
handles PuppetStrings::Yard::Parsers::Puppet::DefinedTypeStatement
process do
# Register the object
object = PuppetStrings::Yard::CodeObjects::DefinedType.new(statement)
register object
# Log a warning if missing documentation
log.warn "Missing documentation for Puppet defined type '#{object.name}' at #{statement.file}:#{statement.line}." if object.docstring.empty?
# Set the parameter types
set_parameter_types(object)
# Mark the defined type 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

View File

@ -0,0 +1,42 @@
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::FunctionHandler < PuppetStrings::Yard::Handlers::Puppet::Base
handles PuppetStrings::Yard::Parsers::Puppet::FunctionStatement
process do
# Register the object
object = PuppetStrings::Yard::CodeObjects::Function.new(statement.name, PuppetStrings::Yard::CodeObjects::Function::PUPPET)
object.source = statement.source
object.source_type = parser.parser_type
register object
# Log a warning if missing documentation
log.warn "Missing documentation for Puppet function '#{object.name}' at #{statement.file}:#{statement.line}." if object.docstring.empty?
# Set the parameter tag types
set_parameter_types(object)
# Add a return tag
add_return_tag(object)
# Set the parameters on the object
object.parameters = statement.parameters.map { |p| [p.name, p.value] }
# 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
private
def add_return_tag(object)
tag = object.tag(:return)
if tag
tag.types = ['Any'] unless tag.types
return
end
log.warn "Missing @return tag near #{statement.file}:#{statement.line}."
object.add_tag YARD::Tags::Tag.new(:return, '', 'Any')
end
end

View File

@ -0,0 +1,38 @@
require 'ripper'
# Implements the base handler for Ruby language handlers.
class PuppetStrings::Yard::Handlers::Ruby::Base < YARD::Handlers::Ruby::Base
# A regular expression for detecting the start of a Ruby heredoc.
# Note: the first character of the heredoc start may have been cut off by YARD.
HEREDOC_START = /^<?<[\-~]?['"]?(\w+)['"]?[^\n]*[\n]?/
protected
# Converts the given Ruby AST node to a string representation.
# @param node The Ruby AST node to convert.
# @return [String] Returns a string representation of the node or nil if a string representation was not possible.
def node_as_string(node)
return nil unless node
case node.type
when :symbol, :symbol_literal
node.source[1..-1]
when :label
node.source[0..-2]
when :dyna_symbol
node.source
when :string_literal
content = node.jump(:tstring_content)
return content.source if content != node
# This attempts to work around a bug in YARD (https://github.com/lsegal/yard/issues/779)
# Check to see if the string source appears to have a heredoc open tag (or "most" of one)
# If so, remove the first line and the last line (if the latter contains the heredoc tag)
source = node.source
if source =~ HEREDOC_START
lines = source.split("\n")
source = lines[1..(lines.last.include?($1) ? -2 : -1)].join("\n") if lines.size > 1
end
source
end
end
end

View File

@ -0,0 +1,357 @@
require 'puppet-strings/yard/handlers/ruby/base'
require 'puppet-strings/yard/code_objects'
require 'puppet/util/docs'
# Implements the handler for Puppet functions written in Ruby.
class PuppetStrings::Yard::Handlers::Ruby::FunctionHandler < PuppetStrings::Yard::Handlers::Ruby::Base
# Represents the list of Puppet 4.x function API methods to support.
DISPATCH_METHOD_NAMES = %w(
param
required_param
optional_param
repeated_param
optional_repeated_param
required_repeated_param
block_param
required_block_param
optional_block_param
).freeze
namespace_only
handles method_call(:create_function)
handles method_call(:newfunction)
process do
# Only accept calls to Puppet::Functions (4.x) or Puppet::Parser::Functions (3.x)
return unless statement.count > 1
module_name = statement[0].source
return unless module_name == 'Puppet::Functions' || module_name == 'Puppet::Parser::Functions'
# Create and register the function object
is_3x = module_name == 'Puppet::Parser::Functions'
object = PuppetStrings::Yard::CodeObjects::Function.new(
get_name,
is_3x ? PuppetStrings::Yard::CodeObjects::Function::RUBY_3X : PuppetStrings::Yard::CodeObjects::Function::RUBY_4X
)
object.source = statement
register object
# For 3x, parse the doc parameter for the docstring
# This must be done after the `register` call above because `register` always uses the statement's docstring
if is_3x
docstring = get_3x_docstring(object.name)
register_docstring(object, docstring, nil) if docstring
# Default any typeless param tag to 'Any'
object.tags(:param).each do |tag|
tag.types = ['Any'] unless tag.types && !tag.types.empty?
end
# Populate the parameters and the return tag
object.parameters = object.tags(:param).map{ |p| [p.name, nil] }
add_return_tag(object, statement.file, statement.line)
else
# For 4x, auto generate tags based on dispatch docstrings
add_tags(object)
end
# Mark the function 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
private
def get_name
parameters = statement.parameters(false)
raise YARD::Parser::UndocumentableError, "Expected at least one parameter to Puppet::Functions.create_function at #{statement.file}:#{statement.line}." if parameters.empty?
name = node_as_string(parameters.first)
raise YARD::Parser::UndocumentableError, "Expected a symbol or string literal for first parameter but found '#{parameters.first.type}' at #{statement.file}:#{statement.line}." unless name
name
end
def add_tags(object)
log.warn "Missing documentation for Puppet function '#{object.name}' at #{statement.file}:#{statement.line}." if object.docstring.empty?
log.warn "The docstring for Puppet 4.x function '#{object.name}' contains @param tags near #{object.file}:#{object.line}: parameter documentation should be made on the dispatch call." unless object.tags(:param).empty?
log.warn "The docstring for Puppet 4.x function '#{object.name}' contains @return tags near #{object.file}:#{object.line}: return value documentation should be made on the dispatch call." unless object.tags(:return).empty?
log.warn "The docstring for Puppet 4.x function '#{object.name}' contains @overload tags near #{object.file}:#{object.line}: overload tags are automatically generated from the dispatch calls." unless object.tags(:overload).empty?
# Delete any existing param/return/overload tags
object.docstring.delete_tags(:param)
object.docstring.delete_tags(:return)
object.docstring.delete_tags(:overload)
block = statement.block
return unless block && block.count >= 2
# Get the unqualified name of the Puppet function
unqualified_name = object.name.to_s.split('::').last
# Walk the block statements looking for dispatch calls and methods with the same name as the Puppet function
default = nil
block[1].children.each do |node|
if node.is_a?(YARD::Parser::Ruby::MethodCallNode)
add_overload_tag(object, node)
elsif node.is_a?(YARD::Parser::Ruby::MethodDefinitionNode)
default = node if node.method_name && node.method_name.source == unqualified_name
end
end
# Create an overload for the default method if there is one
overloads = object.tags(:overload)
if overloads.empty? && default
add_method_overload(object, default)
overloads = object.tags(:overload)
end
# If there's only one overload, move the tags to the object itself
if overloads.count == 1
overload = overloads.first
object.parameters = overload.parameters
object.add_tag(*overload.tags)
object.docstring.delete_tags(:overload)
end
end
def add_overload_tag(object, node)
# Look for a call to a dispatch method with a block
return unless node.is_a?(YARD::Parser::Ruby::MethodCallNode) &&
node.method_name &&
node.method_name.source == 'dispatch' &&
node.parameters(false).count == 1 &&
node.block &&
node.block.count >= 2
overload_tag = PuppetStrings::Yard::Tags::OverloadTag.new(object.name, node.docstring || '')
param_tags = overload_tag.tags(:param)
block = nil
node.block[1].children.each do |child|
next unless child.is_a?(YARD::Parser::Ruby::MethodCallNode) && child.method_name
method_name = child.method_name.source
next unless DISPATCH_METHOD_NAMES.include?(method_name)
# Check for block
if method_name.include?('block')
if block
log.warn "A duplicate block parameter was found for Puppet function '#{object.name}' at #{child.file}:#{child.line}."
next
end
# Store the block; needs to be appended last
block = child
next
end
# Ensure two parameters to parameter definition
parameters = child.parameters(false)
unless parameters.count == 2
log.warn "Expected 2 arguments to '#{method_name}' call at #{child.file}:#{child.line}: parameter information may not be correct."
next
end
add_param_tag(
overload_tag,
param_tags,
node_as_string(parameters[1]),
child.file,
child.line,
node_as_string(parameters[0]),
nil, # TODO: determine default from corresponding Ruby method signature?
method_name.include?('optional'),
method_name.include?('repeated')
)
end
# Handle the block parameter after others so it appears last in the list
if block
parameters = block.parameters(false)
if parameters.empty?
name = 'block'
type = 'Callable'
elsif parameters.count == 1
name = node_as_string(parameters[0])
type = 'Callable'
elsif parameters.count == 2
type = node_as_string(parameters[0])
name = node_as_string(parameters[1])
else
log.warn "Unexpected number of arguments to block definition at #{block.file}:#{block.line}."
end
if name && type
add_param_tag(
overload_tag,
param_tags,
name,
block.file,
block.line,
type,
nil, # TODO: determine default from corresponding Ruby method signature?
block.method_name.source.include?('optional'),
false, # Not repeated
true # Is block
)
end
end
# Add a return tag if missing
add_return_tag(overload_tag, node.file, node.line)
# Validate that tags have parameters
validate_overload(overload_tag, node.file, node.line)
object.add_tag overload_tag
end
def add_method_overload(object, node)
overload_tag = PuppetStrings::Yard::Tags::OverloadTag.new(object.name, node.docstring || '')
param_tags = overload_tag.tags(:param)
parameters = node.parameters
# Populate the required parameters
params = parameters.unnamed_required_params
if params
params.each do |parameter|
add_param_tag(
overload_tag,
param_tags,
parameter.source,
parameter.file,
parameter.line
)
end
end
# Populate the optional parameters
params = parameters.unnamed_optional_params
if params
params.each do |parameter|
add_param_tag(
overload_tag,
param_tags,
parameter[0].source,
parameter.file,
parameter.line,
nil,
parameter[1].source,
true
)
end
end
# Populate the splat parameter
param = parameters.splat_param
if param
add_param_tag(
overload_tag,
param_tags,
param.source,
param.file,
param.line,
nil,
nil,
false,
true
)
end
# Populate the block parameter
param = parameters.block_param
if param
add_param_tag(
overload_tag,
param_tags,
param.source,
param.file,
param.line,
nil,
nil,
false,
false,
true
)
end
# Add a return tag if missing
add_return_tag(overload_tag, node.file, node.line)
# Validate that tags have parameters
validate_overload(overload_tag, node.file, node.line)
object.add_tag overload_tag
end
def add_param_tag(object, tags, name, file, line, type = nil, default = nil, optional = false, repeated = false, block = false)
tag = tags.find { |tag| tag.name == name } if tags
log.warn "Missing @param tag for parameter '#{name}' near #{file}:#{line}." unless tag || object.docstring.all.empty?
log.warn "The @param tag for parameter '#{name}' should not contain a type specification near #{file}:#{line}: ignoring in favor of dispatch type information." if type && tag && tag.types && !tag.types.empty?
if repeated
name = '*' + name
elsif block
name = '&' + name
end
unless type
type = tag && tag.types ? tag.type : 'Any'
end
type = optional ? "Optional[#{type}]" : type
object.parameters << [name, to_puppet_literal(default)]
if tag
tag.name = name
tag.types = [type]
else
object.add_tag YARD::Tags::Tag.new(:param, '', type, name)
end
end
def add_return_tag(object, file, line)
tag = object.tag(:return)
if tag
tag.types = ['Any'] unless tag.types
return
end
log.warn "Missing @return tag near #{file}:#{line}."
object.add_tag YARD::Tags::Tag.new(:return, '', 'Any')
end
def validate_overload(overload, file, line)
# Validate that tags have matching parameters
overload.tags(:param).each do |tag|
next if overload.parameters.find { |p| tag.name == p[0] }
log.warn "The @param tag for parameter '#{tag.name}' has no matching parameter at #{file}:#{line}."
end
end
def get_3x_docstring(name)
parameters = statement.parameters(false)
if parameters.count >= 2
parameters[1].each do |kvp|
next unless kvp.count == 2
next unless node_as_string(kvp[0]) == 'doc'
docstring = node_as_string(kvp[1])
log.error "Failed to parse docstring for 3.x Puppet function '#{name}' near #{statement.file}:#{statement.line}." and return nil unless docstring
return Puppet::Util::Docs.scrub(docstring)
end
end
# Log a warning for missing docstring
log.warn "Missing documentation for Puppet function '#{name}' at #{statement.file}:#{statement.line}."
nil
end
def to_puppet_literal(literal)
case literal
when 'nil'
'undef'
when ':default'
'default'
else
literal
end
end
end

View File

@ -0,0 +1,113 @@
require 'puppet-strings/yard/handlers/ruby/base'
require 'puppet-strings/yard/code_objects'
require 'puppet/util/docs'
# Implements the handler for Puppet providers written in Ruby.
class PuppetStrings::Yard::Handlers::Ruby::ProviderHandler < PuppetStrings::Yard::Handlers::Ruby::Base
namespace_only
handles method_call(:provide)
process do
return unless statement.count >= 2
# Check that provide is being called on Puppet::Type.type(<name>)
type_call = statement[0]
return unless type_call.is_a?(YARD::Parser::Ruby::MethodCallNode) && type_call.count >= 3
return unless type_call[0].source == 'Puppet::Type'
return unless type_call[2].source == 'type'
# Extract the type name
type_call_parameters = type_call.parameters(false)
return unless type_call_parameters.count >= 1
type_name = node_as_string(type_call_parameters.first)
raise YARD::Parser::UndocumentableError, "Could not determine the resource type name for the provider defined at #{statement.file}:#{statement.line}." unless type_name
# Register the object
object = PuppetStrings::Yard::CodeObjects::Provider.new(type_name, get_name)
register object
# Extract the docstring
register_provider_docstring object
# Populate the provider data
populate_provider_data object
# Mark the provider 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
private
def get_name
parameters = statement.parameters(false)
raise YARD::Parser::UndocumentableError, "Expected at least one parameter to 'provide' at #{statement.file}:#{statement.line}." if parameters.empty?
name = node_as_string(parameters.first)
raise YARD::Parser::UndocumentableError, "Expected a symbol or string literal for first parameter but found '#{parameters.first.type}' at #{statement.file}:#{statement.line}." unless name
name
end
def register_provider_docstring(object)
# Walk the tree searching for assignments or calls to desc/doc=
statement.traverse do |child|
if child.type == :assign
ivar = child.jump(:ivar)
next unless ivar != child && ivar.source == '@doc'
docstring = node_as_string(child[1])
log.error "Failed to parse docstring for Puppet provider '#{object.name}' (resource type '#{object.type_name}') near #{child.file}:#{child.line}." and return nil unless docstring
register_docstring(object, Puppet::Util::Docs.scrub(docstring), nil)
return nil
elsif child.is_a?(YARD::Parser::Ruby::MethodCallNode)
# Look for a call to a dispatch method with a block
next unless
child.method_name &&
(child.method_name.source == 'desc' || child.method_name.source == 'doc=') &&
child.parameters(false).count == 1
docstring = node_as_string(child.parameters[0])
log.error "Failed to parse docstring for Puppet provider '#{object.name}' (resource type '#{object.type_name}') near #{child.file}:#{child.line}." and return nil unless docstring
register_docstring(object, Puppet::Util::Docs.scrub(docstring), nil)
return nil
end
end
log.warn "Missing a description for Puppet provider '#{object.name}' (resource type '#{object.type_name}') at #{statement.file}:#{statement.line}."
end
def populate_provider_data(object)
# Traverse the block looking for confines/defaults/commands
block = statement.block
return unless block && block.count >= 2
block[1].children.each do |node|
next unless node.is_a?(YARD::Parser::Ruby::MethodCallNode) && node.method_name
method_name = node.method_name.source
parameters = node.parameters(false)
if method_name == 'confine'
# Add a confine to the object
next unless parameters.count >= 1
parameters[0].each do |kvp|
next unless kvp.count == 2
object.add_confine(node_as_string(kvp[0]) || kvp[0].source, node_as_string(kvp[1]) || kvp[1].source)
end
elsif method_name == 'has_feature' || method_name == 'has_features'
# Add the features to the object
parameters.each do |parameter|
object.add_feature(node_as_string(parameter) || parameter.source)
end
elsif method_name == 'defaultfor'
# Add a default to the object
next unless parameters.count >= 1
parameters[0].each do |kvp|
next unless kvp.count == 2
object.add_default(node_as_string(kvp[0]) || kvp[0].source, node_as_string(kvp[1]) || kvp[1].source)
end
elsif method_name == 'commands'
# Add the commands to the object
next unless parameters.count >= 1
parameters[0].each do |kvp|
next unless kvp.count == 2
object.add_command(node_as_string(kvp[0]) || kvp[0].source, node_as_string(kvp[1]) || kvp[1].source)
end
end
end
end
end

View File

@ -0,0 +1,194 @@
require 'puppet-strings/yard/handlers/ruby/base'
require 'puppet-strings/yard/code_objects'
require 'puppet/util'
# Implements the handler for Puppet resource types written in Ruby.
class PuppetStrings::Yard::Handlers::Ruby::TypeHandler < PuppetStrings::Yard::Handlers::Ruby::Base
# The default docstring when ensurable is used without given a docstring.
DEFAULT_ENSURABLE_DOCSTRING = 'The basic property that the resource should be in.'.freeze
namespace_only
handles method_call(:newtype)
process do
# Only accept calls to Puppet::Type
return unless statement.count > 1
module_name = statement[0].source
return unless module_name == 'Puppet::Type' || module_name == 'Type'
object = PuppetStrings::Yard::CodeObjects::Type.new(get_name)
register object
docstring = find_docstring(statement, "Puppet resource type '#{object.name}'")
register_docstring(object, docstring, nil) if docstring
# Populate the parameters/properties/features to the type
populate_type_data(object)
# Set the default namevar
set_default_namevar(object)
# Mark the type 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
private
def get_name
parameters = statement.parameters(false)
raise YARD::Parser::UndocumentableError, "Expected at least one parameter to Puppet::Type.newtype at #{statement.file}:#{statement.line}." if parameters.empty?
name = node_as_string(parameters.first)
raise YARD::Parser::UndocumentableError, "Expected a symbol or string literal for first parameter but found '#{parameters.first.type}' at #{statement.file}:#{statement.line}." unless name
name
end
def find_docstring(node, kind)
# Walk the tree searching for assignments or calls to desc/doc=
node.traverse do |child|
if child.type == :assign
ivar = child.jump(:ivar)
next unless ivar != child && ivar.source == '@doc'
docstring = node_as_string(child[1])
log.error "Failed to parse docstring for #{kind} near #{child.file}:#{child.line}." and return nil unless docstring
return Puppet::Util::Docs.scrub(docstring)
elsif child.is_a?(YARD::Parser::Ruby::MethodCallNode)
# Look for a call to a dispatch method with a block
next unless child.method_name &&
(child.method_name.source == 'desc' || child.method_name.source == 'doc=') &&
child.parameters(false).count == 1
docstring = node_as_string(child.parameters[0])
log.error "Failed to parse docstring for #{kind} near #{child.file}:#{child.line}." and return nil unless docstring
return Puppet::Util::Docs.scrub(docstring)
end
end
log.warn "Missing a description for #{kind} at #{node.file}:#{node.line}."
nil
end
def populate_type_data(object)
# Traverse the block looking for properties/parameters/features
block = statement.block
return unless block && block.count >= 2
block[1].children.each do |node|
next unless node.is_a?(YARD::Parser::Ruby::MethodCallNode) &&
node.method_name
method_name = node.method_name.source
parameters = node.parameters(false)
if method_name == 'newproperty'
# Add a property to the object
next unless parameters.count >= 1
name = node_as_string(parameters[0])
next unless name
object.add_property(create_property(name, node))
elsif method_name == 'newparam'
# Add a parameter to the object
next unless parameters.count >= 1
name = node_as_string(parameters[0])
next unless name
object.add_parameter(create_parameter(name, node))
elsif method_name == 'feature'
# Add a feature to the object
next unless parameters.count >= 2
name = node_as_string(parameters[0])
next unless name
docstring = node_as_string(parameters[1])
next unless docstring
object.add_feature(PuppetStrings::Yard::CodeObjects::Type::Feature.new(name, docstring))
elsif method_name == 'ensurable'
if node.block
property = create_property('ensure', node)
property.docstring = DEFAULT_ENSURABLE_DOCSTRING if property.docstring.empty?
else
property = PuppetStrings::Yard::CodeObjects::Type::Property.new('ensure', DEFAULT_ENSURABLE_DOCSTRING)
property.add('present')
property.add('absent')
property.default = 'present'
end
object.add_property property
end
end
end
def create_parameter(name, node)
parameter = PuppetStrings::Yard::CodeObjects::Type::Parameter.new(name, find_docstring(node, "Puppet resource parameter '#{name}'"))
set_values(node, parameter)
parameter
end
def create_property(name, node)
property = PuppetStrings::Yard::CodeObjects::Type::Property.new(name, find_docstring(node, "Puppet resource property '#{name}'"))
set_values(node, property)
property
end
def set_values(node, object)
return unless node.block && node.block.count >= 2
node.block[1].children.each do |child|
next unless child.is_a?(YARD::Parser::Ruby::MethodCallNode) && child.method_name
method_name = child.method_name.source
parameters = child.parameters(false)
if method_name == 'newvalue'
next unless parameters.count >= 1
object.add(node_as_string(parameters[0]) || parameters[0].source)
elsif method_name == 'newvalues'
parameters.each do |p|
object.add(node_as_string(p) || p.source)
end
elsif method_name == 'aliasvalue'
next unless parameters.count >= 2
object.alias(node_as_string(parameters[0]) || parameters[0].source, node_as_string(parameters[1]) || parameters[1].source)
elsif method_name == 'defaultto'
next unless parameters.count >= 1
object.default = node_as_string(parameters[0]) || parameters[0].source
elsif method_name == 'isnamevar'
object.isnamevar = true
elsif method_name == 'defaultvalues' && object.name == 'ensure'
object.add('present')
object.add('absent')
object.default = 'present'
end
end
if object.is_a? PuppetStrings::Yard::CodeObjects::Type::Parameter
# Process the options for parameter base types
parameters = node.parameters(false)
if parameters.count >= 2
parameters[1].each do |kvp|
next unless kvp.count == 2
next unless node_as_string(kvp[0]) == 'parent'
if kvp[1].source == 'Puppet::Parameter::Boolean'
object.add('true') unless object.values.include? 'true'
object.add('false') unless object.values.include? 'false'
object.add('yes') unless object.values.include? 'yes'
object.add('no') unless object.values.include? 'no'
end
break
end
end
end
end
def set_default_namevar(object)
return unless object.properties || object.parameters
default = nil
if object.properties
object.properties.each do |property|
return nil if property.isnamevar
default = property if property.name == 'name'
end
end
if object.parameters
object.parameters.each do |parameter|
return nil if parameter.isnamevar
default ||= parameter if parameter.name == 'name'
end
end
default.isnamevar = true if default
end
end

View File

@ -0,0 +1,7 @@
# The module for custom YARD parsers.
module PuppetStrings::Yard::Parsers
# The module for custom YARD parsers for the Puppet language.
module Puppet
require 'puppet-strings/yard/parsers/puppet/parser'
end
end

View File

@ -0,0 +1,70 @@
require 'puppet'
require 'puppet/pops'
require 'puppet-strings/yard/parsers/puppet/statement'
# Implements the Puppet language parser.
class PuppetStrings::Yard::Parsers::Puppet::Parser < YARD::Parser::Base
attr_reader :file, :source
# Initializes the parser.
# @param [String] source The source being parsed.
# @param [String] filename The file name of the file being parsed.
# @return [void]
def initialize(source, filename)
@source = source
@file = filename
@visitor = ::Puppet::Pops::Visitor.new(self, 'transform')
end
# Parses the source.
# @return [void]
def parse
begin
@statements ||= (@visitor.visit(::Puppet::Pops::Parser::Parser.new.parse_string(source)) || []).compact
rescue ::Puppet::ParseError => ex
log.error "Failed to parse #{@file}: #{ex.message}"
@statements = []
end
@statements.freeze
self
end
# Gets an enumerator for the statements that were parsed.
# @return Returns an enumerator for the statements that were parsed.
def enumerator
@statements
end
private
def transform_Program(o)
# Cache the lines of the source text; we'll use this to locate comments
@lines = o.source_text.lines.to_a
o.definitions.map { |d| @visitor.visit(d) }
end
def transform_Factory(o)
@visitor.visit(o.current)
end
def transform_HostClassDefinition(o)
statement = PuppetStrings::Yard::Parsers::Puppet::ClassStatement.new(o, @file)
statement.extract_docstring(@lines)
statement
end
def transform_ResourceTypeDefinition(o)
statement = PuppetStrings::Yard::Parsers::Puppet::DefinedTypeStatement.new(o, @file)
statement.extract_docstring(@lines)
statement
end
def transform_FunctionDefinition(o)
statement = PuppetStrings::Yard::Parsers::Puppet::FunctionStatement.new(o, @file)
statement.extract_docstring(@lines)
statement
end
def transform_Object(o)
# Ignore anything else (will be compacted out of the resulting array)
end
end

View File

@ -0,0 +1,146 @@
require 'puppet'
require 'puppet/pops'
module PuppetStrings::Yard::Parsers::Puppet
# Represents the base Puppet language statement.
class Statement
# The pattern for parsing docstring comments.
COMMENT_REGEX = /^\s*#+\s?/
attr_reader :source
attr_reader :file
attr_reader :line
attr_reader :docstring
attr_reader :comments_range
# Initializes the Puppet language statement.
# @param object The Puppet parser model object for the statement.
# @param [String] file The file name of the file containing the statement.
def initialize(object, file)
@file = file
adapter = ::Puppet::Pops::Adapters::SourcePosAdapter.adapt(object)
@source = adapter.extract_text
@line = adapter.line
@comments_range = nil
end
# Extracts the docstring for the statement given the source lines.
# @param [Array<String>] lines The source lines for the file containing the statement.
# @return [void]
def extract_docstring(lines)
comment = []
(0..@line-2).reverse_each do |index|
break unless index <= lines.count
line = lines[index].strip
count = line.size
line.gsub!(COMMENT_REGEX, '')
# Break out if nothing was removed (wasn't a comment line)
break unless line.size < count
comment << line
end
@comments_range = (@line - comment.size - 1..@line - 1)
@docstring = YARD::Docstring.new(comment.reverse.join("\n"))
end
# Shows the first line context for the statement.
# @return [String] Returns the first line context for the statement.
def show
"\t#{@line}: #{first_line}"
end
# Gets the full comments of the statement.
# @return [String] Returns the full comments of the statement.
def comments
@docstring.all
end
# Determines if the comments have hash flag.
# @return [Boolean] Returns true if the comments have a hash flag or false if not.
def comments_hash_flag
false
end
private
def first_line
@source.split(/\r?\n/).first.strip
end
end
# Implements a parameterized statement (a statement that takes parameters).
class ParameterizedStatement < Statement
# Implements a parameter for a parameterized statement.
class Parameter
attr_reader :name
attr_reader :type
attr_reader :value
# Initializes the parameter.
# @param [Puppet::Pops::Model::Parameter] parameter The parameter model object.
def initialize(parameter)
@name = parameter.name
# Take the exact text for the type expression
if parameter.type_expr
adapter = ::Puppet::Pops::Adapters::SourcePosAdapter.adapt(parameter.type_expr)
@type = adapter.extract_text
end
# Take the exact text for the default value expression
if parameter.value
adapter = ::Puppet::Pops::Adapters::SourcePosAdapter.adapt(parameter.value)
@value = adapter.extract_text
end
end
end
attr_reader :parameters
# Initializes the parameterized statement.
# @param object The Puppet parser model object that has parameters.
# @param [String] file The file containing the statement.
def initialize(object, file)
super(object, file)
@parameters = object.parameters.map { |parameter| Parameter.new(parameter) }
end
end
# Implements the Puppet class statement.
class ClassStatement < ParameterizedStatement
attr_reader :name
attr_reader :parent_class
# Initializes the Puppet class statement.
# @param [Puppet::Pops::Model::HostClassDefinition] object The model object for the class statement.
# @param [String] file The file containing the statement.
def initialize(object, file)
super(object, file)
@name = object.name
@parent_class = object.parent_class
end
end
# Implements the Puppet defined type statement.
class DefinedTypeStatement < ParameterizedStatement
attr_reader :name
# Initializes the Puppet defined type statement.
# @param [Puppet::Pops::Model::ResourceTypeDefinition] object The model object for the defined type statement.
# @param [String] file The file containing the statement.
def initialize(object, file)
super(object, file)
@name = object.name
end
end
# Implements the Puppet function statement.
class FunctionStatement < ParameterizedStatement
attr_reader :name
# Initializes the Puppet function statement.
# @param [Puppet::Pops::Model::FunctionDefinition] object The model object for the function statement.
# @param [String] file The file containing the statement.
def initialize(object, file)
super(object, file)
@name = object.name
end
end
end

View File

@ -0,0 +1,6 @@
# The module for custom YARD tags.
module PuppetStrings::Yard::Tags
require 'puppet-strings/yard/tags/parameter_directive'
require 'puppet-strings/yard/tags/property_directive'
require 'puppet-strings/yard/tags/overload_tag'
end

View File

@ -0,0 +1,109 @@
# Implements an overload tag for Puppet functions
#
# This differs from Yard's overload tag in that the signatures are formatted according to Puppet language rules.
class PuppetStrings::Yard::Tags::OverloadTag < YARD::Tags::Tag
attr_reader :parameters, :docstring
# Initializes the overload tag.
# @param [String, Symbol] name The name of the function being overloaded.
# @param [String] docstring The docstring for the overload.
# @return [void]
def initialize(name, docstring)
super(:overload, nil)
@name = name.to_s
@parameters = []
@docstring = YARD::Docstring.new(docstring)
end
# Gets the signature of the overload.
# @return [String] Returns the signature of the overload.
def signature
tags = self.tags(:param)
args = @parameters.map do |parameter|
name, default = parameter
tag = tags.find { |tag| tag.name == name } if tags
type = tag && tag.types ? "#{tag.type} " : 'Any '
prefix = "#{name[0]}" if name.start_with?('*', '&')
name = name[1..-1] if prefix
default = " = #{default}" if default
"#{type}#{prefix}$#{name}#{default}"
end.join(', ')
@name + '(' + args + ')'
end
# Adds a tag to the overload's docstring.
# @param [YARD::Tag] tag The tag to add to the overload's docstring.
# @return [void]
def add_tag(tag)
@docstring.add_tag(tag)
end
# Gets the first tag of the given name.
# @param [String, Symbol] name The name of the tag.
# @return [YARD::Tag] Returns the first tag if found or nil if not found.
def tag(name)
@docstring.tag(name)
end
# Gets all tags or tags of a given name.
# @param [String, Symbol] name The name of the tag to get or nil for all tags.
# @return [Array<Yard::Tag>] Returns an array of tags.
def tags(name = nil)
@docstring.tags(name)
end
# Determines if a tag with the given name is present.
# @param [String, Symbol] name The tag name.
# @return [Boolean] Returns true if there is at least one tag with the given name or false if not.
def has_tag?(name)
@docstring.has_tag?(name)
end
# Sets the object associated with this tag.
# @param [Object] value The object to associate with this tag.
# @return [void]
def object=(value)
super(value)
@docstring.object = value
@docstring.tags.each {|tag| tag.object = value }
end
# Responsible for forwarding method calls to the associated object.
# @param [Symbol] method_name The method being invoked.
# @param [Array] args The args passed to the method.
# @param block The block passed to the method.
# @return Returns what the method call on the object would return.
def method_missing(method_name, *args, &block)
return object.send(method_name, *args, &block) if object.respond_to? method_name
super
end
# Determines if the associated object responds to the give missing method name.
# @param [Symbol, String] method_name The name of the method to check.
# @param [Boolean] include_all True to include all methods in the check or false for only public methods.
# @return [Boolean] Returns true if the object responds to the method or false if not.
def respond_to_missing?(method_name, include_all = false)
object.respond_to?(method_name, include_all) || super
end
# Gets the type of the object associated with this tag.
# @return [Symbol] Returns the type of the object associated with this tag.
def type
object.type
end
# Converts the overload tag to a hash representation.
# @return [Hash] Returns a hash representation of the overload.
def to_hash
hash = {}
hash[:tag_name] = tag_name
hash[:text] = text if text
hash[:signature] = signature
hash[:docstring] = PuppetStrings::Json.docstring_to_hash(docstring) if !docstring.empty?
defaults = Hash[*parameters.select{ |p| !p[1].nil? }.flatten]
hash[:defaults] = defaults unless defaults.empty?
hash[:types] = types if types
hash[:name] = name if name
hash
end
end

View File

@ -0,0 +1,24 @@
require 'puppet-strings/yard/code_objects'
# Implements a parameter directive (e.g. #@!puppet.type.param) for documenting Puppet resource types.
class PuppetStrings::Yard::Tags::ParameterDirective < YARD::Tags::Directive
# Called to invoke the directive.
# @return [void]
def call
return unless object && object.respond_to?(:add_parameter)
# Add a parameter to the resource
parameter = PuppetStrings::Yard::CodeObjects::Type::Parameter.new(tag.name, tag.text)
if tag.types
tag.types.each do |value|
parameter.add(value)
end
end
object.add_parameter parameter
end
# Registers the directive with YARD.
# @return [void]
def self.register!
YARD::Tags::Library.define_directive('puppet.type.param', :with_types_and_name, self)
end
end

View File

@ -0,0 +1,24 @@
require 'puppet-strings/yard/code_objects'
# Implements a parameter directive (e.g. #@!puppet.type.property) for documenting Puppet resource types.
class PuppetStrings::Yard::Tags::PropertyDirective < YARD::Tags::Directive
# Called to invoke the directive.
# @return [void]
def call
return unless object && object.respond_to?(:add_property)
# Add a property to the resource
property = PuppetStrings::Yard::CodeObjects::Type::Property.new(tag.name, tag.text)
if tag.types
tag.types.each do |value|
property.add(value)
end
end
object.add_property property
end
# Registers the directive with YARD.
# @return [void]
def self.register!
YARD::Tags::Library.define_directive('puppet.type.property', :with_types_and_name, self)
end
end

View File

@ -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 %>

View File

@ -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 %>

View File

@ -0,0 +1,10 @@
<% even = false %>
<% @items.each do |item| %>
<li id="object_<%=item.path%>" class="<%= even ? 'even' : 'odd' %>">
<div class="item">
<%= linkify item, h(item.name(false)) %>
<small><%= item.function_type %></small>
</div>
</li>
<% even = !even %>
<% end %>

View File

@ -0,0 +1,10 @@
<% even = false %>
<% @items.each do |item| %>
<li id="object_<%=item.path%>" class="<%= even ? 'even' : 'odd' %>">
<div class="item">
<%= linkify item, h(item.name(true)) %>
<small>Resource type: <em><%=item.type_name%></em></small>
</div>
</li>
<% even = !even %>
<% end %>

View File

@ -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 %>

View File

@ -0,0 +1,64 @@
# 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 Puppet defined type list.
# @return [void]
def generate_puppet_defined_type_list
@items = Registry.all(:puppet_defined_type).sort_by {|dt| dt.name.to_s }
@list_title = 'Defined Type List'
@list_type = 'puppet_defined_type'
generate_list_contents
end
# Generates the searchable Puppet resource type list.
# @return [void]
def generate_puppet_type_list
@items = Registry.all(:puppet_type).sort_by {|t| t.name.to_s }
@list_title = 'Resource Type List'
@list_type = 'puppet_type'
generate_list_contents
end
# Generates the searchable Puppet provider list.
# @return [void]
def generate_puppet_provider_list
@items = Registry.all(:puppet_provider).sort_by {|p| p.name.to_s }
@list_title = 'Provider List'
@list_type = 'puppet_provider'
generate_list_contents
end
# Generates the searchable Puppet function list.
# @return [void]
def generate_puppet_function_list
@items = Registry.all(:puppet_function).sort_by {|f| f.name.to_s }
@list_title = 'Puppet Function List'
@list_type = 'puppet_function'
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

View File

@ -0,0 +1,35 @@
<% 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>
<% elsif obj.type == :puppet_provider %>
<small>(Resource type: <%= obj.type_name %>)</small>
<% elsif obj.type == :puppet_function %>
<small>(<%= obj.function_type %>)</small>
<% end %>
</li>
<% end %>
</ul>
</ul>
<% end %>
</td>
</tr>
</table>
<% end %>

View File

@ -0,0 +1,172 @@
# Initializes the template.
# @return [void]
def init
case object
when '_index.html'
@page_title = options.title
sections :layout, [:index, [:listing, [:classes, :defined_types, :types, :providers, :functions, :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
when PuppetStrings::Yard::CodeObjects::DefinedType
@nav_url = url_for_list('puppet_defined_type')
@page_title = "Defined Type: #{object.name}"
@path = object.path
when PuppetStrings::Yard::CodeObjects::Type
@nav_url = url_for_list('puppet_type')
@page_title = "Resource Type: #{object.name}"
@path = object.path
when PuppetStrings::Yard::CodeObjects::Provider
@nav_url = url_for_list('puppet_provider')
@page_title = "Provider: #{object.name}"
@path = object.path
when PuppetStrings::Yard::CodeObjects::Function
@nav_url = url_for_list('puppet_function')
@page_title = "Puppet Function: #{object.name} (#{object.function_type})"
@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: 'puppet_defined_type',
title: 'Defined Types',
search_title: 'Defined Types',
},
{
type: 'puppet_type',
title: 'Resource Types',
search_title: 'Resource Types'
},
{
type: 'puppet_provider',
title: 'Providers',
search_title: 'Providers'
},
{
type: 'puppet_function',
title: 'Puppet Functions',
search_title: 'Puppet Functions'
},
{
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 defined types section.
# @return [String] Returns the rendered section.
def defined_types
@title = 'Defined Type Listing A-Z'
@objects_by_letter = objects_by_letter(:puppet_defined_type)
erb(:objects)
end
# Renders the types section.
# @return [String] Returns the rendered section.
def types
@title = 'Resource Type Listing A-Z'
@objects_by_letter = objects_by_letter(:puppet_type)
erb(:objects)
end
# Renders the providers section.
# @return [String] Returns the rendered section.
def providers
@title = 'Puppet Provider Listing A-Z'
@objects_by_letter = objects_by_letter(:puppet_provider)
erb(:objects)
end
# Renders the functions section.
# @return [String] Returns the rendered section.
def functions
@title = 'Puppet Function Listing A-Z'
@objects_by_letter = objects_by_letter(:puppet_function)
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

View File

@ -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>

View File

@ -0,0 +1 @@
<h1>Puppet Class: <%= object.name %></h1>

View File

@ -0,0 +1,6 @@
<h2>Overview</h2>
<div class="docstring">
<div class="discussion">
<%= htmlify(object.docstring) %>
</div>
</div>

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1 @@
<h1>Defined Type: <%= object.name %></h1>

View File

@ -0,0 +1,6 @@
<h2>Overview</h2>
<div class="docstring">
<div class="discussion">
<%= htmlify(object.docstring) %>
</div>
</div>

View File

@ -0,0 +1,5 @@
# Initializes the template.
# @return [void]
def init
sections :header, :box_info, :overview, T('tags'), :source
end

View File

@ -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>

View File

@ -0,0 +1,14 @@
<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>
<dl>
<dt>Function type:</dt>
<dd><%= object.function_type %></dd>
</dl>
</div>

View File

@ -0,0 +1 @@
<h1>Puppet Function: <%= object.name %></h1>

View File

@ -0,0 +1,18 @@
<h2>Overview</h2>
<div class="method_details first">
<% unless object.has_tag? :overload %>
<div class="tags overload overload_item">
<span class="overload">
<span class="overload_item">
<span class="signature first" style="margin-left: 0px;"><%= "<strong>#{h(object.signature)}</strong> &#x21d2; #{signature_types(object, false)}" %></span>
</span>
</span>
</div>
<% end %>
<div class="docstring">
<div class="discussion">
<%= htmlify(object.docstring) %>
</div>
</div>
<%= yieldall %>
</div>

View File

@ -0,0 +1,5 @@
# Initializes the template.
# @return [void]
def init
sections :header, :box_info, :overview, [T('tags'), :source]
end

View File

@ -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>

View File

@ -0,0 +1,14 @@
<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>
<dl>
<dt>Resource type:</dt>
<dd><%= linkify(Registry["puppet_types::#{object.type_name}"], object.type_name) %></dd>
</dl>
</div>

View File

@ -0,0 +1,10 @@
<% if @collection && !@collection.empty? %>
<div class="tags">
<p class="tag_title"><%= @title %></p>
<ul>
<% @collection.each do |key, value| %>
<li><tt><%= key %> &mdash; <%= value %></tt></li>
<% end %>
</ul>
</div>
<% end %>

View File

@ -0,0 +1,12 @@
<% if object.features && !object.features.empty? %>
<div class="tags">
<p class="tag_title">Features</p>
<ul>
<% object.features.each do |feature| %>
<li>
<span class="name"><%= feature %></span>
</li>
<% end %>
</ul>
</div>
<% end %>

View File

@ -0,0 +1 @@
<h1>Provider: <%= object.name %></h1>

View File

@ -0,0 +1,6 @@
<h2>Overview</h2>
<div class="docstring">
<div class="discussion">
<%= htmlify(object.docstring) %>
</div>
</div>

View File

@ -0,0 +1,29 @@
# Initializes the template.
# @return [void]
def init
sections :header, :box_info, :overview, T('tags'), :features, :confines, :defaults, :commands
end
# Renders the confines section.
# @return [String] Returns the rendered section.
def confines
@title = 'Confines'
@collection = object.confines
erb(:collection)
end
# Renders the defaults section.
# @return [String] Returns the rendered section.
def defaults
@title = 'Default Provider For'
@collection = object.defaults
erb(:collection)
end
# Renders the commands section.
# @return [String] Returns the rendered section.
def commands
@title = 'Commands'
@collection = object.commands
erb(:collection)
end

View File

@ -0,0 +1,20 @@
<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>
<% if @providers && !@providers.empty? %>
<dl>
<dt>Providers:</dt>
<dd>
<% @providers.each do |provider| %>
<%= linkify(provider, provider.name.to_s) %><br/>
<% end %>
</dd>
</dl>
<% end %>
</div>

View File

@ -0,0 +1,13 @@
<% if object.features && !object.features.empty? %>
<div class="tags">
<p class="tag_title">Features</p>
<ul>
<% object.features.each do |feature| %>
<li>
<span class="name"><%= feature.name %></span>
<% unless feature.docstring.empty? %> &mdash; <%= htmlify_line(feature.docstring) %><% end %>
</li>
<% end %>
</ul>
</div>
<% end %>

View File

@ -0,0 +1 @@
<h1>Resource Type: <%= object.name %></h1>

View File

@ -0,0 +1,6 @@
<h2>Overview</h2>
<div class="docstring">
<div class="discussion">
<%= htmlify(object.docstring) %>
</div>
</div>

View File

@ -0,0 +1,35 @@
<% if @parameters && !@parameters.empty? %>
<div class="tags">
<p class="tag_title"><%= @tag_title %></p>
<ul>
<% @parameters.each do |parameter| %>
<li>
<span class="name"><%= parameter.name + (parameter.isnamevar ? ' (namevar)' : '') %></span>
<% if parameter.default %>
<span class="default"> (defaults to: <em><%= parameter.default %></em>)</span>
<% end %>
<% unless parameter.docstring.empty? %>
<div class="docstring">
<div class="discussion">
<%= htmlify(parameter.docstring) %>
</div>
</div>
<% end %>
<% unless parameter.values.empty? %>
<div>
Supported values:
<ul>
<% parameter.values.each do |value| %>
<li>
<% other = parameter.aliases[value] %>
<%= value %><% if other %> (alias for: <em><%= other %></em>)<% end %>
</li>
<% end %>
</ul>
</div>
<% end %>
</li>
<% end %>
</ul>
</div>
<% end %>

View File

@ -0,0 +1,32 @@
# Initializes the template.
# @return [void]
def init
sections :header, :box_info, :overview, T('tags'), :properties, :parameters, :features
end
# Renders the box_info section.
# @return [String] Returns the rendered section.
def box_info
@providers = PuppetStrings::Yard::CodeObjects::Providers.instance(object.name).children
erb(:box_info)
end
# Renders the properties section.
# @return [String] Returns the rendered section.
def properties
# 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 = 'Properties'
erb(:parameters)
end
# Renders the parameters section.
# @return [String] Returns the rendered section.
def parameters
@parameters = object.parameters || []
@parameters.sort_by! { |p| p.name }
@tag_title = 'Parameters'
erb(:parameters)
end

View File

@ -0,0 +1,12 @@
<% if object.has_tag?(:overload) && object.tags(:overload).any? {|o| !o.docstring.blank? } %>
<p class="tag_title">Overloads:</p>
<ul class="overload">
<% object.tags(:overload).each_with_index do |overload, index| %>
<% next if overload.docstring.blank? %>
<li class="overload_item">
<span class="signature"><%= "<strong>#{h(overload.signature)}</strong> &#x21d2; #{signature_types(overload, false)}" %></span>
<%= yieldall :object => overload %>
</li>
<% end %>
</ul>
<% end %>

View File

@ -0,0 +1,15 @@
# 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 ||
object.type == :puppet_defined_type ||
object.type == :puppet_function
end
# Renders the overload section.
# @return [String] Returns the rendered section.
def overload
erb(if object.type == :puppet_function then :puppet_overload else :overload end)
end

View File

@ -1,4 +1,5 @@
require 'puppet/application/face_base'
# Implements the 'puppet strings' application.
class Puppet::Application::Strings < Puppet::Application::FaceBase
end

View File

@ -1,64 +1,105 @@
require 'puppet/face'
require 'puppet_x/puppetlabs/strings/yard/tags/directives'
# Implements the 'puppet strings' interface.
Puppet::Face.define(:strings, '0.0.1') do
summary "Generate Puppet documentation with YARD."
summary 'Generate Puppet documentation with YARD.'
# Ensures that the user has the needed features to use puppet strings
def check_required_features
unless Puppet.features.yard?
raise RuntimeError, "The 'yard' gem must be installed in order to use this face."
end
unless Puppet.features.rgen?
raise RuntimeError, "The 'rgen' gem must be installed in order to use this face."
end
if RUBY_VERSION.match(/^1\.8/)
raise RuntimeError, "This face requires Ruby 1.9 or greater."
end
end
action(:yardoc) do
action(:generate) do
default
option "--emit-json-stdout" do
summary "Print json representation of the documentation to stdout"
option '--emit-json-stdout' do
summary 'Print JSON representation of the documentation to stdout.'
end
option "--emit-json FILE" do
summary "Write json representation of the documentation to FILE"
option '--emit-json FILE' do
summary 'Write JSON representation of the documentation to the given file.'
end
option '--markup FORMAT' do
summary "The markup format to use for docstring text (defaults to 'markdown')."
end
summary "Generate YARD documentation from files."
arguments "[manifest_file.pp ...]"
summary 'Generate documentation from files.'
arguments '[[search_pattern] ...]'
when_invoked do |*args|
check_required_features
require 'puppet_x/puppetlabs/strings/util'
require 'puppet-strings'
PuppetX::PuppetLabs::Strings::Util.generate(args)
# Puppet prints the return value of the action. The return value of this
# action is that of the yardoc_actions invocation, which is the boolean
# "true". This clutters the statistics yard prints, so instead return the
# empty string. Note an extra newline will also be printed.
""
PuppetStrings::generate(
args.count > 1 ? args[0..-2] : PuppetStrings::DEFAULT_SEARCH_PATTERNS,
build_generate_options(args.last)
)
nil
end
end
# NOTE: Modeled after the `yard gems` command which builds doc indicies
# (.yardoc directories) for Ruby Gems. Currently lacks the fine-grained
# control over where these indicies are created and just dumps them in the
# module roots.
action(:server) do
summary "Serve YARD documentation for modules."
option '--markup FORMAT' do
summary "The markup format to use for docstring text (defaults to 'markdown')."
end
summary 'Runs a local documentation server for the modules in the current Puppet environment.'
arguments '[[module_name] ...]'
when_invoked do |*args|
check_required_features
require 'puppet_x/puppetlabs/strings/util'
require 'puppet-strings'
PuppetX::PuppetLabs::Strings::Util.serve(args)
modules = args.count > 1 ? args[0..-2] : []
# Generate documentation for all (or the given) modules
module_docs = []
environment = Puppet.lookup(:current_environment)
environment.modules.each do |mod|
next unless modules.empty? || modules.include?(mod.name)
db = File.join(mod.path, '.yardoc')
patterns = PuppetStrings::DEFAULT_SEARCH_PATTERNS.map do |p|
File.join(mod.path, p)
end
puts "Generating documentation for Puppet module '#{mod.name}'."
PuppetStrings.generate(patterns, build_generate_options(args.last, '--db', db))
# Clear the registry so that the next call to generate has a clean database
YARD::Registry.clear
module_docs << mod.name
module_docs << db
end
if module_docs.empty?
puts 'No Puppet modules were found to serve documentation for.'
return
end
puts 'Starting YARD documentation server.'
PuppetStrings::run_server('-m', *module_docs)
nil
end
end
# Checks that the required features are installed.
# @return [void]
def check_required_features
raise RuntimeError, "The 'yard' gem must be installed in order to use this face." unless Puppet.features.yard?
raise RuntimeError, "The 'rgen' gem must be installed in order to use this face." unless Puppet.features.rgen?
raise RuntimeError, 'This face requires Ruby 1.9 or greater.' if RUBY_VERSION =~ /^1\.8/
end
# Builds the options to PuppetStrings.generate.
# @param [Hash] options The Puppet face options hash.
# @param [Array] yard_args The additional arguments to pass to YARD.
# @return [Hash] Returns the PuppetStrings.generate options hash.
def build_generate_options(options = nil, *yard_args)
generate_options = {}
generate_options[:debug] = Puppet[:debug]
generate_options[:backtrace] = Puppet[:trace]
generate_options[:yard_args] = yard_args unless yard_args.empty?
if options
markup = options[:markup]
generate_options[:markup] = markup if markup
json_file = options[:emit_json]
generate_options[:json] = json_file if json_file
generate_options[:json] = nil if options[:emit_json_stdout]
end
generate_options
end
end

View File

@ -1,64 +0,0 @@
require 'puppet'
require 'puppet/pops'
require 'puppet/util/docs'
require 'yard'
module PuppetX
end
# Nothing to see here except forward declarations.
module PuppetX::PuppetLabs
module Strings
# This submodule contains bits that operate on the Pops module produced by
# the Future parser.
module Pops
require 'puppet_x/puppetlabs/strings/pops/yard_statement'
require 'puppet_x/puppetlabs/strings/pops/yard_transformer'
end
# This submodule contains bits that interface with the YARD plugin system.
module YARD
require 'puppet_x/puppetlabs/strings/yard/monkey_patches'
require 'puppet_x/puppetlabs/strings/yard/parser'
module Tags
require 'puppet_x/puppetlabs/strings/yard/tags/directives'
end
# This submodule contains code objects which are used to represent relevant
# aspects of puppet code in YARD's Registry
module CodeObjects
require 'puppet_x/puppetlabs/strings/yard/code_objects/puppet_namespace_object'
require 'puppet_x/puppetlabs/strings/yard/code_objects/method_object'
require 'puppet_x/puppetlabs/strings/yard/code_objects/defined_type_object'
require 'puppet_x/puppetlabs/strings/yard/code_objects/host_class_object'
require 'puppet_x/puppetlabs/strings/yard/code_objects/type_object'
require 'puppet_x/puppetlabs/strings/yard/code_objects/provider_object'
end
# This submodule contains handlers which are used to extract relevant data about
# puppet code from the ASTs produced by the Ruby and Puppet parsers
module Handlers
# This utility library contains some tools for working with Puppet docstrings
require 'puppet_x/puppetlabs/strings/yard/handlers/base'
require 'puppet_x/puppetlabs/strings/yard/handlers/defined_type_handler'
require 'puppet_x/puppetlabs/strings/yard/handlers/host_class_handler'
require 'puppet_x/puppetlabs/strings/yard/handlers/puppet_3x_function_handler'
require 'puppet_x/puppetlabs/strings/yard/handlers/puppet_4x_function_handler'
require 'puppet_x/puppetlabs/strings/yard/handlers/type_handler'
require 'puppet_x/puppetlabs/strings/yard/handlers/provider_handler'
end
::YARD::Parser::SourceParser.register_parser_type(:puppet,
PuppetX::PuppetLabs::Strings::YARD::PuppetParser,
['pp'])
::YARD::Handlers::Processor.register_handler_namespace(:puppet,
PuppetX::PuppetLabs::Strings::YARD::Handlers)
# FIXME: Might not be the best idea to have the template code on the Ruby
# LOAD_PATH as the contents of this directory really aren't library code.
::YARD::Templates::Engine.register_template_path(
File.join(File.dirname(__FILE__), 'strings', 'yard', 'templates'))
end
end
end

View File

@ -1,92 +0,0 @@
require 'puppet_x/puppetlabs/strings'
class PuppetX::PuppetLabs::Strings::Actions
# Creates a new instance of the Actions class by determining
# whether or not debug and backtrace arguments should be sent
# to YARD
def initialize(puppet_debug, puppet_backtrace)
@debug = puppet_debug
@backtrace = puppet_backtrace
end
# Holds the name of a module and the file path to its YARD index
ModuleIndex = Struct.new(:name, :index_path)
# Maps things like the Puppet `--debug` flag to YARD options.
def merge_puppet_args!(yard_args)
yard_args.unshift '--debug' if @debug
yard_args.unshift '--backtrace' if @backtrace
yard_args
end
# Builds doc indices (.yardoc directories) for modules.
# Currently lacks the fine-grained control over where these
# indices are created and just dumps them in the module roots.
#
# @return [Array<Module>] the modules to be documented
#
# @param [Array<String>] module_names a list of the module source files
# @param [Array<String>] module_sourcefiles default list of module files
def index_documentation_for_modules(module_names, module_sourcefiles)
# NOTE: The return value of the `module` Face seems to have changed in
# 3.6.x. This part of the code will blow up if run under an earlier
# version of Puppet.
modules = Puppet::Face[:module, :current].list
module_list = modules[:modules_by_path].values.flatten
module_list.select! {|m| module_names.include? m.name} unless module_names.empty?
# Invoke `yardoc` with -n so that it doesn't generate any HTML output but
# does build a `.yardoc` index that other tools can generate output from.
yard_args = %w[--no-stats -n] + module_sourcefiles
merge_puppet_args!(yard_args)
module_list.each do |m|
Dir.chdir(m.path) do
YARD::CLI::Yardoc.run(*yard_args)
# Clear the global Registry so that objects from one module don't
# bleed into the next.
YARD::Registry.clear
end
end
end
# Extracts the needed information of the modules we're documenting
#
# @return [Array<ModuleIndex>] An array of representation of the modules
# to produce documentation for. Each ModuleIndex contains the module name
# and the path to its YARD index
#
# @param [Array<String>] module_list a list of the module source files
def generate_module_tuples(module_list)
module_list.map do |mod|
name = (mod.forge_name || mod.name).gsub('/', '-')
yard_index = File.join(mod.path, '.yardoc')
ModuleIndex.new(name, yard_index)
end
end
# Hands off the needed information to YARD so it may
# serve the documentation
#
# @param [Array<String>] yard_args a list of all the arguments to pass to YARD
def serve_documentation(*yard_args)
merge_puppet_args!(yard_args)
YARD::CLI::Server.run(*yard_args)
end
# Hands off the needed information to YARD so it may
# generate the documentation
#
# @param [Array<String>] yard_args a list of all the arguments to pass to YARD
def generate_documentation(*yard_args)
merge_puppet_args!(yard_args)
YARD::CLI::Yardoc.run(*yard_args)
end
end

View File

@ -1,79 +0,0 @@
require 'ostruct'
# 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({})
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.
# NOTE: We cannot just trim any amount of whitespace as indentation
# is sometimes significant in markdown. We would need a real parser.
# Comments which begin with some whitespace, a hash and then some
# tabs and spaces should be scrubbed. Comments which just have a
# solitary hash then a newline should keep that newline since newlines
# are significant in markdown.
comments.unshift line.gsub(/^\s*#[\t ]/, '').gsub(/^\s*#\n/, "\n")
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

View File

@ -1,47 +0,0 @@
# 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
obj
end
# Catch-all visitor.
def transform_Positioned(o)
PuppetX::PuppetLabs::Strings::Pops::YARDStatement.new(o)
end
# nil in... nil out!
def transform_NilClass(o)
nil
end
end

View File

@ -1,65 +0,0 @@
require 'puppet_x/puppetlabs/strings/actions'
module PuppetX::PuppetLabs::Strings::Util
MODULE_SOURCEFILES = ['manifests/**/*.pp', 'lib/**/*.rb']
def self.generate(args = [])
yardoc_actions = PuppetX::PuppetLabs::Strings::Actions.new(Puppet[:debug], Puppet[:trace])
# The last element of the argument array should be the options hash.
# We don't have any options yet, so for now just pop the hash off and
# toss it.
#
# NOTE: The Puppet Face will throw 'unrecognized option' errors if any
# YARD options are passed to it. The best way to approach this problem is
# by using the `.yardopts` file. YARD will autoload any options placed in
# that file.
options = args.pop
YARD::Config.options = YARD::Config.options.merge(options) if options
# For now, assume the remaining positional args are a list of manifest
# and ruby files to parse.
yard_args = (args.empty? ? MODULE_SOURCEFILES : args)
# If json is going to be emitted to stdout, suppress statistics.
if options && options[:emit_json_stdout]
yard_args.push('--no-stats')
end
# This line monkeypatches yard's progress indicator so it doesn't write
# all over the terminal. This should definitely not be in real code, but
# it's very handy for debugging with pry
#class YARD::Logger; def progress(*args); end; end
YARD::Tags::Library.define_directive("puppet.type.param",
:with_types_and_name,
PuppetX::PuppetLabs::Strings::YARD::Tags::PuppetTypeParameterDirective)
yardoc_actions.generate_documentation(*yard_args)
end
def self.serve(args = [])
server_actions = PuppetX::PuppetLabs::Strings::Actions.new(Puppet[:debug], Puppet[:trace])
args.pop
module_names = args
# FIXME: This is pretty inefficient as it forcibly re-generates the YARD
# indicies each time the server is started. However, it ensures things are
# generated properly.
module_list = server_actions.index_documentation_for_modules(module_names, MODULE_SOURCEFILES)
module_tuples = server_actions.generate_module_tuples(module_list)
module_tuples.map! do |mod|
[mod[:name], mod[:index_path]]
end
# The `-m` flag means a list of name/path pairs will follow. The name is
# used as the module name and the path indicates which `.yardoc` index to
# generate documentation from.
yard_args = %w[-m -q] + module_tuples.flatten
server_actions.serve_documentation(*yard_args)
end
end

View File

@ -1,33 +0,0 @@
require 'json'
class PuppetX::PuppetLabs::Strings::YARD::CodeObjects::DefinedTypeObject < PuppetX::PuppetLabs::Strings::YARD::CodeObjects::PuppetNamespaceObject
# A list of parameters attached to this class.
# @return [Array<Array(String, String)>]
attr_accessor :parameters
attr_accessor :type_info
def to_s
name.to_s
end
def to_json(*a)
{
"name" => @name,
"file" => file,
"line" => line,
"parameters" => Hash[@parameters],
"docstring" => Puppet::Util::Docs.scrub(@docstring),
"signatures" => @type_info.map do |signature|
signature.map do |key, value|
{
"name" => key,
"type" => value,
}
end
end,
"examples" => self.tags.map do |tag|
tag.text if tag.tag_name == 'example'
end.compact,
}.to_json(*a)
end
end

View File

@ -1,22 +0,0 @@
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?(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

View File

@ -1,62 +0,0 @@
class YARD::CodeObjects::MethodObject
# Override to_s and to_json methods in Yard's MethodObject so that they
# return output formatted as I like for puppet 3x and 4x methods.
def to_s
if self[:puppet_4x_function] || self[:puppet_3x_function]
name.to_s
else
super
end
end
def to_json(*a)
if self[:puppet_4x_function]
{
"name" => @name,
"file" => file,
"line" => line,
"function_api_version" => 4,
"docstring" => Puppet::Util::Docs.scrub(@docstring),
"examples" => self.tags.map do |tag|
tag.text if tag.tag_name == 'example'
end.compact,
"documented_params" => @parameters.map do |tuple|
{
"name" => tuple[0],
"type" => tuple[1],
}
end,
"signatures" => @type_info.map do |signature|
signature.map do |key, value|
{
"name" => key,
"type" => value,
}
end
end,
}.to_json(*a)
elsif self[:puppet_3x_function]
{
"name" => @name,
"file" => file,
"line" => line,
"function_api_version" => 3,
"docstring" => Puppet::Util::Docs.scrub(@docstring),
"documented_params" => @parameters.map do |tuple|
{
"name" => tuple[0],
"type" => tuple[1],
}
end,
"examples" => self.tags.map do |tag|
tag.text if tag.tag_name == 'example'
end.compact,
}.to_json(*a)
else
super
end
end
end

View File

@ -1,24 +0,0 @@
class PuppetX::PuppetLabs::Strings::YARD::CodeObjects::ProviderObject < PuppetX::PuppetLabs::Strings::YARD::CodeObjects::PuppetNamespaceObject
# A list of parameters attached to this class.
# @return [Array<Array(String, String)>]
attr_accessor :parameters
def to_json(*a)
{
"name" => @name,
"type_name" => @type_name,
"file" => file,
"line" => line,
"docstring" => Puppet::Util::Docs.scrub(@docstring),
"commands" => @commands,
"confines" => @confines,
"defaults" => @defaults,
"features" => @features,
"examples" => self.tags.map do |tag|
tag.text if tag.tag_name == 'example'
end.compact,
}.to_json(*a)
end
end

View File

@ -1,48 +0,0 @@
class PuppetX::PuppetLabs::Strings::YARD::CodeObjects::PuppetNamespaceObject < YARD::CodeObjects::NamespaceObject
attr_accessor :type_info
# 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
def to_s
name.to_s
end
def to_json(*a)
{
"name" => @name,
"file" => file,
"line" => line,
"docstring" => @docstring,
"examples" => self.tags.map do |tag|
tag.text if tag.tag_name == 'example'
end.compact,
}.to_json(*a)
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

View File

@ -1,42 +0,0 @@
class PuppetX::PuppetLabs::Strings::YARD::CodeObjects::TypeObject < PuppetX::PuppetLabs::Strings::YARD::CodeObjects::PuppetNamespaceObject
# A list of parameters attached to this class.
# @return [Array<Array(String, String)>]
attr_accessor :parameters
def to_json(*a)
{
"name" => @name,
"file" => file,
"line" => line,
"docstring" => Puppet::Util::Docs.scrub(@docstring),
"parameters" => @parameter_details.map do |obj|
{
"allowed_values" => obj[:allowed_values] ? obj[:allowed_values].flatten : [],
"default" => obj[:default],
"docstring" => Puppet::Util::Docs.scrub(obj[:desc] || ''),
"namevar" => obj[:namevar],
"name" => obj[:name],
}
end,
"examples" => self.tags.map do |tag|
tag.text if tag.tag_name == 'example'
end.compact,
"properties" => @property_details.map do |obj|
{
"allowed_values" => obj[:allowed_values] ? obj[:allowed_values].flatten : [],
"default" => obj[:default],
"docstring" => Puppet::Util::Docs.scrub(obj[:desc] || ''),
"name" => obj[:name],
}
end,
"features" => @features.map do |obj|
{
"docstring" => Puppet::Util::Docs.scrub(obj[:desc] || ''),
"methods" => obj[:methods],
"name" => obj[:name],
}
end,
}.to_json(*a)
end
end

View File

@ -1,40 +0,0 @@
require 'yard'
require 'puppet_x/puppetlabs/strings'
# Patch the regular expression used to match namespaces
# so it will allow namespace segments that begin with
# both uppercase and lowercase letters (i.e. both
# Puppet::Namespace and puppet::namespace)
YARD::CodeObjects.send(:remove_const, :CONSTANTMATCH)
YARD::CodeObjects::CONSTANTMATCH = /[a-zA-Z]\w*/
# This is a temporary hack until a new version of YARD is
# released. We submitted a patch to YARD to add the
# CONSTANTSTART constant so that we could patch it and
# successfully match our own namesapces. However until
# the next version of the YARD gem is released, we must
# patch the problematic method itself as it is not yet
# using the added variable
if defined? YARD::CodeObjects::CONSTANTSTART
YARD::CodeObjects.send(:remove_const, :CONSTANTSTART)
YARD::CodeObjects::CONSTANTSTART = /^[a-zA-Z]/
else
class YARD::CodeObjects::Proxy
def proxy_path
if @namespace.root?
(@imethod ? YARD::CodeObjects::ISEP : "") + name.to_s
elsif @origname
if @origname =~ /^[a-zA-Z]/
@origname
else
[namespace.path, @origname].join
end
elsif name.to_s =~ /^[a-zA-Z]/ # const
name.to_s
else # class meth?
[namespace.path, name.to_s].join(YARD::CodeObjects::CSEP)
end
end
end
end

View File

@ -1,13 +0,0 @@
require 'puppet_x/puppetlabs/strings/yard/core_ext/yard'
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

Some files were not shown because too many files have changed in this diff Show More