puppet-strings 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -0
  3. data/Gemfile +4 -2
  4. data/README.md +53 -10
  5. data/codecov.yml +3 -0
  6. data/lib/puppet-strings.rb +30 -11
  7. data/lib/puppet-strings/json.rb +7 -0
  8. data/lib/puppet-strings/markdown.rb +35 -0
  9. data/lib/puppet-strings/markdown/base.rb +168 -0
  10. data/lib/puppet-strings/markdown/defined_type.rb +14 -0
  11. data/lib/puppet-strings/markdown/defined_types.rb +37 -0
  12. data/lib/puppet-strings/markdown/function.rb +52 -0
  13. data/lib/puppet-strings/markdown/functions.rb +38 -0
  14. data/lib/puppet-strings/markdown/puppet_class.rb +14 -0
  15. data/lib/puppet-strings/markdown/puppet_classes.rb +37 -0
  16. data/lib/puppet-strings/markdown/resource_type.rb +27 -0
  17. data/lib/puppet-strings/markdown/resource_types.rb +37 -0
  18. data/lib/puppet-strings/markdown/table_of_contents.rb +21 -0
  19. data/lib/puppet-strings/markdown/templates/classes_and_defines.erb +63 -0
  20. data/lib/puppet-strings/markdown/templates/function.erb +50 -0
  21. data/lib/puppet-strings/markdown/templates/resource_type.erb +114 -0
  22. data/lib/puppet-strings/markdown/templates/table_of_contents.erb +21 -0
  23. data/lib/puppet-strings/tasks/generate.rb +24 -5
  24. data/lib/puppet-strings/yard/code_objects/function.rb +3 -3
  25. data/lib/puppet-strings/yard/code_objects/type.rb +3 -1
  26. data/lib/puppet-strings/yard/handlers.rb +1 -0
  27. data/lib/puppet-strings/yard/handlers/puppet/function_handler.rb +1 -1
  28. data/lib/puppet-strings/yard/handlers/ruby/base.rb +1 -1
  29. data/lib/puppet-strings/yard/handlers/ruby/rsapi_handler.rb +141 -0
  30. data/lib/puppet/face/strings.rb +28 -7
  31. data/spec/acceptance/emit_json_options.rb +4 -4
  32. data/spec/acceptance/generate_markdown_spec.rb +49 -0
  33. data/spec/fixtures/unit/json/output.json +43 -0
  34. data/spec/fixtures/unit/markdown/output.md +383 -0
  35. data/spec/spec_helper.rb +19 -1
  36. data/spec/unit/puppet-strings/json_spec.rb +40 -0
  37. data/spec/unit/puppet-strings/markdown/base_spec.rb +146 -0
  38. data/spec/unit/puppet-strings/markdown_spec.rb +248 -0
  39. data/spec/unit/puppet-strings/yard/handlers/puppet/function_handler_spec.rb +16 -1
  40. data/spec/unit/puppet-strings/yard/handlers/ruby/provider_handler_spec.rb +1 -1
  41. data/spec/unit/puppet-strings/yard/handlers/ruby/rsapi_handler_spec.rb +213 -0
  42. metadata +38 -2
