puppet-strings 2.4.0 → 2.8.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 (134) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +97 -5
  3. data/COMMITTERS.md +17 -17
  4. data/CONTRIBUTING.md +6 -6
  5. data/README.md +10 -10
  6. data/lib/puppet-strings.rb +4 -2
  7. data/lib/puppet-strings/describe.rb +2 -0
  8. data/lib/puppet-strings/json.rb +2 -0
  9. data/lib/puppet-strings/markdown.rb +12 -10
  10. data/lib/puppet-strings/markdown/base.rb +17 -3
  11. data/lib/puppet-strings/markdown/data_type.rb +18 -0
  12. data/lib/puppet-strings/markdown/data_types.rb +3 -1
  13. data/lib/puppet-strings/markdown/defined_type.rb +2 -0
  14. data/lib/puppet-strings/markdown/defined_types.rb +3 -1
  15. data/lib/puppet-strings/markdown/function.rb +9 -7
  16. data/lib/puppet-strings/markdown/functions.rb +3 -1
  17. data/lib/puppet-strings/markdown/puppet_class.rb +2 -0
  18. data/lib/puppet-strings/markdown/puppet_classes.rb +3 -1
  19. data/lib/puppet-strings/markdown/puppet_plan.rb +2 -0
  20. data/lib/puppet-strings/markdown/puppet_plans.rb +3 -1
  21. data/lib/puppet-strings/markdown/puppet_task.rb +2 -0
  22. data/lib/puppet-strings/markdown/puppet_tasks.rb +3 -1
  23. data/lib/puppet-strings/markdown/resource_type.rb +21 -2
  24. data/lib/puppet-strings/markdown/resource_types.rb +3 -1
  25. data/lib/puppet-strings/markdown/table_of_contents.rb +3 -1
  26. data/lib/puppet-strings/markdown/templates/classes_and_defines.erb +10 -6
  27. data/lib/puppet-strings/markdown/templates/data_type.erb +22 -7
  28. data/lib/puppet-strings/markdown/templates/data_type_function.erb +67 -0
  29. data/lib/puppet-strings/markdown/templates/function.erb +2 -1
  30. data/lib/puppet-strings/markdown/templates/puppet_task.erb +1 -1
  31. data/lib/puppet-strings/markdown/templates/resource_type.erb +19 -13
  32. data/lib/puppet-strings/markdown/templates/table_of_contents.erb +6 -6
  33. data/lib/puppet-strings/monkey_patches/display_object_command.rb +2 -0
  34. data/lib/puppet-strings/tasks.rb +2 -0
  35. data/lib/puppet-strings/tasks/generate.rb +2 -0
  36. data/lib/puppet-strings/tasks/gh_pages.rb +3 -0
  37. data/lib/puppet-strings/version.rb +3 -1
  38. data/lib/puppet-strings/yard.rb +3 -0
  39. data/lib/puppet-strings/yard/code_objects.rb +2 -0
  40. data/lib/puppet-strings/yard/code_objects/base.rb +2 -0
  41. data/lib/puppet-strings/yard/code_objects/class.rb +4 -2
  42. data/lib/puppet-strings/yard/code_objects/data_type.rb +30 -8
  43. data/lib/puppet-strings/yard/code_objects/data_type_alias.rb +2 -0
  44. data/lib/puppet-strings/yard/code_objects/defined_type.rb +4 -2
  45. data/lib/puppet-strings/yard/code_objects/function.rb +6 -3
  46. data/lib/puppet-strings/yard/code_objects/group.rb +3 -0
  47. data/lib/puppet-strings/yard/code_objects/plan.rb +4 -2
  48. data/lib/puppet-strings/yard/code_objects/provider.rb +6 -0
  49. data/lib/puppet-strings/yard/code_objects/task.rb +2 -0
  50. data/lib/puppet-strings/yard/code_objects/type.rb +50 -5
  51. data/lib/puppet-strings/yard/handlers.rb +2 -0
  52. data/lib/puppet-strings/yard/handlers/helpers.rb +2 -0
  53. data/lib/puppet-strings/yard/handlers/json/base.rb +2 -0
  54. data/lib/puppet-strings/yard/handlers/json/task_handler.rb +2 -0
  55. data/lib/puppet-strings/yard/handlers/puppet/base.rb +3 -0
  56. data/lib/puppet-strings/yard/handlers/puppet/class_handler.rb +2 -0
  57. data/lib/puppet-strings/yard/handlers/puppet/data_type_alias_handler.rb +2 -0
  58. data/lib/puppet-strings/yard/handlers/puppet/defined_type_handler.rb +2 -0
  59. data/lib/puppet-strings/yard/handlers/puppet/function_handler.rb +3 -1
  60. data/lib/puppet-strings/yard/handlers/puppet/plan_handler.rb +2 -0
  61. data/lib/puppet-strings/yard/handlers/ruby/base.rb +5 -0
  62. data/lib/puppet-strings/yard/handlers/ruby/data_type_handler.rb +223 -60
  63. data/lib/puppet-strings/yard/handlers/ruby/function_handler.rb +9 -7
  64. data/lib/puppet-strings/yard/handlers/ruby/provider_handler.rb +13 -0
  65. data/lib/puppet-strings/yard/handlers/ruby/rsapi_handler.rb +4 -1
  66. data/lib/puppet-strings/yard/handlers/ruby/type_base.rb +20 -6
  67. data/lib/puppet-strings/yard/handlers/ruby/type_extras_handler.rb +16 -6
  68. data/lib/puppet-strings/yard/handlers/ruby/type_handler.rb +18 -1
  69. data/lib/puppet-strings/yard/parsers.rb +2 -0
  70. data/lib/puppet-strings/yard/parsers/json/parser.rb +2 -0
  71. data/lib/puppet-strings/yard/parsers/json/task_statement.rb +2 -0
  72. data/lib/puppet-strings/yard/parsers/puppet/parser.rb +18 -14
  73. data/lib/puppet-strings/yard/parsers/puppet/statement.rb +4 -0
  74. data/lib/puppet-strings/yard/tags.rb +2 -0
  75. data/lib/puppet-strings/yard/tags/enum_tag.rb +2 -0
  76. data/lib/puppet-strings/yard/tags/factory.rb +2 -0
  77. data/lib/puppet-strings/yard/tags/overload_tag.rb +4 -1
  78. data/lib/puppet-strings/yard/tags/parameter_directive.rb +5 -4
  79. data/lib/puppet-strings/yard/tags/property_directive.rb +5 -4
  80. data/lib/puppet-strings/yard/tags/summary_tag.rb +2 -0
  81. data/lib/puppet-strings/yard/templates/default/puppet_data_type/html/method_details_list.erb +6 -0
  82. data/lib/puppet-strings/yard/templates/default/puppet_data_type/html/setup.rb +9 -1
  83. data/lib/puppet-strings/yard/templates/default/puppet_function/html/setup.rb +1 -1
  84. data/lib/puppet-strings/yard/templates/default/puppet_type/html/setup.rb +3 -1
  85. data/lib/puppet-strings/yard/util.rb +4 -1
  86. data/lib/puppet/application/strings.rb +2 -0
  87. data/lib/puppet/face/strings.rb +4 -1
  88. data/lib/puppet/feature/rgen.rb +2 -0
  89. data/lib/puppet/feature/yard.rb +2 -0
  90. metadata +11 -52
  91. data/CODEOWNERS +0 -1
  92. data/Gemfile +0 -53
  93. data/HISTORY.md +0 -218
  94. data/JSON.md +0 -832
  95. data/Rakefile +0 -160
  96. data/codecov.yml +0 -3
  97. data/misc/ANNOUNCEMENT_TEMPLATE.md +0 -40
  98. data/spec/acceptance/emit_json_options_spec.rb +0 -69
  99. data/spec/acceptance/generate_markdown_spec.rb +0 -47
  100. data/spec/acceptance/running_strings_generate_spec.rb +0 -88
  101. data/spec/fixtures/acceptance/modules/test/functions/add.pp +0 -9
  102. data/spec/fixtures/acceptance/modules/test/lib/puppet/functions/4x_function.rb +0 -5
  103. data/spec/fixtures/acceptance/modules/test/lib/puppet/parser/functions/function3x.rb +0 -2
  104. data/spec/fixtures/acceptance/modules/test/lib/puppet/provider/server/linux.rb +0 -9
  105. data/spec/fixtures/acceptance/modules/test/lib/puppet/type/database.rb +0 -15
  106. data/spec/fixtures/acceptance/modules/test/manifests/init.pp +0 -31
  107. data/spec/fixtures/acceptance/modules/test/manifests/triple_nested_classes.pp +0 -27
  108. data/spec/fixtures/acceptance/modules/test/metadata.json +0 -10
  109. data/spec/fixtures/acceptance/modules/test/types/elephant.pp +0 -2
  110. data/spec/fixtures/unit/markdown/output.md +0 -561
  111. data/spec/fixtures/unit/markdown/output_with_data_types.md +0 -606
  112. data/spec/fixtures/unit/markdown/output_with_plan.md +0 -595
  113. data/spec/spec_helper.rb +0 -49
  114. data/spec/spec_helper_acceptance.rb +0 -58
  115. data/spec/spec_helper_acceptance_local.rb +0 -10
  116. data/spec/unit/puppet-strings/describe_spec.rb +0 -141
  117. data/spec/unit/puppet-strings/json_spec.rb +0 -302
  118. data/spec/unit/puppet-strings/markdown/base_spec.rb +0 -146
  119. data/spec/unit/puppet-strings/markdown_spec.rb +0 -374
  120. data/spec/unit/puppet-strings/yard/code_objects/task_spec.rb +0 -92
  121. data/spec/unit/puppet-strings/yard/handlers/json/task_handler_spec.rb +0 -116
  122. data/spec/unit/puppet-strings/yard/handlers/puppet/class_handler_spec.rb +0 -217
  123. data/spec/unit/puppet-strings/yard/handlers/puppet/data_type_alias_handler_spec.rb +0 -65
  124. data/spec/unit/puppet-strings/yard/handlers/puppet/defined_type_handler_spec.rb +0 -231
  125. data/spec/unit/puppet-strings/yard/handlers/puppet/function_handler_spec.rb +0 -315
  126. data/spec/unit/puppet-strings/yard/handlers/ruby/data_type_handler_spec.rb +0 -309
  127. data/spec/unit/puppet-strings/yard/handlers/ruby/function_handler_spec.rb +0 -746
  128. data/spec/unit/puppet-strings/yard/handlers/ruby/provider_handler_spec.rb +0 -158
  129. data/spec/unit/puppet-strings/yard/handlers/ruby/rsapi_handler_spec.rb +0 -235
  130. data/spec/unit/puppet-strings/yard/handlers/ruby/type_handler_spec.rb +0 -311
  131. data/spec/unit/puppet-strings/yard/parsers/json/parser_spec.rb +0 -72
  132. data/spec/unit/puppet-strings/yard/parsers/json/task_statement_spec.rb +0 -56
  133. data/spec/unit/puppet-strings/yard/parsers/puppet/parser_spec.rb +0 -251
  134. data/spec/unit/puppet-strings/yard/util_spec.rb +0 -48
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # The module for custom YARD handlers.
2
4
  module PuppetStrings::Yard::Handlers
