(PDOC-63) Delete the old implementation.

This commit deletes the old implementation to assist in cleaner code reviews of
the upcoming reimplementation.

This commit also moves YARD to version 0.9.5 and lays down a bare bones
implementation of Puppet Strings that currently does nothing.
This commit is contained in:
Peter Huene 2016-09-09 13:44:05 -07:00
parent 56f266db4b
commit ea9dd0c846
No known key found for this signature in database
GPG Key ID: 6B585C1742BE3C3C
97 changed files with 158 additions and 3862 deletions

View File

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

View File

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

View File

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

View File

@ -68,30 +68,36 @@ $ cd /path/to/module
$ puppet strings $ puppet strings
``` ```
This processes `README` and all puppet and ruby files under `manifests/` This processes `README` and all Puppet and Ruby source files under the `./manifests/`, `./functions/`, and `./lib/`
and `lib/`. directories by default.
To document specific files: To document specific files:
``` ```
$ puppet strings some_manifest.pp [another_if_you_feel_like_it.rb] $ puppet strings first.pp second.pp ...
```
To document specific directories:
```
$ puppet strings 'modules/foo/lib/**/*.rb' 'modules/foo/manifests/**/*.pp' 'modules/foo/functions/**/*.pp' ...
``` ```
Strings can also emit the generated documentation as JSON: Strings can also emit the generated documentation as JSON:
``` ```
$ puppet strings yardoc some_manifest.pp --emit-json documentation.json $ puppet strings generate manifest.pp --emit-json documentation.json
``` ```
It can also print the JSON to stdout: It can also print the JSON to stdout:
``` ```
$ puppet strings yardoc some_manifest.pp --emit-json-stdout $ puppet strings generate manifest.pp --emit-json-stdout
``` ```
The schema for the JSON which Strings emits is [well documented](https://github.com/puppetlabs/puppet-strings/blob/master/json_dom.md). The schema for the JSON which Strings emits is [documented here](https://github.com/puppetlabs/puppet-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. Processing is delegated to the `yard` 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.
In addition to generating a directory full of HTML, you can also serve up documentation for all your modules using the `server` action: In addition to generating a directory full of HTML, you can also serve up documentation for all your modules using the `server` action:
@ -155,7 +161,7 @@ class example (
### Types and Providers ### Types and Providers
Strings will automatically extract the `@doc` provider docstring and any `desc` parameter/property docstrings. Strings will automatically extract the `@doc` provider docstring and any `desc` parameter/property docstrings.
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. 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` or `@!puppet.type.property` which may take types, the parameter or property name, and a description.
```ruby ```ruby
# @!puppet.type.param my_parameter This parameter needs to be explicitly # @!puppet.type.param my_parameter This parameter needs to be explicitly

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

@ -0,0 +1,56 @@
# The root module for Puppet Strings.
module PuppetStrings
# The glob patterns used to search for files to document.
DEFAULT_SEARCH_PATTERNS = %w(
manifests/**/*.pp
functions/**/*.pp
types/**/*.pp
lib/**/*.rb
).freeze
# Generates documentation.
# @param [Array<String>] search_patterns The search patterns (e.g. manifests/**/*.pp) to look for files.
# @param [Hash] options The options hash.
# @option options [Boolean] :debug Enable YARD debug output.
# @option options [Boolean] :backtrace Enable YARD backtraces.
# @option options [String] :markup The YARD markup format to use (defaults to 'markdown').
# @option options [String] :json Enables JSON output to the given file. If the file is nil, STDOUT is used.
# @option options [Array<String>] :yard_args The arguments to pass to yard.
# @return [void]
def self.generate(search_patterns = DEFAULT_SEARCH_PATTERNS, options = {})
# 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
# TODO: implement
end
end
# Runs the YARD documentation server.
# @param [Array<String>] args The arguments to YARD.
def self.run_server(*args)
YARD::CLI::Server.run(*args)
end
end

View File

@ -1,58 +0,0 @@
require 'rake'
require 'rake/tasklib'
require 'puppet/face'
require 'puppet_x/puppet/strings/util'
namespace :strings do
desc 'Generate Puppet documentation with YARD.'
task :generate do
PuppetX::Puppet::Strings::Util.generate([
{emit_json: 'strings.json'}
])
end
desc 'Serve YARD documentation for modules.'
task :serve do
PuppetX::Puppet::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."
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'
end
else
Dir.mkdir('doc')
Dir.chdir('doc') do
system 'git init'
system "git remote add origin #{git_uri}"
system 'git pull'
system 'git checkout -b gh-pages'
end
end
end
desc "Push new docs to GitHub."
task :push do
Dir.chdir('doc') do
system 'git add .'
system "git commit -m '[strings] Generated Documentation Update'"
system 'git push origin gh-pages -f'
end
end
desc "Run checkout, generate, and push tasks."
task :update => [
:checkout,
:'strings:generate',
:push,
]
end
end

View File

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

View File

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

View File

@ -1,64 +1,105 @@
require 'puppet/face' require 'puppet/face'
require 'puppet_x/puppet/strings/yard/tags/directives'
# Implements the 'puppet strings' interface.
Puppet::Face.define(:strings, '0.0.1') do 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 action(:generate) do
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
default default
option "--emit-json-stdout" do option '--emit-json-stdout' do
summary "Print json representation of the documentation to stdout" summary 'Print JSON representation of the documentation to stdout.'
end end
option "--emit-json FILE" do option '--emit-json FILE' do
summary "Write json representation of the documentation to FILE" 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 end
summary "Generate YARD documentation from files." summary 'Generate documentation from files.'
arguments "[manifest_file.pp ...]" arguments '[[search_pattern] ...]'
when_invoked do |*args| when_invoked do |*args|
check_required_features check_required_features
require 'puppet_x/puppet/strings/util' require 'puppet-strings'
PuppetX::Puppet::Strings::Util.generate(args) PuppetStrings::generate(
args.count > 1 ? args[0..-2] : PuppetStrings::DEFAULT_SEARCH_PATTERNS,
# Puppet prints the return value of the action. The return value of this build_generate_options(args.last)
# action is that of the yardoc_actions invocation, which is the boolean )
# "true". This clutters the statistics yard prints, so instead return the nil
# empty string. Note an extra newline will also be printed.
""
end end
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 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| when_invoked do |*args|
check_required_features check_required_features
require 'puppet_x/puppet/strings/util' require 'puppet-strings'
PuppetX::Puppet::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
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.match(/^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 end

View File

@ -1,64 +0,0 @@
require 'puppet'
require 'puppet/pops'
require 'puppet/util/docs'
require 'yard'
module PuppetX
end
# Nothing to see here except forward declarations.
module PuppetX::Puppet
module Strings
# This submodule contains bits that operate on the Pops module produced by
# the Future parser.
module Pops
require 'puppet_x/puppet/strings/pops/yard_statement'
require 'puppet_x/puppet/strings/pops/yard_transformer'
end
# This submodule contains bits that interface with the YARD plugin system.
module YARD
require 'puppet_x/puppet/strings/yard/monkey_patches'
require 'puppet_x/puppet/strings/yard/parser'
module Tags
require 'puppet_x/puppet/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/puppet/strings/yard/code_objects/puppet_namespace_object'
require 'puppet_x/puppet/strings/yard/code_objects/method_object'
require 'puppet_x/puppet/strings/yard/code_objects/defined_type_object'
require 'puppet_x/puppet/strings/yard/code_objects/host_class_object'
require 'puppet_x/puppet/strings/yard/code_objects/type_object'
require 'puppet_x/puppet/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/puppet/strings/yard/handlers/base'
require 'puppet_x/puppet/strings/yard/handlers/defined_type_handler'
require 'puppet_x/puppet/strings/yard/handlers/host_class_handler'
require 'puppet_x/puppet/strings/yard/handlers/puppet_3x_function_handler'
require 'puppet_x/puppet/strings/yard/handlers/puppet_4x_function_handler'
require 'puppet_x/puppet/strings/yard/handlers/type_handler'
require 'puppet_x/puppet/strings/yard/handlers/provider_handler'
end
::YARD::Parser::SourceParser.register_parser_type(:puppet,
PuppetX::Puppet::Strings::YARD::PuppetParser,
['pp'])
::YARD::Handlers::Processor.register_handler_namespace(:puppet,
PuppetX::Puppet::Strings::YARD::Handlers)
# FIXME: Might not be the best idea to have the template code on the Ruby
# LOAD_PATH as the contents of this directory really aren't library code.
::YARD::Templates::Engine.register_template_path(
File.join(File.dirname(__FILE__), 'strings', 'yard', 'templates'))
end
end
end

View File

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

View File

@ -1,79 +0,0 @@
require 'ostruct'
# An adapter class that conforms a Pops model instance + adapters to the
# interface expected by YARD handlers.
#
# FIXME: Inhertiting from OpenStruct is a bit of a hack. It allows attributes
# to be declared as needed but in the long run understandibility of the code
# would be improved by having a concrete model.
class PuppetX::Puppet::Strings::Pops::YARDStatement < OpenStruct
attr_reader :pops_obj, :comments
def initialize(pops_obj)
# Initialize OpenStruct
super({})
unless pops_obj.is_a? Puppet::Pops::Model::PopsObject
raise ArgumentError, "A YARDStatement can only be initialized from a PopsObject. Got a: #{pops_obj.class}"
end
@pops_obj = pops_obj
@pos_adapter = Puppet::Pops::Adapters::SourcePosAdapter.adapt(@pops_obj)
# FIXME: Perhaps this should be a seperate adapter?
@comments = extract_comments
end
def type
pops_obj.class
end
def line
@line ||= @pos_adapter.line
end
def source
@source ||= @pos_adapter.extract_text
end
# FIXME: I don't know quite what these are supposed to do, but they show up
# quite often in the YARD handler code. Figure out whether they are
# necessary.
alias_method :show, :source
def comments_hash_flag; nil end
def comments_range; nil end
private
# TODO: This stuff should probably be part of a separate class/adapter.
COMMENT_PATTERN = /^\s*#.*\n/
def extract_comments
comments = []
program = pops_obj.eAllContainers.find {|c| c.is_a?(Puppet::Pops::Model::Program) }
# FIXME: Horribly inefficient. Multiple copies. Generator pattern would
# be much better.
source_text = program.source_text.lines.to_a
source_text.slice(0, line-1).reverse.each do |line|
if COMMENT_PATTERN.match(line)
# FIXME: The gsub trims comments, but is extremely optimistic: It
# assumes only one space separates the comment body from the
# comment character.
# NOTE: We cannot just trim any amount of whitespace as indentation
# is sometimes significant in markdown. We would need a real parser.
# Comments which begin with some whitespace, a hash and then some
# tabs and spaces should be scrubbed. Comments which just have a
# solitary hash then a newline should keep that newline since newlines
# are significant in markdown.
comments.unshift line.gsub(/^\s*#[\t ]/, '').gsub(/^\s*#\n/, "\n")
else
# No comment found on this line. We must be done piecing together a
# comment block.
break
end
end
# Stick everything back together.
comments.join
end
end

View File

@ -1,47 +0,0 @@
# Loosely based on the TreeDumper classes in Pops::Model. The responsibility of
# this class is to walk a Pops::Model and output objects that can be consumed
# by YARD handlers.
#
# @note Currently, this class only extracts node, host class and type
# definitions.
class PuppetX::Puppet::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::Puppet::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::Puppet::Strings::Pops::YARDStatement.new(o)
end
# nil in... nil out!
def transform_NilClass(o)
nil
end
end

View File

@ -1,65 +0,0 @@
require 'puppet_x/puppet/strings/actions'
module PuppetX::Puppet::Strings::Util
MODULE_SOURCEFILES = ['manifests/**/*.pp', 'lib/**/*.rb']
def self.generate(args = [])
yardoc_actions = PuppetX::Puppet::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::Puppet::Strings::YARD::Tags::PuppetTypeParameterDirective)
yardoc_actions.generate_documentation(*yard_args)
end
def self.serve(args = [])
server_actions = PuppetX::Puppet::Strings::Actions.new(Puppet[:debug], Puppet[:trace])
args.pop
module_names = args
# FIXME: This is pretty inefficient as it forcibly re-generates the YARD
# indicies each time the server is started. However, it ensures things are
# generated properly.
module_list = server_actions.index_documentation_for_modules(module_names, MODULE_SOURCEFILES)
module_tuples = server_actions.generate_module_tuples(module_list)
module_tuples.map! do |mod|
[mod[:name], mod[:index_path]]
end
# The `-m` flag means a list of name/path pairs will follow. The name is
# used as the module name and the path indicates which `.yardoc` index to
# generate documentation from.
yard_args = %w[-m -q] + module_tuples.flatten
server_actions.serve_documentation(*yard_args)
end
end

View File

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

View File

@ -1,22 +0,0 @@
class PuppetX::Puppet::Strings::YARD::CodeObjects::HostClassObject < PuppetX::Puppet::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::Puppet::Strings::YARD::CodeObjects::HostClassObject)
# Cool. We got a host class. Return self + parent inheritance tree.
[self] + parent_class.inheritance_tree
elsif parent_class.is_a?(YARD::CodeObjects::Proxy)
# We have a reference to a parent that has not been created yet. Just
# return it.
[self, parent_class]
else
# Most likely no parent class. But also possibly an object that we
# shouldn't inherit from. Just return self.
[self]
end
end
end

View File

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

View File

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

View File

@ -1,48 +0,0 @@
class PuppetX::Puppet::Strings::YARD::CodeObjects::PuppetNamespaceObject < YARD::CodeObjects::NamespaceObject
attr_accessor :type_info
# NOTE: `YARD::Registry#resolve` requires a method with this signature to
# be present on all subclasses of `NamespaceObject`.
def inheritance_tree(include_mods = false)
[self]
end
def to_s
name.to_s
end
def to_json(*a)
{
"name" => @name,
"file" => file,
"line" => line,
"docstring" => @docstring,
"examples" => self.tags.map do |tag|
tag.text if tag.tag_name == 'example'
end.compact,
}.to_json(*a)
end
# FIXME: We used to override `self.new` to ensure no YARD proxies were
# created for namespaces segments that did not map to a host class or
# defined type. Fighting the system in this way turned out to be
# counter-productive.
#
# However, if a proxy is left in, YARD will drop back to namspace-mangling
# heuristics that are very specific to Ruby and which produce ugly paths in
# the resulting output. Need to find a way to address this.
#
# Tried:
#
# - Overriding self.new in the code object. Failed because self.new
# overrides are gross and difficult to pull off. Especially when
# replacing an existing override.
#
# - Adding functionality to the base handler to ensure something other
# than a proxy occupies each namespace segment. Failed because once a
# code object is created with a namespace, it will never update.
# Unless that namespace is set to a Proxy.
#
# def self.new(namespace, name, *args, &block)
end

View File

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

View File

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

View File

@ -1,13 +0,0 @@
require 'puppet_x/puppet/strings/yard/core_ext/yard'
class PuppetX::Puppet::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::Puppet::Strings::YARD::CodeObjects
def self.handles?(statement)
handlers.any? {|h| h == statement.type}
end
end

View File

@ -1,31 +0,0 @@
class PuppetX::Puppet::Strings::YARD::Handlers::DefinedTypeHandler < PuppetX::Puppet::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

View File

@ -1,82 +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 = /^<?<-/
def is_heredoc?(str)
HEREDOC_START.match(str)
end
# Turns an entry in the method parameter list into a string.
#
# @param ele [YARD::Parser::Ruby::AstNode]
# @return [String]
def process_element(ele)
ele = ele.jump(:ident, :symbol, :string_content)
case ele.type
when :ident
ele.source
when :symbol
ele.source[1..-1]
when :string_content
source = ele.source
if is_heredoc? source
process_heredoc(source)
else
source
end
end
end
# Cleans up and formats Heredoc contents parsed by YARD.
#
# @param source [String]
# @return [String]
def process_heredoc(source)
source = source.lines.to_a
# YARD adds a line of source context on either side of the Heredoc
# contents.
source.shift
source.pop
# This utility method normalizes indentation and trims whitespace.
Puppet::Util::Docs.scrub(source.join)
end
end

View File

@ -1,55 +0,0 @@
class PuppetX::Puppet::Strings::YARD::Handlers::HostClassHandler < PuppetX::Puppet::Strings::YARD::Handlers::Base
handles HostClassDefinition
process do
begin
obj = HostClassObject.new(:root, statement.pops_obj.name)
obj.parameters = statement.parameters.map do |a|
param_tuple = [a[0].pops_obj.name]
param_tuple << ( a[1].nil? ? nil : a[1].source )
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
# This is a bit of a hack because we were using a method that was previously
# API private. See PDOC-75 for more details
if Puppet::Pops::Types::TypeParser.instance_method(:interpret_any).arity == 2
param_type_info[pop_param.name] = tp.interpret_any(pop_param.type_expr, nil)
else
param_type_info[pop_param.name] = tp.interpret_any(pop_param.type_expr)
end
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]
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

View File

@ -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::Puppet::Strings::YARD::Handlers::PuppetProviderHandler < YARD::Handlers::Ruby::Base
include PuppetX::Puppet::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

View File

@ -1,54 +0,0 @@
require File.join(File.dirname(__FILE__),'./heredoc_helper')
class PuppetX::Puppet::Strings::YARD::Handlers::Puppet3xFunctionHandler < YARD::Handlers::Ruby::Base
include PuppetX::Puppet::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

View File

@ -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::Puppet::Strings::YARD::Handlers::Puppet4xFunctionHandler < YARD::Handlers::Ruby::Base
include PuppetX::Puppet::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::Puppet::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 = /^<?<-/
# Turns an entry in the method parameter array into a string.
#
# @param ele [YARD::Parser::Ruby::AstNode]
# @return [String]
def process_element(ele)
ele = ele.jump(:ident, :string_content, :tstring_content)
case ele.type
when :ident
ele.source
when :string_content, :tstring_content
source = ele.source
if HEREDOC_START.match(source)
process_heredoc(source)
else
source
end
end
end
# Cleans up and formats Heredoc contents parsed by YARD.
#
# @param source [String]
# @return [String]
def process_heredoc(source)
source = source.lines.to_a
# YARD adds a line of source context on either side of the Heredoc
# contents.
source.shift
source.pop
# This utility method normalizes indentation and trims whitespace.
Puppet::Util::Docs.scrub(source.join)
end
end

View File

@ -1,295 +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::Puppet::Strings::YARD::Handlers::PuppetTypeHandler < YARD::Handlers::Ruby::Base
include PuppetX::Puppet::Strings::YARD::CodeObjects
handles :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
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.puppet.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

View File

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

View File

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

View File

@ -1,30 +0,0 @@
require 'yard'
require 'puppet/pops'
require 'puppet_x/puppet/strings'
require 'puppet_x/puppet/strings//pops/yard_transformer'
class PuppetX::Puppet::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::Puppet::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

View File

@ -1,34 +0,0 @@
<div class="docstring">
<div class="discussion">
<p><%= htmlify(@class_details[:desc]) %></p>
</div>
</div>
<div class="tags">
<% if @class_details[:examples] != {}%>
<div class="examples">
<p class="tag_title">Examples:</p>
<% @class_details[:examples].each do |title, text| %>
<div class="inline"><p><%= title %></p></div>
<pre class="example code"><code><span><%= text %></span></code></pre>
<% end %>
</div>
<% end %>
<% if @class_details[:since] %>
<p class="tag_title">Since:</p>
<ul class="since">
<li>
<div class="inline">
<p><%= @class_details[:since] %></p>
</div>
</li>
</ul>
<% end %>
<% if @class_details[:return] %>
<p class="tag_title">Return:</p>
<ul class="return">
<li>
<%= @html_helper.generate_return_types(@class_details[:return][1], @class_details[:return][0]) %>
</li>
</ul>
<% end %>
</div>

View File

@ -1,5 +0,0 @@
<div class='module_header'>
<h1>
<%= @header_text %>
</h1>
</div>

View File

@ -1,6 +0,0 @@
<h2>Parameter Summary</h2>
<div class="tags">
<ul class="param">
<%= @html_helper.generate_parameters(@param_details, object) %>
</ul>
</div>

View File

@ -1 +0,0 @@
include T('default/module/html')

View File

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

View File

@ -1,2 +0,0 @@
<li><%= link_object(Registry.root, Registry.root.title, nil, false) %></li>
<%= namespace_list %>

View File

@ -1 +0,0 @@
<%= namespace_list(:namespace_types => [:hostclass, :definedtype]) %>

View File

@ -1,21 +0,0 @@
<% unless P(:root, 'Puppet3xFunctions').is_a?(CodeObjects::Proxy) %>
<li>
<a class='toggle'></a>
<%= link_object(P(:root, 'Puppet3xFunctions'), 'Puppet 3x Functions', nil, false) %>
<small class='search_info'>Puppet3xFunctions</small>
</li>
<ul>
<%= namespace_list(:root => P(:root,'Puppet3xFunctions'), :namespace_types => [:puppetnamespace, :method]) %>
</ul>
<% end %>
<% unless P(:root, 'Puppet4xFunctions').is_a?(CodeObjects::Proxy) %>
<li>
<a class='toggle'></a>
<%= link_object(P(:root, 'Puppet4xFunctions'), 'Puppet 4x Functions', nil, false) %>
<small class='search_info'>Puppet4xFunctions</small>
</li>
<ul>
<%= namespace_list(:root => P(:root,'Puppet4xFunctions'), :namespace_types => [:puppetnamespace, :method]) %>
</ul>
<% end %>

View File

@ -1 +0,0 @@
<%= namespace_list(:namespace_types => [:provider]) %>

View File

@ -1 +0,0 @@
<%= namespace_list(:namespace_types => [:type]) %>

View File

@ -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::Puppet::Strings::YARD::CodeObjects::TypeObject) || child.is_a?(PuppetX::Puppet::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 << "<li>"
out << "<a class='toggle'></a> " if has_children
out << linkify(child, name)
out << " &lt; #{child.superclass.name}" if child.is_a?(CodeObjects::ClassObject) && child.superclass
out << "<small class='search_info'>"
out << child.namespace.title
out << "</small>"
out << "</li>"
out << "<ul>#{namespace_list(:root => child, :namespace_types => namespace_types)}</ul>" if has_children
end
end
out
end

View File

@ -1,22 +0,0 @@
<% n = 1 %>
<dl class="box">
<% if object.parent_class %>
<dt class="r<%=n%>">Inherits:</dt>
<dd class="r<%=n%>">
<span class="inheritName"><%= linkify object.parent_class, object.parent_class.path %></span>
<ul class="fullTree">
<% object.inheritance_tree.reverse.each_with_index do |obj, i| %>
<li class="next"><%= obj == object ? obj.path : linkify(obj, obj.path) %></li>
<% end %>
</ul>
<a href="#" class="inheritanceTree">show all</a>
</dd>
<% n = 2 %>
<% end %>
<% unless object.root? %>
<dt class="r<%=n%> last">Defined in:</dt>
<dd class="r<%=n%> last"><%= erb(:defines) %></dd>
<% end %>
</dl>
<div class="clear"></div>

View File

@ -1 +0,0 @@
include T('default/definedtype/html')

View File

@ -1,4 +0,0 @@
<div id="subclasses">
<h2>Direct Known Subclasses</h2>
<p class="children"><%= @subclasses.map {|child| linkify(child, child.path) }.join(", ") %></p>
</div>

View File

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

View File

@ -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 << "(<span class=\"type\"><tt>" << types.join(", ") << "</tt></span>)"
if !desc.nil?
result << "- <div class=\"inline\"><p>#{desc}</p></div>"
end
result.join
end
def generate_features features, object
result = []
if features
features.each do |feat|
result << "<li>"
result << "<span class=\"name\">#{feat[:name]} </span>"
if feat[:desc]
result << "- <br/><div class=\"inline\"><p> #{feat[:desc]} </p></div>"
end
if feat[:methods]
result << "<h3> Methods </h3>"
result << "<ul>"
feat[:methods].each do |method|
result << "<li> <tt>" << method << "</tt> </li>"
end
result << "</ul>"
end
result << "</li>"
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 << "<li>"
# 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 << "<strike>"
end
result << "<span class=\"name\">#{param[:name]} </span>"
result << "<span class=\"type\">"
# If the docstring specifies types, use those
if param[:types]
result << "(" << "<tt>" << param[:types].join(", ") << "</tt>" << ")"
# 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 << "(" << "<tt>Unknown</tt>" << ")"
else
result << "(" << "<tt>" << possible_types.join(", ") << "</tt>" << ")"
end
# Give up. It can probably be anything.
elsif not (param[:puppet_3_func] or param[:puppet_type])
result << "(<tt>Unknown</tt>)"
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: <tt>" << param[:default] << "</tt> "
end
result << "</span>"
# 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 << "<tt> => #{param[:fq_name]}</tt>"
end
if param[:desc]
result << " - <div class=\"inline\"><p> #{param[:desc]} </p></div>"
end
if !param[:exists?]
result << "</strike>"
end
if param[:allowed_values] and param[:allowed_values] != []
result << "\n<b> Allowed Values: </b>"
result << "<ul>"
param[:allowed_values].each do |value_thing|
result << "<li>"
result << "<tt>" << value_thing.first << "</tt>"
if value_thing[1]
result << " only available if " << "<tt>" << value_thing[1] << "</tt>"
end
result << "</li>"
end
result << "</ul>\n"
end
result << "</li>"
end
result.join
end
end

View File

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

View File

@ -1,17 +0,0 @@
<h1>Method: <%= object.path %></h1>
<div class="box_info">
<dl>
<dt class="">Defined in:</dt>
<dd class="">
<%= object.file %><% if object.files.size > 1 %><span class="defines">,<br />
<%= object.files[1..-1].map {|f| f.first }.join(",<br /> ") %></div>
<% end %>
</dd>
</dl>
</div>
<div class="method_details_list">
<div id="method_details">
<%= yieldall :index => 0 %>
</div>
</div>

View File

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

View File

@ -1,8 +0,0 @@
<h2>Commands Summary</h2>
<div class="tags">
<ul class="command">
<% @command_details.each do |command| %>
<li><tt><%= command %></tt></li>
<% end %>
</ul>
</div>

View File

@ -1,10 +0,0 @@
<h2>Confines</h2>
<% if @confine_details != {} %>
<div class="tags">
<ul class="command">
<% @confine_details.each_pair do |key, value| %>
<li><tt><%= key %> - <%= value %></tt></li>
<% end %>
</ul>
</div>
<% end %>

View File

@ -1,10 +0,0 @@
<h2>Defaults</h2>
<% if @default_details != {} %>
<div class="tags">
<ul class="command">
<% @default_details.each_pair do |key, value| %>
<li><tt><%= key %> - <%= value %></tt></li>
<% end %>
</ul>
</div>
<% end %>

View File

@ -1,34 +0,0 @@
<div class="docstring">
<div class="discussion">
<p><%= htmlify(Puppet::Util::Docs::scrub(@class_details[:desc])) %></p>
</div>
</div>
<div class="tags">
<% if @class_details[:examples] != {}%>
<div class="examples">
<p class="tag_title">Examples:</p>
<% @class_details[:examples].each do |title, text| %>
<div class="inline"><p><%= title %></p></div>
<pre class="example code"><code><span><%= text %></span></code></pre>
<% end %>
</div>
<% end %>
<% if @class_details[:since] %>
<p class="tag_title">Since:</p>
<ul class="since">
<li>
<div class="inline">
<p><%= @class_details[:since] %></p>
</div>
</li>
</ul>
<% end %>
<% if @class_details[:return] %>
<p class="tag_title">Return:</p>
<ul class="return">
<li>
<%= @html_helper.generate_return_types(@class_details[:return][1], @class_details[:return][0]) %>
</li>
</ul>
<% end %>
</div>

View File

@ -1,10 +0,0 @@
<h2>Features</h2>
<% if @feature_details != [] %>
<div class="tags">
<ul class="command">
<% @feature_details.each do |feature| %>
<li><tt><%= feature %></tt></li>
<% end %>
</ul>
</div>
<% end %>

View File

@ -1,5 +0,0 @@
<div class='module_header'>
<h1>
<%= @header_text %>
</h1>
</div>

View File

@ -1 +0,0 @@
include T('default/module/html')

View File

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

View File

@ -1,11 +0,0 @@
<dl class="box">
<dt class="r1 last" style="height: 16px;">Defined in:</dt>
<dd class="r1 last">
<% @source_files.each do |file| %>
<em><%= file[0] %></em>:
<%= file[1] %>
<br/>
<% end %>
</dd>
</dl>
<div class ="clear"></div>

View File

@ -1,5 +0,0 @@
<div class='module_header'>
<h1>
<%= @header_text %>
</h1>
</div>

View File

@ -1,53 +0,0 @@
<h2>Function Details</h2>
<% @class_details.each do |func| %>
<h3 class="signature" id = <%= "#{func[:name]}-instance_method" %>>
<strong>
<% if func[:return] %>
<%= @html_helper.generate_return_types(func[:return][1]) %>
<% end %>
<%= func[:name] %>
</strong>
</h3>
<div class="docstring">
<div class="discussion">
<p><%= htmlify(func[:desc]) %></p>
</div>
</div>
<div class="tags">
<% if func[:examples] != {}%>
<div class="examples">
<p class="tag_title">Examples:</p>
<% func[:examples].each do |title, text| %>
<div class="inline"><p><%= title %></p></div>
<pre class="example code"><code><span><%= text %></span></code></pre>
<% end %>
</div>
<% end %>
<% if func[:since] %>
<p class="tag_title">Since:</p>
<ul class="since">
<li>
<div class="inline">
<p><%= func[:since] %></p>
</div>
</li>
</ul>
<% end %>
<% if func[:return] %>
<p class="tag_title">Returns:</p>
<ul class="return">
<li>
<%= @html_helper.generate_return_types(func[:return][1], func[:return][0]) %>
</li>
</ul>
<% end %>
<% if func[:params] != nil %>
<p class="tag_title">Parameters:</p>
<div class="tags">
<ul class="param">
<%= @html_helper.generate_parameters(func[:params], object.child) %>
</ul>
</div>
<% end %>
</div>
<% end %>

View File

@ -1,20 +0,0 @@
<h2>Available Functions</h2>
<ul class="summary">
<% @method_details.each do |method| %>
<li class = "private">
<span class = "summary_signature">
<a href =<%= "##{method[:name]}-instance_method"%>>
<% if ! method[:return_types].nil? %>
<%= @html_helper.generate_return_types(method[:return_types]) %>
<% end %>
-
<strong><%= method[:name] %></strong>
</span>
</li></a>
<span class = "summary_desc">
<div class = "inline">
<p><%= method[:short_desc] %></p>
</div>
</span>
<% end %>
</ul>

View File

@ -1 +0,0 @@
include T('default/module/html')

View File

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

View File

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

View File

@ -1,34 +0,0 @@
<div class="docstring">
<div class="discussion">
<p><%= htmlify(Puppet::Util::Docs::scrub(@class_details[:desc])) %></p>
</div>
</div>
<div class="tags">
<% if @class_details[:examples] != {}%>
<div class="examples">
<p class="tag_title">Examples:</p>
<% @class_details[:examples].each do |title, text| %>
<div class="inline"><p><%= title %></p></div>
<pre class="example code"><code><span><%= text %></span></code></pre>
<% end %>
</div>
<% end %>
<% if @class_details[:since] %>
<p class="tag_title">Since:</p>
<ul class="since">
<li>
<div class="inline">
<p><%= @class_details[:since] %></p>
</div>
</li>
</ul>
<% end %>
<% if @class_details[:return] %>
<p class="tag_title">Return:</p>
<ul class="return">
<li>
<%= @html_helper.generate_return_types(@class_details[:return][1], @class_details[:return][0]) %>
</li>
</ul>
<% end %>
</div>

View File

@ -1,5 +0,0 @@
<div class='module_header'>
<h1>
<%= @header_text %>
</h1>
</div>

View File

@ -1,12 +0,0 @@
<h2>Parameter Summary</h2>
<div class="tags">
<ul class="param">
<%= @html_helper.generate_parameters(@param_details, object) %>
</ul>
</div>
<h2>Features</h2>
<div class="tags">
<ul class="feature">
<%= @html_helper.generate_features(@feature_details, object) %>
</ul>
</div>

View File

@ -1,10 +0,0 @@
<h2>Available Providers</h2>
<% if @providers != [] %>
<div class="tags">
<ul class="command">
<% @providers.each do |provider| %>
<li><a href="<%= provider.name.to_s %>.html"><tt><%= provider.name.to_s %></tt></a></li>
<% end %>
</ul>
</div>
<% end %>

View File

@ -1 +0,0 @@
include T('default/module/html')

View File

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

View File

@ -18,5 +18,5 @@ Gem::Specification.new do |s|
s.files = `git ls-files`.split("\n") - Dir['.*', '*.gemspec'] s.files = `git ls-files`.split("\n") - Dir['.*', '*.gemspec']
s.add_runtime_dependency 'puppet', '>= 3.7.0' s.add_runtime_dependency 'puppet', '>= 3.7.0'
s.add_runtime_dependency 'yard', '~> 0.8' s.add_runtime_dependency 'yard', '~> 0.9.5'
end end

View File

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

View File

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

View File

@ -5,7 +5,7 @@ require 'mocha'
require 'puppet' require 'puppet'
require 'rspec' require 'rspec'
require 'puppet_x/puppet/strings' require 'puppet-strings'
RSpec.configure do |config| RSpec.configure do |config|
config.mock_with :mocha config.mock_with :mocha

View File

@ -1,5 +0,0 @@
# function 4x
#
# This is a function which is used to test puppet strings
Puppet::Functions.create_function(:function4x) do
end

View File

@ -1,3 +0,0 @@
Puppet::Parser::Functions.newfunction(:function3x, :doc => "This is the
function documentation for `function3x`") do |args|
end

View File

@ -1,27 +0,0 @@
# Class: test
#
# This class exists to serve as fixture data for testing the puppet strings face
#
# @example
# class { "test": }
#
# @param package_name The name of the package
# @param service_name The name of the service
class test (
$package_name = $test::params::package_name,
$service_name = $test::params::service_name,
) inherits test::params {
# validate parameters here
class { 'test::install': } ->
class { 'test::config': } ~>
class { 'test::service': } ->
Class['test']
File {
owner => 'user',
path => 'some/file/path',
}
}

View File

@ -1,27 +0,0 @@
# Testing tested classes
# docs stuff
# @param nameservers [String] Don't ask me what this does!
# @param default_lease_time [Integer[1024, 8192]] text goes here
# @param max_lease_time does stuff
class outer (
$dnsdomain,
$nameservers,
$default_lease_time = 3600,
$max_lease_time = 86400
) {
# @param options [String[5,7]] gives user choices
# @param multicast [Boolean] foobar
# @param servers yep, that's right
class middle (
$options = "iburst",
$servers,
$multicast = false
) {
class inner (
$choices = "uburst",
$secenekler = "weallburst",
$boxen,
$manyspell = true
) {}
}
}

View File

@ -1,6 +0,0 @@
{
"name": "username-test",
"version": "0.0.1",
"author": "username",
"license": "Apache 2.0"
}

View File

@ -1,132 +0,0 @@
require 'spec_helper'
require 'lib/strings_spec/module_helper'
require 'puppet/face/strings'
require 'tmpdir'
require 'stringio'
describe Puppet::Face do
describe "YARDoc action" do
it "should raise an error if yard is absent" do
Puppet.features.stubs(:yard?).returns(false)
expect{Puppet::Face[:strings, :current].yardoc}.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].yardoc}.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 verion is less than 1.9", :if => 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

