puppet-strings 0.4.0 → 0.99.0

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