3
5
  # The module for custom Ruby YARD handlers.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PuppetStrings::Yard::Handlers::Helpers
2
4
  # Logs a warning if a summary tag has more than 140 characters
3
5
  def self.validate_summary_tag(object)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class PuppetStrings::Yard::Handlers::JSON::Base < YARD::Handlers::Base
2
4
  def self.handles?(statement)
3
5
  handlers.any? {|handler| statement.is_a?(handler)}
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'puppet-strings/yard/handlers/json/base'
2
4
  require 'puppet-strings/yard/parsers'
3
5
  require 'puppet-strings/yard/parsers/json/parser'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Implements the base handler for Puppet language handlers.
2
4
  class PuppetStrings::Yard::Handlers::Puppet::Base < YARD::Handlers::Base
3
5
  # Determine sif the handler handles the given statement.
@@ -17,6 +19,7 @@ class PuppetStrings::Yard::Handlers::Puppet::Base < YARD::Handlers::Base
17
19
  tags = object.tags(:param)
18
20
  tags.each do |tag|
19
21
  next if statement.parameters.find { |p| tag.name == p.name }
22
+
20
23
  log.warn "The @param tag for parameter '#{tag.name}' has no matching parameter at #{statement.file}:#{statement.line}." unless tag.name == 'name' || tag.name == 'title'
