puppet-strings 0.4.0 → 0.99.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 (168) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +122 -0
  3. data/COMMITTERS.md +185 -0
  4. data/CONTRIBUTING.md +89 -0
  5. data/Gemfile +38 -0
  6. data/JSON.md +511 -0
  7. data/LICENSE +13 -0
  8. data/README.md +416 -0
  9. data/Rakefile +49 -0
  10. data/lib/puppet-strings.rb +63 -0
  11. data/lib/puppet-strings/json.rb +49 -0
  12. data/lib/puppet-strings/tasks.rb +10 -0
  13. data/lib/puppet-strings/tasks/generate.rb +23 -0
  14. data/lib/puppet-strings/tasks/gh_pages.rb +43 -0
  15. data/lib/puppet-strings/yard.rb +96 -0
  16. data/lib/puppet-strings/yard/code_objects.rb +8 -0
  17. data/lib/puppet-strings/yard/code_objects/base.rb +14 -0
  18. data/lib/puppet-strings/yard/code_objects/class.rb +59 -0
  19. data/lib/puppet-strings/yard/code_objects/defined_type.rb +58 -0
  20. data/lib/puppet-strings/yard/code_objects/function.rb +93 -0
  21. data/lib/puppet-strings/yard/code_objects/group.rb +30 -0
  22. data/lib/puppet-strings/yard/code_objects/provider.rb +93 -0
  23. data/lib/puppet-strings/yard/code_objects/type.rb +146 -0
  24. data/lib/puppet-strings/yard/handlers.rb +16 -0
  25. data/lib/puppet-strings/yard/handlers/puppet/base.rb +44 -0
  26. data/lib/puppet-strings/yard/handlers/puppet/class_handler.rb +23 -0
  27. data/lib/puppet-strings/yard/handlers/puppet/defined_type_handler.rb +23 -0
  28. data/lib/puppet-strings/yard/handlers/puppet/function_handler.rb +42 -0
  29. data/lib/puppet-strings/yard/handlers/ruby/base.rb +38 -0
  30. data/lib/puppet-strings/yard/handlers/ruby/function_handler.rb +357 -0
  31. data/lib/puppet-strings/yard/handlers/ruby/provider_handler.rb +113 -0
  32. data/lib/puppet-strings/yard/handlers/ruby/type_handler.rb +194 -0
  33. data/lib/puppet-strings/yard/parsers.rb +7 -0
  34. data/lib/puppet-strings/yard/parsers/puppet/parser.rb +70 -0
  35. data/lib/puppet-strings/yard/parsers/puppet/statement.rb +146 -0
  36. data/lib/puppet-strings/yard/tags.rb +6 -0
  37. data/lib/puppet-strings/yard/tags/overload_tag.rb +109 -0
  38. data/lib/puppet-strings/yard/tags/parameter_directive.rb +24 -0
  39. data/lib/puppet-strings/yard/tags/property_directive.rb +24 -0
  40. data/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_class.erb +9 -0
  41. data/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_defined_type.erb +9 -0
  42. data/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_function.erb +10 -0
  43. data/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_provider.erb +10 -0
  44. data/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_type.erb +9 -0
  45. data/lib/puppet-strings/yard/templates/default/fulldoc/html/setup.rb +64 -0
  46. data/lib/puppet-strings/yard/templates/default/layout/html/objects.erb +35 -0
  47. data/lib/puppet-strings/yard/templates/default/layout/html/setup.rb +172 -0
  48. data/lib/puppet-strings/yard/templates/default/puppet_class/html/box_info.erb +26 -0
  49. data/lib/puppet-strings/yard/templates/default/puppet_class/html/header.erb +1 -0
  50. data/lib/puppet-strings/yard/templates/default/puppet_class/html/overview.erb +6 -0
  51. data/lib/puppet-strings/yard/templates/default/puppet_class/html/setup.rb +14 -0
  52. data/lib/puppet-strings/yard/templates/default/puppet_class/html/source.erb +12 -0
  53. data/lib/puppet-strings/yard/templates/default/puppet_defined_type/html/box_info.erb +10 -0
  54. data/lib/puppet-strings/yard/templates/default/puppet_defined_type/html/header.erb +1 -0
  55. data/lib/puppet-strings/yard/templates/default/puppet_defined_type/html/overview.erb +6 -0
  56. data/lib/puppet-strings/yard/templates/default/puppet_defined_type/html/setup.rb +5 -0
  57. data/lib/puppet-strings/yard/templates/default/puppet_defined_type/html/source.erb +12 -0
  58. data/lib/puppet-strings/yard/templates/default/puppet_function/html/box_info.erb +14 -0
  59. data/lib/puppet-strings/yard/templates/default/puppet_function/html/header.erb +1 -0
  60. data/lib/puppet-strings/yard/templates/default/puppet_function/html/overview.erb +18 -0
  61. data/lib/puppet-strings/yard/templates/default/puppet_function/html/setup.rb +5 -0
  62. data/lib/puppet-strings/yard/templates/default/puppet_function/html/source.erb +12 -0
  63. data/lib/puppet-strings/yard/templates/default/puppet_provider/html/box_info.erb +14 -0
  64. data/lib/puppet-strings/yard/templates/default/puppet_provider/html/collection.erb +10 -0
  65. data/lib/puppet-strings/yard/templates/default/puppet_provider/html/features.erb +12 -0
  66. data/lib/puppet-strings/yard/templates/default/puppet_provider/html/header.erb +1 -0
  67. data/lib/puppet-strings/yard/templates/default/puppet_provider/html/overview.erb +6 -0
  68. data/lib/puppet-strings/yard/templates/default/puppet_provider/html/setup.rb +29 -0
  69. data/lib/puppet-strings/yard/templates/default/puppet_type/html/box_info.erb +20 -0
  70. data/lib/puppet-strings/yard/templates/default/puppet_type/html/features.erb +13 -0
  71. data/lib/puppet-strings/yard/templates/default/puppet_type/html/header.erb +1 -0
  72. data/lib/puppet-strings/yard/templates/default/puppet_type/html/overview.erb +6 -0
  73. data/lib/puppet-strings/yard/templates/default/puppet_type/html/parameters.erb +35 -0
  74. data/lib/puppet-strings/yard/templates/default/puppet_type/html/setup.rb +32 -0
  75. data/lib/puppet-strings/yard/templates/default/tags/html/puppet_overload.erb +12 -0
  76. data/lib/puppet-strings/yard/templates/default/tags/setup.rb +15 -0
  77. data/lib/puppet/application/strings.rb +1 -0
  78. data/lib/puppet/face/strings.rb +80 -39
  79. data/spec/acceptance/emit_json_options.rb +41 -0
  80. data/spec/acceptance/lib/util.rb +15 -0
  81. data/spec/acceptance/running_strings_generate.rb +54 -0
  82. data/spec/fixtures/acceptance/modules/test/functions/add.pp +9 -0
  83. data/spec/fixtures/acceptance/modules/test/lib/puppet/functions/4x_function.rb +5 -0
  84. data/spec/fixtures/acceptance/modules/test/lib/puppet/parser/functions/function3x.rb +2 -0
  85. data/spec/fixtures/acceptance/modules/test/lib/puppet/provider/server/linux.rb +9 -0
  86. data/spec/fixtures/acceptance/modules/test/lib/puppet/type/database.rb +15 -0
  87. data/spec/fixtures/acceptance/modules/test/manifests/init.pp +27 -0
  88. data/spec/fixtures/acceptance/modules/test/manifests/triple_nested_classes.pp +27 -0
  89. data/spec/fixtures/acceptance/modules/test/metadata.json +6 -0
  90. data/spec/fixtures/unit/json/output.json +348 -0
  91. data/spec/fixtures/unit/json/output_without_puppet_function.json +301 -0
  92. data/spec/spec_helper.rb +21 -0
  93. data/spec/spec_helper_acceptance.rb +27 -0
  94. data/spec/unit/puppet-strings/json_spec.rb +136 -0
  95. data/spec/unit/puppet-strings/yard/handlers/puppet/class_handler_spec.rb +155 -0
  96. data/spec/unit/puppet-strings/yard/handlers/puppet/defined_type_handler_spec.rb +155 -0
  97. data/spec/unit/puppet-strings/yard/handlers/puppet/function_handler_spec.rb +169 -0
  98. data/spec/unit/puppet-strings/yard/handlers/ruby/function_handler_spec.rb +613 -0
  99. data/spec/unit/puppet-strings/yard/handlers/ruby/provider_handler_spec.rb +90 -0
  100. data/spec/unit/puppet-strings/yard/handlers/ruby/type_handler_spec.rb +214 -0
  101. data/spec/unit/puppet-strings/yard/parsers/puppet/parser_spec.rb +171 -0
  102. metadata +115 -92
  103. data/lib/puppet-strings/rake_tasks.rb +0 -18
  104. data/lib/puppet_x/puppetlabs/strings.rb +0 -64
  105. data/lib/puppet_x/puppetlabs/strings/actions.rb +0 -92
  106. data/lib/puppet_x/puppetlabs/strings/pops/yard_statement.rb +0 -79
  107. data/lib/puppet_x/puppetlabs/strings/pops/yard_transformer.rb +0 -47
  108. data/lib/puppet_x/puppetlabs/strings/util.rb +0 -65
  109. data/lib/puppet_x/puppetlabs/strings/yard/code_objects/defined_type_object.rb +0 -33
  110. data/lib/puppet_x/puppetlabs/strings/yard/code_objects/host_class_object.rb +0 -22
  111. data/lib/puppet_x/puppetlabs/strings/yard/code_objects/method_object.rb +0 -62
  112. data/lib/puppet_x/puppetlabs/strings/yard/code_objects/provider_object.rb +0 -24
  113. data/lib/puppet_x/puppetlabs/strings/yard/code_objects/puppet_namespace_object.rb +0 -48
  114. data/lib/puppet_x/puppetlabs/strings/yard/code_objects/type_object.rb +0 -42
  115. data/lib/puppet_x/puppetlabs/strings/yard/core_ext/yard.rb +0 -40
  116. data/lib/puppet_x/puppetlabs/strings/yard/handlers/base.rb +0 -13
  117. data/lib/puppet_x/puppetlabs/strings/yard/handlers/defined_type_handler.rb +0 -31
  118. data/lib/puppet_x/puppetlabs/strings/yard/handlers/heredoc_helper.rb +0 -80
  119. data/lib/puppet_x/puppetlabs/strings/yard/handlers/host_class_handler.rb +0 -42
  120. data/lib/puppet_x/puppetlabs/strings/yard/handlers/provider_handler.rb +0 -95
  121. data/lib/puppet_x/puppetlabs/strings/yard/handlers/puppet_3x_function_handler.rb +0 -54
  122. data/lib/puppet_x/puppetlabs/strings/yard/handlers/puppet_4x_function_handler.rb +0 -234
  123. data/lib/puppet_x/puppetlabs/strings/yard/handlers/type_handler.rb +0 -295
  124. data/lib/puppet_x/puppetlabs/strings/yard/json_registry_store.rb +0 -85
  125. data/lib/puppet_x/puppetlabs/strings/yard/monkey_patches.rb +0 -68
  126. data/lib/puppet_x/puppetlabs/strings/yard/parser.rb +0 -30
  127. data/lib/puppet_x/puppetlabs/strings/yard/tags/directives.rb +0 -9
  128. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/definedtype/html/docstring.erb +0 -34
  129. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/definedtype/html/header.erb +0 -5
  130. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/definedtype/html/parameter_details.erb +0 -6
  131. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/definedtype/html/setup.rb +0 -1
  132. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/definedtype/setup.rb +0 -49
  133. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/full_list_class.erb +0 -2
  134. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/full_list_puppet_manifest.erb +0 -1
  135. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/full_list_puppet_plugin.erb +0 -21
  136. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/full_list_puppet_provider.erb +0 -1
  137. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/full_list_puppet_type.erb +0 -1
  138. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/fulldoc/html/setup.rb +0 -82
  139. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/hostclass/html/box_info.erb +0 -22
  140. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/hostclass/html/setup.rb +0 -1
  141. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/hostclass/html/subclasses.erb +0 -4
  142. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/hostclass/setup.rb +0 -21
  143. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/html_helper.rb +0 -139
  144. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/layout/html/setup.rb +0 -18
  145. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/method_details/html/header.erb +0 -17
  146. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/method_details/setup.rb +0 -21
  147. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/method_details/text/header.erb +0 -2
  148. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/command_details.erb +0 -8
  149. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/confine_details.erb +0 -10
  150. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/default_details.erb +0 -10
  151. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/docstring.erb +0 -34
  152. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/feature_details.erb +0 -10
  153. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/header.erb +0 -5
  154. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/html/setup.rb +0 -1
  155. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/provider/setup.rb +0 -50
  156. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/html/box_info.erb +0 -11
  157. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/html/header.erb +0 -5
  158. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/html/method_details_list.erb +0 -53
  159. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/html/method_summary.erb +0 -20
  160. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/html/setup.rb +0 -1
  161. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/puppetnamespace/setup.rb +0 -91
  162. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/template_helper.rb +0 -192
  163. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/html/docstring.erb +0 -34
  164. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/html/header.erb +0 -5
  165. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/html/parameter_details.erb +0 -12
  166. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/html/provider_details.erb +0 -10
  167. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/html/setup.rb +0 -1
  168. data/lib/puppet_x/puppetlabs/strings/yard/templates/default/type/setup.rb +0 -55
