puppet-strings 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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)",