@@ -0,0 +1,21 @@
1
+ <% if group.length > 0 -%>
2
+ ## <%= group_name %>
3
+ <% if priv -%>
4
+ ### Public <%= group_name %>
5
+ <% group.each do |item| -%>
6
+ <% unless item[:private] -%>
7
+ * [`<%= item[:name] %>`](#<%= item[:link] %>): <%= item[:desc] %>
8
+ <% end -%>
9
+ <% end -%>
10
+ ### Private <%= group_name %>
11
+ <% group.each do |item| -%>
12
+ <% if item[:private] -%>
13
+ * `<%= item[:name] %>`: <%= item[:desc] %>
14
+ <% end -%>
15
+ <% end -%>
16
+ <% else -%>
17
+ <% group.each do |item| -%>
18
+ * [`<%= item[:name] %>`](#<%= item[:link] %>): <%= item[:desc] %>
19
+ <% end -%>
20
+ <% end -%>
21
+ <% end -%>
@@ -3,7 +3,7 @@ require 'puppet-strings'
3
3
  # Implements the strings:generate task.
4
4
  namespace :strings do
5
5
  desc 'Generate Puppet documentation with YARD.'
6
- task :generate, :patterns, :debug, :backtrace, :markup, :json, :yard_args do |t, args|
6
+ task :generate, [:patterns, :debug, :backtrace, :markup, :json, :markdown, :yard_args] do |t, args|
7
7
  patterns = args[:patterns]
8
8
  patterns = patterns.split if patterns
9
9
  patterns ||= PuppetStrings::DEFAULT_SEARCH_PATTERNS
@@ -14,11 +14,30 @@ namespace :strings do
14
14
  markup: args[:markup] || 'markdown',
15
15
  }
16
16
 
17
- # rubocop:disable Style/PreferredHashMethods
18
- # `args` is a Rake::TaskArguments and has no key? method
19
- options[:json] = args[:json] if args.has_key? :json
17
+ raise("Error: Both JSON and Markdown output have been selected. Please select one.") if args[:json] == 'true' && args[:markdown] == 'true'
18
+
19
+ # rubocop:disable Style/PreferredHashMethods
20
+ # Because of Ruby, true and false from the args are both strings and both true. Here,
21
+ # when the arg is set to false (or empty), set it to real false, else real true. Then,
22
+ # if the arg is set simply to 'true', assume default behavior is expected and set the path
23
+ # to nil to elicit that, else set to the path given.
24
+ # @param [Hash] args from the Rake task cli
25
+ # @param [Hash] options to send to the generate function
26
+ # @param [Symbol] possible format option
27
+ # @return nil
28
+ def parse_format_option(args, options, format)
29
+ if args.has_key? format
30
+ options[format] = args[format] == 'false' || args[format].empty? ? false : true
31
+ if options[format]
32
+ options[:path] = args[format] == 'true' ? nil : args[format]
33
+ end
34
+ end
35
+ end
36
+
37
+ [:json,:markdown].each { |format| parse_format_option(args, options, format) }
38
+
39
+ warn('yard_args behavior is a little dodgy, use at your own risk') if args[:yard_args]
20
40
  options[:yard_args] = args[:yard_args].split if args.has_key? :yard_args
21
- # rubocop:enable Style/PreferredHashMethods
22
41
 
23
42
  PuppetStrings.generate(patterns, options)
24
43
  end
@@ -84,14 +84,14 @@ class PuppetStrings::Yard::CodeObjects::Function < PuppetStrings::Yard::CodeObje
84
84
  hash[:line] = line
85
85
  hash[:type] = @function_type.to_s
86
86
  hash[:signatures] = []
87
-
87
+
88
88
  if self.has_tag? :overload
89
89
  # loop over overloads and append onto the signatures array
90
90
  self.tags(:overload).each do |o|
91
- hash[:signatures] << { :signature => o.signature, :docstring => PuppetStrings::Json.docstring_to_hash(o.docstring, [:param, :return]) }
91
+ hash[:signatures] << { :signature => o.signature, :docstring => PuppetStrings::Json.docstring_to_hash(o.docstring, [:param, :option, :return]) }
92
92
  end
93
93
  else
94
- hash[:signatures] << { :signature => self.signature, :docstring => PuppetStrings::Json.docstring_to_hash(docstring, [:param, :return]) }
94
+ hash[:signatures] << { :signature => self.signature, :docstring => PuppetStrings::Json.docstring_to_hash(docstring, [:param, :option, :return]) }
95
95
  end
96
96
 
97
97
  hash[:docstring] = PuppetStrings::Json.docstring_to_hash(docstring)
@@ -22,7 +22,7 @@ class PuppetStrings::Yard::CodeObjects::Type < PuppetStrings::Yard::CodeObjects:
22
22
  # Represents a resource type parameter.
23
23
  class Parameter
24
24
  attr_reader :name, :values, :aliases
25
- attr_accessor :docstring, :isnamevar, :default
25
+ attr_accessor :docstring, :isnamevar, :default, :data_type
26
26
 
27
27
  # Initializes a resource type parameter or property.
28
28
  # @param [String] name The name of the parameter or property.
@@ -31,6 +31,7 @@ class PuppetStrings::Yard::CodeObjects::Type < PuppetStrings::Yard::CodeObjects:
31
31
  @name = name
32
32
  @docstring = docstring || ''
33
33
  @values = []
34
+ @data_type = []
34
35
  @aliases = {}
35
36
  @isnamevar = false
36
37
  @default = nil
@@ -59,6 +60,7 @@ class PuppetStrings::Yard::CodeObjects::Type < PuppetStrings::Yard::CodeObjects:
59
60
  hash[:name] = name
60
61
  hash[:description] = docstring unless docstring.empty?
61
62
  hash[:values] = values unless values.empty?
63
+ hash[:data_type] = data_type unless data_type.empty?
62
64
  hash[:aliases] = aliases unless aliases.empty?
63
65
  hash[:isnamevar] = true if isnamevar
64
66
  hash[:default] = default if default
@@ -3,6 +3,7 @@ module PuppetStrings::Yard::Handlers
3
3
  # The module for custom Ruby YARD handlers.
4
4
  module Ruby
5
5
  require 'puppet-strings/yard/handlers/ruby/type_handler'
6
+ require 'puppet-strings/yard/handlers/ruby/rsapi_handler'
6
7
  require 'puppet-strings/yard/handlers/ruby/provider_handler'
7
8
  require 'puppet-strings/yard/handlers/ruby/function_handler'
8
9
  end
@@ -37,7 +37,7 @@ class PuppetStrings::Yard::Handlers::Puppet::FunctionHandler < PuppetStrings::Ya
37
37
  def add_return_tag(object, type=nil)
38
38
  tag = object.tag(:return)
39
39
  if tag
40
- if (type && tag.types) && (type != tag.types)
40
+ if (type && tag.types.first) && (type != tag.types.first)
41
41
  log.warn "Documented return type does not match return type in function definition near #{statement.file}:#{statement.line}."
42
42
  end
43
43
 
@@ -29,7 +29,7 @@ class PuppetStrings::Yard::Handlers::Ruby::Base < YARD::Handlers::Ruby::Base
29
29
  source = node.source
30
30
  if source =~ HEREDOC_START
31
31
  lines = source.split("\n")
32
- source = lines[1..(lines.last.include?($1) ? -2 : -1)].join("\n") if lines.size > 1
32
+ source = lines[1..(lines.last.include?($1[0..-2]) ? -2 : -1)].join("\n") if lines.size > 1
33
33
  end
34
34
 
35
35
  source
@@ -0,0 +1,141 @@
1
+ require 'puppet-strings/yard/handlers/helpers'
2
+ require 'puppet-strings/yard/handlers/ruby/base'
3
+ require 'puppet-strings/yard/code_objects'
4
+ require 'puppet-strings/yard/util'
5
+
6
+ # Implements the handler for Puppet resource types written in Ruby.
7
+ class PuppetStrings::Yard::Handlers::Ruby::RsapiHandler < PuppetStrings::Yard::Handlers::Ruby::Base
8
+ # The default docstring when ensurable is used without given a docstring.
9
+ DEFAULT_ENSURABLE_DOCSTRING = 'The basic property that the resource should be in.'.freeze
10
+
11
+ namespace_only
12
+ handles method_call(:register_type)
13
+
14
+ process do
15
+ # Only accept calls to Puppet::ResourceApi
16
+ return unless statement.count > 1
17
+ module_name = statement[0].source
18
+ return unless [ 'Puppet::ResourceApi' ].include? module_name
19
+
20
+ schema = extract_schema
21
+
22
+ # puts "Schema: #{schema.inspect}"
23
+
24
+ object = PuppetStrings::Yard::CodeObjects::Type.new(schema['name'])
25
+ register object
26
+
27
+ docstring = schema['desc'] || ""
28
+ if docstring
29
+ register_docstring(object, PuppetStrings::Yard::Util.scrub_string(docstring.to_s), nil)
30
+ else
31
+ log.warn "Missing a description for Puppet resource type '#{object.name}' at #{statement.file}:#{statement.line}."
32
+ end
33
+
34
+ # Populate the parameters/properties/features to the type
35
+ populate_type_data(object, schema)
36
+
37
+ # Mark the type as public if it doesn't already have an api tag
38
+ object.add_tag YARD::Tags::Tag.new(:api, 'public') unless object.has_tag? :api
39
+
40
+ # Warn if a summary longer than 140 characters was provided
41
+ PuppetStrings::Yard::Handlers::Helpers.validate_summary_tag(object) if object.has_tag? :summary
42
+ end
43
+
44
+ private
45
+
46
+ def raise_parse_error(msg, location = statement)
47
+ raise YARD::Parser::UndocumentableError, "#{msg} at #{location.file}:#{location.line}." if parameters.empty?
48
+ end
49
+
50
+ # check that the params of the register_type call are key/value pairs.
51
+ def kv_arg_list?(params)
52
+ params.type == :list && params.children.count > 0 && params.children.first.type == :list && params.children.first.children.count > 0 && statement.parameters.children.first.children.first.type == :assoc
53
+ end
54
+
55
+ def extract_schema
56
+ raise_parse_error("Expected list of key/value pairs as argument") unless kv_arg_list?(statement.parameters)
57
+ hash_from_node(statement.parameters.children.first)
58
+ end
59
+
60
+ def value_from_node(node)
61
+ return nil unless node
62
+
63
+ # puts "value from #{node.inspect}"
64
+
65
+ case node.type
66
+ when :int
67
+ node.source.to_i
68
+ when :hash
69
+ hash_from_node(node)
70
+ when :var_ref
71
+ var_ref_from_node(node)
72
+ when :symbol, :symbol_literal, :label, :dyna_symbol, :string_literal
73
+ node_as_string(node)
74
+ else
75
+ raise_parse_error("unexpected construct #{node.type}")
76
+ end
77
+ end
78
+
79
+ def hash_from_node(node)
80
+ return nil unless node
81
+
82
+ # puts "hash from #{node.inspect}"
83
+
84
+ kv_pairs = node.children.collect do |assoc|
85
+ [ value_from_node(assoc.children[0]), value_from_node(assoc.children[1]) ]
86
+ end
87
+ Hash[kv_pairs]
88
+ end
89
+
90
+ def var_ref_from_node(node)
91
+ return nil unless node
92
+
93
+ # puts "var_ref from #{node.inspect}"
94
+
95
+ if node.children.first.type == :kw
96
+ case node.children.first.source
97
+ when "false"
98
+ return false
99
+ when "true"
100
+ return true
101
+ when "nil"
102
+ return nil
103
+ else
104
+ raise_parse_error("unexpected keyword '#{node.children.first.source}'")
105
+ end
106
+ end
107
+ raise_parse_error("unexpected variable")
108
+ end
109
+
110
+
111
+ def populate_type_data(object, schema)
112
+ return if schema['attributes'].nil?
113
+
114
+ schema['attributes'].each do |name, definition|
115
+ # puts "Processing #{name}: #{definition.inspect}"
116
+ if ['parameter', 'namevar'].include? definition['behaviour']
117
+ object.add_parameter(create_parameter(name, definition))
118
+ else
119
+ object.add_property(create_property(name, definition))
120
+ end
121
+ end
122
+ end
123
+
124
+ def create_parameter(name, definition)
125
+ parameter = PuppetStrings::Yard::CodeObjects::Type::Parameter.new(name, definition['desc'])
126
+ set_values(definition, parameter)
127
+ parameter
128
+ end
129
+
130
+ def create_property(name, definition)
131
+ property = PuppetStrings::Yard::CodeObjects::Type::Property.new(name, definition['desc'])
132
+ set_values(definition, property)
133
+ property
134
+ end
135
+
136
+ def set_values(definition, object)
137
+ object.data_type = definition['type'] if definition.key? 'type'
138
+ object.default = definition['default'] if definition.key? 'default'
139
+ object.isnamevar = definition.key?('behaviour') && definition['behaviour'] == 'namevar'
140
+ end
141
+ end
@@ -7,15 +7,21 @@ Puppet::Face.define(:strings, '0.0.1') do
7
7
  action(:generate) do
8
8
  default
9
9
 
10
- option '--emit-json-stdout' do
11
- summary 'Print JSON representation of the documentation to stdout.'
10
+ option '--format OUTPUT_FORMAT' do
11
+ summary 'Designate output format, JSON or markdown.'
12
12
  end
13
- option '--emit-json FILE' do
14
- summary 'Write JSON representation of the documentation to the given file.'
13
+ option '--out PATH' do
14
+ summary 'Write selected format to PATH. If no path is designated, strings prints to STDOUT.'
15
15
  end
16
16
  option '--markup FORMAT' do
17
17
  summary "The markup format to use for docstring text (defaults to 'markdown')."
18
18
  end
19
+ option '--emit-json-stdout' do
20
+ summary 'DEPRECATED: Print JSON representation of the documentation to stdout.'
21
+ end
22
+ option '--emit-json PATH' do
23
+ summary 'DEPRECATED: Write JSON representation of the documentation to the given file.'
24
+ end
19
25
 
20
26
  summary 'Generate documentation from files.'
21
27
  arguments '[[search_pattern] ...]'
@@ -94,11 +100,26 @@ Puppet::Face.define(:strings, '0.0.1') do
94
100
  generate_options[:yard_args] = yard_args unless yard_args.empty?
95
101
 
96
102
  if options
103
+ if options[:emit_json]
104
+ $stderr.puts "WARNING: '--emit-json PATH' is deprecated. Use '--format json --out PATH' instead."
105
+ end
106
+ if options[:emit_json_stdout]
107
+ $stderr.puts "WARNING: '--emit-json-stdout' is deprecated. Use '--format json' instead."
108
+ end
97
109
  markup = options[:markup]
98
110
  generate_options[:markup] = markup if markup
99
- json_file = options[:emit_json]
100
- generate_options[:json] = json_file if json_file
101
- generate_options[:json] = nil if options[:emit_json_stdout]
111
+ generate_options[:path] = options[:out] if options[:out]
112
+ generate_options[:stdout] = options[:stdout]
113
+ format = options[:format]
114
+ if format
115
+ if format.casecmp('markdown').zero?
116
+ generate_options[:markdown] = true
117
+ elsif format.casecmp('json').zero? || options[:emit_json] || options[:emit_json_stdout]
118
+ generate_options[:json] = true
119
+ else
120
+ raise RuntimeError, "Invalid format #{options[:format]}. Please select 'json' or 'markdown'."
121
+ end
122
+ end
102
123
  end
103
124
  generate_options
104
125
  end
@@ -37,18 +37,18 @@ describe 'Emitting JSON' do
37
37
  ]