21
24
  end
22
25
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'puppet-strings/yard/handlers/helpers'
2
4
  require 'puppet-strings/yard/handlers/puppet/base'
3
5
  require 'puppet-strings/yard/parsers'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'puppet-strings/yard/handlers/helpers'
2
4
  require 'puppet-strings/yard/handlers/puppet/base'
3
5
  require 'puppet-strings/yard/parsers'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'puppet-strings/yard/handlers/helpers'
2
4
  require 'puppet-strings/yard/handlers/puppet/base'
3
5
  require 'puppet-strings/yard/parsers'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'puppet-strings/yard/handlers/helpers'
2
4
  require 'puppet-strings/yard/handlers/puppet/base'
3
5
  require 'puppet-strings/yard/parsers'
@@ -37,7 +39,7 @@ class PuppetStrings::Yard::Handlers::Puppet::FunctionHandler < PuppetStrings::Ya
37
39
  def add_return_tag(object, type=nil)
38
40
  tag = object.tag(:return)
39
41
  if tag
40
- if (type && tag.types.first) && (type != tag.types.first)
42
+ if (type && tag.types && tag.types.first) && (type != tag.types.first)
41
43
  log.warn "Documented return type does not match return type in function definition near #{statement.file}:#{statement.line}."
42
44
  end