@@ -0,0 +1,357 @@
1
+ require 'puppet-strings/yard/handlers/ruby/base'
2
+ require 'puppet-strings/yard/code_objects'
3
+ require 'puppet/util/docs'
4
+
5
+ # Implements the handler for Puppet functions written in Ruby.
6
+ class PuppetStrings::Yard::Handlers::Ruby::FunctionHandler < PuppetStrings::Yard::Handlers::Ruby::Base
7
+ # Represents the list of Puppet 4.x function API methods to support.
8
+ DISPATCH_METHOD_NAMES = %w(
9
+ param
10
+ required_param
11
+ optional_param
12
+ repeated_param
13
+ optional_repeated_param
14
+ required_repeated_param
15
+ block_param
16
+ required_block_param
17
+ optional_block_param
18
+ ).freeze
19
+
20
+ namespace_only
21
+ handles method_call(:create_function)
22
+ handles method_call(:newfunction)
23
+
24
+ process do
25
+ # Only accept calls to Puppet::Functions (4.x) or Puppet::Parser::Functions (3.x)
26
+ return unless statement.count > 1
27
+ module_name = statement[0].source
28
+ return unless module_name == 'Puppet::Functions' || module_name == 'Puppet::Parser::Functions'
29
+
30
+ # Create and register the function object
31
+ is_3x = module_name == 'Puppet::Parser::Functions'
32
+ object = PuppetStrings::Yard::CodeObjects::Function.new(
33
+ get_name,
34
+ is_3x ? PuppetStrings::Yard::CodeObjects::Function::RUBY_3X : PuppetStrings::Yard::CodeObjects::Function::RUBY_4X
35
+ )
36
+ object.source = statement
37
+ register object
38
+
39
+ # For 3x, parse the doc parameter for the docstring
40
+ # This must be done after the `register` call above because `register` always uses the statement's docstring
41
+ if is_3x
42
+ docstring = get_3x_docstring(object.name)
43
+ register_docstring(object, docstring, nil) if docstring
44
+
45
+ # Default any typeless param tag to 'Any'
46
+ object.tags(:param).each do |tag|
47
+ tag.types = ['Any'] unless tag.types && !tag.types.empty?
48
+ end
49
+
50
+ # Populate the parameters and the return tag
51
+ object.parameters = object.tags(:param).map{ |p| [p.name, nil] }
52
+ add_return_tag(object, statement.file, statement.line)
53
+ else
54
+ # For 4x, auto generate tags based on dispatch docstrings
55
+ add_tags(object)
56
+ end
57
+
58
+ # Mark the function as public if it doesn't already have an api tag
59
+ object.add_tag YARD::Tags::Tag.new(:api, 'public') unless object.has_tag? :api
60
+ end
61
+
62
+ private
63
+ def get_name
64
+ parameters = statement.parameters(false)
65
+ raise YARD::Parser::UndocumentableError, "Expected at least one parameter to Puppet::Functions.create_function at #{statement.file}:#{statement.line}." if parameters.empty?
66
+ name = node_as_string(parameters.first)
67
+ 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
68
+ name
69
+ end
70
+
71
+ def add_tags(object)
72
+ log.warn "Missing documentation for Puppet function '#{object.name}' at #{statement.file}:#{statement.line}." if object.docstring.empty?
73
+ log.warn "The docstring for Puppet 4.x function '#{object.name}' contains @param tags near #{object.file}:#{object.line}: parameter documentation should be made on the dispatch call." unless object.tags(:param).empty?
74
+ log.warn "The docstring for Puppet 4.x function '#{object.name}' contains @return tags near #{object.file}:#{object.line}: return value documentation should be made on the dispatch call." unless object.tags(:return).empty?
75
+ log.warn "The docstring for Puppet 4.x function '#{object.name}' contains @overload tags near #{object.file}:#{object.line}: overload tags are automatically generated from the dispatch calls." unless object.tags(:overload).empty?
76
+
77
+ # Delete any existing param/return/overload tags
78
+ object.docstring.delete_tags(:param)
79
+ object.docstring.delete_tags(:return)
80
+ object.docstring.delete_tags(:overload)
81
+
82
+ block = statement.block
83
+ return unless block && block.count >= 2
84
+
85
+ # Get the unqualified name of the Puppet function
86
+ unqualified_name = object.name.to_s.split('::').last
87
+
88
+ # Walk the block statements looking for dispatch calls and methods with the same name as the Puppet function
89
+ default = nil
90
+ block[1].children.each do |node|
91
+ if node.is_a?(YARD::Parser::Ruby::MethodCallNode)
92
+ add_overload_tag(object, node)
93
+ elsif node.is_a?(YARD::Parser::Ruby::MethodDefinitionNode)
94
+ default = node if node.method_name && node.method_name.source == unqualified_name
95
+ end
96
+ end
97
+
98
+ # Create an overload for the default method if there is one
99
+ overloads = object.tags(:overload)
100
+ if overloads.empty? && default
101
+ add_method_overload(object, default)
102
+ overloads = object.tags(:overload)
103
+ end
104
+
105
+ # If there's only one overload, move the tags to the object itself
106
+ if overloads.count == 1
107
+ overload = overloads.first
108
+ object.parameters = overload.parameters
109
+ object.add_tag(*overload.tags)
110
+ object.docstring.delete_tags(:overload)
111
+ end
112
+ end
113
+
114
+ def add_overload_tag(object, node)
115
+ # Look for a call to a dispatch method with a block
116
+ return unless node.is_a?(YARD::Parser::Ruby::MethodCallNode) &&
117
+ node.method_name &&
118
+ node.method_name.source == 'dispatch' &&
119
+ node.parameters(false).count == 1 &&
120
+ node.block &&
121
+ node.block.count >= 2
122
+
123
+ overload_tag = PuppetStrings::Yard::Tags::OverloadTag.new(object.name, node.docstring || '')
124
+ param_tags = overload_tag.tags(:param)
125
+
126
+ block = nil
127
+ node.block[1].children.each do |child|
128
+ next unless child.is_a?(YARD::Parser::Ruby::MethodCallNode) && child.method_name
129
+
130
+ method_name = child.method_name.source
131
+ next unless DISPATCH_METHOD_NAMES.include?(method_name)
132
+
133
+ # Check for block
134
+ if method_name.include?('block')
135
+ if block
136
+ log.warn "A duplicate block parameter was found for Puppet function '#{object.name}' at #{child.file}:#{child.line}."
137
+ next
138
+ end
139
+
140
+ # Store the block; needs to be appended last
141
+ block = child
142
+ next
143
+ end
144
+
145
+ # Ensure two parameters to parameter definition
146
+ parameters = child.parameters(false)
147
+ unless parameters.count == 2
148
+ log.warn "Expected 2 arguments to '#{method_name}' call at #{child.file}:#{child.line}: parameter information may not be correct."
149
+ next
150
+ end
151
+
152
+ add_param_tag(
153
+ overload_tag,
154
+ param_tags,
155
+ node_as_string(parameters[1]),
156
+ child.file,
157
+ child.line,
158
+ node_as_string(parameters[0]),
159
+ nil, # TODO: determine default from corresponding Ruby method signature?
160
+ method_name.include?('optional'),
161
+ method_name.include?('repeated')
162
+ )
163
+ end
164
+
165
+ # Handle the block parameter after others so it appears last in the list
166
+ if block
167
+ parameters = block.parameters(false)
168
+ if parameters.empty?
169
+ name = 'block'
170
+ type = 'Callable'
171
+ elsif parameters.count == 1
172
+ name = node_as_string(parameters[0])
173
+ type = 'Callable'
174
+ elsif parameters.count == 2
175
+ type = node_as_string(parameters[0])
176
+ name = node_as_string(parameters[1])
177
+ else
178
+ log.warn "Unexpected number of arguments to block definition at #{block.file}:#{block.line}."
179
+ end
180
+
181
+ if name && type
182
+ add_param_tag(
183
+ overload_tag,
184
+ param_tags,
185
+ name,
186
+ block.file,
187
+ block.line,
188
+ type,
189
+ nil, # TODO: determine default from corresponding Ruby method signature?
190
+ block.method_name.source.include?('optional'),
191
+ false, # Not repeated
192
+ true # Is block
193
+ )
194
+ end
195
+ end
196
+
197
+ # Add a return tag if missing
198
+ add_return_tag(overload_tag, node.file, node.line)
199
+
200
+ # Validate that tags have parameters
201
+ validate_overload(overload_tag, node.file, node.line)
202
+
203
+ object.add_tag overload_tag
204
+ end
205
+
206
+ def add_method_overload(object, node)
207
+ overload_tag = PuppetStrings::Yard::Tags::OverloadTag.new(object.name, node.docstring || '')
208
+ param_tags = overload_tag.tags(:param)
209
+
210
+ parameters = node.parameters
211
+
212
+ # Populate the required parameters
213
+ params = parameters.unnamed_required_params
214
+ if params
215
+ params.each do |parameter|
216
+ add_param_tag(
217
+ overload_tag,
218
+ param_tags,
219
+ parameter.source,
220
+ parameter.file,
221
+ parameter.line
222
+ )
223
+ end
224
+ end
225
+
226
+ # Populate the optional parameters
227
+ params = parameters.unnamed_optional_params
228
+ if params
229
+ params.each do |parameter|
230
+ add_param_tag(
231
+ overload_tag,
232
+ param_tags,
233
+ parameter[0].source,
234
+ parameter.file,
235
+ parameter.line,
236
+ nil,
237
+ parameter[1].source,
238
+ true
239
+ )
240
+ end
241
+ end
242
+
243
+ # Populate the splat parameter
244
+ param = parameters.splat_param
245
+ if param
246
+ add_param_tag(
247
+ overload_tag,
248
+ param_tags,
249
+ param.source,
250
+ param.file,
251
+ param.line,
252
+ nil,
253
+ nil,
254
+ false,
255
+ true
256
+ )
257
+ end
258
+
259
+ # Populate the block parameter
260
+ param = parameters.block_param
261
+ if param
262
+ add_param_tag(
263
+ overload_tag,
264
+ param_tags,
265
+ param.source,
266
+ param.file,
267
+ param.line,
268
+ nil,
269
+ nil,
270
+ false,
271
+ false,
272
+ true
273
+ )
274
+ end
275
+
276
+ # Add a return tag if missing
277
+ add_return_tag(overload_tag, node.file, node.line)
278
+
279
+ # Validate that tags have parameters
280
+ validate_overload(overload_tag, node.file, node.line)
281
+
282
+ object.add_tag overload_tag
283
+ end
284
+
285
+ def add_param_tag(object, tags, name, file, line, type = nil, default = nil, optional = false, repeated = false, block = false)
286
+ tag = tags.find { |tag| tag.name == name } if tags
287
+ log.warn "Missing @param tag for parameter '#{name}' near #{file}:#{line}." unless tag || object.docstring.all.empty?
288
+ log.warn "The @param tag for parameter '#{name}' should not contain a type specification near #{file}:#{line}: ignoring in favor of dispatch type information." if type && tag && tag.types && !tag.types.empty?
289
+
290
+ if repeated
291
+ name = '*' + name
292
+ elsif block
293
+ name = '&' + name
294
+ end
295
+
296
+ unless type
297
+ type = tag && tag.types ? tag.type : 'Any'
298
+ end
299
+ type = optional ? "Optional[#{type}]" : type
300
+
301
+ object.parameters << [name, to_puppet_literal(default)]
302
+
303
+ if tag
304
+ tag.name = name
305
+ tag.types = [type]
306
+ else
307
+ object.add_tag YARD::Tags::Tag.new(:param, '', type, name)
308
+ end
309
+ end
310
+
311
+ def add_return_tag(object, file, line)
312
+ tag = object.tag(:return)
313
+ if tag
314
+ tag.types = ['Any'] unless tag.types
315
+ return
316
+ end
317
+ log.warn "Missing @return tag near #{file}:#{line}."
318
+ object.add_tag YARD::Tags::Tag.new(:return, '', 'Any')
319
+ end
320
+
321
+ def validate_overload(overload, file, line)
322
+ # Validate that tags have matching parameters
323
+ overload.tags(:param).each do |tag|
324
+ next if overload.parameters.find { |p| tag.name == p[0] }
325
+ log.warn "The @param tag for parameter '#{tag.name}' has no matching parameter at #{file}:#{line}."
326
+ end
327
+ end
328
+
329
+ def get_3x_docstring(name)
330
+ parameters = statement.parameters(false)
331
+ if parameters.count >= 2
332
+ parameters[1].each do |kvp|
333
+ next unless kvp.count == 2
334
+ next unless node_as_string(kvp[0]) == 'doc'
335
+ docstring = node_as_string(kvp[1])
336
+
337
+ log.error "Failed to parse docstring for 3.x Puppet function '#{name}' near #{statement.file}:#{statement.line}." and return nil unless docstring
338
+ return Puppet::Util::Docs.scrub(docstring)
339
+ end
340
+ end
341
+
342
+ # Log a warning for missing docstring
343
+ log.warn "Missing documentation for Puppet function '#{name}' at #{statement.file}:#{statement.line}."
344
+ nil
345
+ end
346
+
347
+ def to_puppet_literal(literal)
348
+ case literal
349
+ when 'nil'
350
+ 'undef'
351
+ when ':default'
352
+ 'default'
353
+ else
354
+ literal
355
+ end
356
+ end
357
+ end
@@ -0,0 +1,113 @@
1
+ require 'puppet-strings/yard/handlers/ruby/base'
2
+ require 'puppet-strings/yard/code_objects'
3
+ require 'puppet/util/docs'
4
+
5
+ # Implements the handler for Puppet providers written in Ruby.
6
+ class PuppetStrings::Yard::Handlers::Ruby::ProviderHandler < PuppetStrings::Yard::Handlers::Ruby::Base
7
+ namespace_only
8
+ handles method_call(:provide)
9
+
10
+ process do
11
+ return unless statement.count >= 2
12
+
13
+ # Check that provide is being called on Puppet::Type.type(<name>)
14
+ type_call = statement[0]
15
+ return unless type_call.is_a?(YARD::Parser::Ruby::MethodCallNode) && type_call.count >= 3
16
+ return unless type_call[0].source == 'Puppet::Type'
17
+ return unless type_call[2].source == 'type'
18
+
19
+ # Extract the type name
20
+ type_call_parameters = type_call.parameters(false)
21
+ return unless type_call_parameters.count >= 1
22
+ type_name = node_as_string(type_call_parameters.first)
23
+ raise YARD::Parser::UndocumentableError, "Could not determine the resource type name for the provider defined at #{statement.file}:#{statement.line}." unless type_name
24
+
25
+ # Register the object
26
+ object = PuppetStrings::Yard::CodeObjects::Provider.new(type_name, get_name)
27
+ register object
28
+
29
+ # Extract the docstring
30
+ register_provider_docstring object
31
+
32
+ # Populate the provider data
33
+ populate_provider_data object
34
+
35
+ # Mark the provider as public if it doesn't already have an api tag
36
+ object.add_tag YARD::Tags::Tag.new(:api, 'public') unless object.has_tag? :api
37
+ end
38
+
39
+ private
40
+ def get_name
41
+ parameters = statement.parameters(false)
42
+ raise YARD::Parser::UndocumentableError, "Expected at least one parameter to 'provide' at #{statement.file}:#{statement.line}." if parameters.empty?
43
+ name = node_as_string(parameters.first)
44
+ 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
45
+ name
46
+ end
47
+
48
+ def register_provider_docstring(object)
49
+ # Walk the tree searching for assignments or calls to desc/doc=
50
+ statement.traverse do |child|
51
+ if child.type == :assign
52
+ ivar = child.jump(:ivar)
53
+ next unless ivar != child && ivar.source == '@doc'
54
+ docstring = node_as_string(child[1])
55
+ log.error "Failed to parse docstring for Puppet provider '#{object.name}' (resource type '#{object.type_name}') near #{child.file}:#{child.line}." and return nil unless docstring
56
+ register_docstring(object, Puppet::Util::Docs.scrub(docstring), nil)
57
+ return nil
58
+ elsif child.is_a?(YARD::Parser::Ruby::MethodCallNode)
59
+ # Look for a call to a dispatch method with a block
60
+ next unless
61
+ child.method_name &&
62
+ (child.method_name.source == 'desc' || child.method_name.source == 'doc=') &&
63
+ child.parameters(false).count == 1
64
+
65
+ docstring = node_as_string(child.parameters[0])
66
+ log.error "Failed to parse docstring for Puppet provider '#{object.name}' (resource type '#{object.type_name}') near #{child.file}:#{child.line}." and return nil unless docstring
67
+ register_docstring(object, Puppet::Util::Docs.scrub(docstring), nil)
68
+ return nil
69
+ end
70
+ end
71
+ log.warn "Missing a description for Puppet provider '#{object.name}' (resource type '#{object.type_name}') at #{statement.file}:#{statement.line}."
72
+ end
73
+
74
+ def populate_provider_data(object)
75
+ # Traverse the block looking for confines/defaults/commands
76
+ block = statement.block
77
+ return unless block && block.count >= 2
78
+ block[1].children.each do |node|
79
+ next unless node.is_a?(YARD::Parser::Ruby::MethodCallNode) && node.method_name
80
+
81
+ method_name = node.method_name.source
82
+ parameters = node.parameters(false)
83
+
84
+ if method_name == 'confine'
85
+ # Add a confine to the object
86
+ next unless parameters.count >= 1
87
+ parameters[0].each do |kvp|
88
+ next unless kvp.count == 2
89
+ object.add_confine(node_as_string(kvp[0]) || kvp[0].source, node_as_string(kvp[1]) || kvp[1].source)
90
+ end
91
+ elsif method_name == 'has_feature' || method_name == 'has_features'
92
+ # Add the features to the object
93
+ parameters.each do |parameter|
94
+ object.add_feature(node_as_string(parameter) || parameter.source)
95
+ end
96
+ elsif method_name == 'defaultfor'
97
+ # Add a default to the object
98
+ next unless parameters.count >= 1
99
+ parameters[0].each do |kvp|
100
+ next unless kvp.count == 2
101
+ object.add_default(node_as_string(kvp[0]) || kvp[0].source, node_as_string(kvp[1]) || kvp[1].source)
102
+ end
103
+ elsif method_name == 'commands'
104
+ # Add the commands to the object
105
+ next unless parameters.count >= 1
106
+ parameters[0].each do |kvp|
107
+ next unless kvp.count == 2
108
+ object.add_command(node_as_string(kvp[0]) || kvp[0].source, node_as_string(kvp[1]) || kvp[1].source)
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end