38
38
  }
39
39
 
40
- it 'should emit JSON to stdout when using the --emit-json-stdout option' do
40
+ it 'should emit JSON to stdout when using --format json and --stdout' do
41
41
  test_module_path = get_test_module_path(master, /Module test/)
42
- on master, puppet('strings', 'generate', '--emit-json-stdout', "#{test_module_path}/lib/puppet/parser/functions/function3x.rb") do
42
+ on master, puppet('strings', 'generate', '--format json', "#{test_module_path}/lib/puppet/parser/functions/function3x.rb") do
43
43
  output = stdout.chomp
44
44
  expect(JSON.parse(output)).to eq(expected)
45
45
  end
46
46
  end
47
47
 
48
- it 'should write JSON to a file when using the --emit-json option' do
48
+ it 'should write JSON to a file when using --format json and --out' do
49
49
  test_module_path = get_test_module_path(master, /Module test/)
50
50
  tmpfile = master.tmpfile('json_output.json')
51
- on master, puppet('strings', 'generate', "--emit-json #{tmpfile}", "#{test_module_path}/lib/puppet/parser/functions/function3x.rb")
51
+ on master, puppet('strings', 'generate', '--format json', "--out #{tmpfile}", "#{test_module_path}/lib/puppet/parser/functions/function3x.rb")
52
52
  output = read_file_on(master, tmpfile)