43
45
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'puppet-strings/yard/handlers/helpers'
2
4
  require 'puppet-strings/yard/handlers/puppet/base'
3
5
  require 'puppet-strings/yard/parsers'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'ripper'
2
4
 
3
5
  # Implements the base handler for Ruby language handlers.
@@ -12,6 +14,7 @@ class PuppetStrings::Yard::Handlers::Ruby::Base < YARD::Handlers::Ruby::Base
12
14
  # @return [String] Returns a string representation of the node or nil if a string representation was not possible.
13
15
  def node_as_string(node)
14
16
  return nil unless node
17
+
15
18
  case node.type
16
19
  when :symbol, :symbol_literal
17
20
  node.source[1..-1]
@@ -42,8 +45,10 @@ class PuppetStrings::Yard::Handlers::Ruby::Base < YARD::Handlers::Ruby::Base
42
45
  def get_name(statementobject, statementtype)
43
46
  parameters = statementobject.parameters(false)
44
47
  raise YARD::Parser::UndocumentableError, "Expected at least one parameter to #{statementtype} at #{statementobject.file}:#{statementobject.line}." if parameters.empty?
48
+
45
49
  name = node_as_string(parameters.first)
46
50
  raise YARD::Parser::UndocumentableError, "Expected a symbol or string literal for first parameter but found '#{parameters.first.type}' at #{statement.file}:#{statement.line}." unless name
51
+
47
52
  name
48
53
  end
49
54
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'puppet-strings/yard/handlers/helpers'
2
4
  require 'puppet-strings/yard/handlers/ruby/base'
3
5
  require 'puppet-strings/yard/code_objects'
@@ -10,16 +12,21 @@ class PuppetStrings::Yard::Handlers::Ruby::DataTypeHandler < PuppetStrings::Yard
10
12
 
11
13
  process do
12
14
  return unless statement.count > 1
15
+
13
16
  ruby_module_name = statement[0].source
14
17
  return unless ruby_module_name == 'Puppet::DataTypes' || ruby_module_name == 'DataTypes' # rubocop:disable Style/MultipleComparison This reads better
15
- object = get_datatype_yard_object(get_name(statement, 'Puppet::DataTypes.create_type'))
16
18
 
17
- actual_params = extract_params_for_data_type
19
+ object = get_datatype_yard_object(get_name(statement, 'Puppet::DataTypes.create_type'))
20
+ # Extract the interface definition
21
+ type_interface = extract_data_type_interface
22
+ actual_params = extract_params(type_interface)
23
+ actual_funcs = extract_functions(object, type_interface)
18
24
 
19
25
  # Mark the data type as public if it doesn't already have an api tag
20
26
  object.add_tag YARD::Tags::Tag.new(:api, 'public') unless object.has_tag? :api
21
27
 
22
- validate_tags!(object, actual_params)
28
+ validate_param_tags!(object, actual_params)
29
+ validate_methods!(object, actual_funcs)
23
30
 
24
31
  # Set the default values for all parameters
25
32
  actual_params.each { |name, data| object.set_parameter_default(name, data[:default]) }
@@ -49,44 +56,64 @@ class PuppetStrings::Yard::Handlers::Ruby::DataTypeHandler < PuppetStrings::Yard
49
56
  object
50
57
  end
51
58
 
52
- def extract_params_for_data_type
53
- params = {}
54
- # Traverse the block looking for interface
59
+ # @return [Hash{Object => Object}] The Puppet DataType interface definition as a ruby Hash
60
+ def extract_data_type_interface
55
61
  block = statement.block
56
- return unless block && block.count >= 2
57
- block[1].children.each do |node|
58
- next unless node.is_a?(YARD::Parser::Ruby::MethodCallNode) &&
59
- node.method_name
62
+ return {} unless block
63
+
64
+ # Declare the parsed interface outside of the closure
65
+ parsed_interface = nil
66
+
67
+ # Recursively traverse the block looking for the first valid 'interface' call
68
+ interface_node = find_ruby_ast_node(block, true) do |node|
69
+ next false unless node.is_a?(YARD::Parser::Ruby::MethodCallNode) &&
70
+ node.method_name &&
71
+ node.method_name.source == 'interface'
60
72
 