View File

@ -1,38 +0,0 @@
require 'spec_helper'
require 'puppet_x/puppet/strings/pops/yard_statement'
describe PuppetX::Puppet::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::Puppet::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::Puppet::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::Puppet::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

View File

@ -1,74 +0,0 @@
require 'spec_helper'
require 'puppet_x/puppet/strings/yard/handlers/defined_type_handler'
require 'strings_spec/parsing'
describe PuppetX::Puppet::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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +0,0 @@
# @param [Float] ident identification
class foo( String $ident = "Bob" , Integer $age = 10, )
{
notify {'$ident':}
notify {'$age':}
}

View File

@ -1,93 +0,0 @@
require 'spec_helper'
require 'lib/strings_spec/module_helper'
require 'puppet/face/strings'
require 'puppet_x/puppet/strings/yard/handlers/host_class_handler'
require 'strings_spec/parsing'
describe PuppetX::Puppet::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

View File

@ -1,63 +0,0 @@
require 'spec_helper'
require 'puppet_x/puppet/strings/yard/handlers/puppet_3x_function_handler'
require 'strings_spec/parsing'
describe PuppetX::Puppet::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

View File

@ -1,150 +0,0 @@
require 'spec_helper'
require 'lib/strings_spec/module_helper'
require 'puppet_x/puppet/strings/yard/handlers/puppet_4x_function_handler'
require 'puppet/face/strings'
require 'strings_spec/parsing'
describe PuppetX::Puppet::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

View File

@ -1,125 +0,0 @@
require 'spec_helper'
require 'puppet_x/puppet/strings/yard/templates/default/template_helper'
require 'puppet_x/puppet/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::Puppet::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::Puppet::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::Puppet::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::Puppet::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::Puppet::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::Puppet::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

View File

@ -1,67 +0,0 @@
require 'spec_helper'
require 'puppet_x/puppet/strings/yard/handlers/type_handler'
require 'strings_spec/parsing'
describe PuppetX::Puppet::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