53
53
  expect(JSON.parse(output)).to eq(expected)
54
54
  end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper_acceptance'
2
+ require 'util'
3
+
4
+ include PuppetStrings::Acceptance::Util
5
+
6
+ describe 'Generating Markdown' do
7
+ expected = <<-EOF
8
+ # Reference
9
+
10
+ ## Classes
11
+ * [`test`](#test): This class exists to serve as fixture data for testing the puppet strings face
12
+
13
+ ## Classes
14
+
15
+ ### test
16
+
17
+ #### Examples
18
+ ```puppet
19
+ class { "test": }
20
+ ```
21
+
22
+ #### Parameters
23
+
24
+ ##### `package_name`
25
+
26
+ The name of the package
27
+
28
+ ##### `service_name`
29
+
30
+ The name of the service
31
+
32
+ EOF
33
+
34
+ it 'should render Markdown to stdout when using --format markdown and --stdout' do
35
+ test_module_path = get_test_module_path(master, /Module test/)
36
+ on master, puppet('strings', 'generate', '--format markdown', "#{test_module_path}/manifests/init.pp") do
37
+ output = stdout.chomp
38
+ expect(JSON.parse(output)).to eq(expected)
39
+ end
40
+ end
41
+
42
+ it 'should write Markdown to a file when using --format markdown and --out' do
43
+ test_module_path = get_test_module_path(master, /Module test/)
44
+ tmpfile = master.tmpfile('md_output.md')
45
+ on master, puppet('strings', 'generate', '--format markdown', "--out #{tmpfile}", "#{test_module_path}/manifests/init.pp")
46
+ output = read_file_on(master, tmpfile)
47
+ expect(JSON.parse(output)).to eq(expected)
48
+ end
49
+ end
@@ -81,6 +81,49 @@
81
81
  }