61
- method_name = node.method_name.source
62
73
  parameters = node.parameters(false)
63
- if method_name == 'interface'
64
- next unless parameters.count >= 1
65
- interface_string = node_as_string(parameters[0])
66
- next unless interface_string
74
+ next false unless parameters.count >= 1
75
+
76
+ interface_string = node_as_string(parameters[0])
77
+ next false unless interface_string
78
+
79
+ begin
67
80
  # Ref - https://github.com/puppetlabs/puppet/blob/ba4d1a1aba0095d3c70b98fea5c67434a4876a61/lib/puppet/datatypes.rb#L159
68
- parsed_interface = nil
69
- begin
70
- parsed_interface = Puppet::Pops::Parser::EvaluatingParser.new.parse_string("{ #{interface_string} }").body
71
- rescue Puppet::Error => e
72
- log.warn "Invalid datatype definition at #{statement.file}:#{statement.line}: #{e.basic_message}"
73
- next
74
- end
75
- next unless parsed_interface
76
-
77
- # Now that we parsed the Puppet code (as a string) into a LiteralHash PCore type (Puppet AST),
78
- #
79
- # We need to convert the LiteralHash into a conventional ruby hash of strings. The
80
- # LazyLiteralEvaluator does this by traversing the AST tree can converting objects to strings
81
- # where possible and ignoring object types which cannot (thus the 'Lazy' name)
82
- #
83
- # Once we have it as a standard ruby hash we can then look at the keys and populate the YARD
84
- # Code object with the correct attributes etc.
85
- literal_eval = LazyLiteralEvaluator.new
86
- populate_data_type_params_from_literal_hash!(literal_eval.literal(parsed_interface), params)
81
+ parsed_interface = Puppet::Pops::Parser::EvaluatingParser.new.parse_string("{ #{interface_string} }").body
82
+ rescue Puppet::Error => e
83
+ log.warn "Invalid datatype definition at #{statement.file}:#{statement.line}: #{e.basic_message}"
84
+ next false
85
+ end
86
+ !parsed_interface.nil?
87
+ end
88
+
89
+ # Now that we parsed the Puppet code (as a string) into a LiteralHash PCore type (Puppet AST),
90
+ # We need to convert the LiteralHash into a conventional ruby hash of strings. The
91
+ # LazyLiteralEvaluator does this by traversing the AST tree can converting objects to strings
92
+ # where possible and ignoring object types which cannot (thus the 'Lazy' name)
93
+ literal_eval = LazyLiteralEvaluator.new
94
+ literal_eval.literal(parsed_interface)
95
+ end
96
+
97
+ # Find the first Ruby AST node within an AST Tree, optionally recursively. Returns nil of none could be found
98
+ #
99
+ # @param [YARD::Parser::Ruby::AstNode] ast_node The root AST node object to inspect
100
+ # @param [Boolean] recurse Whether to search the tree recursively. Defaults to false
101
+ # @yieldparam [YARD::Parser::Ruby::AstNode] ast_node The AST Node that should be checked
102
+ # @yieldreturn [Boolean] Whether the node was what was searched for
103
+ # @return [YARD::Parser::Ruby::AstNode, nil]
104
+ def find_ruby_ast_node(ast_node, recurse = false, &block)
105
+ raise ArgumentError, 'find_ruby_ast_node requires a block' unless block_given?
106
+
107
+ is_found = yield ast_node
108
+ return ast_node if is_found
109
+
110
+ if ast_node.respond_to?(:children)
111
+ ast_node.children.each do |child_node|
112
+ child_found = find_ruby_ast_node(child_node, recurse, &block)
113
+ return child_found unless child_found.nil?
87
114
  end
88
115
  end
89
- params
116
+ nil
90
117
  end
91
118
 
92
119
  # Lazily evaluates a Pops object, ignoring any objects that cannot
@@ -109,7 +136,7 @@ class PuppetStrings::Yard::Handlers::Ruby::DataTypeHandler < PuppetStrings::Yard
109
136
  # Anything else is ignored
110
137
  class LazyLiteralEvaluator
111
138
  def initialize
112
- @literal_visitor ||= ::Puppet::Pops::Visitor.new(self, "literal", 0, 0)
139
+ @literal_visitor = ::Puppet::Pops::Visitor.new(self, "literal", 0, 0)
113
140
  end
114
141
 
115
142
  def literal(ast)
@@ -117,72 +144,72 @@ class PuppetStrings::Yard::Handlers::Ruby::DataTypeHandler < PuppetStrings::Yard
117
144
  end
