puppet-strings 2.1.0 → 2.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +78 -4
- data/CONTRIBUTING.md +32 -2
- data/README.md +81 -17
- data/lib/puppet-strings.rb +13 -4
- data/lib/puppet-strings/describe.rb +68 -0
- data/lib/puppet-strings/json.rb +2 -38
- data/lib/puppet-strings/markdown.rb +3 -1
- data/lib/puppet-strings/markdown/base.rb +37 -16
- data/lib/puppet-strings/markdown/data_type.rb +34 -0
- data/lib/puppet-strings/markdown/data_types.rb +41 -0
- data/lib/puppet-strings/markdown/function.rb +2 -2
- data/lib/puppet-strings/markdown/resource_type.rb +19 -2
- data/lib/puppet-strings/markdown/table_of_contents.rb +1 -0
- data/lib/puppet-strings/markdown/templates/classes_and_defines.erb +12 -4
- data/lib/puppet-strings/markdown/templates/data_type.erb +93 -0
- data/lib/puppet-strings/markdown/templates/data_type_function.erb +67 -0
- data/lib/puppet-strings/markdown/templates/function.erb +36 -1
- data/lib/puppet-strings/markdown/templates/puppet_task.erb +1 -1
- data/lib/puppet-strings/markdown/templates/resource_type.erb +32 -12
- data/lib/puppet-strings/markdown/templates/table_of_contents.erb +6 -6
- data/lib/puppet-strings/tasks/generate.rb +10 -3
- data/lib/puppet-strings/version.rb +1 -1
- data/lib/puppet-strings/yard.rb +16 -0
- data/lib/puppet-strings/yard/code_objects.rb +2 -0
- data/lib/puppet-strings/yard/code_objects/class.rb +2 -2
- data/lib/puppet-strings/yard/code_objects/data_type.rb +100 -0
- data/lib/puppet-strings/yard/code_objects/data_type_alias.rb +58 -0
- data/lib/puppet-strings/yard/code_objects/defined_type.rb +2 -2
- data/lib/puppet-strings/yard/code_objects/function.rb +4 -4
- data/lib/puppet-strings/yard/code_objects/plan.rb +2 -2
- data/lib/puppet-strings/yard/code_objects/provider.rb +1 -1
- data/lib/puppet-strings/yard/code_objects/task.rb +1 -1
- data/lib/puppet-strings/yard/code_objects/type.rb +48 -6
- data/lib/puppet-strings/yard/handlers.rb +3 -0
- data/lib/puppet-strings/yard/handlers/puppet/data_type_alias_handler.rb +24 -0
- data/lib/puppet-strings/yard/handlers/ruby/base.rb +12 -1
- data/lib/puppet-strings/yard/handlers/ruby/data_type_handler.rb +393 -0
- data/lib/puppet-strings/yard/handlers/ruby/function_handler.rb +2 -12
- data/lib/puppet-strings/yard/handlers/ruby/provider_handler.rb +1 -9
- data/lib/puppet-strings/yard/handlers/ruby/rsapi_handler.rb +3 -3
- data/lib/puppet-strings/yard/handlers/ruby/type_base.rb +135 -0
- data/lib/puppet-strings/yard/handlers/ruby/type_extras_handler.rb +56 -0
- data/lib/puppet-strings/yard/handlers/ruby/type_handler.rb +9 -115
- data/lib/puppet-strings/yard/parsers/json/parser.rb +4 -2
- data/lib/puppet-strings/yard/parsers/puppet/parser.rb +14 -7
- data/lib/puppet-strings/yard/parsers/puppet/statement.rb +25 -0
- data/lib/puppet-strings/yard/tags.rb +2 -0
- data/lib/puppet-strings/yard/tags/enum_tag.rb +12 -0
- data/lib/puppet-strings/yard/tags/factory.rb +16 -0
- data/lib/puppet-strings/yard/tags/overload_tag.rb +2 -2
- data/lib/puppet-strings/yard/templates/default/fulldoc/html/full_list_puppet_data_type.erb +10 -0
- data/lib/puppet-strings/yard/templates/default/fulldoc/html/setup.rb +9 -0
- data/lib/puppet-strings/yard/templates/default/layout/html/objects.erb +2 -0
- data/lib/puppet-strings/yard/templates/default/layout/html/setup.rb +18 -1
- data/lib/puppet-strings/yard/templates/default/puppet_data_type/html/box_info.erb +10 -0
- data/lib/puppet-strings/yard/templates/default/puppet_data_type/html/header.erb +1 -0
- data/lib/puppet-strings/yard/templates/default/puppet_data_type/html/method_details_list.erb +6 -0
- data/lib/puppet-strings/yard/templates/default/puppet_data_type/html/note.erb +6 -0
- data/lib/puppet-strings/yard/templates/default/puppet_data_type/html/overview.erb +6 -0
- data/lib/puppet-strings/yard/templates/default/puppet_data_type/html/setup.rb +13 -0
- data/lib/puppet-strings/yard/templates/default/puppet_data_type/html/source.erb +12 -0
- data/lib/puppet-strings/yard/templates/default/puppet_data_type/html/summary.erb +4 -0
- data/lib/puppet-strings/yard/templates/default/puppet_data_type/html/todo.erb +6 -0
- data/lib/puppet-strings/yard/templates/default/puppet_data_type_alias/html/alias_of.erb +10 -0
- data/lib/puppet-strings/yard/templates/default/puppet_data_type_alias/html/box_info.erb +10 -0
- data/lib/puppet-strings/yard/templates/default/puppet_data_type_alias/html/header.erb +1 -0
- data/lib/puppet-strings/yard/templates/default/puppet_data_type_alias/html/note.erb +6 -0
- data/lib/puppet-strings/yard/templates/default/puppet_data_type_alias/html/overview.erb +6 -0
- data/lib/puppet-strings/yard/templates/default/puppet_data_type_alias/html/setup.rb +17 -0
- data/lib/puppet-strings/yard/templates/default/puppet_data_type_alias/html/source.erb +12 -0
- data/lib/puppet-strings/yard/templates/default/puppet_data_type_alias/html/summary.erb +4 -0
- data/lib/puppet-strings/yard/templates/default/puppet_data_type_alias/html/todo.erb +6 -0
- data/lib/puppet-strings/yard/templates/default/puppet_type/html/setup.rb +3 -1
- data/lib/puppet-strings/yard/templates/default/tags/html/enum.erb +17 -0
- data/lib/puppet-strings/yard/templates/default/tags/setup.rb +7 -0
- data/lib/puppet-strings/yard/util.rb +48 -0
- data/lib/puppet/face/strings.rb +68 -3
- metadata +36 -45
- data/Gemfile +0 -37
- data/HISTORY.md +0 -218
- data/JSON.md +0 -802
- data/Rakefile +0 -93
- data/codecov.yml +0 -3
- data/misc/ANNOUNCEMENT_TEMPLATE.md +0 -40
- data/spec/acceptance/emit_json_options.rb +0 -71
- data/spec/acceptance/generate_markdown_spec.rb +0 -49
- data/spec/acceptance/lib/util.rb +0 -163
- data/spec/acceptance/running_strings_generate.rb +0 -54
- data/spec/fixtures/acceptance/modules/test/functions/add.pp +0 -9
- data/spec/fixtures/acceptance/modules/test/lib/puppet/functions/4x_function.rb +0 -5
- data/spec/fixtures/acceptance/modules/test/lib/puppet/parser/functions/function3x.rb +0 -2
- data/spec/fixtures/acceptance/modules/test/lib/puppet/provider/server/linux.rb +0 -9
- data/spec/fixtures/acceptance/modules/test/lib/puppet/type/database.rb +0 -15
- data/spec/fixtures/acceptance/modules/test/manifests/init.pp +0 -27
- data/spec/fixtures/acceptance/modules/test/manifests/triple_nested_classes.pp +0 -27
- data/spec/fixtures/acceptance/modules/test/metadata.json +0 -6
- data/spec/fixtures/unit/json/output.json +0 -660
- data/spec/fixtures/unit/json/output_with_plan.json +0 -697
- data/spec/fixtures/unit/json/output_without_puppet_function.json +0 -480
- data/spec/fixtures/unit/markdown/output.md +0 -444
- data/spec/fixtures/unit/markdown/output_with_plan.md +0 -478
- data/spec/spec_helper.rb +0 -45
- data/spec/spec_helper_acceptance.rb +0 -28
- data/spec/unit/puppet-strings/json_spec.rb +0 -229
- data/spec/unit/puppet-strings/markdown/base_spec.rb +0 -146
- data/spec/unit/puppet-strings/markdown_spec.rb +0 -283
- data/spec/unit/puppet-strings/yard/code_objects/task_spec.rb +0 -92
- data/spec/unit/puppet-strings/yard/handlers/json/task_handler_spec.rb +0 -124
- data/spec/unit/puppet-strings/yard/handlers/puppet/class_handler_spec.rb +0 -217
- data/spec/unit/puppet-strings/yard/handlers/puppet/defined_type_handler_spec.rb +0 -231
- data/spec/unit/puppet-strings/yard/handlers/puppet/function_handler_spec.rb +0 -315
- data/spec/unit/puppet-strings/yard/handlers/ruby/function_handler_spec.rb +0 -729
- data/spec/unit/puppet-strings/yard/handlers/ruby/provider_handler_spec.rb +0 -139
- data/spec/unit/puppet-strings/yard/handlers/ruby/rsapi_handler_spec.rb +0 -214
- data/spec/unit/puppet-strings/yard/handlers/ruby/type_handler_spec.rb +0 -269
- data/spec/unit/puppet-strings/yard/parsers/json/parser_spec.rb +0 -70
- data/spec/unit/puppet-strings/yard/parsers/json/task_statement_spec.rb +0 -56
- data/spec/unit/puppet-strings/yard/parsers/puppet/parser_spec.rb +0 -209
- data/spec/unit/puppet-strings/yard/util_spec.rb +0 -48
|
@@ -49,8 +49,8 @@ class PuppetStrings::Yard::CodeObjects::DefinedType < PuppetStrings::Yard::CodeO
|
|
|
49
49
|
hash[:name] = name
|
|
50
50
|
hash[:file] = file
|
|
51
51
|
hash[:line] = line
|
|
52
|
-
hash[:docstring] = PuppetStrings::
|
|
53
|
-
defaults = Hash[*parameters.
|
|
52
|
+
hash[:docstring] = PuppetStrings::Yard::Util.docstring_to_hash(docstring)
|
|
53
|
+
defaults = Hash[*parameters.reject{ |p| p[1].nil? }.flatten]
|
|
54
54
|
hash[:defaults] = defaults unless defaults.empty?
|
|
55
55
|
hash[:source] = source unless source && source.empty?
|
|
56
56
|
hash
|
|
@@ -88,14 +88,14 @@ class PuppetStrings::Yard::CodeObjects::Function < PuppetStrings::Yard::CodeObje
|
|
|
88
88
|
if self.has_tag? :overload
|
|
89
89
|
# loop over overloads and append onto the signatures array
|
|
90
90
|
self.tags(:overload).each do |o|
|
|
91
|
-
hash[:signatures] << { :signature => o.signature, :docstring => PuppetStrings::
|
|
91
|
+
hash[:signatures] << { :signature => o.signature, :docstring => PuppetStrings::Yard::Util.docstring_to_hash(o.docstring, %i[param option enum return example]) }
|
|
92
92
|
end
|
|
93
93
|
else
|
|
94
|
-
hash[:signatures] << { :signature => self.signature, :docstring => PuppetStrings::
|
|
94
|
+
hash[:signatures] << { :signature => self.signature, :docstring => PuppetStrings::Yard::Util.docstring_to_hash(docstring, %i[param option enum return example]) }
|
|
95
95
|
end
|
|
96
96
|
|
|
97
|
-
hash[:docstring] = PuppetStrings::
|
|
98
|
-
defaults = Hash[*parameters.
|
|
97
|
+
hash[:docstring] = PuppetStrings::Yard::Util.docstring_to_hash(docstring)
|
|
98
|
+
defaults = Hash[*parameters.reject{ |p| p[1].nil? }.flatten]
|
|
99
99
|
hash[:defaults] = defaults unless defaults.empty?
|
|
100
100
|
hash[:source] = source unless source && source.empty?
|
|
101
101
|
hash
|
|
@@ -47,8 +47,8 @@ class PuppetStrings::Yard::CodeObjects::Plan < PuppetStrings::Yard::CodeObjects:
|
|
|
47
47
|
hash[:name] = name
|
|
48
48
|
hash[:file] = file
|
|
49
49
|
hash[:line] = line
|
|
50
|
-
hash[:docstring] = PuppetStrings::
|
|
51
|
-
defaults = Hash[*parameters.
|
|
50
|
+
hash[:docstring] = PuppetStrings::Yard::Util.docstring_to_hash(docstring)
|
|
51
|
+
defaults = Hash[*parameters.reject{ |p| p[1].nil? }.flatten]
|
|
52
52
|
hash[:defaults] = defaults unless defaults.empty?
|
|
53
53
|
hash[:source] = source unless source && source.empty?
|
|
54
54
|
hash
|
|
@@ -82,7 +82,7 @@ class PuppetStrings::Yard::CodeObjects::Provider < PuppetStrings::Yard::CodeObje
|
|
|
82
82
|
hash[:type_name] = type_name
|
|
83
83
|
hash[:file] = file
|
|
84
84
|
hash[:line] = line
|
|
85
|
-
hash[:docstring] = PuppetStrings::
|
|
85
|
+
hash[:docstring] = PuppetStrings::Yard::Util.docstring_to_hash(docstring)
|
|
86
86
|
hash[:confines] = confines if confines && !confines.empty?
|
|
87
87
|
hash[:features] = features if features && !features.empty?
|
|
88
88
|
hash[:defaults] = defaults if defaults && !defaults.empty?
|
|
@@ -43,7 +43,7 @@ class PuppetStrings::Yard::CodeObjects::Task < PuppetStrings::Yard::CodeObjects:
|
|
|
43
43
|
|
|
44
44
|
def parameters
|
|
45
45
|
parameters = []
|
|
46
|
-
statement.
|
|
46
|
+
statement.parameters.each do |name,props|
|
|
47
47
|
parameters.push({ name: name.to_s,
|
|
48
48
|
tag_name: 'param',
|
|
49
49
|
text: props['description'] || "",
|
|
@@ -22,7 +22,7 @@ class PuppetStrings::Yard::CodeObjects::Type < PuppetStrings::Yard::CodeObjects:
|
|
|
22
22
|
# Represents a resource type parameter.
|
|
23
23
|
class Parameter
|
|
24
24
|
attr_reader :name, :values, :aliases
|
|
25
|
-
attr_accessor :docstring, :isnamevar, :default, :data_type
|
|
25
|
+
attr_accessor :docstring, :isnamevar, :default, :data_type, :required_features
|
|
26
26
|
|
|
27
27
|
# Initializes a resource type parameter or property.
|
|
28
28
|
# @param [String] name The name of the parameter or property.
|
|
@@ -63,6 +63,7 @@ class PuppetStrings::Yard::CodeObjects::Type < PuppetStrings::Yard::CodeObjects:
|
|
|
63
63
|
hash[:data_type] = data_type unless data_type.empty?
|
|
64
64
|
hash[:aliases] = aliases unless aliases.empty?
|
|
65
65
|
hash[:isnamevar] = true if isnamevar
|
|
66
|
+
hash[:required_features] = required_features if required_features
|
|
66
67
|
hash[:default] = default if default
|
|
67
68
|
hash
|
|
68
69
|
end
|
|
@@ -72,6 +73,9 @@ class PuppetStrings::Yard::CodeObjects::Type < PuppetStrings::Yard::CodeObjects:
|
|
|
72
73
|
class Property < Parameter
|
|
73
74
|
end
|
|
74
75
|
|
|
76
|
+
class Check < Parameter
|
|
77
|
+
end
|
|
78
|
+
|
|
75
79
|
# Represents a resource type feature.
|
|
76
80
|
class Feature
|
|
77
81
|
attr_reader :name, :docstring
|
|
@@ -94,7 +98,7 @@ class PuppetStrings::Yard::CodeObjects::Type < PuppetStrings::Yard::CodeObjects:
|
|
|
94
98
|
end
|
|
95
99
|
end
|
|
96
100
|
|
|
97
|
-
attr_reader :properties, :
|
|
101
|
+
attr_reader :properties, :features, :checks
|
|
98
102
|
|
|
99
103
|
# Initializes a new resource type.
|
|
100
104
|
# @param [String] name The resource type name.
|
|
@@ -133,17 +137,55 @@ class PuppetStrings::Yard::CodeObjects::Type < PuppetStrings::Yard::CodeObjects:
|
|
|
133
137
|
@features << feature
|
|
134
138
|
end
|
|
135
139
|
|
|
140
|
+
# Adds a check to the resource type.
|
|
141
|
+
# @param [PuppetStrings::Yard::CodeObjects::Type::Check] check The check to add.
|
|
142
|
+
# @return [void]
|
|
143
|
+
def add_check(check)
|
|
144
|
+
@checks ||= []
|
|
145
|
+
@checks << check
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def parameters
|
|
149
|
+
# just return params if there are no providers
|
|
150
|
+
return @parameters if providers.empty?
|
|
151
|
+
|
|
152
|
+
# return existing params if we have already added provider
|
|
153
|
+
return @parameters if @parameters.any? { |p| p.name == 'provider' }
|
|
154
|
+
|
|
155
|
+
provider_param = Parameter.new(
|
|
156
|
+
'provider',
|
|
157
|
+
"The specific backend to use for this `#{self.name.to_s}` resource. You will seldom need " + \
|
|
158
|
+
"to specify this --- Puppet will usually discover the appropriate provider for your platform."
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
@parameters << provider_param
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Not sure if this is where this belongs or if providers should only be resolved at
|
|
165
|
+
# render-time. For now, this should re-resolve on every call.
|
|
166
|
+
# may be able to memoize this
|
|
167
|
+
def providers
|
|
168
|
+
providers = YARD::Registry.all("puppet_providers_#{name}".intern)
|
|
169
|
+
return providers if providers.empty?
|
|
170
|
+
providers.first.children
|
|
171
|
+
end
|
|
172
|
+
|
|
136
173
|
# Converts the code object to a hash representation.
|
|
137
174
|
# @return [Hash] Returns a hash representation of the code object.
|
|
138
175
|
def to_hash
|
|
139
176
|
hash = {}
|
|
177
|
+
|
|
140
178
|
hash[:name] = name
|
|
141
179
|
hash[:file] = file
|
|
142
180
|
hash[:line] = line
|
|
143
|
-
|
|
144
|
-
hash[:
|
|
145
|
-
hash[:
|
|
146
|
-
hash[:
|
|
181
|
+
|
|
182
|
+
hash[:docstring] = PuppetStrings::Yard::Util.docstring_to_hash(docstring)
|
|
183
|
+
hash[:properties] = properties.sort_by { |p| p.name }.map(&:to_hash) if properties && !properties.empty?
|
|
184
|
+
hash[:parameters] = parameters.sort_by { |p| p.name }.map(&:to_hash) if parameters && !parameters.empty?
|
|
185
|
+
hash[:checks] = checks.sort_by { |c| c.name }.map(&:to_hash) if checks && !checks.empty?
|
|
186
|
+
hash[:features] = features.sort_by { |f| f.name }.map(&:to_hash) if features && !features.empty?
|
|
187
|
+
hash[:providers] = providers.sort_by { |p| p.name }.map(&:to_hash) if providers && !providers.empty?
|
|
188
|
+
|
|
147
189
|
hash
|
|
148
190
|
end
|
|
149
191
|
end
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
module PuppetStrings::Yard::Handlers
|
|
3
3
|
# The module for custom Ruby YARD handlers.
|
|
4
4
|
module Ruby
|
|
5
|
+
require 'puppet-strings/yard/handlers/ruby/data_type_handler'
|
|
5
6
|
require 'puppet-strings/yard/handlers/ruby/type_handler'
|
|
7
|
+
require 'puppet-strings/yard/handlers/ruby/type_extras_handler'
|
|
6
8
|
require 'puppet-strings/yard/handlers/ruby/rsapi_handler'
|
|
7
9
|
require 'puppet-strings/yard/handlers/ruby/provider_handler'
|
|
8
10
|
require 'puppet-strings/yard/handlers/ruby/function_handler'
|
|
@@ -16,6 +18,7 @@ module PuppetStrings::Yard::Handlers
|
|
|
16
18
|
# The module for custom Puppet YARD handlers.
|
|
17
19
|
module Puppet
|
|
18
20
|
require 'puppet-strings/yard/handlers/puppet/class_handler'
|
|
21
|
+
require 'puppet-strings/yard/handlers/puppet/data_type_alias_handler'
|
|
19
22
|
require 'puppet-strings/yard/handlers/puppet/defined_type_handler'
|
|
20
23
|
require 'puppet-strings/yard/handlers/puppet/function_handler'
|
|
21
24
|
require 'puppet-strings/yard/handlers/puppet/plan_handler'
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'puppet-strings/yard/handlers/helpers'
|
|
2
|
+
require 'puppet-strings/yard/handlers/puppet/base'
|
|
3
|
+
require 'puppet-strings/yard/parsers'
|
|
4
|
+
require 'puppet-strings/yard/code_objects'
|
|
5
|
+
|
|
6
|
+
# Implements the handler for Puppet Data Type Alias.
|
|
7
|
+
class PuppetStrings::Yard::Handlers::Puppet::DataTypeAliasHandler < PuppetStrings::Yard::Handlers::Puppet::Base
|
|
8
|
+
handles PuppetStrings::Yard::Parsers::Puppet::DataTypeAliasStatement
|
|
9
|
+
|
|
10
|
+
process do
|
|
11
|
+
# Register the object
|
|
12
|
+
object = PuppetStrings::Yard::CodeObjects::DataTypeAlias.new(statement)
|
|
13
|
+
register object
|
|
14
|
+
|
|
15
|
+
# Log a warning if missing documentation
|
|
16
|
+
log.warn "Missing documentation for Puppet type alias '#{object.name}' at #{statement.file}:#{statement.line}." if object.docstring.empty? && object.tags.empty?
|
|
17
|
+
|
|
18
|
+
# Mark the class as public if it doesn't already have an api tag
|
|
19
|
+
object.add_tag YARD::Tags::Tag.new(:api, 'public') unless object.has_tag? :api
|
|
20
|
+
|
|
21
|
+
# Warn if a summary longer than 140 characters was provided
|
|
22
|
+
PuppetStrings::Yard::Handlers::Helpers.validate_summary_tag(object) if object.has_tag? :summary
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -18,7 +18,8 @@ class PuppetStrings::Yard::Handlers::Ruby::Base < YARD::Handlers::Ruby::Base
|
|
|
18
18
|
when :label
|
|
19
19
|
node.source[0..-2]
|
|
20
20
|
when :dyna_symbol
|
|
21
|
-
node.
|
|
21
|
+
content = node.jump(:tstring_content)
|
|
22
|
+
content.nil? ? node.source : content.source
|
|
22
23
|
when :string_literal
|
|
23
24
|
content = node.jump(:tstring_content)
|
|
24
25
|
return content.source if content != node
|
|
@@ -33,6 +34,16 @@ class PuppetStrings::Yard::Handlers::Ruby::Base < YARD::Handlers::Ruby::Base
|
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
source
|
|
37
|
+
when :regexp_literal
|
|
38
|
+
node.source
|
|
36
39
|
end
|
|
37
40
|
end
|
|
41
|
+
|
|
42
|
+
def get_name(statementobject, statementtype)
|
|
43
|
+
parameters = statementobject.parameters(false)
|
|
44
|
+
raise YARD::Parser::UndocumentableError, "Expected at least one parameter to #{statementtype} at #{statementobject.file}:#{statementobject.line}." if parameters.empty?
|
|
45
|
+
name = node_as_string(parameters.first)
|
|
46
|
+
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
|
|
47
|
+
name
|
|
48
|
+
end
|
|
38
49
|
end
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
require 'puppet-strings/yard/handlers/helpers'
|
|
2
|
+
require 'puppet-strings/yard/handlers/ruby/base'
|
|
3
|
+
require 'puppet-strings/yard/code_objects'
|
|
4
|
+
require 'puppet-strings/yard/util'
|
|
5
|
+
|
|
6
|
+
# Implements the handler for Puppet Data Types written in Ruby.
|
|
7
|
+
class PuppetStrings::Yard::Handlers::Ruby::DataTypeHandler < PuppetStrings::Yard::Handlers::Ruby::Base
|
|
8
|
+
namespace_only
|
|
9
|
+
handles method_call(:create_type)
|
|
10
|
+
|
|
11
|
+
process do
|
|
12
|
+
return unless statement.count > 1
|
|
13
|
+
ruby_module_name = statement[0].source
|
|
14
|
+
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
|
+
# Extract the interface definition
|
|
17
|
+
type_interface = extract_data_type_interface
|
|
18
|
+
actual_params = extract_params(type_interface)
|
|
19
|
+
actual_funcs = extract_functions(object, type_interface)
|
|
20
|
+
|
|
21
|
+
# Mark the data type as public if it doesn't already have an api tag
|
|
22
|
+
object.add_tag YARD::Tags::Tag.new(:api, 'public') unless object.has_tag? :api
|
|
23
|
+
|
|
24
|
+
validate_param_tags!(object, actual_params)
|
|
25
|
+
validate_methods!(object, actual_funcs)
|
|
26
|
+
|
|
27
|
+
# Set the default values for all parameters
|
|
28
|
+
actual_params.each { |name, data| object.set_parameter_default(name, data[:default]) }
|
|
29
|
+
|
|
30
|
+
# Default any typeless param tag to 'Any'
|
|
31
|
+
object.tags(:param).each do |tag|
|
|
32
|
+
tag.types = ['Any'] unless tag.types && !tag.types.empty?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Warn if a summary longer than 140 characters was provided
|
|
36
|
+
PuppetStrings::Yard::Handlers::Helpers.validate_summary_tag(object) if object.has_tag? :summary
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def get_datatype_yard_object(name)
|
|
42
|
+
# Have to guess the path - if we create the object to get the true path from the code,
|
|
43
|
+
# it also shows up in the .at call - self registering?
|
|
44
|
+
guess_path = "puppet_data_types::#{name}"
|
|
45
|
+
object = YARD::Registry.at(guess_path)
|
|
46
|
+
|
|
47
|
+
return object unless object.nil?
|
|
48
|
+
|
|
49
|
+
# Didn't find, create instead
|
|
50
|
+
object = PuppetStrings::Yard::CodeObjects::DataType.new(name)
|
|
51
|
+
register object
|
|
52
|
+
object
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @return [Hash{Object => Object}] The Puppet DataType interface definition as a ruby Hash
|
|
56
|
+
def extract_data_type_interface
|
|
57
|
+
block = statement.block
|
|
58
|
+
return {} unless block
|
|
59
|
+
|
|
60
|
+
# Declare the parsed interface outside of the closure
|
|
61
|
+
parsed_interface = nil
|
|
62
|
+
|
|
63
|
+
# Recursively traverse the block looking for the first valid 'interface' call
|
|
64
|
+
interface_node = find_ruby_ast_node(block, true) do |node|
|
|
65
|
+
next false unless node.is_a?(YARD::Parser::Ruby::MethodCallNode) &&
|
|
66
|
+
node.method_name &&
|
|
67
|
+
node.method_name.source == 'interface'
|
|
68
|
+
parameters = node.parameters(false)
|
|
69
|
+
next false unless parameters.count >= 1
|
|
70
|
+
interface_string = node_as_string(parameters[0])
|
|
71
|
+
next false unless interface_string
|
|
72
|
+
|
|
73
|
+
begin
|
|
74
|
+
# Ref - https://github.com/puppetlabs/puppet/blob/ba4d1a1aba0095d3c70b98fea5c67434a4876a61/lib/puppet/datatypes.rb#L159
|
|
75
|
+
parsed_interface = Puppet::Pops::Parser::EvaluatingParser.new.parse_string("{ #{interface_string} }").body
|
|
76
|
+
rescue Puppet::Error => e
|
|
77
|
+
log.warn "Invalid datatype definition at #{statement.file}:#{statement.line}: #{e.basic_message}"
|
|
78
|
+
next false
|
|
79
|
+
end
|
|
80
|
+
!parsed_interface.nil?
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Now that we parsed the Puppet code (as a string) into a LiteralHash PCore type (Puppet AST),
|
|
84
|
+
# We need to convert the LiteralHash into a conventional ruby hash of strings. The
|
|
85
|
+
# LazyLiteralEvaluator does this by traversing the AST tree can converting objects to strings
|
|
86
|
+
# where possible and ignoring object types which cannot (thus the 'Lazy' name)
|
|
87
|
+
literal_eval = LazyLiteralEvaluator.new
|
|
88
|
+
literal_eval.literal(parsed_interface)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Find the first Ruby AST node within an AST Tree, optionally recursively. Returns nil of none could be found
|
|
92
|
+
#
|
|
93
|
+
# @param [YARD::Parser::Ruby::AstNode] ast_node The root AST node object to inspect
|
|
94
|
+
# @param [Boolean] recurse Whether to search the tree recursively. Defaults to false
|
|
95
|
+
# @yieldparam [YARD::Parser::Ruby::AstNode] ast_node The AST Node that should be checked
|
|
96
|
+
# @yieldreturn [Boolean] Whether the node was what was searched for
|
|
97
|
+
# @return [YARD::Parser::Ruby::AstNode, nil]
|
|
98
|
+
def find_ruby_ast_node(ast_node, recurse = false, &block)
|
|
99
|
+
raise ArgumentError, 'find_ruby_ast_node requires a block' unless block_given?
|
|
100
|
+
is_found = yield ast_node
|
|
101
|
+
return ast_node if is_found
|
|
102
|
+
if ast_node.respond_to?(:children)
|
|
103
|
+
ast_node.children.each do |child_node|
|
|
104
|
+
child_found = find_ruby_ast_node(child_node, recurse, &block)
|
|
105
|
+
return child_found unless child_found.nil?
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
nil
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Lazily evaluates a Pops object, ignoring any objects that cannot
|
|
112
|
+
# be converted to a literal value. Based on the Puppet Literal Evaluator
|
|
113
|
+
# Ref - https://github.com/puppetlabs/puppet/blob/ba4d1a1aba0095d3c70b98fea5c67434a4876a61/lib/puppet/pops/evaluator/literal_evaluator.rb
|
|
114
|
+
#
|
|
115
|
+
# Literal values for:
|
|
116
|
+
# String (not containing interpolation)
|
|
117
|
+
# Numbers
|
|
118
|
+
# Booleans
|
|
119
|
+
# Undef (produces nil)
|
|
120
|
+
# Array
|
|
121
|
+
# Hash
|
|
122
|
+
# QualifiedName
|
|
123
|
+
# Default (produced :default)
|
|
124
|
+
# Regular Expression (produces ruby regular expression)
|
|
125
|
+
# QualifiedReference e.g. File, FooBar
|
|
126
|
+
# AccessExpression
|
|
127
|
+
#
|
|
128
|
+
# Anything else is ignored
|
|
129
|
+
class LazyLiteralEvaluator
|
|
130
|
+
def initialize
|
|
131
|
+
@literal_visitor ||= ::Puppet::Pops::Visitor.new(self, "literal", 0, 0)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def literal(ast)
|
|
135
|
+
@literal_visitor.visit_this_0(self, ast)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# ----- The following methods are different/additions from the original Literal_evaluator
|
|
139
|
+
def literal_Object(o) # rubocop:disable Naming/UncommunicativeMethodParamName
|
|
140
|
+
# Ignore any other object types
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def literal_AccessExpression(o) # rubocop:disable Naming/UncommunicativeMethodParamName
|
|
144
|
+
# Extract the raw text of the Access Expression
|
|
145
|
+
::Puppet::Pops::Adapters::SourcePosAdapter.adapt(o).extract_text
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def literal_QualifiedReference(o) # rubocop:disable Naming/UncommunicativeMethodParamName
|
|
149
|
+
# Extract the raw text of the Qualified Reference
|
|
150
|
+
::Puppet::Pops::Adapters::SourcePosAdapter.adapt(o).extract_text
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# ----- The following methods are the same as the original Literal_evaluator
|
|
154
|
+
def literal_Factory(o) # rubocop:disable Naming/UncommunicativeMethodParamName
|
|
155
|
+
literal(o.model)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def literal_Program(o) # rubocop:disable Naming/UncommunicativeMethodParamName
|
|
159
|
+
literal(o.body)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def literal_LiteralString(o) # rubocop:disable Naming/UncommunicativeMethodParamName
|
|
163
|
+
o.value
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def literal_QualifiedName(o) # rubocop:disable Naming/UncommunicativeMethodParamName
|
|
167
|
+
o.value
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def literal_LiteralNumber(o) # rubocop:disable Naming/UncommunicativeMethodParamName
|
|
171
|
+
o.value
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def literal_UnaryMinusExpression(o) # rubocop:disable Naming/UncommunicativeMethodParamName
|
|
175
|
+
-1 * literal(o.expr)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def literal_LiteralBoolean(o) # rubocop:disable Naming/UncommunicativeMethodParamName
|
|
179
|
+
o.value
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def literal_LiteralUndef(o) # rubocop:disable Naming/UncommunicativeMethodParamName
|
|
183
|
+
nil
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def literal_LiteralDefault(o) # rubocop:disable Naming/UncommunicativeMethodParamName
|
|
187
|
+
:default
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def literal_LiteralRegularExpression(o) # rubocop:disable Naming/UncommunicativeMethodParamName
|
|
191
|
+
o.value
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def literal_ConcatenatedString(o) # rubocop:disable Naming/UncommunicativeMethodParamName
|
|
195
|
+
# use double quoted string value if there is no interpolation
|
|
196
|
+
throw :not_literal unless o.segments.size == 1 && o.segments[0].is_a?(Model::LiteralString)
|
|
197
|
+
o.segments[0].value
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def literal_LiteralList(o) # rubocop:disable Naming/UncommunicativeMethodParamName
|
|
201
|
+
o.values.map { |v| literal(v) }
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def literal_LiteralHash(o) # rubocop:disable Naming/UncommunicativeMethodParamName
|
|
205
|
+
o.entries.reduce({}) do |result, entry|
|
|
206
|
+
result[literal(entry.key)] = literal(entry.value)
|
|
207
|
+
result
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Extracts the datatype attributes from a Puppet Data Type interface hash.
|
|
213
|
+
# Returns a Hash with a :types key (Array of data types for the parameter) and :default key (The default value of the parameter)
|
|
214
|
+
# @return Hash[Symbol => Hash] The Datatype Attributes as a hash
|
|
215
|
+
def extract_params(hash)
|
|
216
|
+
params_hash = {}
|
|
217
|
+
# Exit early if there are no entries in the hash
|
|
218
|
+
return params_hash if hash.nil? || hash['attributes'].nil? || hash['attributes'].empty?
|
|
219
|
+
|
|
220
|
+
hash['attributes'].each do |key, value|
|
|
221
|
+
data_type = nil
|
|
222
|
+
default = nil
|
|
223
|
+
if value.is_a?(String)
|
|
224
|
+
data_type = value
|
|
225
|
+
elsif value.is_a?(Hash)
|
|
226
|
+
data_type = value['type'] unless value['type'].nil?
|
|
227
|
+
default = value['value'] unless value['value'].nil?
|
|
228
|
+
end
|
|
229
|
+
data_type = [data_type] unless data_type.nil? || data_type.is_a?(Array)
|
|
230
|
+
params_hash[key] = { :types => data_type, :default => default }
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
params_hash
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Extracts the datatype functions from a Puppet Data Type interface hash.
|
|
237
|
+
# Returns a Hash with a :param_types key (Array of types for each parameter) and :return_type key (The return type of the function)
|
|
238
|
+
# @return Hash[Symbol => Hash] The Datatype Attributes as a hash
|
|
239
|
+
def extract_functions(object, hash)
|
|
240
|
+
funcs_hash = {}
|
|
241
|
+
# Exit early if there are no entries in the hash
|
|
242
|
+
return funcs_hash if hash.nil? || hash['functions'].nil? || hash['functions'].empty?
|
|
243
|
+
|
|
244
|
+
hash['functions'].each do |key, func_type|
|
|
245
|
+
func_hash = { :param_types => [], :return_type => nil }
|
|
246
|
+
begin
|
|
247
|
+
callable_type = Puppet::Pops::Types::TypeParser.singleton.parse(func_type)
|
|
248
|
+
if callable_type.is_a?(Puppet::Pops::Types::PCallableType)
|
|
249
|
+
func_hash[:param_types] = callable_type.param_types.map { |pt| pt.to_s }
|
|
250
|
+
func_hash[:return_type] = callable_type.return_type.to_s
|
|
251
|
+
else
|
|
252
|
+
log.warn "The function definition for '#{key}' near #{object.file}:#{object.line} is not a Callable type"
|
|
253
|
+
end
|
|
254
|
+
rescue Puppet::ParseError => e
|
|
255
|
+
log.warn "Unable to parse the function definition for '#{key}' near #{object.file}:#{object.line}. #{e}"
|
|
256
|
+
end
|
|
257
|
+
funcs_hash[key] = func_hash
|
|
258
|
+
end
|
|
259
|
+
funcs_hash
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# Validates and automatically fixes yard @param tags for the data type
|
|
263
|
+
def validate_param_tags!(object, actual_params_hash)
|
|
264
|
+
actual_param_names = actual_params_hash.keys
|
|
265
|
+
tagged_param_names = object.tags(:param).map(&:name)
|
|
266
|
+
# Log any errors
|
|
267
|
+
# Find attributes which are not documented
|
|
268
|
+
(actual_param_names - tagged_param_names).each do |item|
|
|
269
|
+
log.warn "Missing @param tag for attribute '#{item}' near #{object.file}:#{object.line}."
|
|
270
|
+
end
|
|
271
|
+
# Find param tags with no matching attribute
|
|
272
|
+
(tagged_param_names - actual_param_names).each do |item|
|
|
273
|
+
log.warn "The @param tag for '#{item}' has no matching attribute near #{object.file}:#{object.line}."
|
|
274
|
+
end
|
|
275
|
+
# Find param tags with a type that is different from the actual definition
|
|
276
|
+
object.tags(:param).reject { |tag| tag.types.nil? }.each do |tag|
|
|
277
|
+
next if actual_params_hash[tag.name].nil?
|
|
278
|
+
actual_data_type = actual_params_hash[tag.name][:types]
|
|
279
|
+
next if actual_data_type.nil?
|
|
280
|
+
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
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# Automatically fix missing @param tags
|
|
284
|
+
(actual_param_names - tagged_param_names).each do |name|
|
|
285
|
+
object.add_parameter(name, actual_params_hash[name][:types], actual_params_hash[name][:default])
|
|
286
|
+
end
|
|
287
|
+
# Remove extra param tags
|
|
288
|
+
object.docstring.delete_tag_if { |item| item.tag_name == 'param' && !actual_param_names.include?(item.name) }
|
|
289
|
+
|
|
290
|
+
# Set the type in the param tag
|
|
291
|
+
object.tags(:param).each do |tag|
|
|
292
|
+
next if actual_params_hash[tag.name].nil?
|
|
293
|
+
tag.types = actual_params_hash[tag.name][:types]
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# Validates and automatically fixes yard @method! tags for the data type
|
|
298
|
+
def validate_methods!(object, actual_functions_hash)
|
|
299
|
+
actual_func_names = actual_functions_hash.keys
|
|
300
|
+
tagged_func_names = object.meths.map { |meth| meth.name.to_s }
|
|
301
|
+
|
|
302
|
+
# Log any errors
|
|
303
|
+
# Find functions which are not documented
|
|
304
|
+
(actual_func_names - tagged_func_names).each do |item|
|
|
305
|
+
log.warn "Missing @!method tag for function '#{item}' near #{object.file}:#{object.line}."
|
|
306
|
+
end
|
|
307
|
+
# Find functions which are not defined
|
|
308
|
+
(tagged_func_names - actual_func_names).each do |item|
|
|
309
|
+
log.warn "The @!method tag for '#{item}' has no matching function definition near #{object.file}:#{object.line}."
|
|
310
|
+
end
|
|
311
|
+
# Functions with the wrong return type
|
|
312
|
+
object.meths.each do |meth|
|
|
313
|
+
next unless actual_func_names.include?(meth.name.to_s)
|
|
314
|
+
return_tag = meth.docstring.tag(:return)
|
|
315
|
+
next if return_tag.nil?
|
|
316
|
+
actual_return_types = [actual_functions_hash[meth.name.to_s][:return_type]]
|
|
317
|
+
next if return_tag.types == actual_return_types
|
|
318
|
+
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}"
|
|
319
|
+
return_tag.types = actual_return_types
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
# Automatically fix missing methods
|
|
323
|
+
(actual_func_names - tagged_func_names).each do |name|
|
|
324
|
+
object.add_function(name, actual_functions_hash[name][:return_type], actual_functions_hash[name][:param_types])
|
|
325
|
+
end
|
|
326
|
+
# Remove extra methods. Can't use `meths` as that's a derived property
|
|
327
|
+
object.children.reject! { |child| child.is_a?(YARD::CodeObjects::MethodObject) && !actual_func_names.include?(child.name.to_s) }
|
|
328
|
+
|
|
329
|
+
# Add the return type for the methods if missing
|
|
330
|
+
object.meths.each do |meth|
|
|
331
|
+
next unless meth.docstring.tag(:return).nil?
|
|
332
|
+
meth.docstring.add_tag(YARD::Tags::Tag.new(:return, '', actual_functions_hash[meth.name.to_s][:return_type]))
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# Sync the method properties and add the return type for the methods if missing
|
|
336
|
+
object.meths.each do |meth|
|
|
337
|
+
validate_function_method!(object, meth, actual_functions_hash[meth.name.to_s])
|
|
338
|
+
next unless meth.docstring.tag(:return).nil?
|
|
339
|
+
meth.docstring.add_tag(YARD::Tags::Tag.new(:return, '', actual_functions_hash[meth.name.to_s][:return_type]))
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
# The default meth.signature assumes ruby invocation (e.g. def meth(...)) but this doesn't make sense for a
|
|
343
|
+
# Puppet Data Type function invocation. So instead we derive a signature from the method definition.
|
|
344
|
+
object.meths.each do |meth|
|
|
345
|
+
params = ''
|
|
346
|
+
unless meth.docstring.tags(:param).empty?
|
|
347
|
+
params += '(' + meth.docstring.tags(:param).map { |t| t.name }.join(', ') + ')'
|
|
348
|
+
end
|
|
349
|
+
meth.signature = "#{object.name}.#{meth.name}" + params
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
nil
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
# Validates and automatically fixes a single yard @method!
|
|
356
|
+
# Used by the validate_methods! method.
|
|
357
|
+
def validate_function_method!(object, meth, actual_function)
|
|
358
|
+
# Remove extra params
|
|
359
|
+
if meth.docstring.tags(:param).count > actual_function[:param_types].count
|
|
360
|
+
index = 0
|
|
361
|
+
meth.docstring.delete_tag_if do |tag|
|
|
362
|
+
if tag.tag_name == 'param'
|
|
363
|
+
index += 1
|
|
364
|
+
if index > actual_function[:param_types].count
|
|
365
|
+
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"
|
|
366
|
+
true
|
|
367
|
+
else
|
|
368
|
+
false
|
|
369
|
+
end
|
|
370
|
+
else
|
|
371
|
+
false
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
# Add missing params
|
|
377
|
+
if meth.docstring.tags(:param).count < actual_function[:param_types].count
|
|
378
|
+
start = meth.docstring.tags(:param).count + 1
|
|
379
|
+
(start..actual_function[:param_types].count).each do |index| # Using 1-based index here instead of usual zero
|
|
380
|
+
meth.add_tag(YARD::Tags::Tag.new(:param, '', actual_function[:param_types][index - 1], "param#{index}"))
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
# Ensure the parameter types are correct
|
|
385
|
+
meth.docstring.tags(:param).each_with_index do |tag, index|
|
|
386
|
+
actual_types = [actual_function[:param_types][index]]
|
|
387
|
+
if tag.types != actual_types
|
|
388
|
+
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}"
|
|
389
|
+
tag.types = actual_types
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
end
|