diff --git a/.gitignore b/.gitignore index 936d40a..93e8f9a 100644 --- a/.gitignore +++ b/.gitignore @@ -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/ diff --git a/.rubocop.yml b/.rubocop.yml index 56eb8da..1a14db7 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -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 diff --git a/.yardopts b/.yardopts index e30cb06..a0729ad 100644 --- a/.yardopts +++ b/.yardopts @@ -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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 413df44..0e91af0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/COMMITTERS.md b/COMMITTERS.md index b7a6b77..03a2a64 100644 --- a/COMMITTERS.md +++ b/COMMITTERS.md @@ -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. * Don’t 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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ed3c7c3..f366645 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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 diff --git a/Gemfile b/Gemfile index bb48c59..756f11d 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,7 @@ gemspec gem 'rgen' gem 'redcarpet' -gem "yard", "~> 0.8.7" +gem 'yard', '~> 0.9.5' puppetversion = ENV['PUPPET_VERSION'] diff --git a/JSON.md b/JSON.md new file mode 100644 index 0000000..3029de9 --- /dev/null +++ b/JSON.md @@ -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 `: 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" + } + ] +} +``` + diff --git a/README.md b/README.md index 37acad8..de38923 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/Rakefile b/Rakefile index 459dda0..fa3dcb1 100644 --- a/Rakefile +++ b/Rakefile @@ -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 diff --git a/spec/unit/puppet/examples/test/lib/puppet/functions/4x_function.rb b/acceptance/fixtures/modules/test/lib/puppet/functions/4x_function.rb similarity index 100% rename from spec/unit/puppet/examples/test/lib/puppet/functions/4x_function.rb rename to acceptance/fixtures/modules/test/lib/puppet/functions/4x_function.rb diff --git a/spec/unit/puppet/examples/test/lib/puppet/parser/functions/function3x.rb b/acceptance/fixtures/modules/test/lib/puppet/parser/functions/function3x.rb similarity index 100% rename from spec/unit/puppet/examples/test/lib/puppet/parser/functions/function3x.rb rename to acceptance/fixtures/modules/test/lib/puppet/parser/functions/function3x.rb diff --git a/spec/unit/puppet/examples/test/manifests/init.pp b/acceptance/fixtures/modules/test/manifests/init.pp similarity index 100% rename from spec/unit/puppet/examples/test/manifests/init.pp rename to acceptance/fixtures/modules/test/manifests/init.pp diff --git a/spec/unit/puppet/examples/test/manifests/triple_nested_classes.pp b/acceptance/fixtures/modules/test/manifests/triple_nested_classes.pp similarity index 100% rename from spec/unit/puppet/examples/test/manifests/triple_nested_classes.pp rename to acceptance/fixtures/modules/test/manifests/triple_nested_classes.pp diff --git a/spec/unit/puppet/examples/test/metadata.json b/acceptance/fixtures/modules/test/metadata.json similarity index 100% rename from spec/unit/puppet/examples/test/metadata.json rename to acceptance/fixtures/modules/test/metadata.json diff --git a/acceptance/running_strings_generate.rb b/acceptance/running_strings_generate.rb new file mode 100644 index 0000000..fa13c63 --- /dev/null +++ b/acceptance/running_strings_generate.rb @@ -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 diff --git a/json_dom.md b/json_dom.md deleted file mode 100644 index 3274307..0000000 --- a/json_dom.md +++ /dev/null @@ -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. diff --git a/lib/puppet-strings.rb b/lib/puppet-strings.rb new file mode 100644 index 0000000..410d92b --- /dev/null +++ b/lib/puppet-strings.rb @@ -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] 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] :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] args The arguments to YARD. + def self.run_server(*args) + require 'puppet-strings/yard' + PuppetStrings::Yard.setup! + + YARD::CLI::Server.run(*args) + end +end diff --git a/lib/puppet-strings/json.rb b/lib/puppet-strings/json.rb new file mode 100644 index 0000000..d6d2e42 --- /dev/null +++ b/lib/puppet-strings/json.rb @@ -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 diff --git a/lib/puppet-strings/rake_tasks/generate.rb b/lib/puppet-strings/rake_tasks/generate.rb deleted file mode 100644 index e435c41..0000000 --- a/lib/puppet-strings/rake_tasks/generate.rb +++ /dev/null @@ -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] module_resourcefiles globs used to specify which files to document. - # Defaults to {PuppetX::PuppetLabs::Strings::Util::MODULE_SOURCEFILES} - # @attr [Array] excludes a list of paths or patterns of files and directories to ignore. - # @attr [Array, 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] 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] prefix_paths an array with paths - # @param [Array] 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] args Arguments. Last element should be a Hash. - def execute_task(args) - PuppetX::PuppetLabs::Strings::Util.generate(args) - end - end - end -end diff --git a/lib/puppet-strings/tasks.rb b/lib/puppet-strings/tasks.rb new file mode 100644 index 0000000..255ff60 --- /dev/null +++ b/lib/puppet-strings/tasks.rb @@ -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 diff --git a/lib/puppet-strings/tasks/generate.rb b/lib/puppet-strings/tasks/generate.rb new file mode 100644 index 0000000..616deda --- /dev/null +++ b/lib/puppet-strings/tasks/generate.rb @@ -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 + diff --git a/lib/puppet-strings/rake_tasks.rb b/lib/puppet-strings/tasks/gh_pages.rb similarity index 57% rename from lib/puppet-strings/rake_tasks.rb rename to lib/puppet-strings/tasks/gh_pages.rb index eda4b5c..c6b5262 100644 --- a/lib/puppet-strings/rake_tasks.rb +++ b/lib/puppet-strings/tasks/gh_pages.rb @@ -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', diff --git a/lib/puppet-strings/yard.rb b/lib/puppet-strings/yard.rb new file mode 100644 index 0000000..726b062 --- /dev/null +++ b/lib/puppet-strings/yard.rb @@ -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 diff --git a/lib/puppet-strings/yard/code_objects.rb b/lib/puppet-strings/yard/code_objects.rb new file mode 100644 index 0000000..076eb90 --- /dev/null +++ b/lib/puppet-strings/yard/code_objects.rb @@ -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 diff --git a/lib/puppet-strings/yard/code_objects/base.rb b/lib/puppet-strings/yard/code_objects/base.rb new file mode 100644 index 0000000..ba9840e --- /dev/null +++ b/lib/puppet-strings/yard/code_objects/base.rb @@ -0,0 +1,14 @@ +# Implements the base code object. +class PuppetStrings::Yard::CodeObjects::Base < YARD::CodeObjects::NamespaceObject + # Allocates a new code object. + # @param [Array] args The arguments to initialize the code object with. + # @return Returns the code object. + def self.new(*args) + # Skip the super class' implementation because it detects :: in names and this will cause namespaces in the output we don't want + object = Object.class.instance_method(:new).bind(self).call(*args) + existing = YARD::Registry.at(object.path) + object = existing if existing && existing.class == self + yield(object) if block_given? + object + end +end diff --git a/lib/puppet-strings/yard/code_objects/class.rb b/lib/puppet-strings/yard/code_objects/class.rb new file mode 100644 index 0000000..121ebd2 --- /dev/null +++ b/lib/puppet-strings/yard/code_objects/class.rb @@ -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 diff --git a/lib/puppet-strings/yard/code_objects/defined_type.rb b/lib/puppet-strings/yard/code_objects/defined_type.rb new file mode 100644 index 0000000..e9088c1 --- /dev/null +++ b/lib/puppet-strings/yard/code_objects/defined_type.rb @@ -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 diff --git a/lib/puppet-strings/yard/code_objects/function.rb b/lib/puppet-strings/yard/code_objects/function.rb new file mode 100644 index 0000000..f295d29 --- /dev/null +++ b/lib/puppet-strings/yard/code_objects/function.rb @@ -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 diff --git a/lib/puppet-strings/yard/code_objects/group.rb b/lib/puppet-strings/yard/code_objects/group.rb new file mode 100644 index 0000000..f3becac --- /dev/null +++ b/lib/puppet-strings/yard/code_objects/group.rb @@ -0,0 +1,30 @@ +require 'puppet-strings/yard/code_objects/base' + +# Implements the base class for "groups". +# +# A group behaves like a YARD namespace object, but displays differently in the HTML output. +class PuppetStrings::Yard::CodeObjects::Group < PuppetStrings::Yard::CodeObjects::Base + # Gets the singleton instance of the group. + # @param [Symbol] key The key to lookup the group for. + # @return Returns the singleton instance of the group. + def self.instance(key) + instance = P(:root, key) + return instance unless instance.is_a?(YARD::CodeObjects::Proxy) + instance = self.new(:root, key) + instance.visibility = :hidden + P(:root).children << instance + instance + end + + # Gets the path to the group. + # @return [String] Returns the path to the group. + def path + @name.to_s + end + + # Gets the type of the group. + # @return [Symbol] Returns the type of the group. + def type + @name + end +end diff --git a/lib/puppet-strings/yard/code_objects/provider.rb b/lib/puppet-strings/yard/code_objects/provider.rb new file mode 100644 index 0000000..06a4f1b --- /dev/null +++ b/lib/puppet-strings/yard/code_objects/provider.rb @@ -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 diff --git a/lib/puppet-strings/yard/code_objects/type.rb b/lib/puppet-strings/yard/code_objects/type.rb new file mode 100644 index 0000000..384ccf8 --- /dev/null +++ b/lib/puppet-strings/yard/code_objects/type.rb @@ -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 diff --git a/lib/puppet-strings/yard/handlers.rb b/lib/puppet-strings/yard/handlers.rb new file mode 100644 index 0000000..8168343 --- /dev/null +++ b/lib/puppet-strings/yard/handlers.rb @@ -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 diff --git a/lib/puppet-strings/yard/handlers/puppet/base.rb b/lib/puppet-strings/yard/handlers/puppet/base.rb new file mode 100644 index 0000000..8bcd8d2 --- /dev/null +++ b/lib/puppet-strings/yard/handlers/puppet/base.rb @@ -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 diff --git a/lib/puppet-strings/yard/handlers/puppet/class_handler.rb b/lib/puppet-strings/yard/handlers/puppet/class_handler.rb new file mode 100644 index 0000000..9415bc5 --- /dev/null +++ b/lib/puppet-strings/yard/handlers/puppet/class_handler.rb @@ -0,0 +1,23 @@ +require 'puppet-strings/yard/handlers/puppet/base' +require 'puppet-strings/yard/parsers' +require 'puppet-strings/yard/code_objects' + +# Implements the handler for Puppet classes. +class PuppetStrings::Yard::Handlers::Puppet::ClassHandler < PuppetStrings::Yard::Handlers::Puppet::Base + handles PuppetStrings::Yard::Parsers::Puppet::ClassStatement + + process do + # Register the object + object = PuppetStrings::Yard::CodeObjects::Class.new(statement) + register object + + # Log a warning if missing documentation + log.warn "Missing documentation for Puppet class '#{object.name}' at #{statement.file}:#{statement.line}." if object.docstring.empty? + + # Set the parameter types + set_parameter_types(object) + + # Mark the class as public if it doesn't already have an api tag + object.add_tag YARD::Tags::Tag.new(:api, 'public') unless object.has_tag? :api + end +end diff --git a/lib/puppet-strings/yard/handlers/puppet/defined_type_handler.rb b/lib/puppet-strings/yard/handlers/puppet/defined_type_handler.rb new file mode 100644 index 0000000..4786c24 --- /dev/null +++ b/lib/puppet-strings/yard/handlers/puppet/defined_type_handler.rb @@ -0,0 +1,23 @@ +require 'puppet-strings/yard/handlers/puppet/base' +require 'puppet-strings/yard/parsers' +require 'puppet-strings/yard/code_objects' + +# Implements the handler for Puppet 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 diff --git a/lib/puppet-strings/yard/handlers/puppet/function_handler.rb b/lib/puppet-strings/yard/handlers/puppet/function_handler.rb new file mode 100644 index 0000000..fbb9f56 --- /dev/null +++ b/lib/puppet-strings/yard/handlers/puppet/function_handler.rb @@ -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 diff --git a/lib/puppet-strings/yard/handlers/ruby/base.rb b/lib/puppet-strings/yard/handlers/ruby/base.rb new file mode 100644 index 0000000..d2fb041 --- /dev/null +++ b/lib/puppet-strings/yard/handlers/ruby/base.rb @@ -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 = /^ 1 + end + + source + end + end +end diff --git a/lib/puppet-strings/yard/handlers/ruby/function_handler.rb b/lib/puppet-strings/yard/handlers/ruby/function_handler.rb new file mode 100644 index 0000000..65c1420 --- /dev/null +++ b/lib/puppet-strings/yard/handlers/ruby/function_handler.rb @@ -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 diff --git a/lib/puppet-strings/yard/handlers/ruby/provider_handler.rb b/lib/puppet-strings/yard/handlers/ruby/provider_handler.rb new file mode 100644 index 0000000..5d4565e --- /dev/null +++ b/lib/puppet-strings/yard/handlers/ruby/provider_handler.rb @@ -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() + 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 diff --git a/lib/puppet-strings/yard/handlers/ruby/type_handler.rb b/lib/puppet-strings/yard/handlers/ruby/type_handler.rb new file mode 100644 index 0000000..86ee94e --- /dev/null +++ b/lib/puppet-strings/yard/handlers/ruby/type_handler.rb @@ -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 diff --git a/lib/puppet-strings/yard/parsers.rb b/lib/puppet-strings/yard/parsers.rb new file mode 100644 index 0000000..6916fee --- /dev/null +++ b/lib/puppet-strings/yard/parsers.rb @@ -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 diff --git a/lib/puppet-strings/yard/parsers/puppet/parser.rb b/lib/puppet-strings/yard/parsers/puppet/parser.rb new file mode 100644 index 0000000..a74675b --- /dev/null +++ b/lib/puppet-strings/yard/parsers/puppet/parser.rb @@ -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 diff --git a/lib/puppet-strings/yard/parsers/puppet/statement.rb b/lib/puppet-strings/yard/parsers/puppet/statement.rb new file mode 100644 index 0000000..8dd9868 --- /dev/null +++ b/lib/puppet-strings/yard/parsers/puppet/statement.rb @@ -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] 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 diff --git a/lib/puppet-strings/yard/tags.rb b/lib/puppet-strings/yard/tags.rb new file mode 100644 index 0000000..dd46c76 --- /dev/null +++ b/lib/puppet-strings/yard/tags.rb @@ -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 diff --git a/lib/puppet-strings/yard/tags/overload_tag.rb b/lib/puppet-strings/yard/tags/overload_tag.rb new file mode 100644 index 0000000..84ce878 --- /dev/null +++ b/lib/puppet-strings/yard/tags/overload_tag.rb @@ -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] 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 diff --git a/lib/puppet-strings/yard/tags/parameter_directive.rb b/lib/puppet-strings/yard/tags/parameter_directive.rb new file mode 100644 index 0000000..9cf68f9 --- /dev/null +++ b/lib/puppet-strings/yard/tags/parameter_directive.rb @@ -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 diff --git a/lib/puppet-strings/yard/tags/property_directive.rb b/lib/puppet-strings/yard/tags/property_directive.rb new file mode 100644 index 0000000..a1c1e00 --- /dev/null +++ b/lib/puppet-strings/yard/tags/property_directive.rb @@ -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 diff --git a/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_class.erb b/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_class.erb new file mode 100644 index 0000000..095188f --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_class.erb @@ -0,0 +1,9 @@ +<% even = false %> +<% @items.each do |item| %> +
  • +
    + <%= linkify item, h(item.name(true)) %> +
    +
  • + <% even = !even %> +<% end %> diff --git a/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_defined_type.erb b/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_defined_type.erb new file mode 100644 index 0000000..095188f --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_defined_type.erb @@ -0,0 +1,9 @@ +<% even = false %> +<% @items.each do |item| %> +
  • +
    + <%= linkify item, h(item.name(true)) %> +
    +
  • + <% even = !even %> +<% end %> diff --git a/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_function.erb b/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_function.erb new file mode 100644 index 0000000..237a747 --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_function.erb @@ -0,0 +1,10 @@ +<% even = false %> +<% @items.each do |item| %> +
  • +
    + <%= linkify item, h(item.name(false)) %> + <%= item.function_type %> +
    +
  • + <% even = !even %> +<% end %> diff --git a/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_provider.erb b/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_provider.erb new file mode 100644 index 0000000..ec95e02 --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_provider.erb @@ -0,0 +1,10 @@ +<% even = false %> +<% @items.each do |item| %> +
  • +
    + <%= linkify item, h(item.name(true)) %> + Resource type: <%=item.type_name%> +
    +
  • + <% even = !even %> +<% end %> diff --git a/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_type.erb b/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_type.erb new file mode 100644 index 0000000..095188f --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_type.erb @@ -0,0 +1,9 @@ +<% even = false %> +<% @items.each do |item| %> +
  • +
    + <%= linkify item, h(item.name(true)) %> +
    +
  • + <% even = !even %> +<% end %> diff --git a/lib/puppet-strings/yard/templates/default/fulldoc/html/setup.rb b/lib/puppet-strings/yard/templates/default/fulldoc/html/setup.rb new file mode 100644 index 0000000..a71e117 --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/fulldoc/html/setup.rb @@ -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 diff --git a/lib/puppet-strings/yard/templates/default/layout/html/objects.erb b/lib/puppet-strings/yard/templates/default/layout/html/objects.erb new file mode 100644 index 0000000..afc6356 --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/layout/html/objects.erb @@ -0,0 +1,35 @@ +<% unless @objects_by_letter.empty? %> +

    <%= @title %>

    + + <% i = 0 %> + + + + + +
    + <% @objects_by_letter.sort_by {|l,o| l.to_s }.each do |letter, objects| %> + <% if (i += 1) % 8 == 0 %> + + <% i = 0 %> + <% end %> +
      +
    • <%= letter %>
    • +
        + <% objects.each do |obj| %> +
      • + <%= linkify obj, obj.name %> + <% if (obj.type == :module || obj.type == :class) && !obj.namespace.root? %> + (<%= obj.namespace.path %>) + <% elsif obj.type == :puppet_provider %> + (Resource type: <%= obj.type_name %>) + <% elsif obj.type == :puppet_function %> + (<%= obj.function_type %>) + <% end %> +
      • + <% end %> +
      +
    + <% end %> +
    +<% end %> diff --git a/lib/puppet-strings/yard/templates/default/layout/html/setup.rb b/lib/puppet-strings/yard/templates/default/layout/html/setup.rb new file mode 100644 index 0000000..703d2f5 --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/layout/html/setup.rb @@ -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] 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 + <% if object.statement.parent_class %> +
    +
    Inherits:
    +
    <%= linkify(Registry["puppet_classes::#{object.statement.parent_class}"], object.statement.parent_class.dup) %>
    +
    + <% end %> + <% if @subclasses && !@subclasses.empty? %> +
    +
    Inherited by:
    +
    + <% @subclasses.each do |subclass| %> + <%= linkify(subclass, subclass.name.to_s) %>
    + <% end %> +
    +
    + <% end %> +
    +
    Defined in:
    +
    + <%= object.file %><% if object.files.size > 1 %>,
    + <%= object.files[1..-1].map {|f| f.first }.join(",
    ") %> + <% end %> +
    +
    + diff --git a/lib/puppet-strings/yard/templates/default/puppet_class/html/header.erb b/lib/puppet-strings/yard/templates/default/puppet_class/html/header.erb new file mode 100644 index 0000000..a7e847c --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_class/html/header.erb @@ -0,0 +1 @@ +

    Puppet Class: <%= object.name %>

    diff --git a/lib/puppet-strings/yard/templates/default/puppet_class/html/overview.erb b/lib/puppet-strings/yard/templates/default/puppet_class/html/overview.erb new file mode 100644 index 0000000..a5b527a --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_class/html/overview.erb @@ -0,0 +1,6 @@ +

    Overview

    +
    +
    + <%= htmlify(object.docstring) %> +
    +
    diff --git a/lib/puppet-strings/yard/templates/default/puppet_class/html/setup.rb b/lib/puppet-strings/yard/templates/default/puppet_class/html/setup.rb new file mode 100644 index 0000000..2250d2f --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_class/html/setup.rb @@ -0,0 +1,14 @@ +# Initializes the template. +# @return [void] +def init + sections :header, :box_info, :overview, T('tags'), :source +end + +# Renders the box_info section. +# @return [String] Returns the rendered section. +def box_info + @subclasses = Registry.all(:puppet_class).find_all { |c| + c.statement.parent_class == object.name.to_s + } + erb(:box_info) +end diff --git a/lib/puppet-strings/yard/templates/default/puppet_class/html/source.erb b/lib/puppet-strings/yard/templates/default/puppet_class/html/source.erb new file mode 100644 index 0000000..0fd3c5e --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_class/html/source.erb @@ -0,0 +1,12 @@ +
    + + + + + +
    +
    <%= "\n\n\n" %><%= h format_lines(object) %>
    +
    +
    # File '<%= h object.file %>'<% if object.line %>, line <%= object.line %><% end %><%= "\n\n" %><%= html_syntax_highlight object.source %>
    +
    +
    diff --git a/lib/puppet-strings/yard/templates/default/puppet_defined_type/html/box_info.erb b/lib/puppet-strings/yard/templates/default/puppet_defined_type/html/box_info.erb new file mode 100644 index 0000000..49a6460 --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_defined_type/html/box_info.erb @@ -0,0 +1,10 @@ +
    +
    +
    Defined in:
    +
    + <%= object.file %><% if object.files.size > 1 %>,
    + <%= object.files[1..-1].map {|f| f.first }.join(",
    ") %>
    + <% end %> + + + diff --git a/lib/puppet-strings/yard/templates/default/puppet_defined_type/html/header.erb b/lib/puppet-strings/yard/templates/default/puppet_defined_type/html/header.erb new file mode 100644 index 0000000..91f06e7 --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_defined_type/html/header.erb @@ -0,0 +1 @@ +

    Defined Type: <%= object.name %>

    diff --git a/lib/puppet-strings/yard/templates/default/puppet_defined_type/html/overview.erb b/lib/puppet-strings/yard/templates/default/puppet_defined_type/html/overview.erb new file mode 100644 index 0000000..a5b527a --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_defined_type/html/overview.erb @@ -0,0 +1,6 @@ +

    Overview

    +
    +
    + <%= htmlify(object.docstring) %> +
    +
    diff --git a/lib/puppet-strings/yard/templates/default/puppet_defined_type/html/setup.rb b/lib/puppet-strings/yard/templates/default/puppet_defined_type/html/setup.rb new file mode 100644 index 0000000..d8858fb --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_defined_type/html/setup.rb @@ -0,0 +1,5 @@ +# Initializes the template. +# @return [void] +def init + sections :header, :box_info, :overview, T('tags'), :source +end diff --git a/lib/puppet-strings/yard/templates/default/puppet_defined_type/html/source.erb b/lib/puppet-strings/yard/templates/default/puppet_defined_type/html/source.erb new file mode 100644 index 0000000..0fd3c5e --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_defined_type/html/source.erb @@ -0,0 +1,12 @@ +
    + + + + + +
    +
    <%= "\n\n\n" %><%= h format_lines(object) %>
    +
    +
    # File '<%= h object.file %>'<% if object.line %>, line <%= object.line %><% end %><%= "\n\n" %><%= html_syntax_highlight object.source %>
    +
    +
    diff --git a/lib/puppet-strings/yard/templates/default/puppet_function/html/box_info.erb b/lib/puppet-strings/yard/templates/default/puppet_function/html/box_info.erb new file mode 100644 index 0000000..3f1bd40 --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_function/html/box_info.erb @@ -0,0 +1,14 @@ +
    +
    +
    Defined in:
    +
    + <%= object.file %><% if object.files.size > 1 %>,
    + <%= object.files[1..-1].map {|f| f.first }.join(",
    ") %>
    + <% end %> + + +
    +
    Function type:
    +
    <%= object.function_type %>
    +
    + diff --git a/lib/puppet-strings/yard/templates/default/puppet_function/html/header.erb b/lib/puppet-strings/yard/templates/default/puppet_function/html/header.erb new file mode 100644 index 0000000..db96389 --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_function/html/header.erb @@ -0,0 +1 @@ +

    Puppet Function: <%= object.name %>

    diff --git a/lib/puppet-strings/yard/templates/default/puppet_function/html/overview.erb b/lib/puppet-strings/yard/templates/default/puppet_function/html/overview.erb new file mode 100644 index 0000000..8cd6ca4 --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_function/html/overview.erb @@ -0,0 +1,18 @@ +

    Overview

    +
    + <% unless object.has_tag? :overload %> +
    + + + <%= "#{h(object.signature)} ⇒ #{signature_types(object, false)}" %> + + +
    + <% end %> +
    +
    + <%= htmlify(object.docstring) %> +
    +
    + <%= yieldall %> +
    diff --git a/lib/puppet-strings/yard/templates/default/puppet_function/html/setup.rb b/lib/puppet-strings/yard/templates/default/puppet_function/html/setup.rb new file mode 100644 index 0000000..d20b4b6 --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_function/html/setup.rb @@ -0,0 +1,5 @@ +# Initializes the template. +# @return [void] +def init + sections :header, :box_info, :overview, [T('tags'), :source] +end diff --git a/lib/puppet-strings/yard/templates/default/puppet_function/html/source.erb b/lib/puppet-strings/yard/templates/default/puppet_function/html/source.erb new file mode 100644 index 0000000..0fd3c5e --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_function/html/source.erb @@ -0,0 +1,12 @@ +
    + + + + + +
    +
    <%= "\n\n\n" %><%= h format_lines(object) %>
    +
    +
    # File '<%= h object.file %>'<% if object.line %>, line <%= object.line %><% end %><%= "\n\n" %><%= html_syntax_highlight object.source %>
    +
    +
    diff --git a/lib/puppet-strings/yard/templates/default/puppet_provider/html/box_info.erb b/lib/puppet-strings/yard/templates/default/puppet_provider/html/box_info.erb new file mode 100644 index 0000000..6dc586f --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_provider/html/box_info.erb @@ -0,0 +1,14 @@ +
    +
    +
    Defined in:
    +
    + <%= object.file %><% if object.files.size > 1 %>,
    + <%= object.files[1..-1].map {|f| f.first }.join(",
    ") %>
    + <% end %> + + +
    +
    Resource type:
    +
    <%= linkify(Registry["puppet_types::#{object.type_name}"], object.type_name) %>
    +
    + diff --git a/lib/puppet-strings/yard/templates/default/puppet_provider/html/collection.erb b/lib/puppet-strings/yard/templates/default/puppet_provider/html/collection.erb new file mode 100644 index 0000000..ec1bdf8 --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_provider/html/collection.erb @@ -0,0 +1,10 @@ +<% if @collection && !@collection.empty? %> +
    +

    <%= @title %>

    +
      + <% @collection.each do |key, value| %> +
    • <%= key %> — <%= value %>
    • + <% end %> +
    +
    +<% end %> diff --git a/lib/puppet-strings/yard/templates/default/puppet_provider/html/features.erb b/lib/puppet-strings/yard/templates/default/puppet_provider/html/features.erb new file mode 100644 index 0000000..fa3fa3e --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_provider/html/features.erb @@ -0,0 +1,12 @@ +<% if object.features && !object.features.empty? %> +
    +

    Features

    +
      + <% object.features.each do |feature| %> +
    • + <%= feature %> +
    • + <% end %> +
    +
    +<% end %> diff --git a/lib/puppet-strings/yard/templates/default/puppet_provider/html/header.erb b/lib/puppet-strings/yard/templates/default/puppet_provider/html/header.erb new file mode 100644 index 0000000..005785c --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_provider/html/header.erb @@ -0,0 +1 @@ +

    Provider: <%= object.name %>

    diff --git a/lib/puppet-strings/yard/templates/default/puppet_provider/html/overview.erb b/lib/puppet-strings/yard/templates/default/puppet_provider/html/overview.erb new file mode 100644 index 0000000..a5b527a --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_provider/html/overview.erb @@ -0,0 +1,6 @@ +

    Overview

    +
    +
    + <%= htmlify(object.docstring) %> +
    +
    diff --git a/lib/puppet-strings/yard/templates/default/puppet_provider/html/setup.rb b/lib/puppet-strings/yard/templates/default/puppet_provider/html/setup.rb new file mode 100644 index 0000000..57a69a4 --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_provider/html/setup.rb @@ -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 diff --git a/lib/puppet-strings/yard/templates/default/puppet_type/html/box_info.erb b/lib/puppet-strings/yard/templates/default/puppet_type/html/box_info.erb new file mode 100644 index 0000000..0276e3f --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_type/html/box_info.erb @@ -0,0 +1,20 @@ +
    +
    +
    Defined in:
    +
    + <%= object.file %><% if object.files.size > 1 %>,
    + <%= object.files[1..-1].map {|f| f.first }.join(",
    ") %>
    + <% end %> + + + <% if @providers && !@providers.empty? %> +
    +
    Providers:
    +
    + <% @providers.each do |provider| %> + <%= linkify(provider, provider.name.to_s) %>
    + <% end %> +
    +
    + <% end %> + diff --git a/lib/puppet-strings/yard/templates/default/puppet_type/html/features.erb b/lib/puppet-strings/yard/templates/default/puppet_type/html/features.erb new file mode 100644 index 0000000..1cba5c5 --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_type/html/features.erb @@ -0,0 +1,13 @@ +<% if object.features && !object.features.empty? %> +
    +

    Features

    +
      + <% object.features.each do |feature| %> +
    • + <%= feature.name %> + <% unless feature.docstring.empty? %> — <%= htmlify_line(feature.docstring) %><% end %> +
    • + <% end %> +
    +
    +<% end %> diff --git a/lib/puppet-strings/yard/templates/default/puppet_type/html/header.erb b/lib/puppet-strings/yard/templates/default/puppet_type/html/header.erb new file mode 100644 index 0000000..3f79aeb --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_type/html/header.erb @@ -0,0 +1 @@ +

    Resource Type: <%= object.name %>

    diff --git a/lib/puppet-strings/yard/templates/default/puppet_type/html/overview.erb b/lib/puppet-strings/yard/templates/default/puppet_type/html/overview.erb new file mode 100644 index 0000000..a5b527a --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_type/html/overview.erb @@ -0,0 +1,6 @@ +

    Overview

    +
    +
    + <%= htmlify(object.docstring) %> +
    +
    diff --git a/lib/puppet-strings/yard/templates/default/puppet_type/html/parameters.erb b/lib/puppet-strings/yard/templates/default/puppet_type/html/parameters.erb new file mode 100644 index 0000000..2e75754 --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_type/html/parameters.erb @@ -0,0 +1,35 @@ +<% if @parameters && !@parameters.empty? %> +
    +

    <%= @tag_title %>

    +
      + <% @parameters.each do |parameter| %> +
    • + <%= parameter.name + (parameter.isnamevar ? ' (namevar)' : '') %> + <% if parameter.default %> + (defaults to: <%= parameter.default %>) + <% end %> + <% unless parameter.docstring.empty? %> +
      +
      + <%= htmlify(parameter.docstring) %> +
      +
      + <% end %> + <% unless parameter.values.empty? %> +
      + Supported values: +
        + <% parameter.values.each do |value| %> +
      • + <% other = parameter.aliases[value] %> + <%= value %><% if other %> (alias for: <%= other %>)<% end %> +
      • + <% end %> +
      +
      + <% end %> +
    • + <% end %> +
    +
    +<% end %> diff --git a/lib/puppet-strings/yard/templates/default/puppet_type/html/setup.rb b/lib/puppet-strings/yard/templates/default/puppet_type/html/setup.rb new file mode 100644 index 0000000..d0a3043 --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/puppet_type/html/setup.rb @@ -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 diff --git a/lib/puppet-strings/yard/templates/default/tags/html/puppet_overload.erb b/lib/puppet-strings/yard/templates/default/tags/html/puppet_overload.erb new file mode 100644 index 0000000..3d12d59 --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/tags/html/puppet_overload.erb @@ -0,0 +1,12 @@ +<% if object.has_tag?(:overload) && object.tags(:overload).any? {|o| !o.docstring.blank? } %> +

    Overloads:

    +
      + <% object.tags(:overload).each_with_index do |overload, index| %> + <% next if overload.docstring.blank? %> +
    • + <%= "#{h(overload.signature)} ⇒ #{signature_types(overload, false)}" %> + <%= yieldall :object => overload %> +
    • + <% end %> +
    +<% end %> diff --git a/lib/puppet-strings/yard/templates/default/tags/setup.rb b/lib/puppet-strings/yard/templates/default/tags/setup.rb new file mode 100644 index 0000000..888d05a --- /dev/null +++ b/lib/puppet-strings/yard/templates/default/tags/setup.rb @@ -0,0 +1,15 @@ +# Called to return parameter tags. +# @return [Array] Returns the parameter tags if the object should have parameters. +def param + tag(:param) if + object.type == :method || + object.type == :puppet_class || + 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 diff --git a/lib/puppet/application/strings.rb b/lib/puppet/application/strings.rb index 119b003..5a4f06d 100644 --- a/lib/puppet/application/strings.rb +++ b/lib/puppet/application/strings.rb @@ -1,4 +1,5 @@ require 'puppet/application/face_base' +# Implements the 'puppet strings' application. class Puppet::Application::Strings < Puppet::Application::FaceBase end diff --git a/lib/puppet/face/strings.rb b/lib/puppet/face/strings.rb index a695acb..06d4ce6 100644 --- a/lib/puppet/face/strings.rb +++ b/lib/puppet/face/strings.rb @@ -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 diff --git a/lib/puppet_x/puppetlabs/strings.rb b/lib/puppet_x/puppetlabs/strings.rb deleted file mode 100644 index 299af0b..0000000 --- a/lib/puppet_x/puppetlabs/strings.rb +++ /dev/null @@ -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 diff --git a/lib/puppet_x/puppetlabs/strings/actions.rb b/lib/puppet_x/puppetlabs/strings/actions.rb deleted file mode 100644 index 50da802..0000000 --- a/lib/puppet_x/puppetlabs/strings/actions.rb +++ /dev/null @@ -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] the modules to be documented - # - # @param [Array] module_names a list of the module source files - # @param [Array] 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] 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] 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] 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] 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 - diff --git a/lib/puppet_x/puppetlabs/strings/pops/yard_statement.rb b/lib/puppet_x/puppetlabs/strings/pops/yard_statement.rb deleted file mode 100644 index f978d2c..0000000 --- a/lib/puppet_x/puppetlabs/strings/pops/yard_statement.rb +++ /dev/null @@ -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 diff --git a/lib/puppet_x/puppetlabs/strings/pops/yard_transformer.rb b/lib/puppet_x/puppetlabs/strings/pops/yard_transformer.rb deleted file mode 100644 index 0a47435..0000000 --- a/lib/puppet_x/puppetlabs/strings/pops/yard_transformer.rb +++ /dev/null @@ -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 diff --git a/lib/puppet_x/puppetlabs/strings/util.rb b/lib/puppet_x/puppetlabs/strings/util.rb deleted file mode 100644 index 8d48c62..0000000 --- a/lib/puppet_x/puppetlabs/strings/util.rb +++ /dev/null @@ -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 diff --git a/lib/puppet_x/puppetlabs/strings/yard/code_objects/defined_type_object.rb b/lib/puppet_x/puppetlabs/strings/yard/code_objects/defined_type_object.rb deleted file mode 100644 index e680157..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/code_objects/defined_type_object.rb +++ /dev/null @@ -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] - 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 diff --git a/lib/puppet_x/puppetlabs/strings/yard/code_objects/host_class_object.rb b/lib/puppet_x/puppetlabs/strings/yard/code_objects/host_class_object.rb deleted file mode 100644 index 0fc1c53..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/code_objects/host_class_object.rb +++ /dev/null @@ -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 diff --git a/lib/puppet_x/puppetlabs/strings/yard/code_objects/method_object.rb b/lib/puppet_x/puppetlabs/strings/yard/code_objects/method_object.rb deleted file mode 100644 index 5ce5bf8..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/code_objects/method_object.rb +++ /dev/null @@ -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 diff --git a/lib/puppet_x/puppetlabs/strings/yard/code_objects/provider_object.rb b/lib/puppet_x/puppetlabs/strings/yard/code_objects/provider_object.rb deleted file mode 100644 index cf63c9d..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/code_objects/provider_object.rb +++ /dev/null @@ -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] - 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 diff --git a/lib/puppet_x/puppetlabs/strings/yard/code_objects/puppet_namespace_object.rb b/lib/puppet_x/puppetlabs/strings/yard/code_objects/puppet_namespace_object.rb deleted file mode 100644 index c3ba60b..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/code_objects/puppet_namespace_object.rb +++ /dev/null @@ -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 - diff --git a/lib/puppet_x/puppetlabs/strings/yard/code_objects/type_object.rb b/lib/puppet_x/puppetlabs/strings/yard/code_objects/type_object.rb deleted file mode 100644 index 513543f..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/code_objects/type_object.rb +++ /dev/null @@ -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] - 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 diff --git a/lib/puppet_x/puppetlabs/strings/yard/core_ext/yard.rb b/lib/puppet_x/puppetlabs/strings/yard/core_ext/yard.rb deleted file mode 100644 index d8accf2..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/core_ext/yard.rb +++ /dev/null @@ -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 diff --git a/lib/puppet_x/puppetlabs/strings/yard/handlers/base.rb b/lib/puppet_x/puppetlabs/strings/yard/handlers/base.rb deleted file mode 100644 index 0bd3e60..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/handlers/base.rb +++ /dev/null @@ -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 diff --git a/lib/puppet_x/puppetlabs/strings/yard/handlers/defined_type_handler.rb b/lib/puppet_x/puppetlabs/strings/yard/handlers/defined_type_handler.rb deleted file mode 100644 index 78bff28..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/handlers/defined_type_handler.rb +++ /dev/null @@ -1,31 +0,0 @@ -class PuppetX::PuppetLabs::Strings::YARD::Handlers::DefinedTypeHandler < PuppetX::PuppetLabs::Strings::YARD::Handlers:: Base - handles ResourceTypeDefinition - - process do - obj = DefinedTypeObject.new(:root, statement.pops_obj.name) do |o| - o.parameters = statement.parameters.map do |a| - param_tuple = [a[0].pops_obj.name] - param_tuple << ( a[1].nil? ? nil : a[1].source ) - end - end - tp = Puppet::Pops::Types::TypeParser.new - param_type_info = {} - statement.pops_obj.parameters.each do |pop_param| - # If the parameter's type expression is nil, default to Any - if pop_param.type_expr == nil - param_type_info[pop_param.name] = Puppet::Pops::Types::TypeFactory.any() - else - begin - param_type_info[pop_param.name] = tp.interpret_any(pop_param.type_expr) - rescue Puppet::ParseError => e - # If the type could not be interpreted insert a prominent warning - param_type_info[pop_param.name] = "Type Error: #{e.message}" - end - end - end - obj.type_info = [param_type_info] - - - register obj - end -end diff --git a/lib/puppet_x/puppetlabs/strings/yard/handlers/heredoc_helper.rb b/lib/puppet_x/puppetlabs/strings/yard/handlers/heredoc_helper.rb deleted file mode 100644 index 73fe2aa..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/handlers/heredoc_helper.rb +++ /dev/null @@ -1,80 +0,0 @@ -class HereDocHelper - # NOTE: The following methods duplicate functionality from - # Puppet::Util::Reference and Puppet::Parser::Functions.functiondocs - # - # However, implementing this natively in YARD is a good test for the - # feasibility of extracting custom Ruby documentation. In the end, the - # existing approach taken by Puppet::Util::Reference may be the best due to - # the heavy use of metaprogramming in Types and Providers. - - # Extracts the Puppet function name and options hash from the parsed - # definition. - # - # @return [(String, Hash{String => String})] - def process_parameters(statement) - # Passing `false` to prameters excludes the block param from the returned - # list. - name, opts = statement.parameters(false).compact - - name = process_element(name) - - # Don't try to process options if we don't have any - if !opts.nil? - opts = opts.map do |tuple| - # Jump down into the S-Expression that represents a hashrocket, `=>`, - # and the values on either side of it. - tuple.jump(:assoc).map{|e| process_element(e)} - end - - options = Hash[opts] - else - options = {} - end - - [name, options] - end - - # Sometimes the YARD parser returns Heredoc strings that start with `<-` - # instead of `<<-`. - HEREDOC_START = /^ e - # If the type could not be interpreted insert a prominent warning - param_type_info[pop_param.name] = "Type Error: #{e.message}" - end - end - end - obj.type_info = [param_type_info] - - statement.pops_obj.tap do |o| - if o.parent_class - obj.parent_class = P(:root, o.parent_class) - end - end - - register obj - rescue StandardError, SystemStackError => e - # If we hit this, we've thrown an exception somewhere that should be - # addressed but should not break the build. - # - # SystemStackError is being caught due to a presently untraced bug in - # either YARD or the Puppet Parser. - # - # Note: Documentation will *not* be generated for any item listed here, - # but you will get the rest of your documentation! - - $stderr.puts("Ignored: #{e.inspect} at #{obj.title}") - end - end -end diff --git a/lib/puppet_x/puppetlabs/strings/yard/handlers/provider_handler.rb b/lib/puppet_x/puppetlabs/strings/yard/handlers/provider_handler.rb deleted file mode 100644 index a22d2ae..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/handlers/provider_handler.rb +++ /dev/null @@ -1,96 +0,0 @@ -# Handles `dispatch` calls within a future parser function declaration. For -# now, it just treats any docstring as an `@overlaod` tag and attaches the -# overload to the parent function. -class PuppetX::PuppetLabs::Strings::YARD::Handlers::PuppetProviderHandler < YARD::Handlers::Ruby::Base - include PuppetX::PuppetLabs::Strings::YARD::CodeObjects - - handles :command_call, :call - - process do - @heredoc_helper = HereDocHelper.new - # Puppet types always begin with: - # Puppet::Types.newtype... - # Therefore, we match the corresponding trees which look like this: - # s(:call, - # s(:const_path_ref, - # s(:var_ref, s(:const, "Puppet", ...), ...), - # s(:const, "Type", ...), - # You think this is ugly? It's better than the alternative. - return unless statement.children.length > 2 - first = statement.children.first.first - return unless (first.source == 'Puppet::Type') || - (first.type == :var_ref && - first.source == 'Type') && - statement[2].source == 'provide' - i = statement.index { |s| YARD::Parser::Ruby::AstNode === s && s.type == :ident && s.source == 'provide' } - provider_name = statement[i+1].jump(:ident).source - type_name = statement.jump(:symbol).first.source - provider_name = "#{type_name}:#{provider_name}" - - obj = ProviderObject.new(:root, "#{provider_name}_provider") - - docstring = nil - features = [] - commands = [] - confines = {} - defaults = {} - do_block = statement.jump(:do_block) - do_block.traverse do |node| - if is_a_func_call_named?('desc', node) - content = node.jump(:tstring_content) - # if we found the string content extract its source - if content != node - # The docstring is either the source stripped of heredoc - # annotations or the raw source. - if @heredoc_helper.is_heredoc?(content.source) - docstring = @heredoc_helper.process_heredoc content.source - else - docstring = content.source - end - end - elsif is_a_func_call_named?('confine', node) - node.traverse do |s| - if s.type == :assoc - k = s.first.jump(:ident).source - v = s[1].first.source - confines[k] = v - end - end - elsif is_a_func_call_named?('has_feature', node) - list = node.jump :list - if list != nil && list != node - features += list.map { |s| s.source if YARD::Parser::Ruby::AstNode === s }.compact - end - elsif is_a_func_call_named?('commands', node) - assoc = node.jump(:assoc) - if assoc && assoc != node - ident = assoc.jump(:ident) - if ident && ident != assoc - commands << ident.source - end - end - elsif is_a_func_call_named?('defaultfor', node) - node.traverse do |s| - if s.type == :assoc - k = s.first.jump(:ident).source - v = s[1].first.source - defaults[k] = v - end - end - end - end - obj.features = features - obj.commands = commands - obj.confines = confines - obj.defaults = defaults - obj.type_name = type_name - obj.header_name = provider_name - - register_docstring(obj, docstring, nil) - register obj - end - - def is_a_func_call_named?(name, node) - (node.type == :fcall || node.type == :command || node.type == :vcall) && node.children.first.source == name - end -end diff --git a/lib/puppet_x/puppetlabs/strings/yard/handlers/puppet_3x_function_handler.rb b/lib/puppet_x/puppetlabs/strings/yard/handlers/puppet_3x_function_handler.rb deleted file mode 100644 index f55ad4f..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/handlers/puppet_3x_function_handler.rb +++ /dev/null @@ -1,54 +0,0 @@ -require File.join(File.dirname(__FILE__),'./heredoc_helper') - -class PuppetX::PuppetLabs::Strings::YARD::Handlers::Puppet3xFunctionHandler < YARD::Handlers::Ruby::Base - include PuppetX::PuppetLabs::Strings::YARD::CodeObjects - - handles method_call(:newfunction) - - process do - @heredoc_helper = HereDocHelper.new - name, options = @heredoc_helper.process_parameters statement - - obj = MethodObject.new(function_namespace, name) - obj[:puppet_3x_function] = true - - register obj - if options['doc'] - register_docstring(obj, options['doc'], nil) - end - - # This has to be done _after_ register_docstring as all tags on the - # object are overwritten by tags parsed out of the docstring. - return_type = options['type'] - return_type ||= 'statement' # Default for newfunction - obj.add_tag YARD::Tags::Tag.new(:return, '', return_type) - - # FIXME: This is a hack that allows us to document the Puppet Core which - # uses `--no-transitive-tag api` and then only shows things explicitly - # tagged with `public` or `private` api. This is kind of insane and - # should be fixed upstream. - obj.add_tag YARD::Tags::Tag.new(:api, 'public') - end - - private - - # Returns a {PuppetNamespaceObject} for holding functions. Creates this - # object if necessary. - # - # @return [PuppetNamespaceObject] - def function_namespace - # NOTE: This tricky. If there is ever a Ruby class or module with the - # name ::Puppet3xFunctions, then there will be a clash. Hopefully the name - # is sufficiently uncommon. - obj = P(:root, 'Puppet3xFunctions') - if obj.is_a? Proxy - namespace_obj = PuppetNamespaceObject.new(:root, 'Puppet3xFunctions') - namespace_obj.add_tag YARD::Tags::Tag.new(:api, 'public') - - register namespace_obj - end - - obj - end - -end diff --git a/lib/puppet_x/puppetlabs/strings/yard/handlers/puppet_4x_function_handler.rb b/lib/puppet_x/puppetlabs/strings/yard/handlers/puppet_4x_function_handler.rb deleted file mode 100644 index 298c8da..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/handlers/puppet_4x_function_handler.rb +++ /dev/null @@ -1,234 +0,0 @@ -# Handles `dispatch` calls within a future parser function declaration. For -# now, it just treats any docstring as an `@overlaod` tag and attaches the -# overload to the parent function. -class PuppetX::PuppetLabs::Strings::YARD::Handlers::Puppet4xFunctionHandler < YARD::Handlers::Ruby::Base - include PuppetX::PuppetLabs::Strings::YARD::CodeObjects - - handles method_call(:dispatch) - - process do - return unless owner.is_a?(MethodObject) && owner['puppet_4x_function'] - return unless statement.docstring - - docstring = ::YARD::Docstring.new(statement.docstring, nil) - - # FIXME: This does a wholesale copy of all possible tags. But, we're only - # interested in the @overload tag. - owner.add_tag *docstring.tags - end -end - -class Puppet4xFunctionHandler < YARD::Handlers::Ruby::Base - include PuppetX::PuppetLabs::Strings::YARD::CodeObjects - - handles method_call(:create_function) - - # Given a command node which represents code like this: - # param 'Optional[Type]', :value_type - # Extract the type name and type signature and return them as a array. - def extract_type_from_command command - return [] if command.children.length < 2 or command.children[1].children.length < 2 - type_specifier = command.children[1] - # the parameter signature is the first child of the specifier and an - # identifier. Jump to the content inside the quotes and convert it to a - # string. - param_signature = type_specifier.children[0].jump(:tstring_content).source - # The parameter name is the second child of the specifier and a symbol. - # convert it to a string. - param_name_ident = type_specifier.jump :ident - return [] if param_name_ident == type_specifier - param_name = param_name_ident.source - [param_name, param_signature] - end - - process do - name = process_parameters - - method_arguments = [] - - # To attach the method parameters to the new code object, traverse the - # ruby AST until a node is found which defines a array of parameters. - # Then, traverse the children of the parameters, storing each identifier - # in the array of method arguments. - obj = MethodObject.new(function_namespace, name) do |o| - end - - # The data structure for overload_signatures is an array of hashes. Each - # hash represents the arguments a single function dispatch (aka overload) - # can take. - # overload_signatures = [ - # { # First function dispatch arguments - # # argument name, argument type - # 'arg0': 'Variant[String,Array[String]]', - # 'arg1': 'Optional[Type]' - # }, - # { # Second function dispatch arguments - # 'arg0': 'Variant[String,Array[String]]', - # 'arg1': 'Optional[Type]', - # 'arg2': 'Any' - # } - # ] - # Note that the order for arguments to a function doesn't actually matter - # because we allow users flexibility when listing their arguments in the - # comments. - overload_signatures = [] - statement.traverse do |node| - # Find all of the dispatch methods - if node.type == :ident and node.source == 'dispatch' - command = node.parent - do_block = command.jump :do_block - # If the command doesn't have a do_block we can't extract type info - if do_block == command - next - end - signature = {} - # Iterate through each of the children of the do block and build - # tuples of parameter names and parameter type signatures - do_block.children.first.children.each do |child| - name, type = extract_type_from_command(child) - # This can happen if there is a function or something we aren't - # expecting. - if name != nil and type != nil - signature[name] = type - end - end - overload_signatures <<= signature - end - end - - # If the overload_signatures list is empty because we couldn't find any - # dispatch blocks, then there must be one function named the same as the - # name of the function being created. - if overload_signatures.length == 0 - statement.traverse do |node| - # Find the function definition with the same name as the puppet - # function being created. - if (node.type == :def and node.children.first.type == :ident and - node.children.first.source == obj.name.to_s) - signature = {} - # Find its parameters. If they don't exist, fine - params = node.jump :params - break if params == node - params.traverse do |param| - if param.type == :ident - # The parameters of Puppet functions with no defined dispatch are - # as though they are Any type. - signature[param[0]] = 'Any' - end - end - overload_signatures <<= signature - # Now that the parameters have been found, break out of the traversal - break - end - end - end - - # Preserve this type information. We'll need it later when we look - # at the docstring. - obj.type_info = overload_signatures - - # The yard docstring parser expects a list of lists, not a list of lists of - # lists. - obj.parameters = overload_signatures.map { |sig| sig.to_a }.flatten(1) - - obj['puppet_4x_function'] = true - - register obj - - obj.add_tag YARD::Tags::Tag.new(:api, 'public') - - blk = statement.block.children.first - parse_block(blk, :owner => obj) - end - - private - - # Returns a {PuppetNamespaceObject} for holding functions. Creates this - # object if necessary. - # - # @return [PuppetNamespaceObject] - def function_namespace - # NOTE: This tricky. If there is ever a Ruby class or module with the - # name ::Puppet4xFunctions, then there will be a clash. Hopefully the name - # is sufficiently uncommon. - obj = P(:root, 'Puppet4xFunctions') - if obj.is_a? Proxy - namespace_obj = PuppetNamespaceObject.new(:root, 'Puppet4xFunctions') - - register namespace_obj - # FIXME: The docstring has to be cleared. Otherwise, the namespace - # object will be registered using the docstring of the - # `create_function` call that is currently being processed. - # - # Figure out how to properly register the namespace without using the - # function handler object. - register_docstring(namespace_obj, '', nil) - namespace_obj.add_tag YARD::Tags::Tag.new(:api, 'public') - end - - obj - end - - # NOTE: The following methods duplicate functionality from - # Puppet::Util::Reference and Puppet::Parser::Functions.functiondocs - # - # However, implementing this natively in YARD is a good test for the - # feasibility of extracting custom Ruby documentation. In the end, the - # existing approach taken by Puppet::Util::Reference may be the best due to - # the heavy use of metaprogramming in Types and Providers. - - # Extracts the Puppet function name and options hash from the parsed - # definition. - # - # @return [(String, Hash{String => String})] - def process_parameters - # Passing `false` to parameters excludes the block param from the returned - # array. - name, _ = statement.parameters(false).compact - - name = process_element(name) - - - name - end - - # Sometimes the YARD parser returns Heredoc strings that start with `<-` - # instead of `<<-`. - HEREDOC_START = /^ 2 - first = statement.children.first - return unless (first.type == :const_path_ref and - first.source == 'Puppet::Type') or - (first.type == :var_ref and - first.source == 'Type') and - statement.children[1].source == "newtype" - - # Fetch the docstring for the types. The docstring is the string literal - # assigned to the @doc parameter or absent, like this: - # @doc "docstring goes here" - # We assume that docstrings nodes have the following shape in the source - # code: - # ... - # s(s(:assign, - # s(:..., s(:ivar, "@doc", ...), ...), - # s(:..., - # s(:..., - # s(:tstring_content, - # "Manages files, including their content, etc.", ... - # Initialize the docstring to nil, the default value if we don't find - # anything - docstring = nil - # Walk the tree searching for assignments - statement.traverse do |node| - if node.type == :assign - # Once we have found and assignment, jump to the first ivar - # (the l-value) - # If we can't find an ivar return the node. - ivar = node.jump(:ivar) - # If we found and ivar and its source reads '@doc' then... - if ivar != node and ivar.source == '@doc' - # find the next string content - content = node.jump(:tstring_content) - # if we found the string content extract its source - if content != node - # The docstring is either the source stripped of heredoc - # annotations or the raw source. - if @heredoc_helper.is_heredoc? content.source - docstring = @heredoc_helper.process_heredoc content.source - else - docstring = content.source - end - end - # Since we found the @doc parameter (regardless of whether we - # successfully extracted its source), we're done. - break - # But if we didn't find the ivar loop around again. - else - next - end - end - end - - # The types begin with: - # Puppet::Types.newtype(:symbol) - # Jump to the first identifier (':symbol') after the third argument - # ('(:symbol)') to the current statement - name = statement.children[2].jump(:ident).source - parameter_details = [] - property_details = [] - features = [] - obj = TypeObject.new(:root, name) - obj.parameters = [] - - # Find the do block following the Type. - do_block = statement.jump(:do_block) - # traverse the do block's children searching for function calls whose - # identifier is newparam (we're calling the newparam function) - do_block.traverse do |node| - if is_param? node - # The first member of the parameter tuple is the parameter name. - # Find the second identifier node under the fcall tree. The first one - # is 'newparam', the second one is the function name. - # Get its source. - # The second parameter is nil because we cannot infer types for these - # functions. In fact, that's a silly thing to ask because ruby - # types were deprecated with puppet 4 at the same time the type - # system was created. - - # Because of a ripper bug a symbol identifier is sometimes incorrectly parsed as a keyword. - # That is, the symbol `:true` will be represented as s(:symbol s(:kw, true... - param_name = node.children[1].jump(:ident) - if param_name == node.children[1] - param_name = node.children[1].jump(:kw) - end - param_name = param_name.source - obj.parameters << [param_name, nil] - parameter_details << {:name => param_name, - :desc => fetch_description(node), :exists? => true, - :puppet_type => true, - :default => fetch_default(node), - :namevar => is_namevar?(node, param_name, name), - :parameter => true, - :allowed_values => get_parameter_allowed_values(node), - } - elsif is_prop? node - # Because of a ripper bug a symbol identifier is sometimes incorrectly parsed as a keyword. - # That is, the symbol `:true` will be represented as s(:symbol s(:kw, true... - prop_name = node.children[1].jump(:ident) - if prop_name == node.children[1] - prop_name = node.children[1].jump(:kw) - end - prop_name = prop_name.source - property_details << {:name => prop_name, - :desc => fetch_description(node), :exists? => true, - :default => fetch_default(node), - :puppet_type => true, - :property => true, - :allowed_values => get_property_allowed_values(node), - } - elsif is_feature? node - features << get_feature(node) - elsif is_a_func_call_named? 'ensurable', node - # Someone could call the ensurable method and create an ensure - # property. If that happens, they it will be documented twice. Serves - # them right. - property_details << {:name => 'ensure', - :desc => '', :exists? => true, - :default => nil, - :puppet_type => true, - :property => true, - :allowed_values => [], - } - end - end - obj.parameter_details = parameter_details - obj.property_details = property_details - obj.features = features - obj.header_name = name - - register obj - # Register docstring after the object. If the object already has a - # docstring, or more likely has parameters documented with the type - # directive and an empty docstring, we want to override it with the - # docstring we found, assuming we found one. - register_docstring(obj, docstring, nil) if docstring - end - - - # See: - # https://docs.puppetlabs.com/guides/custom_types.html#namevar - # node should be a parameter - def is_namevar? node, param_name, type_name - # Option 1: - # Puppet::Type.newtype(:name) do - # ... - # newparam(:name) do - # ... - # end - if type_name == param_name - return true - end - # Option 2: - # newparam(:path, :namevar => true) do - # ... - # end - if node.children.length >= 2 - node.traverse do |s| - if s.type == :assoc and s.jump(:ident).source == 'namevar' and s.jump(:kw).source == 'true' - return true - end - end - end - # Option 3: - # newparam(:path) do - # isnamevar - # ... - # end - do_block = node.jump(:do_block).traverse do |s| - if is_a_func_call_named? 'isnamevar', s - return true - end - end - # Crazy implementations of types may just call #isnamevar directly on the object. - # We don't handle this today. - return false - end - - def is_param? node - is_a_func_call_named? 'newparam', node - end - def is_prop? node - is_a_func_call_named? 'newproperty', node - end - - def is_feature? node - is_a_func_call_named? 'feature', node - end - - def is_a_func_call_named? name, node - (node.type == :fcall or node.type == :command or node.type == :vcall) and node.children.first.source == name - end - - def get_feature node - name = node[1].jump(:ident).source - desc = node[1].jump(:tstring_content).source - methods = [] - if node[1].length == 4 and node.children[1][2].jump(:ident).source == 'methods' - arr = node[1][2].jump(:array) - if arr != node[1][2] - arr.traverse do |s| - if s.type == :ident - methods << s.source - end - end - end - end - { - :name => name, - :desc => desc, - :methods => methods != [] ? methods : nil, - } - end - - def get_parameter_allowed_values node - vals = [] - node.traverse do |s| - if is_a_func_call_named? 'newvalues', s - list = s.jump(:list) - if list != s - vals += list.map { |item| [item.source] if YARD::Parser::Ruby::AstNode === item } - end - end - end - vals.compact - end - - # Calls to newvalue only apply to properties, according to Dan & Nan's - # "Puppet Types and Providers", page 30. - def get_property_allowed_values node - vals = get_parameter_allowed_values node - node.traverse do |s| - if is_a_func_call_named? 'newvalue', s - required_features = nil - s.traverse do |ss| - if ss.type == :assoc and ss[0].source == ':required_features' - required_features = ss[1].source - end - end - list = s.jump(:list) - if list != s - vals << [list[0].source, required_features].compact - end - end - end - vals - end - - def fetch_default node - do_block = node.jump(:do_block) - do_block.traverse do |s| - if is_a_func_call_named? 'defaultto', s - return s[-1].source - end - end - nil - end - - def fetch_description(fcall) - fcall.traverse do |node| - if is_a_func_call_named? 'desc', node - content = node.jump(:string_content) - if content != node - @heredoc_helper = HereDocHelper.new - if @heredoc_helper.is_heredoc? content.source - docstring = @heredoc_helper.process_heredoc content.source - else - docstring = content.source - end - return docstring - end - end - end - return nil - end -end diff --git a/lib/puppet_x/puppetlabs/strings/yard/json_registry_store.rb b/lib/puppet_x/puppetlabs/strings/yard/json_registry_store.rb deleted file mode 100644 index 22729c4..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/json_registry_store.rb +++ /dev/null @@ -1,85 +0,0 @@ -module YARD - - class JsonRegistryStore < RegistryStore - def save(merge=true, file=nil) - super - - @serializer = Serializers::JsonSerializer.new(@file) - - sdb = Registry.single_object_db - if sdb == true || sdb == nil - serialize_output_schema(@store) - else - values(false).each do |object| - serialize_output_schema(object) - end - end - true - end - - # @param obj [Hash] A hash representing the registry or part of the - # registry. - def serialize_output_schema(obj) - - schema = { - :puppet_functions => [], - :puppet_providers => [], - :puppet_classes => [], - :defined_types => [], - :puppet_types => [], - } - - schema[:puppet_functions] += obj.select do |key, val| - val.type == :method and (val['puppet_4x_function'] or - val['puppet_3x_function']) - end.values - - schema[:puppet_classes] += obj.select do |key, val| - val.type == :hostclass - end.values - - schema[:defined_types] += obj.select do |key, val| - val.type == :definedtype - end.values - - schema[:puppet_providers] += obj.select do |key, val| - val.type == :provider - end.values - - schema[:puppet_types] += obj.select do |key, val| - val.type == :type - end.values - - @serializer.serialize(schema.to_json) - end - end - - # Override the serializer because it puts the data at a wacky path and, more - # importantly, marshals the data with a bunch of non-printable characters. - module Serializers - class JsonSerializer < YardocSerializer - - def initialize o - super - @options = { - :basepath => '.', - :extension => 'json', - } - @extension = 'json' - @basepath = '.' - end - def serialize(data) - - if YARD::Config.options[:emit_json] - path = YARD::Config.options[:emit_json] - log.debug "Serializing json to #{path}" - File.open!(path, "wb") {|f| f.write data } - end - if YARD::Config.options[:emit_json_stdout] - puts data - end - end - end - end - -end diff --git a/lib/puppet_x/puppetlabs/strings/yard/monkey_patches.rb b/lib/puppet_x/puppetlabs/strings/yard/monkey_patches.rb deleted file mode 100644 index f509fb6..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/monkey_patches.rb +++ /dev/null @@ -1,68 +0,0 @@ -require 'yard' -require File.join(File.dirname(__FILE__), './json_registry_store') - -# TODO: As far as I can tell, monkeypatching is the officially recommended way -# to extend these tools to cover custom usecases. Follow up on the YARD mailing -# list or IRC to see if there is a better way. - -class YARD::CLI::Yardoc - def all_objects - YARD::Registry.all(:root, :module, :class, :type, :provider, :puppetnamespace, :hostclass, :definedtype) - end -end - -class YARD::CLI::Stats - def stats_for_hostclasses - output 'Puppet Classes', *type_statistics(:hostclass) - end - - def stats_for_definedtypes - output 'Puppet Defined Types', *type_statistics(:definedtype) - end - - def stats_for_puppet_types - output 'Puppet Types', *type_statistics(:type) - end - - def stats_for_puppet_provider - output 'Puppet Providers', *type_statistics(:provider) - end -end - -class YARD::Logger - def show_progress - return false if YARD.ruby18? # threading is too ineffective for progress support - return false if YARD.windows? # windows has poor ANSI support - return false unless io.tty? # no TTY support on IO - # Here is the actual monkey patch. A simple fix to an inverted conditional. - # Without this Pry is unusable for debugging as the progress bar goes - # craaaaaaaazy. - return false unless level > INFO # no progress in verbose/debug modes - @show_progress - end - - # Redirect Yard command line warnings to a log file called .yardwarns - # Yard warnings may be irrelevant, spurious, or may not conform with our - # styling and UX design. They are also printed on stdout by default. - def warn warning - f = File.new '.yardwarns', 'a' - f.write warning - f.close() - end -end - - -# 15:04:42 radens | lsegal: where would you tell yard to use your custom RegistryStore? -# 15:09:54 @lsegal | https://github.com/lsegal/yard/blob/master/lib/yard/registry.rb#L428-L435 -# 15:09:54 @lsegal | you would set that attr on Registry -# 15:09:54 @lsegal | it might be worth expanding that API to swap out the store class used -# 15:10:49 @lsegal | specifically -# | https://github.com/lsegal/yard/blob/master/lib/yard/registry.rb#L190 and -# | replace RegistryStore there with a storage_class attr -module YARD::Registry - class << self - def clear - self.thread_local_store = YARD::JsonRegistryStore.new - end - end -end diff --git a/lib/puppet_x/puppetlabs/strings/yard/parser.rb b/lib/puppet_x/puppetlabs/strings/yard/parser.rb deleted file mode 100644 index 630846d..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/parser.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'yard' -require 'puppet/pops' - -require 'puppet_x/puppetlabs/strings' -require 'puppet_x/puppetlabs/strings//pops/yard_transformer' - -class PuppetX::PuppetLabs::Strings::YARD::PuppetParser < YARD::Parser::Base - attr_reader :file, :source - - def initialize(source, filename) - @source = source - @file = filename - - @parser = Puppet::Pops::Parser::Parser.new() - @transformer = PuppetX::PuppetLabs::Strings::Pops::YARDTransformer.new() - end - - def parse - @parse_result ||= @parser.parse_string(source) - self - end - - def enumerator - statements = @transformer.transform(@parse_result) - - # Ensure an array is returned and prune any nil values. - Array(statements).compact.reverse - end - -end diff --git a/lib/puppet_x/puppetlabs/strings/yard/tags/directives.rb b/lib/puppet_x/puppetlabs/strings/yard/tags/directives.rb deleted file mode 100644 index 0b94793..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/tags/directives.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'puppet_x/puppetlabs/strings/yard/core_ext/yard' -# Creates a new code object based on the directive -class PuppetX::PuppetLabs::Strings::YARD::Tags::PuppetTypeParameterDirective < YARD::Tags::Directive - def call - return if object.nil? - object.parameters << ([tag.text, tag.types].flatten) - object.parameter_details << {:name => tag.name, :desc => tag.text, :exists? => true, :puppet_type => true} - end -end diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/definedtype/html/docstring.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/definedtype/html/docstring.erb deleted file mode 100644 index 900985f..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/definedtype/html/docstring.erb +++ /dev/null @@ -1,34 +0,0 @@ -
    -
    -

    <%= htmlify(@class_details[:desc]) %>

    -
    -
    -
    - <% if @class_details[:examples] != {}%> -
    -

    Examples:

    - <% @class_details[:examples].each do |title, text| %> -

    <%= title %>

    -
    <%= text %>
    - <% end %> -
    - <% end %> - <% if @class_details[:since] %> -

    Since:

    -
      -
    • -
      -

      <%= @class_details[:since] %>

      -
      -
    • -
    - <% end %> - <% if @class_details[:return] %> -

    Return:

    -
      -
    • - <%= @html_helper.generate_return_types(@class_details[:return][1], @class_details[:return][0]) %> -
    • -
    - <% end %> -
    diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/definedtype/html/header.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/definedtype/html/header.erb deleted file mode 100644 index 74b3dba..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/definedtype/html/header.erb +++ /dev/null @@ -1,5 +0,0 @@ -
    -

    - <%= @header_text %> -

    -
    diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/definedtype/html/parameter_details.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/definedtype/html/parameter_details.erb deleted file mode 100644 index d03666e..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/definedtype/html/parameter_details.erb +++ /dev/null @@ -1,6 +0,0 @@ -

    Parameter Summary

    -
    -
      - <%= @html_helper.generate_parameters(@param_details, object) %> -
    -
    diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/definedtype/html/setup.rb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/definedtype/html/setup.rb deleted file mode 100644 index 91e114a..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/definedtype/html/setup.rb +++ /dev/null @@ -1 +0,0 @@ -include T('default/module/html') diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/definedtype/setup.rb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/definedtype/setup.rb deleted file mode 100644 index 48f2877..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/definedtype/setup.rb +++ /dev/null @@ -1,49 +0,0 @@ -include T('default/module') - -require File.join(File.dirname(__FILE__),'../html_helper') -require File.join(File.dirname(__FILE__),'../template_helper') - -def init - sections :header, :box_info, :pre_docstring, :docstring, :parameter_details - - @template_helper = TemplateHelper.new - @html_helper = HTMLHelper.new - @template_helper.check_parameters_match_docs object - params = object.parameters.map { |param| param.first } - param_tags = object.tags.find_all{ |tag| tag.tag_name == "param"} - param_details = @template_helper.extract_param_details(params, param_tags) unless params.nil? - @template_helper.check_types_match_docs object, param_details - -end - -def parameter_details - return if object.parameters.empty? - - param_tags = object.tags.find_all{ |tag| tag.tag_name == "param"} - params = object.parameters - - @param_details = [] - - @param_details = @template_helper.extract_param_details(params, param_tags, true) - - erb(:parameter_details) -end - -def header - if object.type == :hostclass - @header_text = "Puppet Class: #{object.name}" - elsif object.type == :definedtype - @header_text = "Puppet Defined Type: #{object.name}" - else - @header_text = "#{object.name}" - end - - erb(:header) -end - -def docstring - - @class_details = @template_helper.extract_tag_data(object) - - erb(:docstring) -end diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/full_list_class.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/full_list_class.erb deleted file mode 100644 index 38081a0..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/full_list_class.erb +++ /dev/null @@ -1,2 +0,0 @@ -
  • <%= link_object(Registry.root, Registry.root.title, nil, false) %>
  • -<%= namespace_list %> diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/full_list_puppet_manifest.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/full_list_puppet_manifest.erb deleted file mode 100644 index 6ba286e..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/full_list_puppet_manifest.erb +++ /dev/null @@ -1 +0,0 @@ -<%= namespace_list(:namespace_types => [:hostclass, :definedtype]) %> diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/full_list_puppet_plugin.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/full_list_puppet_plugin.erb deleted file mode 100644 index 241b6cc..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/full_list_puppet_plugin.erb +++ /dev/null @@ -1,21 +0,0 @@ -<% unless P(:root, 'Puppet3xFunctions').is_a?(CodeObjects::Proxy) %> -
  • - - <%= link_object(P(:root, 'Puppet3xFunctions'), 'Puppet 3x Functions', nil, false) %> - Puppet3xFunctions -
  • -
      - <%= namespace_list(:root => P(:root,'Puppet3xFunctions'), :namespace_types => [:puppetnamespace, :method]) %> -
    -<% end %> - -<% unless P(:root, 'Puppet4xFunctions').is_a?(CodeObjects::Proxy) %> -
  • - - <%= link_object(P(:root, 'Puppet4xFunctions'), 'Puppet 4x Functions', nil, false) %> - Puppet4xFunctions -
  • -
      - <%= namespace_list(:root => P(:root,'Puppet4xFunctions'), :namespace_types => [:puppetnamespace, :method]) %> -
    -<% end %> diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/full_list_puppet_provider.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/full_list_puppet_provider.erb deleted file mode 100644 index 582fe67..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/full_list_puppet_provider.erb +++ /dev/null @@ -1 +0,0 @@ -<%= namespace_list(:namespace_types => [:provider]) %> diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/full_list_puppet_type.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/full_list_puppet_type.erb deleted file mode 100644 index 14748c8..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/full_list_puppet_type.erb +++ /dev/null @@ -1 +0,0 @@ -<%= namespace_list(:namespace_types => [:type]) %> diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/setup.rb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/setup.rb deleted file mode 100644 index 7ab0dfe..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/setup.rb +++ /dev/null @@ -1,82 +0,0 @@ -def generate_class_list - @items = options.objects.select{|o| [:module, :class, :root].include? o.type} if options.objects - @list_title = "Class List" - @list_type = "class" - generate_list_contents -end - -def generate_puppet_manifest_list - @items = options.objects.select{|o| [:hostclass, :definedtype].include? o.type} if options.objects - @list_title = "Puppet Manifest List" - # This is important. It causes some YARD JavaScript bits to hook in and - # perform the correct formatting. - @list_class = "class" - @list_type = "puppet_manifest" - generate_list_contents -end - -def generate_puppet_plugin_list - # NOTE: PuppetNamaspaceObject might eventually be used for more than just a - # container for plugins... - @items = options.objects.select{|o| [:puppetnamespace].include? o.type} if options.objects - @list_title = "Puppet Plugin List" - # This is important. It causes some YARD JavaScript bits to hook in and - # perform the correct formatting. - @list_class = "class" - @list_type = "puppet_plugin" - generate_list_contents -end - -def generate_puppet_type_list - @items = options.objects.select{|o| [:type].include? o.type} if options.objects - @list_title = "Puppet Type List" - @list_type = "puppet_type" - generate_list_contents -end - -def generate_puppet_provider_list - @items = options.objects.select{|o| [:provider].include? o.type} if options.objects - @list_title = "Puppet Provider List" - @list_type = "puppet_provider" - generate_list_contents -end - -# A hacked version of class_list that can be instructed to only display certain -# namespace types. This allows us to separate Puppet bits from Ruby bits. -def namespace_list(opts = {}) - o = { - :root => Registry.root, - :namespace_types => [:module, :class] - }.merge(opts) - - root = o[:root] - namespace_types = o[:namespace_types] - - out = "" - children = run_verifier(root.children) - if root == Registry.root - children += @items.select {|o| o.namespace.is_a?(CodeObjects::Proxy) } - end - children.reject {|c| c.nil? }.sort_by {|child| child.path }.map do |child| - if namespace_types.include? child.type - if child.namespace.is_a?(CodeObjects::Proxy) - name = child.path - elsif child.is_a?(PuppetX::PuppetLabs::Strings::YARD::CodeObjects::TypeObject) || child.is_a?(PuppetX::PuppetLabs::Strings::YARD::CodeObjects::ProviderObject) - name = child.header_name - else - name = child.name - end - has_children = child.respond_to?(:children) && run_verifier(child.children).any? {|o| o.is_a?(CodeObjects::NamespaceObject) } - out << "
  • " - out << " " if has_children - out << linkify(child, name) - out << " < #{child.superclass.name}" if child.is_a?(CodeObjects::ClassObject) && child.superclass - out << "" - out << child.namespace.title - out << "" - out << "
  • " - out << "
      #{namespace_list(:root => child, :namespace_types => namespace_types)}
    " if has_children - end - end - out -end diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/hostclass/html/box_info.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/hostclass/html/box_info.erb deleted file mode 100644 index 334c764..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/hostclass/html/box_info.erb +++ /dev/null @@ -1,22 +0,0 @@ -<% n = 1 %> -
    - <% if object.parent_class %> -
    Inherits:
    -
    - <%= linkify object.parent_class, object.parent_class.path %> -
      - <% object.inheritance_tree.reverse.each_with_index do |obj, i| %> - - <% end %> -
    - show all -
    - <% n = 2 %> - <% end %> - <% unless object.root? %> -
    Defined in:
    -
    <%= erb(:defines) %>
    - <% end %> -
    -
    - diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/hostclass/html/setup.rb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/hostclass/html/setup.rb deleted file mode 100644 index 3cb9cdf..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/hostclass/html/setup.rb +++ /dev/null @@ -1 +0,0 @@ -include T('default/definedtype/html') diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/hostclass/html/subclasses.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/hostclass/html/subclasses.erb deleted file mode 100644 index 3948080..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/hostclass/html/subclasses.erb +++ /dev/null @@ -1,4 +0,0 @@ -
    -

    Direct Known Subclasses

    -

    <%= @subclasses.map {|child| linkify(child, child.path) }.join(", ") %>

    -
    diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/hostclass/setup.rb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/hostclass/setup.rb deleted file mode 100644 index b8bda9e..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/hostclass/setup.rb +++ /dev/null @@ -1,21 +0,0 @@ -include T('default/definedtype') - -def init - super - sections.push :subclasses -end - -def subclasses - # The naming is a bit weird because Ruby classes use `globals.subclasses`. - unless globals.hostsubclasses - globals.hostsubclasses = {} - list = run_verifier Registry.all(:hostclass) - list.each {|o| (globals.hostsubclasses[o.parent_class.path] ||= []) << o if o.parent_class } - end - - @subclasses = globals.hostsubclasses[object.path] - - return if @subclasses.nil? || @subclasses.empty? - erb(:subclasses) -end - diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/html_helper.rb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/html_helper.rb deleted file mode 100644 index 463371c..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/html_helper.rb +++ /dev/null @@ -1,139 +0,0 @@ -# A class containing helper methods to aid the generation of HTML -# given formatted data -class HTMLHelper - - # Generates the HTML to format the relevant data about return values - def generate_return_types(types, desc = nil) - result = [] - - result << "(" << types.join(", ") << ")" - - if !desc.nil? - result << "-

    #{desc}

    " - end - - result.join - end - - def generate_features features, object - result = [] - - if features - features.each do |feat| - result << "
  • " - result << "#{feat[:name]} " - if feat[:desc] - result << "-

    #{feat[:desc]}

    " - end - if feat[:methods] - result << "

    Methods

    " - result << "
      " - feat[:methods].each do |method| - result << "
    • " << method << "
    • " - end - result << "
    " - end - result << "
  • " - end - end - result.join - end - - # Generates the HTML to format the relevant data about parameters - def generate_parameters(params, object) - result = [] - - params.each do |param| - result << "
  • " - - # Parameters which are documented in the comments but not - # present in the code itself are given the strike through - # styling in order to show the reader that they do not actually - # exist - if !param[:exists?] - result << "" - end - - result << "#{param[:name]} " - result << "" - - # If the docstring specifies types, use those - if param[:types] - result << "(" << "" << param[:types].join(", ") << "" << ")" - # Otherwise, if typing information could be extracted from the object - # itself, use that - elsif object.type_info - # If the parameter name includes the default value, scrub that. - if param[:name].match(/([^=]*)=/) - param_name = $1 - else - param_name = param[:name] - end - # Collect all the possible types from the object. If no such type - # exists for this parameter name don't do anything. - possible_types = object.type_info.map { - |sig| sig[param_name] or nil - }.compact - - # If no possible types could be determined, put the type down as - # Unknown - if possible_types == [] - result << "(" << "Unknown" << ")" - else - result << "(" << "" << possible_types.join(", ") << "" << ")" - end - # Give up. It can probably be anything. - elsif not (param[:puppet_3_func] or param[:puppet_type]) - result << "(Unknown)" - end - if param[:puppet_type] and param[:parameter] - result << "(Parameter) " - elsif param[:puppet_type] and param[:property] - result << "(Property) " - end - - if param[:namevar] - result << "(Namevar) " - end - if param[:default] - result << " Default value: " << param[:default] << " " - end - - result << "" - - # This is only relevant for manifests, not puppet functions - # This is due to the fact that the scope of a parameter (as illustrated by - # by it's fully qualified name) is not relevant for the parameters in puppet - # functions, but may be for components of a manifest (i.e. classes) - unless param[:fq_name].nil? - result << " => #{param[:fq_name]}" - end - - if param[:desc] - result << " -

    #{param[:desc]}

    " - end - - if !param[:exists?] - result << "
    " - end - - if param[:allowed_values] and param[:allowed_values] != [] - result << "\n Allowed Values: " - result << "
      " - param[:allowed_values].each do |value_thing| - result << "
    • " - result << "" << value_thing.first << "" - if value_thing[1] - result << " only available if " << "" << value_thing[1] << "" - end - result << "
    • " - end - result << "
    \n" - end - - result << "
  • " - end - - result.join - end -end diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/layout/html/setup.rb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/layout/html/setup.rb deleted file mode 100644 index 05d8aeb..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/layout/html/setup.rb +++ /dev/null @@ -1,18 +0,0 @@ -# TODO: This should be extendable. However, the re-assignment of -# @objects_by_letter prevents that. Submit a pull request. -def index - @objects_by_letter = {} - objects = Registry.all(:class, :module, :type, :puppetnamespace, :hostclass, :definedtype, :provider).sort_by {|o| o.name.to_s } - objects = run_verifier(objects) - objects.each {|o| (@objects_by_letter[o.name.to_s[0,1].upcase] ||= []) << o } - erb(:index) -end - -def menu_lists - [ - {:type => 'puppet_manifest', :title => 'Puppet Manifests', :search_title => "Puppet Manifest List"}, - {:type => 'puppet_plugin', :title => 'Puppet Plugins', :search_title => "Puppet Plugin List"}, - {:type => 'puppet_type', :title => 'Puppet Types', :search_title => "Puppet Type List"}, - {:type => 'puppet_provider', :title => 'Puppet Providers', :search_title => "Puppet Provider List"}, - ] + super -end diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/method_details/html/header.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/method_details/html/header.erb deleted file mode 100644 index 1ca030b..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/method_details/html/header.erb +++ /dev/null @@ -1,17 +0,0 @@ -

    Method: <%= object.path %>

    -
    -
    -
    Defined in:
    -
    - <%= object.file %><% if object.files.size > 1 %>,
    - <%= object.files[1..-1].map {|f| f.first }.join(",
    ") %>
    - <% end %> - - - - -
    -
    - <%= yieldall :index => 0 %> -
    -
    \ No newline at end of file diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/method_details/setup.rb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/method_details/setup.rb deleted file mode 100644 index 94b383a..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/method_details/setup.rb +++ /dev/null @@ -1,21 +0,0 @@ -include T('default/module') -require File.join(File.dirname(__FILE__),'../html_helper') -require File.join(File.dirname(__FILE__),'../template_helper') - -def init - sections :header, [:method_signature, T('docstring'), :source] - parents = YARD::Registry.all(:method).reject do |item| - item.name == object.name and item.namespace === PuppetX::PuppetLabs::Strings::YARD::CodeObjects::PuppetNamespaceObject - end - if parents.length == 0 - @template_helper = TemplateHelper.new - @template_helper.check_parameters_match_docs object - end -end - -def source - return if owner != object.namespace - return if Tags::OverloadTag === object - return if object.source.nil? - erb(:source) -end diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/method_details/text/header.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/method_details/text/header.erb deleted file mode 100644 index e0429b1..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/method_details/text/header.erb +++ /dev/null @@ -1,2 +0,0 @@ -<%= yieldall %> - diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/command_details.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/command_details.erb deleted file mode 100644 index 6e9bf56..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/command_details.erb +++ /dev/null @@ -1,8 +0,0 @@ -

    Commands Summary

    -
    -
      - <% @command_details.each do |command| %> -
    • <%= command %>
    • - <% end %> -
    -
    diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/confine_details.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/confine_details.erb deleted file mode 100644 index fa7b88d..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/confine_details.erb +++ /dev/null @@ -1,10 +0,0 @@ -

    Confines

    -<% if @confine_details != {} %> -
    -
      - <% @confine_details.each_pair do |key, value| %> -
    • <%= key %> - <%= value %>
    • - <% end %> -
    -
    -<% end %> diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/default_details.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/default_details.erb deleted file mode 100644 index cc71ded..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/default_details.erb +++ /dev/null @@ -1,10 +0,0 @@ -

    Defaults

    -<% if @default_details != {} %> -
    -
      - <% @default_details.each_pair do |key, value| %> -
    • <%= key %> - <%= value %>
    • - <% end %> -
    -
    -<% end %> diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/docstring.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/docstring.erb deleted file mode 100644 index da20e29..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/docstring.erb +++ /dev/null @@ -1,34 +0,0 @@ -
    -
    -

    <%= htmlify(Puppet::Util::Docs::scrub(@class_details[:desc])) %>

    -
    -
    -
    - <% if @class_details[:examples] != {}%> -
    -

    Examples:

    - <% @class_details[:examples].each do |title, text| %> -

    <%= title %>

    -
    <%= text %>
    - <% end %> -
    - <% end %> - <% if @class_details[:since] %> -

    Since:

    -
      -
    • -
      -

      <%= @class_details[:since] %>

      -
      -
    • -
    - <% end %> - <% if @class_details[:return] %> -

    Return:

    -
      -
    • - <%= @html_helper.generate_return_types(@class_details[:return][1], @class_details[:return][0]) %> -
    • -
    - <% end %> -
    diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/feature_details.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/feature_details.erb deleted file mode 100644 index 6ebfcff..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/feature_details.erb +++ /dev/null @@ -1,10 +0,0 @@ -

    Features

    -<% if @feature_details != [] %> -
    -
      - <% @feature_details.each do |feature| %> -
    • <%= feature %>
    • - <% end %> -
    -
    -<% end %> diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/header.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/header.erb deleted file mode 100644 index 74b3dba..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/header.erb +++ /dev/null @@ -1,5 +0,0 @@ -
    -

    - <%= @header_text %> -

    -
    diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/setup.rb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/setup.rb deleted file mode 100644 index 91e114a..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/setup.rb +++ /dev/null @@ -1 +0,0 @@ -include T('default/module/html') diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/setup.rb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/setup.rb deleted file mode 100644 index 8acd72d..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/setup.rb +++ /dev/null @@ -1,50 +0,0 @@ -include T('default/module') - -require File.join(File.dirname(__FILE__),'../html_helper') -require File.join(File.dirname(__FILE__),'../template_helper') - -def init - sections :header, :box_info, :pre_docstring, :docstring, :command_details, :confine_details, :default_details, :feature_details - - @template_helper = TemplateHelper.new - @html_helper = HTMLHelper.new -end - -def header - @header_text = object.header_name - - erb(:header) -end - -def command_details - @command_details = object.commands - erb(:command_details) -end - -def confine_details - @confine_details = object.confines - erb(:confine_details) -end - -def default_details - @default_details = object.defaults - erb(:default_details) -end - -def feature_details - @feature_details = object.features - erb(:feature_details) -end - -def header - @header_text = "Puppet Provider: #{object.name}" - - erb(:header) -end - -def docstring - - @class_details = @template_helper.extract_tag_data(object) - - erb(:docstring) -end diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/html/box_info.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/html/box_info.erb deleted file mode 100644 index 1780c32..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/html/box_info.erb +++ /dev/null @@ -1,11 +0,0 @@ -
    -
    Defined in:
    -
    - <% @source_files.each do |file| %> - <%= file[0] %>: - <%= file[1] %> -
    - <% end %> -
    -
    -
    diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/html/header.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/html/header.erb deleted file mode 100644 index 74b3dba..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/html/header.erb +++ /dev/null @@ -1,5 +0,0 @@ -
    -

    - <%= @header_text %> -

    -
    diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/html/method_details_list.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/html/method_details_list.erb deleted file mode 100644 index b793a24..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/html/method_details_list.erb +++ /dev/null @@ -1,53 +0,0 @@ -

    Function Details

    -<% @class_details.each do |func| %> -

    > - - <% if func[:return] %> - <%= @html_helper.generate_return_types(func[:return][1]) %> - <% end %> - <%= func[:name] %> - -

    -
    -
    -

    <%= htmlify(func[:desc]) %>

    -
    -
    -
    - <% if func[:examples] != {}%> -
    -

    Examples:

    - <% func[:examples].each do |title, text| %> -

    <%= title %>

    -
    <%= text %>
    - <% end %> -
    - <% end %> - <% if func[:since] %> -

    Since:

    -
      -
    • -
      -

      <%= func[:since] %>

      -
      -
    • -
    - <% end %> - <% if func[:return] %> -

    Returns:

    -
      -
    • - <%= @html_helper.generate_return_types(func[:return][1], func[:return][0]) %> -
    • -
    - <% end %> - <% if func[:params] != nil %> -

    Parameters:

    -
    -
      - <%= @html_helper.generate_parameters(func[:params], object.child) %> -
    -
    - <% end %> -
    -<% end %> diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/html/method_summary.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/html/method_summary.erb deleted file mode 100644 index 11ec619..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/html/method_summary.erb +++ /dev/null @@ -1,20 +0,0 @@ -

    Available Functions

    - diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/html/setup.rb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/html/setup.rb deleted file mode 100644 index 91e114a..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/html/setup.rb +++ /dev/null @@ -1 +0,0 @@ -include T('default/module/html') diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/setup.rb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/setup.rb deleted file mode 100644 index e1e174a..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/setup.rb +++ /dev/null @@ -1,91 +0,0 @@ -include T('default/module') - -require File.join(File.dirname(__FILE__),'../html_helper') -require File.join(File.dirname(__FILE__),'../template_helper') - -def init - sections :header, :box_info, - :method_summary, [:item_summary], - :method_details_list, [T('method_details')] - - @methods = object.children - @template_helper = TemplateHelper.new -end - -def header - # The list is expected to only contain one type of function - if @methods[0]['puppet_4x_function'] - @header_text = "Puppet 4 Functions" - else - @header_text = "Puppet 3 Functions" - end - - erb(:header) -end - -def box_info - @source_files = [] - - @methods.each do |method| - # extract the file name and line number for each method - file_name = method.files[0][0] - line_number = method.files[0][1] - - @source_files.push([method.name, "#{file_name} (#{line_number})"]) - end - - erb(:box_info) -end - -def method_summary - @method_details = [] - @html_helper = HTMLHelper.new - - @methods.each do |method| - # If there are multiple sentences in the method description, only - # use the first one for the summary. If the author did not include - # any periods in their summary, include the whole thing - first_sentence = method.docstring.match(/^(.*?)\./) - brief_summary = first_sentence ? first_sentence : method.docstring - - return_tag = method.tags.find { |tag| tag.tag_name == "return"} - return_types = return_tag.nil? ? nil : return_tag.types - - @method_details.push({:name => method.name, :short_desc => brief_summary, :return_types => return_types}) - end - - erb(:method_summary) -end - -def method_details_list - @class_details = [] - @html_helper = HTMLHelper.new - - @methods.each do |object| - - method_info = @template_helper.extract_tag_data(object) - param_details = nil - param_tags = object.tags.find_all{ |tag| tag.tag_name == "param"} - - if object['puppet_4x_function'] - # Extract the source code - source_code = object.source - # Extract the parameters for the source code - parameters = source_code.match(/(?:def .*)\((.*?)\)/) - # Convert the matched string into an array of strings - params = parameters.nil? ? nil : parameters[1].split(/\s*,\s*/) - - param_details = @template_helper.extract_param_details(params, param_tags) unless params.nil? - @template_helper.check_types_match_docs object, param_details - @template_helper.check_parameters_match_docs object - else - param_details = @template_helper.comment_only_param_details(param_tags) - end - - method_info[:params] = param_details - - @class_details.push(method_info) - end - - erb(:method_details_list) -end diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/template_helper.rb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/template_helper.rb deleted file mode 100644 index fab415b..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/template_helper.rb +++ /dev/null @@ -1,192 +0,0 @@ -require "puppet" - -# A class containing helper methods to aid in the extraction of relevant data -# from comments and YARD tags -class TemplateHelper - # Extracts data from comments which include the supported YARD tags - def extract_tag_data(object) - examples = Hash.new - example_tags = object.tags.find_all { |tag| tag.tag_name == "example" } - example_tags.each do |example| - examples["#{example.name}"] = example.text - end - - return_tag = object.tags.find { |tag| tag.tag_name == "return"} - return_text = return_tag.nil? ? nil : return_tag.text - return_types = return_tag.nil? ? nil : return_tag.types - return_details = (return_text.nil? && return_types.nil?) ? nil : [return_text, return_types] - - since_tag = object.tags.find { |tag| tag.tag_name == "since"} - since_text = since_tag.nil? ? nil : since_tag.text - - {:name => object.name, :desc => object.docstring, :examples => examples, :since => since_text, :return => return_details} - end - - # Given the parameter information and YARD param tags, extracts the - # useful information and returns it as an array of hashes which can - # be printed and formatted as HTML - # - # @param parameters [Array] parameter details obtained programmatically - # @param tags_hash [Array] parameter details obtained from comments - # @param fq_name [Boolean] does this parameter have a fully qualified name? - # - # @return [Hash] The relevant information about each parameter with the following keys/values: - # {:name => [String] The name of the parameter - # :fq_name => [String] The fully qualified parameter name - # :desc => [String] The description provided in the comment - # :types => [Array] The parameter type(s) specified in the comment - # :exists => [Boolean] True only if the parameter exists in the documented logic and not just in a comment} - def extract_param_details(parameters, tags_hash, fq_name = false) - parameter_info = [] - - # Extract the information for parameters that exist - # as opposed to parameters that are defined only in the comments - parameters.each do |param| - if fq_name - param_name = param[0] - fully_qualified_name = param[1] - else - param_name = param - end - - param_tag = tags_hash.find { |tag| tag.name == param_name } - - description = param_tag.nil? ? nil : param_tag.text - param_types = param_tag.nil? ? nil : param_tag.types - - param_details = {:name => param_name, :desc => description, :types => param_types, :exists? => true} - - if fq_name - param_details[:fq_name] = fully_qualified_name - end - - parameter_info.push(param_details) - end - - # Check if there were any comments for parameters that do not exist - tags_hash.each do |tag| - param_exists = false - parameter_info.each do |parameter| - if parameter[:name] == tag.name - param_exists = true - end - end - if !param_exists - parameter_info.push({:name => tag.name, :desc => tag.text, :types => tag.types, :exists? => false}) - end - end - - parameter_info - end - - # Generates parameter information in situations where the information can only - # come from YARD tags in the comments, not from the code itself. For now the only - # use for this is 3x functions. In this case exists? will always be true since we - # cannot verify if the parameter exists in the code itself. We must trust the user - # to provide information in the comments that is accurate. - # - # @param param_tags [Array] parameter details obtained from comments - # - # @return [Hash] The relevant information about each parameter with the following keys/values: - # {:name => [String] The name of the parameter - # :desc => [String] The description provided in the comment - # :types => [Array] The parameter type(s) specified in the comment - # :exists => [Boolean] True only if the parameter exists in the documented logic and not just in a comment - # :puppet_3_func => [Boolean] Are these parameters for a puppet 3 function? (relevant in HTML generation)} - def comment_only_param_details(param_tags) - return if param_tags.empty? - - parameter_info = [] - - param_tags.each do |tag| - parameter_info.push({:name => tag.name, :desc => tag.text, :types => tag.types, :exists? => true, :puppet_3_func => true}) - end - - parameter_info - end - - # Check that any types specified in the docstrings match the actual method - # types. This is used by puppet 4x functions and defined types. - # @param object the code object to examine for parameters names - def check_types_match_docs(object, params_hash) - # We'll need this to extract type info from the type specified by the - # docstring. - type_parser = Puppet::Pops::Types::TypeParser.new - type_calculator = Puppet::Pops::Types::TypeCalculator.new - - object.type_info.each do |function| - function.keys.each do |key| - if function[key].class == String - begin - instantiated = type_parser.parse function[key] - rescue Puppet::ParseError - # Likely the result of a malformed type - next - end - else - instantiated = function[key] - end - params_hash.each do |param| - if param[:name] == key and param[:types] != nil - param[:types].each do |type| - param_instantiated = type_parser.parse type - if not type_calculator.assignable? instantiated, param_instantiated - actual_types = object.type_info.map do |sig| - sig[key].to_s if sig[key] - end.compact - # Get the locations where the object can be found. We only care about - # the first one. - locations = object.files - warning = <<-EOS -[warn]: @param tag types do not match the code. The #{param[:name]} - parameter is declared as types #{param[:types]} in the docstring, - but the code specifies the types #{actual_types} - EOS - - # If the locations aren't in the shape we expect then report that - # the file number couldn't be determined. - if locations.length >= 1 and locations[0].length == 2 - file = locations[0][0] - line = locations[0][1] - warning += " in the file #{file} near line #{line}." - else - warning += " Sorry, the file and line number could " + - "not be determined." - end - $stderr.puts warning - end - end - end - end - end - end - end - - # Check that the actual function parameters match what is stated in the docs. - # If there is a mismatch, print a warning to stderr. - # This is necessary for puppet classes and defined types. This type of - # warning will be issued for ruby code by the ruby docstring parser. - # @param object the code object to examine for parameters names - def check_parameters_match_docs(object) - param_tags = object.tags.find_all{ |tag| tag.tag_name == "param"} - names = object.parameters.map {|l| l.first.gsub(/\W/, '') } - locations = object.files - param_tags.each do |tag| - if not names.include?(tag.name) - if locations.length >= 1 and locations[0].length == 2 - file_name = locations[0][0] - line_number = locations[0][1] - $stderr.puts <<-EOS -[warn]: The parameter #{tag.name} is documented, but doesn't exist in - your code, in file #{file_name} near line #{line_number}. -EOS - else - $stderr.puts <<-EOS -[warn]: The parameter #{tag.name} is documented, but doesn't exist in - your code. Sorry, the file and line number could not be determined. -EOS - end - end - end - end -end diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/html/docstring.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/html/docstring.erb deleted file mode 100644 index da20e29..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/html/docstring.erb +++ /dev/null @@ -1,34 +0,0 @@ -
    -
    -

    <%= htmlify(Puppet::Util::Docs::scrub(@class_details[:desc])) %>

    -
    -
    -
    - <% if @class_details[:examples] != {}%> -
    -

    Examples:

    - <% @class_details[:examples].each do |title, text| %> -

    <%= title %>

    -
    <%= text %>
    - <% end %> -
    - <% end %> - <% if @class_details[:since] %> -

    Since:

    -
      -
    • -
      -

      <%= @class_details[:since] %>

      -
      -
    • -
    - <% end %> - <% if @class_details[:return] %> -

    Return:

    -
      -
    • - <%= @html_helper.generate_return_types(@class_details[:return][1], @class_details[:return][0]) %> -
    • -
    - <% end %> -
    diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/html/header.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/html/header.erb deleted file mode 100644 index 74b3dba..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/html/header.erb +++ /dev/null @@ -1,5 +0,0 @@ -
    -

    - <%= @header_text %> -

    -
    diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/html/parameter_details.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/html/parameter_details.erb deleted file mode 100644 index 3d57a79..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/html/parameter_details.erb +++ /dev/null @@ -1,12 +0,0 @@ -

    Parameter Summary

    -
    -
      - <%= @html_helper.generate_parameters(@param_details, object) %> -
    -
    -

    Features

    -
    -
      - <%= @html_helper.generate_features(@feature_details, object) %> -
    -
    diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/html/provider_details.erb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/html/provider_details.erb deleted file mode 100644 index 779af2d..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/html/provider_details.erb +++ /dev/null @@ -1,10 +0,0 @@ -

    Available Providers

    -<% if @providers != [] %> -
    - -
    -<% end %> diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/html/setup.rb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/html/setup.rb deleted file mode 100644 index 91e114a..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/html/setup.rb +++ /dev/null @@ -1 +0,0 @@ -include T('default/module/html') diff --git a/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/setup.rb b/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/setup.rb deleted file mode 100644 index 016e269..0000000 --- a/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/setup.rb +++ /dev/null @@ -1,55 +0,0 @@ -include T('default/module') - -require File.join(File.dirname(__FILE__),'../html_helper') -require File.join(File.dirname(__FILE__),'../template_helper') - -def init - sections :header, :box_info, :pre_docstring, :docstring, :parameter_details, :provider_details - - @template_helper = TemplateHelper.new - @html_helper = HTMLHelper.new -end - -def header - @header_text = object.header_name - - erb(:header) -end - -def provider_details - type_name = object.name.to_s - @providers = YARD::Registry.all(:provider).select { |t| t.type_name == type_name } - - erb(:provider_details) -end - -def parameter_details - params = object.parameter_details.map { |h| h[:name] } - # Put properties and parameters in one big list where the descriptions are - # scrubbed and htmlified and the namevar is the first element, the ensure - # property the second, and the rest are alphabetized. - @param_details = (object.parameter_details + object.property_details).each { - |h| h[:desc] = htmlify(Puppet::Util::Docs::scrub(h[:desc])) if h[:desc] - }.sort { |a, b| a[:name] <=> b[:name] } - # Float ensurable and namevars to the top of the list - @param_details = @param_details.partition{|a| a[:name] == 'ensure'}.flatten - @param_details = @param_details.partition{|a| a[:namevar]}.flatten - - @feature_details = object.features - @template_helper.check_parameters_match_docs object - - erb(:parameter_details) -end - -def header - @header_text = "Puppet Type: #{object.name}" - - erb(:header) -end - -def docstring - - @class_details = @template_helper.extract_tag_data(object) - - erb(:docstring) -end diff --git a/puppet-strings.gemspec b/puppet-strings.gemspec index 2a28e97..77c8314 100644 --- a/puppet-strings.gemspec +++ b/puppet-strings.gemspec @@ -1,11 +1,11 @@ Gem::Specification.new do |s| s.name = 'puppet-strings' - s.author = 'Puppet Labs' + s.author = 'Puppet Inc.' s.version = '0.4.0' s.license = 'Apache-2.0' s.summary = 'Puppet documentation via YARD' - s.email = 'info@puppetlabs.com' - s.homepage = 'https://github.com/puppetlabs/puppetlabs-strings' + s.email = 'info@puppet.com' + s.homepage = 'https://github.com/puppetlabs/puppet-strings' s.description = s.summary s.extra_rdoc_files = [ @@ -18,5 +18,5 @@ Gem::Specification.new do |s| s.files = `git ls-files`.split("\n") - Dir['.*', '*.gemspec'] s.add_runtime_dependency 'puppet', '>= 3.7.0' - s.add_runtime_dependency 'yard', '~> 0.8' + s.add_runtime_dependency 'yard', '~> 0.9.5' end diff --git a/spec/acceptance/running_strings_yardoc.rb b/spec/acceptance/running_strings_yardoc.rb deleted file mode 100644 index 38b9fd4..0000000 --- a/spec/acceptance/running_strings_yardoc.rb +++ /dev/null @@ -1,28 +0,0 @@ -require 'spec_helper_acceptance' -require 'json' - -describe 'Genearting module documation using yardoc 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", "#{test_module_path}/**/*.{rb,pp}") - end - - it "should generate documentation for manifests" do - expect(read_file_on(master, '/root/doc/test.html')).to include("Class: test") - end - - it "should generate documenation for 3x functions" do - expect(read_file_on(master, '/root/doc/Puppet3xFunctions.html')).to include("This is the function documentation for `function3x`") - end - - it "should generate documenation for 4x functions" do - expect(read_file_on(master, '/root/doc/Puppet4xFunctions.html')).to include("This is a function which is used to test puppet strings") - end -end diff --git a/spec/fixtures/unit/json/output.json b/spec/fixtures/unit/json/output.json new file mode 100644 index 0000000..a6d1197 --- /dev/null +++ b/spec/fixtures/unit/json/output.json @@ -0,0 +1,348 @@ +{ + "puppet_classes": [ + { + "name": "klass", + "file": "(stdin)", + "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 klass(Integer $param1, $param2, String $param3 = hi) inherits foo::bar {\n}" + } + ], + "defined_types": [ + { + "name": "dt", + "file": "(stdin)", + "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": "(stdin)", + "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": "(stdin)", + "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": "(stdin)", + "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": "(stdin)", + "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": "(stdin)", + "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" + } + ] +} diff --git a/spec/lib/strings_spec/module_helper.rb b/spec/lib/strings_spec/module_helper.rb deleted file mode 100644 index 77bc860..0000000 --- a/spec/lib/strings_spec/module_helper.rb +++ /dev/null @@ -1,20 +0,0 @@ -class PuppetModuleHelper -# Helper methods to handle file operations around generating and loading HTML - def self.using_module(path, modulename, &block) - Dir.mktmpdir do |tmp| - module_location = File.join(path, "examples", modulename) - FileUtils.cp_r(module_location, tmp) - old_dir = Dir.pwd - begin - Dir.chdir(tmp) - yield(tmp) - ensure - Dir.chdir(old_dir) - end - end - end - - def self.read_html(dir, modulename, file) - File.read(File.join(dir, modulename, 'doc', file)) - end -end diff --git a/spec/lib/strings_spec/parsing.rb b/spec/lib/strings_spec/parsing.rb deleted file mode 100644 index 9d1f2b3..0000000 --- a/spec/lib/strings_spec/parsing.rb +++ /dev/null @@ -1,42 +0,0 @@ -require 'spec_helper' - -module StringsSpec - module Parsing - - # Cleans up the Registry and gives YARD some source code - # to generate documentation for - def parse(string, parser = :ruby) - YARD::Registry.clear - YARD::Parser::SourceParser.parse_string(string, parser) - end - - # A custom matcher that allows us to compare aspects of a - # Code Objects to the specified values. This gives us a - # simplified way to ensure that the Code Object added to the - # Registry is what we expect when testing handlers - RSpec::Matchers.define :document_a do |arguments| - match do |actual| - @mismatches = compare_values(actual, arguments) - @mismatches.empty? - end - - failure_message do |actual| - @mismatches.collect do |key, value| - "Expected #{key} to be <#{value[1]}>, but got <#{value[0]}>." - end.join("\n") - end - - def compare_values(actual, expected) - mismatched_arguments = {} - expected.each do |key, value| - actual_value = actual.send(key) - if actual_value != value - mismatched_arguments[key] = [actual_value, value] - end - end - mismatched_arguments - end - end - end -end - diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a751957..ac86301 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,13 +1,17 @@ -dir = File.expand_path(File.dirname(__FILE__)) -$LOAD_PATH.unshift File.join(dir, 'lib') - require 'mocha' -require 'puppet' require 'rspec' +require 'puppet-strings' +require 'puppet-strings/yard' -require 'puppet_x/puppetlabs/strings' +# Explicitly set up YARD once +PuppetStrings::Yard.setup! RSpec.configure do |config| - config.mock_with :mocha + config.mock_with :mocha + + config.before(:each) do + # Always clear the YARD registry before each example + YARD::Registry.clear + end end diff --git a/spec/spec_helper_acceptance.rb b/spec/spec_helper_acceptance.rb index 5a69594..e64afc4 100644 --- a/spec/spec_helper_acceptance.rb +++ b/spec/spec_helper_acceptance.rb @@ -6,18 +6,16 @@ unless ENV['RS_PROVISION'] == 'no' end RSpec.configure do |c| - # Readable test descriptions c.formatter = :documentation # Configure all nodes in nodeset c.before :suite do - - hosts.each do |host| + hosts.each do |host| scp_to(host, Dir.glob('puppet-strings*.gem').first, 'puppet-strings.gem') on host, 'gem install puppet-strings.gem' - scp_to(host, Dir.glob('spec/unit/puppet/examples/test/pkg/username-test*.gz').first, 'test.tar.gz') + scp_to(host, Dir.glob('acceptance/fixtures/modules/test/pkg/username-test*.gz').first, 'test.tar.gz') on host, puppet('module', 'install', 'test.tar.gz') on host, 'gem install yard' diff --git a/spec/unit/puppet-strings/json_spec.rb b/spec/unit/puppet-strings/json_spec.rb new file mode 100644 index 0000000..4174f92 --- /dev/null +++ b/spec/unit/puppet-strings/json_spec.rb @@ -0,0 +1,132 @@ +require 'spec_helper' +require 'puppet-strings/json' +require 'tempfile' + +describe PuppetStrings::Json do + before :each do + # Populate the YARD registry with both Puppet and Ruby source + YARD::Parser::SourceParser.parse_string(<<-SOURCE, :puppet) +# A simple class. +# @param param1 First param. +# @param param2 Second param. +# @param param3 Third param. +class klass(Integer $param1, $param2, String $param3 = hi) inherits foo::bar { +} + +# A simple defined type. +# @param param1 First param. +# @param param2 Second param. +# @param param3 Third param. +define dt(Integer $param1, $param2, String $param3 = hi) { +} + +# A simple function. +# @param param1 First param. +# @param param2 Second param. +# @param param3 Third param. +# @return [Undef] Returns nothing. +function func(Integer $param1, $param2, String $param3 = hi) { +} +SOURCE + + YARD::Parser::SourceParser.parse_string(<<-SOURCE, :ruby) +Puppet::Parser::Functions.newfunction(:func3x, doc: <<-DOC +An example 3.x function. +@param [String] first The first parameter. +@param second The second parameter. +@return [Undef] Returns nothing. +DOC +) do |*args| +end + +# An example 4.x function. +Puppet::Functions.create_function(:func4x) do + # The first overload. + # @param param1 The first parameter. + # @param param2 The second parameter. + # @param param3 The third parameter. + # @return [Undef] Returns nothing. + dispatch :foo do + param 'Integer', :param1 + param 'Any', :param2 + optional_param 'Array[String]', :param3 + end + + # The second overload. + # @param param The first parameter. + # @param block The block parameter. + # @return [String] Returns a string. + dispatch :other do + param 'Boolean', :param + block_param + end +end + +Puppet::Type.type(:database).provide :linux do + desc 'An example provider on Linux.' + confine kernel: 'Linux' + confine osfamily: 'RedHat' + defaultfor kernel: 'Linux' + has_feature :implements_some_feature + has_feature :some_other_feature + commands foo: /usr/bin/foo +end + +Puppet::Type.newtype(:database) do + desc 'An example database server resource type.' + feature :encryption, 'The provider supports encryption.', methods: [:encrypt] + ensurable do + desc 'What state the database should be in.' + defaultvalues + aliasvalue(:up, :present) + aliasvalue(:down, :absent) + defaultto :up + end + + newparam(:address) do + isnamevar + desc 'The database server name.' + end + + newparam(:encryption_key, required_features: :encryption) do + desc 'The encryption key to use.' + end + + newparam(:encrypt, :parent => Puppet::Parameter::Boolean) do + desc 'Whether or not to encrypt the database.' + defaultto false + end + + newproperty(:file) do + desc 'The database file to use.' + end + + newproperty(:log_level) do + desc 'The log level to use.' + newvalue(:debug) + newvalue(:warn) + newvalue(:error) + defaultto 'warn' + end +end +SOURCE + end + + let(:baseline_path) { File.join(File.dirname(__FILE__), '../../fixtures/unit/json/output.json') } + let(:baseline) { File.read(baseline_path) } + + describe 'rendering JSON to a file' do + it 'should output the expected JSON content' do + Tempfile.open('json') do |file| + PuppetStrings::Json.render(file.path) + expect(File.read(file.path)).to eq(baseline) + end + end + end + + describe 'rendering JSON to stdout' do + it 'should output the expected JSON content' do + expect{ PuppetStrings::Json.render(nil) }.to output(baseline).to_stdout + end + end +end diff --git a/spec/unit/puppet-strings/yard/handlers/puppet/class_handler_spec.rb b/spec/unit/puppet-strings/yard/handlers/puppet/class_handler_spec.rb new file mode 100644 index 0000000..cc5b7c4 --- /dev/null +++ b/spec/unit/puppet-strings/yard/handlers/puppet/class_handler_spec.rb @@ -0,0 +1,155 @@ +require 'spec_helper' +require 'puppet-strings/yard' + +describe PuppetStrings::Yard::Handlers::Puppet::ClassHandler do + subject { + YARD::Parser::SourceParser.parse_string(source, :puppet) + YARD::Registry.all(:puppet_class) + } + + describe 'parsing source without a class definition' do + let(:source) { 'notice hi' } + + it 'no classes should be in the registry' do + expect(subject.empty?).to eq(true) + end + end + + describe 'parsing source with a syntax error' do + let(:source) { 'class foo{' } + + it 'should log an error' do + expect{ subject }.to output(/\[error\]: Failed to parse \(stdin\): Syntax error at end of file/).to_stdout_from_any_process + expect(subject.empty?).to eq(true) + end + end + + describe 'parsing a class with a missing docstring' do + let(:source) { 'class foo{}' } + + it 'should log a warning' do + expect{ subject }.to output(/\[warn\]: Missing documentation for Puppet class 'foo' at \(stdin\):1\./).to_stdout_from_any_process + end + end + + describe 'parsing a class with a docstring' do + let(:source) { <<-SOURCE +# A simple foo class. +# @param param1 First param. +# @param param2 Second param. +# @param param3 Third param. +class foo(Integer $param1, $param2, String $param3 = hi) inherits foo::bar { + file { '/tmp/foo': + ensure => present + } +} +SOURCE + } + + it 'should register a class object' do + expect(subject.size).to eq(1) + object = subject.first + expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Class) + expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Classes.instance) + expect(object.name).to eq(:foo) + expect(object.statement).not_to eq(nil) + expect(object.parameters).to eq([['param1', nil], ['param2', nil], ['param3', 'hi']]) + expect(object.docstring).to eq('A simple foo class.') + expect(object.docstring.tags.size).to eq(4) + tags = object.docstring.tags(:param) + expect(tags.size).to eq(3) + expect(tags[0].name).to eq('param1') + expect(tags[0].text).to eq('First param.') + expect(tags[0].types).to eq(['Integer']) + expect(tags[1].name).to eq('param2') + expect(tags[1].text).to eq('Second param.') + expect(tags[1].types).to eq(['Any']) + expect(tags[2].name).to eq('param3') + expect(tags[2].text).to eq('Third param.') + expect(tags[2].types).to eq(['String']) + tags = object.docstring.tags(:api) + expect(tags.size).to eq(1) + expect(tags[0].text).to eq('public') + end + end + + describe 'parsing a class with a missing parameter' do + let(:source) { <<-SOURCE +# A simple foo class. +# @param param1 First param. +# @param param2 Second param. +# @param param3 Third param. +# @param param4 missing! +class foo(Integer $param1, $param2, String $param3 = hi) inherits foo::bar { + file { '/tmp/foo': + ensure => present + } +} +SOURCE + } + + it 'should output a warning' do + expect{ subject }.to output(/\[warn\]: The @param tag for parameter 'param4' has no matching parameter at \(stdin\):6\./).to_stdout_from_any_process + end + end + + describe 'parsing a class with a missing @param tag' do + let(:source) { <<-SOURCE +# A simple foo class. +# @param param1 First param. +# @param param2 Second param. +class foo(Integer $param1, $param2, String $param3 = hi) inherits foo::bar { + file { '/tmp/foo': + ensure => present + } +} +SOURCE + } + + it 'should output a warning' do + expect{ subject }.to output(/\[warn\]: Missing @param tag for parameter 'param3' near \(stdin\):4\./).to_stdout_from_any_process + end + end + + describe 'parsing a class with a typed parameter that also has a @param tag type' do + let(:source) { <<-SOURCE +# A simple foo class. +# @param [Boolean] param1 First param. +# @param param2 Second param. +# @param param3 Third param. +class foo(Integer $param1, $param2, String $param3 = hi) inherits foo::bar { + file { '/tmp/foo': + ensure => present + } +} +SOURCE + } + + it 'should output a warning' do + expect{ subject }.to output(/\[warn\]: The @param tag for parameter 'param1' should not contain a type specification near \(stdin\):5: ignoring in favor of parameter type information\./).to_stdout_from_any_process + end + end + + describe 'parsing a class with a untyped parameter that also has a @param tag type' do + let(:source) { <<-SOURCE +# A simple foo class. +# @param param1 First param. +# @param [Boolean] param2 Second param. +# @param param3 Third param. +class foo(Integer $param1, $param2, String $param3 = hi) inherits foo::bar { + file { '/tmp/foo': + ensure => present + } +} +SOURCE + } + + it 'should respect the type that was documented' do + expect{ subject }.to output('').to_stdout_from_any_process + expect(subject.size).to eq(1) + tags = subject.first.tags(:param) + expect(tags.size).to eq(3) + expect(tags[1].types).to eq(['Boolean']) + end + end +end diff --git a/spec/unit/puppet-strings/yard/handlers/puppet/defined_type_handler_spec.rb b/spec/unit/puppet-strings/yard/handlers/puppet/defined_type_handler_spec.rb new file mode 100644 index 0000000..633f8f0 --- /dev/null +++ b/spec/unit/puppet-strings/yard/handlers/puppet/defined_type_handler_spec.rb @@ -0,0 +1,155 @@ +require 'spec_helper' +require 'puppet-strings/yard' + +describe PuppetStrings::Yard::Handlers::Puppet::DefinedTypeHandler do + subject { + YARD::Parser::SourceParser.parse_string(source, :puppet) + YARD::Registry.all(:puppet_defined_type) + } + + describe 'parsing source without a defined type definition' do + let(:source) { 'notice hi' } + + it 'no defined types should be in the registry' do + expect(subject.empty?).to eq(true) + end + end + + describe 'parsing source with a syntax error' do + let(:source) { 'define foo{' } + + it 'should log an error' do + expect{ subject }.to output(/\[error\]: Failed to parse \(stdin\): Syntax error at end of file/).to_stdout_from_any_process + expect(subject.empty?).to eq(true) + end + end + + describe 'parsing a defined type with a missing docstring' do + let(:source) { 'define foo{}' } + + it 'should log a warning' do + expect{ subject }.to output(/\[warn\]: Missing documentation for Puppet defined type 'foo' at \(stdin\):1\./).to_stdout_from_any_process + end + end + + describe 'parsing a defined type with a docstring' do + let(:source) { <<-SOURCE +# A simple foo defined type. +# @param param1 First param. +# @param param2 Second param. +# @param param3 Third param. +define foo(Integer $param1, $param2, String $param3 = hi) { + file { '/tmp/foo': + ensure => present + } +} +SOURCE + } + + it 'should register a defined type object' do + expect(subject.size).to eq(1) + object = subject.first + expect(object).to be_a(PuppetStrings::Yard::CodeObjects::DefinedType) + expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::DefinedTypes.instance) + expect(object.name).to eq(:foo) + expect(object.statement).not_to eq(nil) + expect(object.parameters).to eq([['param1', nil], ['param2', nil], ['param3', 'hi']]) + expect(object.docstring).to eq('A simple foo defined type.') + expect(object.docstring.tags.size).to eq(4) + tags = object.docstring.tags(:param) + expect(tags.size).to eq(3) + expect(tags[0].name).to eq('param1') + expect(tags[0].text).to eq('First param.') + expect(tags[0].types).to eq(['Integer']) + expect(tags[1].name).to eq('param2') + expect(tags[1].text).to eq('Second param.') + expect(tags[1].types).to eq(['Any']) + expect(tags[2].name).to eq('param3') + expect(tags[2].text).to eq('Third param.') + expect(tags[2].types).to eq(['String']) + tags = object.docstring.tags(:api) + expect(tags.size).to eq(1) + expect(tags[0].text).to eq('public') + end + end + + describe 'parsing a defined type with a missing parameter' do + let(:source) { <<-SOURCE +# A simple foo defined type. +# @param param1 First param. +# @param param2 Second param. +# @param param3 Third param. +# @param param4 missing! +define foo(Integer $param1, $param2, String $param3 = hi) { + file { '/tmp/foo': + ensure => present + } +} +SOURCE + } + + it 'should output a warning' do + expect{ subject }.to output(/\[warn\]: The @param tag for parameter 'param4' has no matching parameter at \(stdin\):6\./).to_stdout_from_any_process + end + end + + describe 'parsing a defined type with a missing @param tag' do + let(:source) { <<-SOURCE +# A simple foo defined type. +# @param param1 First param. +# @param param2 Second param. +define foo(Integer $param1, $param2, String $param3 = hi) { + file { '/tmp/foo': + ensure => present + } +} +SOURCE + } + + it 'should output a warning' do + expect{ subject }.to output(/\[warn\]: Missing @param tag for parameter 'param3' near \(stdin\):4\./).to_stdout_from_any_process + end + end + + describe 'parsing a defined type with a typed parameter that also has a @param tag type' do + let(:source) { <<-SOURCE +# A simple foo defined type. +# @param [Boolean] param1 First param. +# @param param2 Second param. +# @param param3 Third param. +define foo(Integer $param1, $param2, String $param3 = hi) { + file { '/tmp/foo': + ensure => present + } +} +SOURCE + } + + it 'should output a warning' do + expect{ subject }.to output(/\[warn\]: The @param tag for parameter 'param1' should not contain a type specification near \(stdin\):5: ignoring in favor of parameter type information\./).to_stdout_from_any_process + end + end + + describe 'parsing a defined type with a untyped parameter that also has a @param tag type' do + let(:source) { <<-SOURCE +# A simple foo defined type. +# @param param1 First param. +# @param [Boolean] param2 Second param. +# @param param3 Third param. +define foo(Integer $param1, $param2, String $param3 = hi) { + file { '/tmp/foo': + ensure => present + } +} +SOURCE + } + + it 'should respect the type that was documented' do + expect{ subject }.to output('').to_stdout_from_any_process + expect(subject.size).to eq(1) + tags = subject.first.tags(:param) + expect(tags.size).to eq(3) + expect(tags[1].types).to eq(['Boolean']) + end + end +end diff --git a/spec/unit/puppet-strings/yard/handlers/puppet/function_handler_spec.rb b/spec/unit/puppet-strings/yard/handlers/puppet/function_handler_spec.rb new file mode 100644 index 0000000..81ae0be --- /dev/null +++ b/spec/unit/puppet-strings/yard/handlers/puppet/function_handler_spec.rb @@ -0,0 +1,168 @@ +require 'spec_helper' +require 'puppet-strings/yard' + +describe PuppetStrings::Yard::Handlers::Puppet::FunctionHandler do + subject { + YARD::Parser::SourceParser.parse_string(source, :puppet) + YARD::Registry.all(:puppet_function) + } + + describe 'parsing source without a function definition' do + let(:source) { 'notice hi' } + + it 'no functions should be in the registry' do + expect(subject.empty?).to eq(true) + end + end + + describe 'parsing source with a syntax error' do + let(:source) { 'function foo{' } + + it 'should log an error' do + expect{ subject }.to output(/\[error\]: Failed to parse \(stdin\): Syntax error at end of file/).to_stdout_from_any_process + expect(subject.empty?).to eq(true) + end + end + + describe 'parsing a function with a missing docstring' do + let(:source) { 'function foo{}' } + + it 'should log a warning' do + expect{ subject }.to output(/\[warn\]: Missing documentation for Puppet function 'foo' at \(stdin\):1\./).to_stdout_from_any_process + end + end + + describe 'parsing a function with a docstring' do + let(:source) { <<-SOURCE +# A simple foo function. +# @param param1 First param. +# @param param2 Second param. +# @param param3 Third param. +# @return [Undef] Returns nothing. +function foo(Integer $param1, $param2, String $param3 = hi) { + notice 'hello world' + undef +} +SOURCE + } + + it 'should register a function object' do + expect(subject.size).to eq(1) + object = subject.first + expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Function) + expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Functions.instance(PuppetStrings::Yard::CodeObjects::Function::PUPPET)) + expect(object.name).to eq(:foo) + expect(object.signature).to eq('foo(Integer $param1, Any $param2, String $param3 = hi)') + expect(object.parameters).to eq([['param1', nil], ['param2', nil], ['param3', 'hi']]) + expect(object.docstring).to eq('A simple foo function.') + expect(object.docstring.tags.size).to eq(5) + tags = object.docstring.tags(:param) + expect(tags.size).to eq(3) + expect(tags[0].name).to eq('param1') + expect(tags[0].text).to eq('First param.') + expect(tags[0].types).to eq(['Integer']) + expect(tags[1].name).to eq('param2') + expect(tags[1].text).to eq('Second param.') + expect(tags[1].types).to eq(['Any']) + expect(tags[2].name).to eq('param3') + expect(tags[2].text).to eq('Third param.') + expect(tags[2].types).to eq(['String']) + tags = object.docstring.tags(:api) + expect(tags.size).to eq(1) + expect(tags[0].text).to eq('public') + end + end + + describe 'parsing a function with a missing parameter' do + let(:source) { <<-SOURCE +# A simple foo function. +# @param param1 First param. +# @param param2 Second param. +# @param param3 Third param. +# @param param4 missing! +# @return [Undef] Returns nothing. +function foo(Integer $param1, $param2, String $param3 = hi) { + notice 'hello world' +} +SOURCE + } + + it 'should output a warning' do + expect{ subject }.to output(/\[warn\]: The @param tag for parameter 'param4' has no matching parameter at \(stdin\):7\./).to_stdout_from_any_process + end + end + + describe 'parsing a function with a missing @param tag' do + let(:source) { <<-SOURCE +# A simple foo function. +# @param param1 First param. +# @param param2 Second param. +# @return [Undef] Returns nothing. +function foo(Integer $param1, $param2, String $param3 = hi) { + notice 'hello world' +} +SOURCE + } + + it 'should output a warning' do + expect{ subject }.to output(/\[warn\]: Missing @param tag for parameter 'param3' near \(stdin\):5\./).to_stdout_from_any_process + end + end + + describe 'parsing a function with a typed parameter that also has a @param tag type' do + let(:source) { <<-SOURCE +# A simple foo function. +# @param [Boolean] param1 First param. +# @param param2 Second param. +# @param param3 Third param. +# @return [Undef] Returns nothing. +function foo(Integer $param1, $param2, String $param3 = hi) { + notice 'hello world' +} +SOURCE + } + + it 'should output a warning' do + expect{ subject }.to output(/\[warn\]: The @param tag for parameter 'param1' should not contain a type specification near \(stdin\):6: ignoring in favor of parameter type information\./).to_stdout_from_any_process + end + end + + describe 'parsing a function with a untyped parameter that also has a @param tag type' do + let(:source) { <<-SOURCE +# A simple foo function. +# @param param1 First param. +# @param [Boolean] param2 Second param. +# @param param3 Third param. +# @return [Undef] Returns nothing. +function foo(Integer $param1, $param2, String $param3 = hi) { + notice 'hello world' +} +SOURCE + } + + it 'should respect the type that was documented' do + expect{ subject }.to output('').to_stdout_from_any_process + expect(subject.size).to eq(1) + tags = subject.first.tags(:param) + expect(tags.size).to eq(3) + expect(tags[1].types).to eq(['Boolean']) + end + end + + describe 'parsing a function with a missing @return tag' do + let(:source) { <<-SOURCE +# A simple foo function. +# @param param1 First param. +# @param param2 Second param. +# @param param3 Third param. +function foo(Integer $param1, $param2, String $param3 = hi) { + notice 'hello world' +} +SOURCE + } + + it 'should output a warning' do + expect{ subject }.to output(/\[warn\]: Missing @return tag near \(stdin\):5\./).to_stdout_from_any_process + end + end +end diff --git a/spec/unit/puppet-strings/yard/handlers/ruby/function_handler_spec.rb b/spec/unit/puppet-strings/yard/handlers/ruby/function_handler_spec.rb new file mode 100644 index 0000000..d80e5a0 --- /dev/null +++ b/spec/unit/puppet-strings/yard/handlers/ruby/function_handler_spec.rb @@ -0,0 +1,613 @@ +require 'spec_helper' +require 'puppet-strings/yard' + +describe PuppetStrings::Yard::Handlers::Ruby::FunctionHandler do + subject { + YARD::Parser::SourceParser.parse_string(source, :ruby) + YARD::Registry.all(:puppet_function) + } + + describe 'parsing source without a function definition' do + let(:source) { 'puts "hi"' } + + it 'no functions should be in the registry' do + expect(subject.empty?).to eq(true) + end + end + + describe 'parsing 3.x API functions' do + describe 'parsing a function with a missing docstring' do + let(:source) { <<-SOURCE +Puppet::Parser::Functions.newfunction(:foo) do |*args| +end +SOURCE + } + + it 'should log a warning' do + expect{ subject }.to output(/\[warn\]: Missing documentation for Puppet function 'foo' at \(stdin\):1\./).to_stdout_from_any_process + end + end + + describe 'parsing a function with a doc parameter' do + let(:source) { <<-SOURCE +Puppet::Parser::Functions.newfunction(:foo, doc: <<-DOC +An example 3.x function. +@param [String] first The first parameter. +@param second The second parameter. +@return [Undef] Returns nothing. +DOC +) do |*args| +end +SOURCE + } + + it 'should register a function object' do + expect(subject.size).to eq(1) + object = subject.first + expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Function) + expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Functions.instance(PuppetStrings::Yard::CodeObjects::Function::RUBY_3X)) + expect(object.name).to eq(:foo) + expect(object.signature).to eq('foo(String $first, Any $second)') + expect(object.parameters).to eq([['first', nil], ['second', nil]]) + expect(object.docstring).to eq('An example 3.x function.') + expect(object.docstring.tags.size).to eq(4) + tags = object.docstring.tags(:param) + expect(tags.size).to eq(2) + expect(tags[0].name).to eq('first') + expect(tags[0].text).to eq('The first parameter.') + expect(tags[0].types).to eq(['String']) + expect(tags[1].name).to eq('second') + expect(tags[1].text).to eq('The second parameter.') + expect(tags[1].types).to eq(['Any']) + tags = object.docstring.tags(:return) + expect(tags.size).to eq(1) + expect(tags[0].name).to be_nil + expect(tags[0].text).to eq('Returns nothing.') + expect(tags[0].types).to eq(['Undef']) + tags = object.docstring.tags(:api) + expect(tags.size).to eq(1) + expect(tags[0].text).to eq('public') + end + end + + describe 'parsing a function with a missing @return tag' do + let(:source) { <<-SOURCE +Puppet::Parser::Functions.newfunction(:foo, doc: <<-DOC) do |*args| +An example 3.x function. +@param [String] first The first parameter. +@param second The second parameter. +DOC +end +SOURCE + } + + it 'should log a warning' do + expect{ subject }.to output(/\[warn\]: Missing @return tag near \(stdin\):1/).to_stdout_from_any_process + end + end + end + + describe 'parsing 4.x API functions' do + describe 'parsing a function with a missing docstring' do + let(:source) { <<-SOURCE +Puppet::Functions.create_function(:foo) do +end +SOURCE + } + + it 'should log a warning' do + expect{ subject }.to output(/\[warn\]: Missing documentation for Puppet function 'foo' at \(stdin\):1\./).to_stdout_from_any_process + end + end + + describe 'parsing a function with a simple docstring' do + let(:source) { <<-SOURCE +# An example 4.x function. +Puppet::Functions.create_function(:foo) do +end +SOURCE + } + + it 'should register a function object' do + expect(subject.size).to eq(1) + object = subject.first + expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Function) + expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Functions.instance(PuppetStrings::Yard::CodeObjects::Function::RUBY_4X)) + expect(object.name).to eq(:foo) + expect(object.signature).to eq('foo()') + expect(object.parameters).to eq([]) + expect(object.docstring).to eq('An example 4.x function.') + expect(object.docstring.tags.size).to eq(1) + tags = object.docstring.tags(:api) + expect(tags.size).to eq(1) + expect(tags[0].text).to eq('public') + end + end + + describe 'parsing a function without any dispatches' do + let(:source) { <<-SOURCE +# An example 4.x function. +Puppet::Functions.create_function(:foo) do + # @param [Integer] param1 The first parameter. + # @param param2 The second parameter. + # @param [String] param3 The third parameter. + # @return [Undef] Returns nothing. + def foo(param1, param2, param3 = nil) + end +end +SOURCE + } + + it 'should register a function object' do + expect(subject.size).to eq(1) + object = subject.first + expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Function) + expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Functions.instance(PuppetStrings::Yard::CodeObjects::Function::RUBY_4X)) + expect(object.name).to eq(:foo) + expect(object.signature).to eq('foo(Integer $param1, Any $param2, Optional[String] $param3 = undef)') + expect(object.parameters).to eq([['param1', nil], ['param2', nil], ['param3', 'undef']]) + expect(object.docstring).to eq('An example 4.x function.') + expect(object.docstring.tags.size).to eq(5) + tags = object.docstring.tags(:param) + expect(tags.size).to eq(3) + expect(tags[0].name).to eq('param1') + expect(tags[0].text).to eq('The first parameter.') + expect(tags[0].types).to eq(['Integer']) + expect(tags[1].name).to eq('param2') + expect(tags[1].text).to eq('The second parameter.') + expect(tags[1].types).to eq(['Any']) + expect(tags[2].name).to eq('param3') + expect(tags[2].text).to eq('The third parameter.') + expect(tags[2].types).to eq(['Optional[String]']) + tags = object.docstring.tags(:return) + expect(tags.size).to eq(1) + expect(tags[0].name).to be_nil + expect(tags[0].text).to eq('Returns nothing.') + expect(tags[0].types).to eq(['Undef']) + tags = object.docstring.tags(:api) + expect(tags.size).to eq(1) + expect(tags[0].text).to eq('public') + end + end + + describe 'parsing a function with a single dispatch' do + let(:source) { <<-SOURCE +# An example 4.x function. +Puppet::Functions.create_function(:foo) do + # @param param1 The first parameter. + # @param param2 The second parameter. + # @param param3 The third parameter. + # @return [Undef] Returns nothing. + dispatch :foo do + param 'Integer', :param1 + param 'Any', :param2 + optional_param 'Array[String]', :param3 + end + + def foo(param1, param2, param3 = nil) + end +end +SOURCE + } + + it 'should register a function object without any overload tags' do + expect(subject.size).to eq(1) + object = subject.first + expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Function) + expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Functions.instance(PuppetStrings::Yard::CodeObjects::Function::RUBY_4X)) + expect(object.name).to eq(:foo) + expect(object.signature).to eq('foo(Integer $param1, Any $param2, Optional[Array[String]] $param3)') + expect(object.parameters).to eq([['param1', nil], ['param2', nil], ['param3', nil]]) + expect(object.docstring).to eq('An example 4.x function.') + expect(object.docstring.tags(:overload).empty?).to be_truthy + expect(object.docstring.tags.size).to eq(5) + tags = object.docstring.tags(:param) + expect(tags.size).to eq(3) + expect(tags[0].name).to eq('param1') + expect(tags[0].text).to eq('The first parameter.') + expect(tags[0].types).to eq(['Integer']) + expect(tags[1].name).to eq('param2') + expect(tags[1].text).to eq('The second parameter.') + expect(tags[1].types).to eq(['Any']) + expect(tags[2].name).to eq('param3') + expect(tags[2].text).to eq('The third parameter.') + expect(tags[2].types).to eq(['Optional[Array[String]]']) + tags = object.docstring.tags(:return) + expect(tags.size).to eq(1) + expect(tags[0].name).to be_nil + expect(tags[0].text).to eq('Returns nothing.') + expect(tags[0].types).to eq(['Undef']) + tags = object.docstring.tags(:api) + expect(tags.size).to eq(1) + expect(tags[0].text).to eq('public') + end + end + + describe 'parsing a function with various dispatch parameters.' do + let(:source) { <<-SOURCE +# An example 4.x function. +Puppet::Functions.create_function(:foo) do + # @param param1 The first parameter. + # @param param2 The second parameter. + # @param param3 The third parameter. + # @param param4 The fourth parameter. + # @return [Undef] Returns nothing. + dispatch :foo do + param 'String', :param1 + required_param 'Integer', :param2 + optional_param 'Array', :param3 + repeated_param 'String', :param4 + end +end +SOURCE + } + + it 'should register a function object with the expected parameters' do + expect(subject.size).to eq(1) + object = subject.first + expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Function) + expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Functions.instance(PuppetStrings::Yard::CodeObjects::Function::RUBY_4X)) + expect(object.name).to eq(:foo) + expect(object.signature).to eq('foo(String $param1, Integer $param2, Optional[Array] $param3, String *$param4)') + expect(object.parameters).to eq([['param1', nil], ['param2', nil], ['param3', nil], ['*param4', nil]]) + expect(object.docstring).to eq('An example 4.x function.') + expect(object.docstring.tags(:overload).empty?).to be_truthy + expect(object.docstring.tags.size).to eq(6) + tags = object.docstring.tags(:param) + expect(tags.size).to eq(4) + expect(tags[0].name).to eq('param1') + expect(tags[0].text).to eq('The first parameter.') + expect(tags[0].types).to eq(['String']) + expect(tags[1].name).to eq('param2') + expect(tags[1].text).to eq('The second parameter.') + expect(tags[1].types).to eq(['Integer']) + expect(tags[2].name).to eq('param3') + expect(tags[2].text).to eq('The third parameter.') + expect(tags[2].types).to eq(['Optional[Array]']) + expect(tags[3].name).to eq('*param4') + expect(tags[3].text).to eq('The fourth parameter.') + expect(tags[3].types).to eq(['String']) + tags = object.docstring.tags(:return) + expect(tags.size).to eq(1) + expect(tags[0].name).to be_nil + expect(tags[0].text).to eq('Returns nothing.') + expect(tags[0].types).to eq(['Undef']) + tags = object.docstring.tags(:api) + expect(tags.size).to eq(1) + expect(tags[0].text).to eq('public') + end + end + + describe 'parsing a function with an optional repeated param.' do + let(:source) { <<-SOURCE +# An example 4.x function. +Puppet::Functions.create_function(:foo) do + # @param param The first parameter. + # @return [Undef] Returns nothing. + dispatch :foo do + optional_repeated_param 'String', :param + end +end +SOURCE + } + + it 'should register a function object with the expected parameters' do + expect(subject.size).to eq(1) + object = subject.first + expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Function) + expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Functions.instance(PuppetStrings::Yard::CodeObjects::Function::RUBY_4X)) + expect(object.name).to eq(:foo) + expect(object.signature).to eq('foo(Optional[String] *$param)') + expect(object.parameters).to eq([['*param', nil]]) + expect(object.docstring).to eq('An example 4.x function.') + expect(object.docstring.tags(:overload).empty?).to be_truthy + expect(object.docstring.tags.size).to eq(3) + tags = object.docstring.tags(:param) + expect(tags.size).to eq(1) + expect(tags[0].name).to eq('*param') + expect(tags[0].text).to eq('The first parameter.') + expect(tags[0].types).to eq(['Optional[String]']) + tags = object.docstring.tags(:return) + expect(tags.size).to eq(1) + expect(tags[0].name).to be_nil + expect(tags[0].text).to eq('Returns nothing.') + expect(tags[0].types).to eq(['Undef']) + tags = object.docstring.tags(:api) + expect(tags.size).to eq(1) + expect(tags[0].text).to eq('public') + end + end + + describe 'parsing a function with a block param with one parameter' do + let(:source) { <<-SOURCE +# An example 4.x function. +Puppet::Functions.create_function(:foo) do + # @param a_block The block parameter. + # @return [Undef] Returns nothing. + dispatch :foo do + block_param :a_block + end +end + SOURCE + } + + it 'should register a function object with the expected parameters' do + expect(subject.size).to eq(1) + object = subject.first + expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Function) + expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Functions.instance(PuppetStrings::Yard::CodeObjects::Function::RUBY_4X)) + expect(object.name).to eq(:foo) + expect(object.signature).to eq('foo(Callable &$a_block)') + expect(object.parameters).to eq([['&a_block', nil]]) + expect(object.docstring).to eq('An example 4.x function.') + expect(object.docstring.tags(:overload).empty?).to be_truthy + expect(object.docstring.tags.size).to eq(3) + tags = object.docstring.tags(:param) + expect(tags.size).to eq(1) + expect(tags[0].name).to eq('&a_block') + expect(tags[0].text).to eq('The block parameter.') + expect(tags[0].types).to eq(['Callable']) + tags = object.docstring.tags(:return) + expect(tags.size).to eq(1) + expect(tags[0].name).to be_nil + expect(tags[0].text).to eq('Returns nothing.') + expect(tags[0].types).to eq(['Undef']) + tags = object.docstring.tags(:api) + expect(tags.size).to eq(1) + expect(tags[0].text).to eq('public') + end + end + + describe 'parsing a function with a block param with two parameter' do + let(:source) { <<-SOURCE +# An example 4.x function. +Puppet::Functions.create_function(:foo) do + # @param a_block The block parameter. + # @return [Undef] Returns nothing. + dispatch :foo do + optional_block_param 'Callable[String]', :a_block + end +end + SOURCE + } + + it 'should register a function object with the expected parameters' do + expect(subject.size).to eq(1) + object = subject.first + expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Function) + expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Functions.instance(PuppetStrings::Yard::CodeObjects::Function::RUBY_4X)) + expect(object.name).to eq(:foo) + expect(object.signature).to eq('foo(Optional[Callable[String]] &$a_block)') + expect(object.parameters).to eq([['&a_block', nil]]) + expect(object.docstring).to eq('An example 4.x function.') + expect(object.docstring.tags(:overload).empty?).to be_truthy + expect(object.docstring.tags.size).to eq(3) + tags = object.docstring.tags(:param) + expect(tags.size).to eq(1) + expect(tags[0].name).to eq('&a_block') + expect(tags[0].text).to eq('The block parameter.') + expect(tags[0].types).to eq(['Optional[Callable[String]]']) + tags = object.docstring.tags(:return) + expect(tags.size).to eq(1) + expect(tags[0].name).to be_nil + expect(tags[0].text).to eq('Returns nothing.') + expect(tags[0].types).to eq(['Undef']) + tags = object.docstring.tags(:api) + expect(tags.size).to eq(1) + expect(tags[0].text).to eq('public') + end + end + end + + describe 'parsing a function with a multiple dispatches' do + let(:source) { <<-SOURCE +# An example 4.x function. +Puppet::Functions.create_function(:foo) do + # The first overload. + # @param param1 The first parameter. + # @param param2 The second parameter. + # @param param3 The third parameter. + # @return [Undef] Returns nothing. + dispatch :foo do + param 'Integer', :param1 + param 'Any', :param2 + optional_param 'Array[String]', :param3 + end + + # The second overload. + # @param param The first parameter. + # @param block The block parameter. + # @return [String] Returns a string. + dispatch :other do + param 'Boolean', :param + block_param + end + + def foo(param1, param2, param3 = nil) + end + + def other(b) + 'lol' + end +end +SOURCE + } + + it 'should register a function object with overload tags' do + expect(subject.size).to eq(1) + object = subject.first + expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Function) + expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Functions.instance(PuppetStrings::Yard::CodeObjects::Function::RUBY_4X)) + expect(object.name).to eq(:foo) + expect(object.signature).to eq('') + expect(object.parameters).to eq([]) + expect(object.docstring).to eq('An example 4.x function.') + expect(object.docstring.tags(:param).empty?).to be_truthy + expect(object.docstring.tags(:return).empty?).to be_truthy + expect(object.docstring.tags.size).to eq(3) + overloads = object.docstring.tags(:overload) + expect(overloads.size).to eq(2) + expect(overloads[0]).to be_a(PuppetStrings::Yard::Tags::OverloadTag) + expect(overloads[0].docstring).to eq('The first overload.') + expect(overloads[0].signature).to eq('foo(Integer $param1, Any $param2, Optional[Array[String]] $param3)') + expect(overloads[0].tags.size).to eq(4) + tags = overloads[0].tags(:param) + expect(tags.size).to eq(3) + expect(tags[0].name).to eq('param1') + expect(tags[0].text).to eq('The first parameter.') + expect(tags[0].types).to eq(['Integer']) + expect(tags[1].name).to eq('param2') + expect(tags[1].text).to eq('The second parameter.') + expect(tags[1].types).to eq(['Any']) + expect(tags[2].name).to eq('param3') + expect(tags[2].text).to eq('The third parameter.') + expect(tags[2].types).to eq(['Optional[Array[String]]']) + tags = overloads[0].tags(:return) + expect(tags.size).to eq(1) + expect(tags[0].name).to be_nil + expect(tags[0].text).to eq('Returns nothing.') + expect(tags[0].types).to eq(['Undef']) + expect(overloads[1]).to be_a(PuppetStrings::Yard::Tags::OverloadTag) + expect(overloads[1].docstring).to eq('The second overload.') + expect(overloads[1].signature).to eq('foo(Boolean $param, Callable &$block)') + expect(overloads[1].tags.size).to eq(3) + tags = overloads[1].tags(:param) + expect(tags.size).to eq(2) + expect(tags[0].name).to eq('param') + expect(tags[0].text).to eq('The first parameter.') + expect(tags[0].types).to eq(['Boolean']) + expect(tags[1].name).to eq('&block') + expect(tags[1].text).to eq('The block parameter.') + expect(tags[1].types).to eq(['Callable']) + tags = overloads[1].tags(:return) + expect(tags.size).to eq(1) + expect(tags[0].name).to be_nil + expect(tags[0].text).to eq('Returns a string.') + expect(tags[0].types).to eq(['String']) + tags = object.docstring.tags(:api) + expect(tags.size).to eq(1) + expect(tags[0].text).to eq('public') + end + end + + describe 'parsing a function with a missing parameter' do + let(:source) { <<-SOURCE +# An example 4.x function. +Puppet::Functions.create_function(:foo) do + # @param missing A missing parameter. + # @return [Undef] Returns nothing. + dispatch :foo do + end +end +SOURCE + } + + it 'should output a warning' do + expect{ subject }.to output(/\[warn\]: The @param tag for parameter 'missing' has no matching parameter at \(stdin\):5/).to_stdout_from_any_process + end + end + + describe 'parsing a function with a missing @param tag' do + let(:source) { <<-SOURCE +# An example 4.x function. +Puppet::Functions.create_function(:foo) do + # @return [Undef] Returns nothing. + dispatch :foo do + param 'String', :param1 + end +end +SOURCE + } + + it 'should output a warning' do + expect{ subject }.to output(/\[warn\]: Missing @param tag for parameter 'param1' near \(stdin\):5/).to_stdout_from_any_process + end + end + + describe 'parsing a function with a typed @param tag' do + let(:source) { <<-SOURCE +# An example 4.x function. +Puppet::Functions.create_function(:foo) do + # @param [Integer] param1 The first parameter. + # @return [Undef] Returns nothing. + dispatch :foo do + param 'String', :param1 + end +end +SOURCE + } + + it 'should output a warning' do + expect{ subject }.to output(/\[warn\]: The @param tag for parameter 'param1' should not contain a type specification near \(stdin\):6: ignoring in favor of dispatch type information\./).to_stdout_from_any_process + end + end + + describe 'parsing a function with a typed @param tag' do + let(:source) { <<-SOURCE +# An example 4.x function. +Puppet::Functions.create_function(:foo) do + # @param param1 The first parameter. + dispatch :foo do + param 'String', :param1 + end +end +SOURCE + } + + it 'should output a warning' do + expect{ subject }.to output(/\[warn\]: Missing @return tag near \(stdin\):4/).to_stdout_from_any_process + end + end + + describe 'parsing a function with a root @param tag' do + let(:source) { <<-SOURCE +# An example 4.x function. +# @param param Nope. +Puppet::Functions.create_function(:foo) do + # @return [Undef] + dispatch :foo do + end +end +SOURCE + } + + it 'should output a warning' do + expect{ subject }.to output(/\[warn\]: The docstring for Puppet 4.x function 'foo' contains @param tags near \(stdin\):3: parameter documentation should be made on the dispatch call\./).to_stdout_from_any_process + end + end + + + describe 'parsing a function with a root @overload tag' do + let(:source) { <<-SOURCE +# An example 4.x function. +# @overload foo +Puppet::Functions.create_function(:foo) do + # @return [Undef] + dispatch :foo do + end +end + SOURCE + } + + it 'should output a warning' do + expect{ subject }.to output(/\[warn\]: The docstring for Puppet 4.x function 'foo' contains @overload tags near \(stdin\):3: overload tags are automatically generated from the dispatch calls\./).to_stdout_from_any_process + end + end + + describe 'parsing a function with a root @return tag' do + let(:source) { <<-SOURCE +# An example 4.x function. +# @return [Undef] foo +Puppet::Functions.create_function(:foo) do + # @return [Undef] + dispatch :foo do + end +end + SOURCE + } + + it 'should output a warning' do + expect{ subject }.to output(/\[warn\]: The docstring for Puppet 4.x function 'foo' contains @return tags near \(stdin\):3: return value documentation should be made on the dispatch call\./).to_stdout_from_any_process + end + end +end diff --git a/spec/unit/puppet-strings/yard/handlers/ruby/provider_handler_spec.rb b/spec/unit/puppet-strings/yard/handlers/ruby/provider_handler_spec.rb new file mode 100644 index 0000000..9ae6b8e --- /dev/null +++ b/spec/unit/puppet-strings/yard/handlers/ruby/provider_handler_spec.rb @@ -0,0 +1,62 @@ +require 'spec_helper' +require 'puppet-strings/yard' + +describe PuppetStrings::Yard::Handlers::Ruby::ProviderHandler do + subject { + YARD::Parser::SourceParser.parse_string(source, :ruby) + YARD::Registry.all(:puppet_provider) + } + + describe 'parsing source without a provider definition' do + let(:source) { 'puts "hi"' } + + it 'no providers should be in the registry' do + expect(subject.empty?).to eq(true) + end + end + + describe 'parsing a provider with a missing description' do + let(:source) { <<-SOURCE +Puppet::Type.type(:custom).provide :linux do +end +SOURCE + } + + it 'should log a warning' do + expect{ subject }.to output(/\[warn\]: Missing a description for Puppet provider 'linux' \(resource type 'custom'\) at \(stdin\):1\./).to_stdout_from_any_process + end + end + + describe 'parsing a provider definition' do + let(:source) { <<-SOURCE +Puppet::Type.type(:custom).provide :linux do + desc 'An example provider on Linux.' + confine kernel: 'Linux' + confine osfamily: 'RedHat' + defaultfor kernel: 'Linux' + has_feature :implements_some_feature + has_feature :some_other_feature + commands foo: /usr/bin/foo +end +SOURCE + } + + it 'should register a provider object' do + expect(subject.size).to eq(1) + object = subject.first + expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Provider) + expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Providers.instance('custom')) + expect(object.name).to eq(:linux) + expect(object.type_name).to eq('custom') + expect(object.docstring).to eq('An example provider on Linux.') + expect(object.docstring.tags.size).to eq(1) + tags = object.docstring.tags(:api) + expect(tags.size).to eq(1) + expect(tags[0].text).to eq('public') + expect(object.confines).to eq({ 'kernel' => 'Linux', 'osfamily' => 'RedHat'}) + expect(object.defaults).to eq({ 'kernel' => 'Linux'}) + expect(object.features).to eq(['implements_some_feature', 'some_other_feature']) + expect(object.commands).to eq({'foo' => '/usr/bin/foo'}) + end + end +end diff --git a/spec/unit/puppet-strings/yard/handlers/ruby/type_handler_spec.rb b/spec/unit/puppet-strings/yard/handlers/ruby/type_handler_spec.rb new file mode 100644 index 0000000..ec0894b --- /dev/null +++ b/spec/unit/puppet-strings/yard/handlers/ruby/type_handler_spec.rb @@ -0,0 +1,126 @@ +require 'spec_helper' +require 'puppet-strings/yard' + +describe PuppetStrings::Yard::Handlers::Ruby::TypeHandler do + subject { + YARD::Parser::SourceParser.parse_string(source, :ruby) + YARD::Registry.all(:puppet_type) + } + + describe 'parsing source without a type definition' do + let(:source) { 'puts "hi"' } + + it 'no types should be in the registry' do + expect(subject.empty?).to eq(true) + end + end + + describe 'parsing a type with a missing description' do + let(:source) { <<-SOURCE +Puppet::Type.newtype(:database) do +end +SOURCE + } + + it 'should log a warning' do + expect{ subject }.to output(/\[warn\]: Missing a description for Puppet resource type 'database' at \(stdin\):1\./).to_stdout_from_any_process + end + end + + describe 'parsing a type definition' do + let(:source) { <<-SOURCE +Puppet::Type.newtype(:database) do + desc 'An example database server resource type.' + feature :encryption, 'The provider supports encryption.', methods: [:encrypt] + ensurable do + desc 'What state the database should be in.' + defaultvalues + aliasvalue(:up, :present) + aliasvalue(:down, :absent) + defaultto :up + end + + newparam(:address) do + isnamevar + desc 'The database server name.' + end + + newparam(:encryption_key, required_features: :encryption) do + desc 'The encryption key to use.' + end + + newparam(:encrypt, :parent => Puppet::Parameter::Boolean) do + desc 'Whether or not to encrypt the database.' + defaultto false + end + + newproperty(:file) do + desc 'The database file to use.' + end + + newproperty(:log_level) do + desc 'The log level to use.' + newvalue(:debug) + newvalue(:warn) + newvalue(:error) + defaultto 'warn' + end +end +SOURCE + } + + it 'should register a type object' do + expect(subject.size).to eq(1) + object = subject.first + expect(object).to be_a(PuppetStrings::Yard::CodeObjects::Type) + expect(object.namespace).to eq(PuppetStrings::Yard::CodeObjects::Types.instance) + expect(object.name).to eq(:database) + expect(object.docstring).to eq('An example database server resource type.') + expect(object.docstring.tags.size).to eq(1) + tags = object.docstring.tags(:api) + expect(tags.size).to eq(1) + expect(tags[0].text).to eq('public') + expect(object.properties.size).to eq(3) + expect(object.properties[0].name).to eq('ensure') + expect(object.properties[0].docstring).to eq('What state the database should be in.') + expect(object.properties[0].isnamevar).to eq(false) + expect(object.properties[0].default).to eq('up') + expect(object.properties[0].values).to eq(%w(present absent up down)) + expect(object.properties[0].aliases).to eq({ 'down' => 'absent', 'up' => 'present' }) + expect(object.properties[1].name).to eq('file') + expect(object.properties[1].docstring).to eq('The database file to use.') + expect(object.properties[1].isnamevar).to eq(false) + expect(object.properties[1].default).to be_nil + expect(object.properties[1].values).to eq([]) + expect(object.properties[1].aliases).to eq({}) + expect(object.properties[2].name).to eq('log_level') + expect(object.properties[2].docstring).to eq('The log level to use.') + expect(object.properties[2].isnamevar).to eq(false) + expect(object.properties[2].default).to eq('warn') + expect(object.properties[2].values).to eq(%w(debug warn error)) + expect(object.properties[2].aliases).to eq({}) + expect(object.parameters.size).to eq(3) + expect(object.parameters[0].name).to eq('address') + expect(object.parameters[0].docstring).to eq('The database server name.') + expect(object.parameters[0].isnamevar).to eq(true) + expect(object.parameters[0].default).to be_nil + expect(object.parameters[0].values).to eq([]) + expect(object.parameters[0].aliases).to eq({}) + expect(object.parameters[1].name).to eq('encryption_key') + expect(object.parameters[1].docstring).to eq('The encryption key to use.') + expect(object.parameters[1].isnamevar).to eq(false) + expect(object.parameters[1].default).to be_nil + expect(object.parameters[1].values).to eq([]) + expect(object.parameters[1].aliases).to eq({}) + expect(object.parameters[2].name).to eq('encrypt') + expect(object.parameters[2].docstring).to eq('Whether or not to encrypt the database.') + expect(object.parameters[2].isnamevar).to eq(false) + expect(object.parameters[2].default).to eq('false') + expect(object.parameters[2].values).to eq(%w(true false yes no)) + expect(object.parameters[2].aliases).to eq({}) + expect(object.features.size).to eq(1) + expect(object.features[0].name).to eq('encryption') + expect(object.features[0].docstring).to eq('The provider supports encryption.') + end + end +end diff --git a/spec/unit/puppet-strings/yard/parsers/puppet/parser_spec.rb b/spec/unit/puppet-strings/yard/parsers/puppet/parser_spec.rb new file mode 100644 index 0000000..12ea9d5 --- /dev/null +++ b/spec/unit/puppet-strings/yard/parsers/puppet/parser_spec.rb @@ -0,0 +1,171 @@ +require 'spec_helper' +require 'puppet-strings/yard' + +describe PuppetStrings::Yard::Parsers::Puppet::Parser do + subject { PuppetStrings::Yard::Parsers::Puppet::Parser.new(source, file) } + let(:file) { 'test.pp' } + + describe 'initialization of the parser' do + let(:source) { 'notice hi' } + + it 'should store the original source' do + expect(subject.source).to eq(source) + end + + it 'should store the original file name' do + expect(subject.file).to eq(file) + end + + it 'should have no relevant statements' do + subject.parse + expect(subject.enumerator.empty?).to be_truthy + end + end + + describe 'parsing invalid Puppet source code' do + let(:source) { < present + } +} +SOURCE + } + + it 'should only return the class statement' do + subject.parse + expect(subject.enumerator.size).to eq(1) + statement = subject.enumerator.first + expect(statement).to be_a(PuppetStrings::Yard::Parsers::Puppet::ClassStatement) + expect(statement.source).to eq("class foo(Integer $param1, $param2, String $param3 = hi) inherits foo::bar {\n file { '/tmp/foo':\n ensure => present\n }\n}") + expect(statement.file).to eq(file) + expect(statement.line).to eq(6) + expect(statement.docstring).to eq('A simple foo class.') + expect(statement.name).to eq('foo') + expect(statement.parent_class).to eq('foo::bar') + expect(statement.parameters.size).to eq(3) + expect(statement.parameters[0].name).to eq('param1') + expect(statement.parameters[0].type).to eq('Integer') + expect(statement.parameters[0].value).to be_nil + expect(statement.parameters[1].name).to eq('param2') + expect(statement.parameters[1].type).to be_nil + expect(statement.parameters[1].value).to be_nil + expect(statement.parameters[2].name).to eq('param3') + expect(statement.parameters[2].type).to eq('String') + expect(statement.parameters[2].value).to eq('hi') + end + end + + describe 'parsing nested class definitions' do + let(:source) { < present + } +} +SOURCE + } + + it 'should parse the defined type statement' do + subject.parse + expect(subject.enumerator.size).to eq(1) + statement = subject.enumerator.first + expect(statement).to be_a(PuppetStrings::Yard::Parsers::Puppet::DefinedTypeStatement) + expect(statement.name).to eq('foo') + expect(statement.source).to eq("define foo(Integer $param1, $param2, String $param3 = hi) {\n file { '/tmp/foo':\n ensure => present\n }\n}") + expect(statement.file).to eq(file) + expect(statement.line).to eq(6) + expect(statement.docstring).to eq('A simple foo defined type.') + expect(statement.parameters.size).to eq(3) + expect(statement.parameters[0].name).to eq('param1') + expect(statement.parameters[0].type).to eq('Integer') + expect(statement.parameters[0].value).to be_nil + expect(statement.parameters[1].name).to eq('param2') + expect(statement.parameters[1].type).to be_nil + expect(statement.parameters[1].value).to be_nil + expect(statement.parameters[2].name).to eq('param3') + expect(statement.parameters[2].type).to eq('String') + expect(statement.parameters[2].value).to eq('hi') + end + end + + describe 'parsing puppet functions' do + let(:source) { < RUBY_VERSION.match(/^1\.8/) do - expect{Puppet::Face[:strings, :current].yardoc}.to raise_error(RuntimeError, "This face requires Ruby 1.9 or greater.") - end - - it "should invoke Yardoc with MODULE_SOURCEFILES if no arguments are provided" do - YARD::CLI::Yardoc.expects(:run).with('manifests/**/*.pp', 'lib/**/*.rb') - Puppet::Face[:strings, :current].yardoc - end - - it "should invoke Yardoc with provided arguments" do - YARD::CLI::Yardoc.expects(:run).with('--debug', 'some_file.rb') - Puppet::Face[:strings, :current].yardoc('--debug', 'some_file.rb') - end - - describe "when generating HTML for documentation" do - - # HACK: In these tests we would like to suppress all output from the yard - # logger so we don't clutter up stdout. - # However, we do want the yard logger for other tests so we can - # assert that the right things are logged. To accomplish this, for - # this block of tests we monkeypatch the yard logger to be a generic - # stringio instance which does nothing and then we restore the - # original afterwards. - before(:all) do - @tmp = YARD::Logger.instance.io - YARD::Logger.instance.io = StringIO.new - end - - after(:all) do - YARD::Logger.instance.io = @tmp - end - - it "should properly generate HTML for manifest comments" do - - - PuppetModuleHelper.using_module(File.dirname(__FILE__), 'test') do |tmp| - Dir.chdir('test') - - Puppet::Face[:strings, :current].yardoc - - expect(PuppetModuleHelper.read_html(tmp, 'test', 'test.html')).to include("Class: test") - end - end - - it "should properly generate HTML for 3x function comments" do - PuppetModuleHelper.using_module(File.dirname(__FILE__), 'test') do |tmp| - Dir.chdir('test') - - Puppet::Face[:strings, :current].yardoc - - expect(PuppetModuleHelper.read_html(tmp, 'test', 'Puppet3xFunctions.html')).to include("This is the function documentation for `function3x`") - end - end - - it "should properly generate HTML for 4x function comments" do - PuppetModuleHelper.using_module(File.dirname(__FILE__), 'test') do |tmp| - Dir.chdir('test') - - Puppet::Face[:strings, :current].yardoc - - expect(PuppetModuleHelper.read_html(tmp, 'test', 'Puppet4xFunctions.html')).to include("This is a function which is used to test puppet strings") - end - end - - it "should create correct files for nested classes" do - PuppetModuleHelper.using_module(File.dirname(__FILE__), 'test') do |tmp| - Dir.chdir('test') - - Puppet::Face[:strings, :current].yardoc - - expect(PuppetModuleHelper.read_html(tmp, - 'test', 'outer.html')).to include("Puppet Class: outer") - expect(PuppetModuleHelper.read_html(tmp, 'test', - 'outer/middle.html')).to include("Puppet Class: middle") - expect(PuppetModuleHelper.read_html(tmp, 'test', - 'outer/middle/inner.html')).to include("Puppet Class: inner") - end - end - - it "should create proper namespace for nested classes" do - PuppetModuleHelper.using_module(File.dirname(__FILE__), 'test') do |tmp| - Dir.chdir('test') - - Puppet::Face[:strings, :current].yardoc - - expect(PuppetModuleHelper.read_html(tmp, - 'test', 'outer.html')).to include("Hostclass: outer") - expect(PuppetModuleHelper.read_html(tmp, 'test', - 'outer/middle.html')).to include("Hostclass: outer::middle") - expect(PuppetModuleHelper.read_html(tmp, 'test', - 'outer/middle/inner.html')).to include("Hostclass: outer::middle::inner") - end - end - end - end - - describe "server action" do - it "should raise an error if yard is absent" do - Puppet.features.stubs(:yard?).returns(false) - expect{Puppet::Face[:strings, :current].server}.to raise_error(RuntimeError, "The 'yard' gem must be installed in order to use this face.") - end - - it "should raise an error if rgen is absent" do - Puppet.features.stubs(:rgen?).returns(false) - expect{Puppet::Face[:strings, :current].server}.to raise_error(RuntimeError, "The 'rgen' gem must be installed in order to use this face.") - end - - it "should raise an error if the Ruby version is less than 1.9", :if => RUBY_VERSION.match(/^1\.8/) do - expect{Puppet::Face[:strings, :current].server}.to raise_error(RuntimeError, "This face requires Ruby 1.9 or greater.") - end - end -end - diff --git a/spec/unit/puppet_x/puppetlabs/strings/pops_spec.rb b/spec/unit/puppet_x/puppetlabs/strings/pops_spec.rb deleted file mode 100644 index d8ac5f2..0000000 --- a/spec/unit/puppet_x/puppetlabs/strings/pops_spec.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'spec_helper' -require 'puppet_x/puppetlabs/strings/pops/yard_statement' - -describe PuppetX::PuppetLabs::Strings::Pops do - let(:parser) {Puppet::Pops::Parser::Parser.new()} - - describe "YARDstatement class" do - let(:manifest) {"#hello world\nclass foo { }"} - let(:model) {parser.parse_string(manifest).current.definitions.first} - let(:test_statement) {PuppetX::PuppetLabs::Strings::Pops::YARDStatement.new(model)} - - describe "when creating a new instance of YARDStatement" do - it "should extract comments from the source code" do - expect(test_statement.comments).to match(/^#hello world/) - end - end - end - - describe "YARDTransfomer class" do - let(:manifest) {"#hello world\nclass foo($bar) { }"} - let(:manifest_default) {"#hello world\nclass foo($bar = 3) { }"} - let(:transformer) {PuppetX::PuppetLabs::Strings::Pops::YARDTransformer.new} - - describe "transform method" do - it "should perform the correct transformation with parameter defaults" do - model = parser.parse_string(manifest_default).current.definitions.first - statements = transformer.transform(model) - expect(statements.parameters[0][0].class).to be(PuppetX::PuppetLabs::Strings::Pops::YARDStatement) - end - - it "should perform the correct transofmration without parameter defaults" do - model = parser.parse_string(manifest).current.definitions.first - statements = transformer.transform(model) - expect(statements.parameters[0][1].class).to be(NilClass) - end - end - end -end diff --git a/spec/unit/puppet_x/puppetlabs/strings/yard/defined_type_handler_spec.rb b/spec/unit/puppet_x/puppetlabs/strings/yard/defined_type_handler_spec.rb deleted file mode 100644 index 8ba38f7..0000000 --- a/spec/unit/puppet_x/puppetlabs/strings/yard/defined_type_handler_spec.rb +++ /dev/null @@ -1,74 +0,0 @@ -require 'spec_helper' -require 'puppet_x/puppetlabs/strings/yard/handlers/defined_type_handler' -require 'strings_spec/parsing' - - -describe PuppetX::PuppetLabs::Strings::YARD::Handlers::DefinedTypeHandler do - include StringsSpec::Parsing - - def the_definedtype() - YARD::Registry.at("foo::bar") - end - - it "should parse single-line documentation strings before a given defined type" do - comment = "Definition: foo::bar" - puppet_code = <<-PUPPET - # #{comment} - define foo::bar ($baz) { } - PUPPET - - parse(puppet_code, :puppet) - - expect(the_definedtype).to document_a(:type => :definedtype, :docstring => comment) - end - - it "should parse multi-line documentation strings before a given defined type" do - puppet_code = <<-PUPPET - # Definition: foo::bar - # - # This class does some stuff - define foo::bar ($baz) { } - PUPPET - - parse(puppet_code, :puppet) - - comment = "Definition: foo::bar\n\nThis class does some stuff" - expect(the_definedtype).to document_a(:type => :definedtype, :docstring => comment) - end - - it "should not parse documentation before a function if it is followed by a new line" do - puppet_code = <<-PUPPET - # Definition: foo::bar - - define foo::bar ($baz) { } - PUPPET - - parse(puppet_code, :puppet) - - expect(the_definedtype).to document_a(:type => :definedtype, :docstring => "") - end - - it "should not add anything to the Registry if incorrect puppet code is present" do - puppet_code = <<-PUPPET - # Definition: foo::bar - This is not puppet code - PUPPET - - parse(puppet_code, :puppet) - - expect(YARD::Registry.all).to be_empty - end - - it "should generate the correct namespace " do - puppet_code = <<-PUPPET - define puppet_enterprise::mcollective::client::certs { } - PUPPET - - parse(puppet_code, :puppet) - # If the namespace is not correctly generated, we will not be able to find the - # object via this name, meaning namespace will be nil - namespace = YARD::Registry.at("puppet_enterprise::mcollective::client::certs").namespace.to_s - - expect(namespace).to_not be_nil - end -end diff --git a/spec/unit/puppet_x/puppetlabs/strings/yard/examples/test-param-names-differ-with-dispatch/lib/test.rb b/spec/unit/puppet_x/puppetlabs/strings/yard/examples/test-param-names-differ-with-dispatch/lib/test.rb deleted file mode 100644 index 4fc372c..0000000 --- a/spec/unit/puppet_x/puppetlabs/strings/yard/examples/test-param-names-differ-with-dispatch/lib/test.rb +++ /dev/null @@ -1,18 +0,0 @@ -# @param not_a_param [Integer] the first number to be compared -# @param also_not_a_param [Integer] the second number to be compared -Puppet::Functions.create_function(:max) do - dispatch max_1 do - param 'Integer[1,2]', :num_a - param 'Integer', :num_b - end - dispatch max_2 { - param 'String', :num_c - param 'String[1,2]', :num_d - } - def max_1(num_a, num_b) - num_a >= num_b ? num_a : num_b - end - def max_2(num_a, num_b) - num_a >= num_b ? num_a : num_b - end -end diff --git a/spec/unit/puppet_x/puppetlabs/strings/yard/examples/test-param-names-differ-with-types/lib/test.rb b/spec/unit/puppet_x/puppetlabs/strings/yard/examples/test-param-names-differ-with-types/lib/test.rb deleted file mode 100644 index 22cb89c..0000000 --- a/spec/unit/puppet_x/puppetlabs/strings/yard/examples/test-param-names-differ-with-types/lib/test.rb +++ /dev/null @@ -1,7 +0,0 @@ -# @param not_a_param [Integer[1,2]] the first number to be compared -# @param also_not_a_param [Integer[1,2]] 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 diff --git a/spec/unit/puppet_x/puppetlabs/strings/yard/examples/test-param-names-differ/lib/test.rb b/spec/unit/puppet_x/puppetlabs/strings/yard/examples/test-param-names-differ/lib/test.rb deleted file mode 100644 index cf46559..0000000 --- a/spec/unit/puppet_x/puppetlabs/strings/yard/examples/test-param-names-differ/lib/test.rb +++ /dev/null @@ -1,7 +0,0 @@ -# @param not_a_param [Integer] the first number to be compared -# @param also_not_a_param [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 diff --git a/spec/unit/puppet_x/puppetlabs/strings/yard/examples/test-param-names-match-with-dispatch/lib/test.rb b/spec/unit/puppet_x/puppetlabs/strings/yard/examples/test-param-names-match-with-dispatch/lib/test.rb deleted file mode 100644 index 6e7342c..0000000 --- a/spec/unit/puppet_x/puppetlabs/strings/yard/examples/test-param-names-match-with-dispatch/lib/test.rb +++ /dev/null @@ -1,11 +0,0 @@ -# @param [Integer] num_a the first number to be compared -# @param num_b [Integer] the second number to be compared -Puppet::Functions.create_function(:max) do - dispatch max_1 do - param 'Integer', :num_a - param 'Integer', :num_b - end - def max_1(num_a, num_b) - num_a >= num_b ? num_a : num_b - end -end diff --git a/spec/unit/puppet_x/puppetlabs/strings/yard/examples/test-param-names-match-with-types/lib/test.rb b/spec/unit/puppet_x/puppetlabs/strings/yard/examples/test-param-names-match-with-types/lib/test.rb deleted file mode 100644 index fc5e92a..0000000 --- a/spec/unit/puppet_x/puppetlabs/strings/yard/examples/test-param-names-match-with-types/lib/test.rb +++ /dev/null @@ -1,7 +0,0 @@ -# @param num_a [Integer[1,2]] the first number to be compared -# @param num_b [Integer[1,2]] 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 diff --git a/spec/unit/puppet_x/puppetlabs/strings/yard/examples/test-param-names-match/lib/test.rb b/spec/unit/puppet_x/puppetlabs/strings/yard/examples/test-param-names-match/lib/test.rb deleted file mode 100644 index 1497270..0000000 --- a/spec/unit/puppet_x/puppetlabs/strings/yard/examples/test-param-names-match/lib/test.rb +++ /dev/null @@ -1,7 +0,0 @@ -# @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 diff --git a/spec/unit/puppet_x/puppetlabs/strings/yard/examples/test/manifests/init.pp b/spec/unit/puppet_x/puppetlabs/strings/yard/examples/test/manifests/init.pp deleted file mode 100644 index 7d1a8ee..0000000 --- a/spec/unit/puppet_x/puppetlabs/strings/yard/examples/test/manifests/init.pp +++ /dev/null @@ -1,7 +0,0 @@ -# @param [Float] ident identification -class foo( String $ident = "Bob" , Integer $age = 10, ) -{ - notify {'$ident':} - notify {'$age':} -} - diff --git a/spec/unit/puppet_x/puppetlabs/strings/yard/host_class_handler_spec.rb b/spec/unit/puppet_x/puppetlabs/strings/yard/host_class_handler_spec.rb deleted file mode 100644 index c31e184..0000000 --- a/spec/unit/puppet_x/puppetlabs/strings/yard/host_class_handler_spec.rb +++ /dev/null @@ -1,93 +0,0 @@ -require 'spec_helper' -require 'lib/strings_spec/module_helper' -require 'puppet/face/strings' -require 'puppet_x/puppetlabs/strings/yard/handlers/host_class_handler' -require 'strings_spec/parsing' - -describe PuppetX::PuppetLabs::Strings::YARD::Handlers::HostClassHandler do - include StringsSpec::Parsing - - def the_hostclass() - YARD::Registry.at("foo::bar") - end - - it "should parse single-line documentation strings before a given class" do - comment = "Class: foo::bar" - puppet_code = <<-PUPPET - # #{comment} - class foo::bar { } - PUPPET - - parse(puppet_code, :puppet) - - expect(the_hostclass).to document_a(:type => :hostclass, :docstring => comment) - end - - it "should parse multi-line documentation strings before a given class" do - puppet_code = <<-PUPPET - # Class: foo::bar - # - # This class does some stuff - class foo::bar { } - PUPPET - - parse(puppet_code, :puppet) - - comment = "Class: foo::bar\n\nThis class does some stuff" - expect(the_hostclass).to document_a(:type => :hostclass, :docstring => comment) - end - - it "should not parse documentation before a class if it is followed by a new line" do - puppet_code = <<-PUPPET - # Class: foo::bar - - class foo::bar { } - PUPPET - - parse(puppet_code, :puppet) - - expect(the_hostclass).to document_a(:type => :hostclass, :docstring => "") - end - - it "should generate the correct namespace " do - puppet_code = <<-PUPPET - class puppet_enterprise::mcollective::client::certs { } - PUPPET - - parse(puppet_code, :puppet) - # If the namespace is not correctly generated, we will not be able to find the - # object via this name, meaning namespace will be nil - namespace = YARD::Registry.at("puppet_enterprise::mcollective::client::certs") - - expect(namespace).to_not be_nil - end - it "should not issue just one warning if the parameter types don't match." do - YARD::Registry.clear - # FIXME The type information here will change with the next version of - # puppet. `expected` is the output expected from the stable branch. The - # output from the master branch will use this instead: - # "...specifies the types [String] in file..." - expected_stout = <<-output -Files: 1 -Modules: 0 ( 0 undocumented) -Classes: 0 ( 0 undocumented) -Constants: 0 ( 0 undocumented) -Methods: 0 ( 0 undocumented) -Puppet Classes: 1 ( 0 undocumented) -Puppet Defined Types: 0 ( 0 undocumented) -Puppet Types: 0 ( 0 undocumented) -Puppet Providers: 0 ( 0 undocumented) - 100.00% documented - output - expected_stderr = "[warn]: @param tag types do not match the code. The ident\n parameter is declared as types [\"Float\"] in the docstring,\n but the code specifies the types [\"String\"]\n in the file manifests/init.pp near line 2.\n" - - expect { - expect { - PuppetModuleHelper.using_module(File.dirname(__FILE__),'test') do |tmp| - Dir.chdir('test') - Puppet::Face[:strings, :current].yardoc - end - }.to output(expected_stderr).to_stderr_from_any_process - }.to output(expected_stout).to_stdout_from_any_process - end -end diff --git a/spec/unit/puppet_x/puppetlabs/strings/yard/puppet_3x_function_handler_spec.rb b/spec/unit/puppet_x/puppetlabs/strings/yard/puppet_3x_function_handler_spec.rb deleted file mode 100644 index 0b58605..0000000 --- a/spec/unit/puppet_x/puppetlabs/strings/yard/puppet_3x_function_handler_spec.rb +++ /dev/null @@ -1,63 +0,0 @@ -require 'spec_helper' -require 'puppet_x/puppetlabs/strings/yard/handlers/puppet_3x_function_handler' -require 'strings_spec/parsing' - -describe PuppetX::PuppetLabs::Strings::YARD::Handlers::Puppet3xFunctionHandler do - include StringsSpec::Parsing - - def the_method() - YARD::Registry.at("Puppet3xFunctions#the_function") - end - - def the_namespace() - YARD::Registry.at("Puppet3xFunctions") - end - - it "should parse single-line documentation strings before a given function" do - comment = "The summary" - parse <<-RUBY - # #{comment} - newfunction(:the_function, :type => rvalue) do |args| - end - RUBY - - expect(the_method).to document_a(:type => :method, :docstring => comment) - expect(the_namespace).to document_a(:type => :puppetnamespace) - end - - it "should parse multi-line documentation strings before a given function" do - parse <<-RUBY - # The summary - # - # The longer description - newfunction(:the_function, :type => rvalue) do |args| - end - RUBY - - comment = "The summary\n\nThe longer description" - expect(the_method).to document_a(:type => :method, :docstring => comment) - expect(the_namespace).to document_a(:type => :puppetnamespace) - end - - it "should not parse documentation before a function if it is followed by two new lines" do - parse <<-RUBY - # The summary - - - newfunction(:the_function, :type => rvalue) do |args| - end - RUBY - - expect(the_method).to document_a(:type => :method, :docstring => "") - expect(the_namespace).to document_a(:type => :puppetnamespace) - end - - it "should process documentation if only one option is passed to newfunction" do - parse <<-RUBY - newfunction(:the_functiion) do |args| - end - RUBY - - expect(the_namespace).to document_a(:type => :puppetnamespace) - end -end diff --git a/spec/unit/puppet_x/puppetlabs/strings/yard/puppet_4x_function_handler_spec.rb b/spec/unit/puppet_x/puppetlabs/strings/yard/puppet_4x_function_handler_spec.rb deleted file mode 100644 index 6dcf9d4..0000000 --- a/spec/unit/puppet_x/puppetlabs/strings/yard/puppet_4x_function_handler_spec.rb +++ /dev/null @@ -1,150 +0,0 @@ -require 'spec_helper' -require 'lib/strings_spec/module_helper' -require 'puppet_x/puppetlabs/strings/yard/handlers/puppet_4x_function_handler' -require 'puppet/face/strings' -require 'strings_spec/parsing' - -describe PuppetX::PuppetLabs::Strings::YARD::Handlers::Puppet4xFunctionHandler do - include StringsSpec::Parsing - - def the_method() - YARD::Registry.at("Puppet4xFunctions#the_function") - end - - def the_namespace() - YARD::Registry.at("Puppet4xFunctions") - end - - it "should parse single-line documentation strings before a given function" do - comment = "The summary" - parse <<-RUBY - # #{comment} - Puppet::Functions.create_function(:the_function) do - end - RUBY - - expect(the_method).to document_a(:type => :method, :docstring => comment) - expect(the_namespace).to document_a(:type => :puppetnamespace) - end - - it "should parse multi-line documentation strings before a given function" do - parse <<-RUBY - # The summary - # - # The longer description - Puppet::Functions.create_function(:the_function) do - end - RUBY - - comment = "The summary\n\nThe longer description" - expect(the_method).to document_a(:type => :method, :docstring => comment) - expect(the_namespace).to document_a(:type => :puppetnamespace) - end - - it "should not parse documentation before a function if it is followed by two new lines" do - parse <<-RUBY - # The summary - # - # The longer description - - - Puppet::Functions.create_function(:the_function) do - end - RUBY - - expect(the_method).to document_a(:type => :method, :docstring => "") - expect(the_namespace).to document_a(:type => :puppetnamespace) - end - - it "should issue a warning if the parameter names do not match the docstring" do - expected_output_not_a_param = "[warn]: The parameter not_a_param is documented, but doesn't exist in\n your code, in file lib/test.rb near line 3." - expected_output_also_not_a_param = "[warn]: The parameter also_not_a_param is documented, but doesn't exist in\n your code, in file lib/test.rb near line 3." - expect { - expect { - PuppetModuleHelper.using_module(File.dirname(__FILE__),'test-param-names-differ') do |tmp| - Dir.chdir('test-param-names-differ') - Puppet::Face[:strings, :current].yardoc - end - }.to output(/documented/).to_stdout_from_any_process - }.to output("#{expected_output_not_a_param}\n#{expected_output_also_not_a_param}\n").to_stderr_from_any_process - end - - it "should not issue a warning when the parameter names match the docstring" do - expected = "" - expect { - expect { - PuppetModuleHelper.using_module(File.dirname(__FILE__),'test-param-names-match') do |tmp| - Dir.chdir('test-param-names-match') - Puppet::Face[:strings, :current].yardoc - end - }.to output(/documented/).to_stdout_from_any_process - }.to output(expected).to_stderr_from_any_process - - end - it "should not issue a warning when there are parametarized types and parameter names are the same" do - expected = "" - expect { - expect { - PuppetModuleHelper.using_module(File.dirname(__FILE__),'test-param-names-match-with-types') do |tmp| - Dir.chdir('test-param-names-match-with-types') - Puppet::Face[:strings, :current].yardoc - end - }.to output(/documented/).to_stdout_from_any_process - }.to output(expected).to_stderr_from_any_process - end - - it "should issue a warning when there are parametarized types and parameter names differ" do - expected_output_not_num_a = "[warn]: @param tag has unknown parameter" + - " name: not_num_a \n in file `(stdin)' near line 3." - expected_output_not_a_param = "[warn]: The parameter not_a_param is documented, but doesn't exist in\n your code, in file lib/test.rb near line 3." - expected_output_also_not_a_param = "[warn]: The parameter also_not_a_param is documented, but doesn't exist in\n your code, in file lib/test.rb near line 3." - expect { - expect { - PuppetModuleHelper.using_module(File.dirname(__FILE__),'test-param-names-differ-with-types') do |tmp| - Dir.chdir('test-param-names-differ-with-types') - Puppet::Face[:strings, :current].yardoc - end - }.to output(/documented/).to_stdout_from_any_process - }.to output("#{expected_output_not_a_param}\n#{expected_output_also_not_a_param}\n").to_stderr_from_any_process - end - - - it "should issue a warning if the parameter names do not match the docstring in dispatch method" do - expected_output_not_a_param = "[warn]: The parameter not_a_param is documented, but doesn't exist in\n your code, in file lib/test.rb near line 3." - expected_output_also_not_a_param = "[warn]: The parameter also_not_a_param is documented, but doesn't exist in\n your code, in file lib/test.rb near line 3." - expect { - expect { - PuppetModuleHelper.using_module(File.dirname(__FILE__),'test-param-names-differ-with-dispatch') do |tmp| - Dir.chdir('test-param-names-differ-with-dispatch') - Puppet::Face[:strings, :current].yardoc - end - }.to output(/documented/).to_stdout_from_any_process - }.to output("#{expected_output_not_a_param}\n#{expected_output_also_not_a_param}\n").to_stderr_from_any_process - end - - it "should not issue a warning if the parameter names do match the " + - "docstring in dispatch method" do - expected = "" - expect { - expect { - PuppetModuleHelper.using_module(File.dirname(__FILE__),'test-param-names-match-with-dispatch') do |tmp| - Dir.chdir('test-param-names-match-with-dispatch') - Puppet::Face[:strings, :current].yardoc - end - }.to output(/documented/).to_stdout_from_any_process - }.to output(expected).to_stderr_from_any_process - end - - it "should parse unusually named functions" do - # This should not raise a ParseErrorWithIssue exceptoin - parse <<-RUBY - Puppet::Functions.create_function :'max' do - def max(num_a, num_b) - num_a >= num_b ? num_a : num_b - end - end - RUBY - end - - -end diff --git a/spec/unit/puppet_x/puppetlabs/strings/yard/template_helper_spec.rb b/spec/unit/puppet_x/puppetlabs/strings/yard/template_helper_spec.rb deleted file mode 100644 index 6772d7d..0000000 --- a/spec/unit/puppet_x/puppetlabs/strings/yard/template_helper_spec.rb +++ /dev/null @@ -1,125 +0,0 @@ -require 'spec_helper' -require 'puppet_x/puppetlabs/strings/yard/templates/default/template_helper' -require 'puppet_x/puppetlabs/strings/yard/code_objects/puppet_namespace_object' -require 'strings_spec/parsing' - -describe TemplateHelper do - it "should not print any warning if the tags and parameters match" do - th = TemplateHelper.new - - # Case 0: If the documented tags do include the parameter, - # nothing is printed - tag0 = YARD::Tags::Tag.new(:param, 'a_parameter') - tag0.name = 'a_parameter' - obj0 = PuppetX::PuppetLabs::Strings::YARD::CodeObjects::PuppetNamespaceObject.new(:root, 'Puppet3xFunctions') - obj0.add_tag tag0 - obj0.parameters = [['a_parameter']] - expect { th.check_parameters_match_docs obj0 }.to output("").to_stderr_from_any_process - - # The docstring is still alive between tests. Clear the tags registered with - # it so the tags won't persist between tests. - obj0.docstring.instance_variable_set("@tags", []) - end - - it "should print the warning with no location data if the tags and " + - "parameters differ and the location data is not properly formed" do - th = TemplateHelper.new - # Case 1: If the parameter and tag differ and the location is not properly - # formed, print out the warning with no location data - tag1 = YARD::Tags::Tag.new(:param, 'aa_parameter') - tag1.name = 'aa_parameter' - obj1 = PuppetX::PuppetLabs::Strings::YARD::CodeObjects::PuppetNamespaceObject.new(:root, 'Puppet3xFunctions') - obj1.add_tag tag1 - obj1.parameters = [['b_parameter']] - expect { th.check_parameters_match_docs obj1 }.to output("[warn]: The parameter aa_parameter is documented, but doesn't exist in\n your code. Sorry, the file and line number could not be determined.\n").to_stderr_from_any_process - - # The docstring is still alive between tests. Clear the tags registered with - # it so the tags won't persist between tests. - obj1.docstring.instance_variable_set("@tags", []) - end - - it "should print the warning with location data if the tags and parameters " + - "differ and the location data is properly formed" do - th = TemplateHelper.new - # Case 2: If the parameter and tag differ and the location is properly - # formed, print out the warning with no location data - tag2 = YARD::Tags::Tag.new(:param, 'aaa_parameter') - tag2.name = 'aaa_parameter' - obj2 = PuppetX::PuppetLabs::Strings::YARD::CodeObjects::PuppetNamespaceObject.new(:root, 'Puppet3xFunctions') - obj2.files = [['some_file.pp', 42]] - obj2.add_tag tag2 - obj2.parameters = [['b_parameter']] - expect { th.check_parameters_match_docs obj2 }.to output("[warn]: The parameter aaa_parameter is documented, but doesn't exist in\n your code, in file some_file.pp near line 42.\n").to_stderr_from_any_process - - # The docstring is still alive between tests. Clear the tags registered with - # it so the tags won't persist between tests. - obj2.docstring.instance_variable_set("@tags", []) - end - - it "should issue a warning if the parameter types do not match the docstring in dispatch method" do - expected_output_not_a_param = "[warn]: @param tag types do not match the" + - " code. The arg1\n parameter is declared as types [\"Integer\"] in the " + - "docstring,\n but the code specifies the types [\"Optional[String]\"]" + - "\n in the file test near line 0.\n" - object = PuppetX::PuppetLabs::Strings::YARD::CodeObjects::PuppetNamespaceObject.new(:root, 'Puppet4xFunctions') - object.files = [['test', 0]] - object.type_info = [{ - 'arg0' => 'Variant[String,Array[String]]', - 'arg1' => 'Optional[String]' - }] - param_details = [{ - :name => 'arg0', - :types => ['Variant[String,Array[String]]'] - }, - { - :name => 'arg1', - :types => ['Integer'] - }] - template_helper = TemplateHelper.new - expect { - template_helper.check_types_match_docs(object, param_details) - }.to output(expected_output_not_a_param).to_stderr_from_any_process - end - - it "should not issue a warning if the parameter types do match the docstring in dispatch method" do - object = PuppetX::PuppetLabs::Strings::YARD::CodeObjects::PuppetNamespaceObject.new(:root, 'Puppet4xFunctions') - object.files = [['test', 0]] - object.type_info = [{ - 'arg0' => 'Variant[String,Array[String]]', - 'arg1' => 'Optional[String]' - }] - param_details = [{ - :name => 'arg0', - :types => ['Variant[String,Array[String]]'] - }, - { - :name => 'arg1', - :types => ['Optional[String]'] - }] - template_helper = TemplateHelper.new - expect { - template_helper.check_types_match_docs(object, param_details) - }.to output("").to_stderr_from_any_process - end - - it "should not issue a warning if the types in the docstring in dispatch method are assignable to parameter types" do - object = PuppetX::PuppetLabs::Strings::YARD::CodeObjects::PuppetNamespaceObject.new(:root, 'Puppet4xFunctions') - object.files = [['test', 0]] - object.type_info = [{ - 'arg0' => 'Variant[String,Array[String]]', - 'arg1' => 'Optional[String]' - }] - param_details = [{ - :name => 'arg0', - :types => ['Variant[String,Array[String]]'] - }, - { - :name => 'arg1', - :types => ['String'] - }] - template_helper = TemplateHelper.new - expect { - template_helper.check_types_match_docs(object, param_details) - }.to output("").to_stderr_from_any_process - end -end diff --git a/spec/unit/puppet_x/puppetlabs/strings/yard/type_handler_spec.rb b/spec/unit/puppet_x/puppetlabs/strings/yard/type_handler_spec.rb deleted file mode 100644 index 338ce5c..0000000 --- a/spec/unit/puppet_x/puppetlabs/strings/yard/type_handler_spec.rb +++ /dev/null @@ -1,67 +0,0 @@ -require 'spec_helper' -require 'puppet_x/puppetlabs/strings/yard/handlers/type_handler' -require 'strings_spec/parsing' - - -describe PuppetX::PuppetLabs::Strings::YARD::Handlers::PuppetTypeHandler do - include StringsSpec::Parsing - - def the_type() - YARD::Registry.at("file") - end - - it "should have the proper docstring" do - parse <<-RUBY - Puppet::Type.newtype(:file) do - @doc = "Manages files, including their content, ownership, and perms." - newparam(:path) do - desc <<-'EOT' - The path to the file to manage. Must be fully qualified. - EOT - end - isnamevar - end - RUBY - - expect(the_type.docstring).to eq("Manages files, including their " + - "content, ownership, and perms.") - end - - it "should have the proper parameter details" do - parse <<-RUBY - Puppet::Type.newtype(:file) do - @doc = "Manages files, including their content, ownership, and perms." - newparam(:file) do - desc <<-'EOT' - The path to the file to manage. Must be fully qualified. - EOT - end - isnamevar - end - RUBY - - expect(the_type.parameter_details).to eq([{ :name => "file", - :desc => "The path to the file to manage. Must be fully qualified.", - :exists? => true, :puppet_type => true, :namevar => true, - :default => nil, - :parameter=>true, - :allowed_values=>[], - }]) - end - - it "should have the proper parameters" do - parse <<-RUBY - Puppet::Type.newtype(:file) do - @doc = "Manages files, including their content, ownership, and perms." - newparam(:path) do - desc <<-'EOT' - The path to the file to manage. Must be fully qualified. - EOT - end - isnamevar - end - RUBY - - expect(the_type.parameters).to eq([["path", nil]]) - end -end