puppet-strings 2.4.0 → 2.8.0

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