118
145
 
119
146
  # ----- The following methods are different/additions from the original Literal_evaluator
120
- def literal_Object(o) # rubocop:disable Naming/UncommunicativeMethodParamName
147
+ def literal_Object(o)
121
148
  # Ignore any other object types
122
149
  end
123
150
 
124
- def literal_AccessExpression(o) # rubocop:disable Naming/UncommunicativeMethodParamName
151
+ def literal_AccessExpression(o)
125
152
  # Extract the raw text of the Access Expression
126
153
  ::Puppet::Pops::Adapters::SourcePosAdapter.adapt(o).extract_text
127
154
  end
128
155
 
129
- def literal_QualifiedReference(o) # rubocop:disable Naming/UncommunicativeMethodParamName
156
+ def literal_QualifiedReference(o)
130
157
  # Extract the raw text of the Qualified Reference
131
158
  ::Puppet::Pops::Adapters::SourcePosAdapter.adapt(o).extract_text
132
159
  end
133
160
 
134
161
  # ----- The following methods are the same as the original Literal_evaluator
135
- def literal_Factory(o) # rubocop:disable Naming/UncommunicativeMethodParamName
162
+ def literal_Factory(o)
136
163
  literal(o.model)
137
164
  end
138
165
 
139
- def literal_Program(o) # rubocop:disable Naming/UncommunicativeMethodParamName
166
+ def literal_Program(o)
140
167
  literal(o.body)
141
168
  end
142
169
 
143
- def literal_LiteralString(o) # rubocop:disable Naming/UncommunicativeMethodParamName
170
+ def literal_LiteralString(o)
144
171
  o.value
145
172
  end
146
173
 
147
- def literal_QualifiedName(o) # rubocop:disable Naming/UncommunicativeMethodParamName
174
+ def literal_QualifiedName(o)
148
175
  o.value
149
176
  end
150
177
 
151
- def literal_LiteralNumber(o) # rubocop:disable Naming/UncommunicativeMethodParamName
178
+ def literal_LiteralNumber(o)
152
179
  o.value
153
180
  end
154
181
 
155
- def literal_UnaryMinusExpression(o) # rubocop:disable Naming/UncommunicativeMethodParamName
182
+ def literal_UnaryMinusExpression(o)
156
183
  -1 * literal(o.expr)
157
184
  end
158
185
 
159
- def literal_LiteralBoolean(o) # rubocop:disable Naming/UncommunicativeMethodParamName
186
+ def literal_LiteralBoolean(o)
160
187
  o.value
161
188
  end
162
189
 
163
- def literal_LiteralUndef(o) # rubocop:disable Naming/UncommunicativeMethodParamName
190
+ def literal_LiteralUndef(o)
164
191
  nil
165
192
  end
166
193
 
167
- def literal_LiteralDefault(o) # rubocop:disable Naming/UncommunicativeMethodParamName
194
+ def literal_LiteralDefault(o)
168
195
  :default
169
196
  end
170
197
 
171
- def literal_LiteralRegularExpression(o) # rubocop:disable Naming/UncommunicativeMethodParamName
198
+ def literal_LiteralRegularExpression(o)
172
199
  o.value
173
200
  end
174
201
 
175
- def literal_ConcatenatedString(o) # rubocop:disable Naming/UncommunicativeMethodParamName
202
+ def literal_ConcatenatedString(o)
176
203
  # use double quoted string value if there is no interpolation
177
204
  throw :not_literal unless o.segments.size == 1 && o.segments[0].is_a?(Model::LiteralString)
178
205
  o.segments[0].value
179
206
  end
180
207
 
181
- def literal_LiteralList(o) # rubocop:disable Naming/UncommunicativeMethodParamName
182
- o.values.map {|v| literal(v) }
208
+ def literal_LiteralList(o)
209
+ o.values.map { |v| literal(v) }
183
210
  end
184
211
 
185
- def literal_LiteralHash(o) # rubocop:disable Naming/UncommunicativeMethodParamName
212
+ def literal_LiteralHash(o)
186
213
  o.entries.reduce({}) do |result, entry|
187
214
  result[literal(entry.key)] = literal(entry.value)
188
215
  result
@@ -190,27 +217,58 @@ class PuppetStrings::Yard::Handlers::Ruby::DataTypeHandler < PuppetStrings::Yard
190
217
  end
191
218
  end
192
219
 