82
82
  ],
83
83
  "resource_types": [
84
+ {
85
+ "name": "apt_key",
86
+ "file": "(stdin)",
87
+ "line": 92,
88
+ "docstring": {
89
+ "text": "This type provides Puppet with the capabilities to manage GPG keys needed\nby apt to perform package validation. Apt has it's own GPG keyring that can\nbe manipulated through the `apt-key` command.\n**Autorequires**:\nIf Puppet is given the location of a key file which looks like an absolute\npath this type will autorequire that file.",
90
+ "tags": [
91
+ {
92
+ "tag_name": "summary",
93
+ "text": "Example resource type using the new API."
94
+ },
95
+ {
96
+ "tag_name": "raise",
97
+ "text": "SomeError"
98
+ },
99
+ {
100
+ "tag_name": "example",
101
+ "text": "apt_key { '6F6B15509CF8E59E6E469F327F438280EF8D349F':\n source => 'http://apt.puppetlabs.com/pubkey.gpg'\n}",
102
+ "name": "here's an example"
103
+ }
104
+ ]
105
+ },
106
+ "properties": [
107
+ {
108
+ "name": "ensure",
109
+ "description": "Whether this apt key should be present or absent on the target system.",
110
+ "data_type": "Enum[present, absent]"
111
+ },
112
+ {
113
+ "name": "created",
114
+ "description": "Date the key was created, in ISO format.",
115
+ "data_type": "String"
116
+ }
117
+ ],
118
+ "parameters": [
119
+ {
120
+ "name": "id",
121
+ "description": "The ID of the key you want to manage.",
122
+ "data_type": "Variant[Pattern[/A(0x)?[0-9a-fA-F]{8}Z/], Pattern[/A(0x)?[0-9a-fA-F]{16}Z/], Pattern[/A(0x)?[0-9a-fA-F]{40}Z/]]",
123
+ "isnamevar": true
124
+ }
125
+ ]
126
+ },
84
127
  {
85
128
  "name": "database",
86
129
  "file": "(stdin)",