193
- def populate_data_type_params_from_literal_hash!(hash, params_hash)
194
- return if hash.nil?
220
+ # Extracts the datatype attributes from a Puppet Data Type interface hash.
221
+ # Returns a Hash with a :types key (Array of data types for the parameter) and :default key (The default value of the parameter)
222
+ # @return Hash[Symbol => Hash] The Datatype Attributes as a hash
223
+ def extract_params(hash)
224
+ params_hash = {}
195
225
  # Exit early if there are no entries in the hash
196
- return if hash['attributes'].nil? || hash['attributes'].count.zero?
226
+ return params_hash if hash.nil? || hash['attributes'].nil? || hash['attributes'].empty?
197
227
 
198
228
  hash['attributes'].each do |key, value|
199
229
  data_type = nil
200
230
  default = nil
201
- case value
202
- when String
231
+ if value.is_a?(String)
203
232
  data_type = value
204
- when Hash
233
+ elsif value.is_a?(Hash)
205
234
  data_type = value['type'] unless value['type'].nil?
206
235
  default = value['value'] unless value['value'].nil?
207
236
  end
208
237
  data_type = [data_type] unless data_type.nil? || data_type.is_a?(Array)
209
238
  params_hash[key] = { :types => data_type, :default => default }
210
239
  end
240
+
241
+ params_hash
242
+ end
243
+
244
+ # Extracts the datatype functions from a Puppet Data Type interface hash.
245
+ # Returns a Hash with a :param_types key (Array of types for each parameter) and :return_type key (The return type of the function)
246
+ # @return Hash[Symbol => Hash] The Datatype Attributes as a hash
247
+ def extract_functions(object, hash)
248
+ funcs_hash = {}
249
+ # Exit early if there are no entries in the hash
250
+ return funcs_hash if hash.nil? || hash['functions'].nil? || hash['functions'].empty?
251
+
252
+ hash['functions'].each do |key, func_type|
253
+ func_hash = { :param_types => [], :return_type => nil }
254
+ begin
255
+ callable_type = Puppet::Pops::Types::TypeParser.singleton.parse(func_type)
256
+ if callable_type.is_a?(Puppet::Pops::Types::PCallableType)
257
+ func_hash[:param_types] = callable_type.param_types.map { |pt| pt.to_s }
258
+ func_hash[:return_type] = callable_type.return_type.to_s
259
+ else
260
+ log.warn "The function definition for '#{key}' near #{object.file}:#{object.line} is not a Callable type"
261
+ end
262
+ rescue Puppet::ParseError => e
263
+ log.warn "Unable to parse the function definition for '#{key}' near #{object.file}:#{object.line}. #{e}"
264
+ end
265
+ funcs_hash[key] = func_hash
266
+ end
267
+ funcs_hash
211
268
  end
212
269
 
213
- def validate_tags!(object, actual_params_hash)
270
+ # Validates and automatically fixes yard @param tags for the data type
271
+ def validate_param_tags!(object, actual_params_hash)
214
272
  actual_param_names = actual_params_hash.keys
215
273
  tagged_param_names = object.tags(:param).map(&:name)
216
274
  # Log any errors
@@ -225,8 +283,10 @@ class PuppetStrings::Yard::Handlers::Ruby::DataTypeHandler < PuppetStrings::Yard
225
283
  # Find param tags with a type that is different from the actual definition
226
284
  object.tags(:param).reject { |tag| tag.types.nil? }.each do |tag|
227
285
  next if actual_params_hash[tag.name].nil?
286
+
228
287
  actual_data_type = actual_params_hash[tag.name][:types]
229
288
  next if actual_data_type.nil?
289
+
230
290
  log.warn "The @param tag for '#{tag.name}' has a different type definition than the actual attribute near #{object.file}:#{object.line}." if tag.types != actual_data_type
231
291
  end
232
292
 
@@ -240,7 +300,110 @@ class PuppetStrings::Yard::Handlers::Ruby::DataTypeHandler < PuppetStrings::Yard
240
300
  # Set the type in the param tag
241
301
  object.tags(:param).each do |tag|
242
302
  next if actual_params_hash[tag.name].nil?
303
+
243
304
  tag.types = actual_params_hash[tag.name][:types]
244
305
  end
245
306
  end
307
+
308
+ # Validates and automatically fixes yard @method! tags for the data type
309
+ def validate_methods!(object, actual_functions_hash)
310
+ actual_func_names = actual_functions_hash.keys
311
+ tagged_func_names = object.meths.map { |meth| meth.name.to_s }
312
+
313
+ # Log any errors
314
+ # Find functions which are not documented
315
+ (actual_func_names - tagged_func_names).each do |item|
316
+ log.warn "Missing @!method tag for function '#{item}' near #{object.file}:#{object.line}."
317
+ end
318
+ # Find functions which are not defined
319
+ (tagged_func_names - actual_func_names).each do |item|
320
+ log.warn "The @!method tag for '#{item}' has no matching function definition near #{object.file}:#{object.line}."
321
+ end
322
+ # Functions with the wrong return type
323
+ object.meths.each do |meth|
324
+ next unless actual_func_names.include?(meth.name.to_s)
325
+
326
+ return_tag = meth.docstring.tag(:return)
327
+ next if return_tag.nil?
328
+
329
+ actual_return_types = [actual_functions_hash[meth.name.to_s][:return_type]]
330
+ next if return_tag.types == actual_return_types
331
+
332
+ log.warn "The @return tag for '#{meth.name}' has a different type definition than the actual function near #{object.file}:#{object.line}. Expected #{actual_return_types}"
333
+ return_tag.types = actual_return_types
334
+ end
335
+
336
+ # Automatically fix missing methods
337
+ (actual_func_names - tagged_func_names).each do |name|
338
+ object.add_function(name, actual_functions_hash[name][:return_type], actual_functions_hash[name][:param_types])
339
+ end
340
+ # Remove extra methods. Can't use `meths` as that's a derived property
341
+ object.children.reject! { |child| child.is_a?(YARD::CodeObjects::MethodObject) && !actual_func_names.include?(child.name.to_s) }
342
+
343
+ # Add the return type for the methods if missing
344
+ object.meths.each do |meth|
345
+ next unless meth.docstring.tag(:return).nil?
346
+
347
+ meth.docstring.add_tag(YARD::Tags::Tag.new(:return, '', actual_functions_hash[meth.name.to_s][:return_type]))
348
+ end
349
+
350
+ # Sync the method properties and add the return type for the methods if missing
351
+ object.meths.each do |meth|
352
+ validate_function_method!(object, meth, actual_functions_hash[meth.name.to_s])
353
+ next unless meth.docstring.tag(:return).nil?
354
+
355
+ meth.docstring.add_tag(YARD::Tags::Tag.new(:return, '', actual_functions_hash[meth.name.to_s][:return_type]))
356
+ end
357
+
358
+ # The default meth.signature assumes ruby invocation (e.g. def meth(...)) but this doesn't make sense for a
359
+ # Puppet Data Type function invocation. So instead we derive a signature from the method definition.
360
+ object.meths.each do |meth|
361
+ params = ''
362
+ unless meth.docstring.tags(:param).empty?
363
+ params += '(' + meth.docstring.tags(:param).map { |t| t.name }.join(', ') + ')'
364
+ end
365
+ meth.signature = "#{object.name}.#{meth.name}" + params
366
+ end
367
+
368
+ nil
369
+ end
370
+
371
+ # Validates and automatically fixes a single yard @method!
372
+ # Used by the validate_methods! method.
373
+ def validate_function_method!(object, meth, actual_function)
374
+ # Remove extra params
375
+ if meth.docstring.tags(:param).count > actual_function[:param_types].count
376
+ index = 0
377
+ meth.docstring.delete_tag_if do |tag|
378
+ if tag.tag_name == 'param'
379
+ index += 1
380
+ if index > actual_function[:param_types].count
381
+ log.warn "The @param tag for '#{tag.name}' should not exist for function '#{meth.name}' that is defined near #{object.file}:#{object.line}. Expected only #{actual_function[:param_types].count} parameter/s"
382
+ true
383
+ else
384
+ false
385
+ end
386
+ else
387
+ false
388
+ end
389
+ end
390
+ end
391
+
392
+ # Add missing params
393
+ if meth.docstring.tags(:param).count < actual_function[:param_types].count
394
+ start = meth.docstring.tags(:param).count + 1
395
+ (start..actual_function[:param_types].count).each do |index| # Using 1-based index here instead of usual zero
396
+ meth.add_tag(YARD::Tags::Tag.new(:param, '', actual_function[:param_types][index - 1], "param#{index}"))
397
+ end
398
+ end
399
+
400
+ # Ensure the parameter types are correct
401
+ meth.docstring.tags(:param).each_with_index do |tag, index|
402
+ actual_types = [actual_function[:param_types][index]]
403
+ if tag.types != actual_types
404
+ log.warn "The @param tag for '#{tag.name}' for function '#{meth.name}' has a different type definition than the actual function near #{object.file}:#{object.line}. Expected #{actual_types}"
405
+ tag.types = actual_types
406
+ end
407
+ end
408
+ end
246
409
  end