aws-sdk-resources 2.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/aws-sdk-resources.rb +36 -0
- data/lib/aws-sdk-resources/batch.rb +100 -0
- data/lib/aws-sdk-resources/builder.rb +86 -0
- data/lib/aws-sdk-resources/builder_sources.rb +136 -0
- data/lib/aws-sdk-resources/collection.rb +137 -0
- data/lib/aws-sdk-resources/definition.rb +363 -0
- data/lib/aws-sdk-resources/documenter.rb +18 -0
- data/lib/aws-sdk-resources/documenter/base_operation_documenter.rb +269 -0
- data/lib/aws-sdk-resources/documenter/data_operation_documenter.rb +47 -0
- data/lib/aws-sdk-resources/documenter/enumerate_data_operation_documenter.rb +50 -0
- data/lib/aws-sdk-resources/documenter/enumerate_resource_operation_documenter.rb +69 -0
- data/lib/aws-sdk-resources/documenter/operation_documenter.rb +43 -0
- data/lib/aws-sdk-resources/documenter/reference_operation_documenter.rb +102 -0
- data/lib/aws-sdk-resources/documenter/resource_operation_documenter.rb +65 -0
- data/lib/aws-sdk-resources/documenter/waiter_operation_documenter.rb +81 -0
- data/lib/aws-sdk-resources/errors.rb +15 -0
- data/lib/aws-sdk-resources/operation_methods.rb +53 -0
- data/lib/aws-sdk-resources/operations.rb +294 -0
- data/lib/aws-sdk-resources/options.rb +23 -0
- data/lib/aws-sdk-resources/request.rb +39 -0
- data/lib/aws-sdk-resources/request_params.rb +225 -0
- data/lib/aws-sdk-resources/resource.rb +137 -0
- data/lib/aws-sdk-resources/source.rb +39 -0
- data/lib/aws-sdk-resources/validator.rb +152 -0
- data/lib/aws-sdk-resources/validator/context.rb +60 -0
- data/lib/aws-sdk-resources/validator/identifier_validator.rb +107 -0
- data/lib/aws-sdk-resources/validator/operation_validator.rb +352 -0
- data/lib/aws-sdk-resources/validator/rule.rb +45 -0
- data/lib/aws-sdk-resources/validator/shape_validator.rb +47 -0
- metadata +87 -0
@@ -0,0 +1,352 @@
|
|
1
|
+
module Aws
|
2
|
+
module Resources
|
3
|
+
module Validator
|
4
|
+
class OperationValidator
|
5
|
+
|
6
|
+
PARAM_SOURCES = %w(identifier dataMember string integer boolean)
|
7
|
+
|
8
|
+
# @param [Validator::Context] context
|
9
|
+
def initialize(context)
|
10
|
+
@context = context
|
11
|
+
@operation = context.value
|
12
|
+
@definition = context.definition
|
13
|
+
@api = context.api
|
14
|
+
@path = context.path
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [Hash] The service resource definition model.
|
18
|
+
attr_reader :definition
|
19
|
+
|
20
|
+
# @return [Hash] The api model.
|
21
|
+
attr_reader :api
|
22
|
+
|
23
|
+
# @return [String] The JSON path inside the definition document.
|
24
|
+
attr_reader :path
|
25
|
+
|
26
|
+
# @return [Boolean] Returns `true` if the operation defines a request.
|
27
|
+
def request_set?
|
28
|
+
@operation.key?('request')
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [Boolean] Returns `true` if the operation defines how to
|
32
|
+
# build a resource.
|
33
|
+
def resource_set?
|
34
|
+
@operation.key?('resource')
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [Boolean] Returns `true` if the operation defines a path.
|
38
|
+
def path_set?
|
39
|
+
@operation.key?('path')
|
40
|
+
end
|
41
|
+
|
42
|
+
# Performs generic request validation including:
|
43
|
+
# * request is defined
|
44
|
+
# * request operation is defined in the api
|
45
|
+
# * request params resolve as input parameters
|
46
|
+
# * etc.
|
47
|
+
# Yields to the given block if the request is defined and exists in
|
48
|
+
# the api.
|
49
|
+
# @return [Boolean]
|
50
|
+
def validate_request(&block)
|
51
|
+
if
|
52
|
+
validate_request_set &&
|
53
|
+
validate_operation_exists
|
54
|
+
then
|
55
|
+
yield if block_given?
|
56
|
+
validate_params
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# @return [Boolean] Returns `true` if the operation does not define
|
61
|
+
# a request. Only hasSome/hasOne references should not have a
|
62
|
+
# request defined.
|
63
|
+
def validate_request_not_set
|
64
|
+
if @operation['request']
|
65
|
+
error("'#{path}/request' is not valid in this context.")
|
66
|
+
else
|
67
|
+
true
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# @return [Boolean] Returns `true` if the operation does not define
|
72
|
+
# how to build a resource. Only load operations may not define
|
73
|
+
# a resource.
|
74
|
+
def validate_resource_not_set
|
75
|
+
if @operation.key?('resource')
|
76
|
+
error("'#{path}/resource' is not valid in this context.")
|
77
|
+
else
|
78
|
+
true
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Performs basic validation on the resource definition.
|
83
|
+
# @return [Boolean]
|
84
|
+
def validate_resource(&block)
|
85
|
+
if resource_set?
|
86
|
+
validate_resource_exists
|
87
|
+
else
|
88
|
+
error("'#{path}/resource' must be set.")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# @return [Boolean] Returns `true` if the operation defines a path.
|
93
|
+
# Load operations require a path.
|
94
|
+
def validate_path_set
|
95
|
+
if path_set?
|
96
|
+
true
|
97
|
+
else
|
98
|
+
error("'#{path}/path' must be set.")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Validates a path by resolving it against the resource definition
|
103
|
+
# and the API model. You must specify the `:origin` and optionally
|
104
|
+
# the `:target`.
|
105
|
+
#
|
106
|
+
# == Origins
|
107
|
+
#
|
108
|
+
# * :response - Resolves the path starting from the operation output.
|
109
|
+
#
|
110
|
+
# * :request - Resolves the path starting from the operation input.
|
111
|
+
#
|
112
|
+
# * :self - Resolves the path starting from the resource data/shape.
|
113
|
+
#
|
114
|
+
# == Target
|
115
|
+
#
|
116
|
+
# * :resource - Requires the path to resolve to the shape of the operation
|
117
|
+
# resource entry.
|
118
|
+
#
|
119
|
+
# * :self - Requries the path to resolve to the shape of the
|
120
|
+
# current resource.
|
121
|
+
#
|
122
|
+
# @option options [:response, :request, :self] :origin
|
123
|
+
# @option options [:self, :resource] :target
|
124
|
+
def validate_path(options = {})
|
125
|
+
if path_set?
|
126
|
+
validate_path_resolves(options)
|
127
|
+
else
|
128
|
+
true
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# @return [Boolean] Returns `true` if the path resolves to an array.
|
133
|
+
# Has many operations require plural paths and at least one plural
|
134
|
+
# input.
|
135
|
+
def validate_path_is_plural
|
136
|
+
if @operation['path'].match(/\[/)
|
137
|
+
true
|
138
|
+
else
|
139
|
+
error("'#{path}/path' must be plural.")
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# @return [Boolean] Returns `true` if none of the request params
|
144
|
+
# are for the given `source_type`. Load operations may not use
|
145
|
+
# "dataMember" source types.
|
146
|
+
def validate_param_source_type_not_used(source_type)
|
147
|
+
(@operation['request']['params'] || []).each.with_index do |param, n|
|
148
|
+
if param['sourceType'] == source_type
|
149
|
+
error("'#{path}/request/params/#{n}/sourceType' is set to '#{source_type}' which is not valid in this context.")
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
def error(msg)
|
157
|
+
@context.error(msg)
|
158
|
+
end
|
159
|
+
|
160
|
+
# @option options [:response, :request, :self] :origin
|
161
|
+
# @option options [:self, :response] :target
|
162
|
+
def validate_path_resolves(options)
|
163
|
+
if origin_exists?(options)
|
164
|
+
resolve_path(options)
|
165
|
+
else
|
166
|
+
error("'#{path}/path' requires that '#{origin_path(options)}' be set.")
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def validate_resource_exists
|
171
|
+
if definition['resources'].key?(operation_resource_name)
|
172
|
+
true
|
173
|
+
else
|
174
|
+
error("'#{path}/resource/type' references '#{operation_resource_name}' which is not defined.")
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def resolve_path(options)
|
179
|
+
if resolved_shape = resolve(@operation['path'], origin(options))
|
180
|
+
validate_resolved_shape(resolved_shape, options)
|
181
|
+
else
|
182
|
+
error("'#{path}/path' does not resolve.")
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def validate_resolved_shape(resolved_shape, options)
|
187
|
+
if options[:target]
|
188
|
+
validate_target_exists(options) &&
|
189
|
+
validate_resolved_shape_matches(resolved_shape, options)
|
190
|
+
else
|
191
|
+
true
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def validate_target_exists(options)
|
196
|
+
if target(options)
|
197
|
+
true
|
198
|
+
else
|
199
|
+
error("'#{path}/path' requires that '#/resources/#{target_resource_name(options)}/shape' is set.")
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def validate_resolved_shape_matches(resolved_shape, options)
|
204
|
+
if resolved_shape == target(options)
|
205
|
+
true
|
206
|
+
else
|
207
|
+
error("'#{path}/path' did not resolve to a '#{target(options)}' shape.")
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def origin(options)
|
212
|
+
case options[:origin]
|
213
|
+
when :request then api_operation['input']
|
214
|
+
when :response then api_operation['output']
|
215
|
+
when :self then { 'shape' => resource_shape_name }
|
216
|
+
else
|
217
|
+
msg = "expected :origin to be one of :output, :input, or :self"
|
218
|
+
raise ArgumentError, msg
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def target_resource_name(options)
|
223
|
+
case options[:target]
|
224
|
+
when :resource then operation_resource_name
|
225
|
+
when :self then resource_name
|
226
|
+
else
|
227
|
+
raise ArgumentError, "expected `:target` as :resource, or :self"
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def target(options)
|
232
|
+
@definition['resources'][target_resource_name(options)]['shape']
|
233
|
+
end
|
234
|
+
|
235
|
+
def origin_path(options)
|
236
|
+
case options[:origin]
|
237
|
+
when :request then "api#/operations/#{operation_name}/input"
|
238
|
+
when :response then "api#/operations/#{operation_name}/output"
|
239
|
+
when :self then raise 'not tested'; "api#/shapes/#{resource_shape_name}"
|
240
|
+
else raise "unhandled origin #{options[:origin].inspect}"
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def origin_exists?(options)
|
245
|
+
case options[:origin]
|
246
|
+
when :request then !!(api_operation && api_operation['input'])
|
247
|
+
when :response then !!(api_operation && api_operation['output'])
|
248
|
+
when :self then !!api['shapes'][resource_shape_name]
|
249
|
+
else raise "unhandled origin #{options[:origin].inspect}"
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
def operation_resource_name
|
254
|
+
@operation['resource']['type']
|
255
|
+
end
|
256
|
+
|
257
|
+
def resolve(jamespath, from)
|
258
|
+
ref = jamespath.scan(/\w+|\[.*?\]/).inject(from) do |ref, part|
|
259
|
+
shape = api['shapes'][ref['shape']]
|
260
|
+
if part[0] == '['
|
261
|
+
return nil unless shape['type'] == 'list'
|
262
|
+
shape['member']
|
263
|
+
else
|
264
|
+
return nil unless shape['type'] == 'structure'
|
265
|
+
return nil unless shape['members'][part]
|
266
|
+
shape['members'][part]
|
267
|
+
end
|
268
|
+
end
|
269
|
+
ref && ref['shape']
|
270
|
+
end
|
271
|
+
|
272
|
+
def validate_request_set
|
273
|
+
if @operation['request']
|
274
|
+
true
|
275
|
+
else
|
276
|
+
error("'#{path}/request' is required in this context.")
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def validate_params
|
281
|
+
if params = @operation['request']['params']
|
282
|
+
if validate_operation_accepts_input
|
283
|
+
params.each.with_index do |param, index|
|
284
|
+
validate_request_param(param, index)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def validate_operation_accepts_input
|
291
|
+
if api_operation['input']
|
292
|
+
true
|
293
|
+
else
|
294
|
+
error("'#{path}/request/params' is set but '#{operation_name}' does not accept input.")
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
def validate_request_param(param, index)
|
299
|
+
validate_request_param_target_resolves(param, index)
|
300
|
+
validate_static_params_match_coral_type(param, index)
|
301
|
+
end
|
302
|
+
|
303
|
+
def validate_request_param_target_resolves(param, index)
|
304
|
+
if resolve(param['target'], api_operation['input'])
|
305
|
+
true
|
306
|
+
else
|
307
|
+
error("'#{path}/request/params/#{index}/target' has the expression '#{param['target']}' which does not resolve from 'api#/operations/#{operation_name}/input'.")
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def validate_static_params_match_coral_type(param, index)
|
312
|
+
if %w(string integer boolean).include?(param['sourceType'])
|
313
|
+
resolved_shape = resolve(param['target'], api_operation['input'])
|
314
|
+
resolved_type = @api['shapes'][resolved_shape]['type']
|
315
|
+
unless param['sourceType'] == resolved_type
|
316
|
+
error("'#{path}/request/params/#{index}/sourceType' given as '#{param['sourceType']}' but resolves to a '#{resolved_type}' shape.")
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
def validate_operation_exists
|
322
|
+
if api_operation
|
323
|
+
true
|
324
|
+
else
|
325
|
+
error("'#{path}/request/operation' is set but is not defined at 'api#/operations/#{operation_name}'.")
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
def resource_name
|
330
|
+
@context.matches[1]
|
331
|
+
end
|
332
|
+
|
333
|
+
def resource
|
334
|
+
definition['resources'][resource_name]
|
335
|
+
end
|
336
|
+
|
337
|
+
def resource_shape_name
|
338
|
+
resource['shape']
|
339
|
+
end
|
340
|
+
|
341
|
+
def operation_name
|
342
|
+
@operation['request']['operation']
|
343
|
+
end
|
344
|
+
|
345
|
+
def api_operation
|
346
|
+
api['operations'][operation_name]
|
347
|
+
end
|
348
|
+
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Aws
|
2
|
+
module Resources
|
3
|
+
module Validator
|
4
|
+
# @api private
|
5
|
+
class Rule
|
6
|
+
|
7
|
+
# @param [String] pattern
|
8
|
+
def initialize(pattern, &block)
|
9
|
+
@pattern = Regexp.new('^' + pattern + '$')
|
10
|
+
@block = block
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param [String] path
|
14
|
+
# @return [Boolean]
|
15
|
+
def applies?(path)
|
16
|
+
!!path.match(@pattern)
|
17
|
+
end
|
18
|
+
|
19
|
+
# @param [String] path
|
20
|
+
# @return [MatchData]
|
21
|
+
def matches(path)
|
22
|
+
path.match(@pattern)
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param [String] path
|
26
|
+
# @param [Object] value
|
27
|
+
# @param [Hash] definition
|
28
|
+
# @param [Hash] api
|
29
|
+
# @return [Array<String>]
|
30
|
+
def validate(path, value, definition, api)
|
31
|
+
context = Validator::Context.new(
|
32
|
+
path: path,
|
33
|
+
value: value,
|
34
|
+
definition: definition,
|
35
|
+
api: api,
|
36
|
+
matches: matches(path),
|
37
|
+
)
|
38
|
+
@block.call(context)
|
39
|
+
context.errors
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Aws
|
2
|
+
module Resources
|
3
|
+
module Validator
|
4
|
+
class ShapeValidator
|
5
|
+
|
6
|
+
# @param [Validator::Context] context
|
7
|
+
# @param [String] shape_name
|
8
|
+
def initialize(context, shape_name)
|
9
|
+
@context = context
|
10
|
+
@shape_name = shape_name
|
11
|
+
@path = context.path
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :shape_name
|
15
|
+
|
16
|
+
attr_reader :path
|
17
|
+
|
18
|
+
def validate
|
19
|
+
validate_shape_is_defined && validate_shape_is_a_structure
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def validate_shape_is_defined
|
25
|
+
if shape
|
26
|
+
true
|
27
|
+
else
|
28
|
+
@context.error("'#{path}' not found at api#/shapes/#{shape_name}.")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def validate_shape_is_a_structure
|
33
|
+
if shape['type'] == 'structure'
|
34
|
+
true
|
35
|
+
else
|
36
|
+
@context.error("'#{path}' must resolve to a structure shape.")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def shape
|
41
|
+
@context.api['shapes'][shape_name]
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: aws-sdk-resources
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.0.0.pre
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Amazon Web Services
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-09-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: aws-sdk-core
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 2.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 2.0.0
|
27
|
+
description: Provides resource-oriented abstractions for AWS.
|
28
|
+
email:
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- lib/aws-sdk-resources/batch.rb
|
34
|
+
- lib/aws-sdk-resources/builder.rb
|
35
|
+
- lib/aws-sdk-resources/builder_sources.rb
|
36
|
+
- lib/aws-sdk-resources/collection.rb
|
37
|
+
- lib/aws-sdk-resources/definition.rb
|
38
|
+
- lib/aws-sdk-resources/documenter/base_operation_documenter.rb
|
39
|
+
- lib/aws-sdk-resources/documenter/data_operation_documenter.rb
|
40
|
+
- lib/aws-sdk-resources/documenter/enumerate_data_operation_documenter.rb
|
41
|
+
- lib/aws-sdk-resources/documenter/enumerate_resource_operation_documenter.rb
|
42
|
+
- lib/aws-sdk-resources/documenter/operation_documenter.rb
|
43
|
+
- lib/aws-sdk-resources/documenter/reference_operation_documenter.rb
|
44
|
+
- lib/aws-sdk-resources/documenter/resource_operation_documenter.rb
|
45
|
+
- lib/aws-sdk-resources/documenter/waiter_operation_documenter.rb
|
46
|
+
- lib/aws-sdk-resources/documenter.rb
|
47
|
+
- lib/aws-sdk-resources/errors.rb
|
48
|
+
- lib/aws-sdk-resources/operation_methods.rb
|
49
|
+
- lib/aws-sdk-resources/operations.rb
|
50
|
+
- lib/aws-sdk-resources/options.rb
|
51
|
+
- lib/aws-sdk-resources/request.rb
|
52
|
+
- lib/aws-sdk-resources/request_params.rb
|
53
|
+
- lib/aws-sdk-resources/resource.rb
|
54
|
+
- lib/aws-sdk-resources/source.rb
|
55
|
+
- lib/aws-sdk-resources/validator/context.rb
|
56
|
+
- lib/aws-sdk-resources/validator/identifier_validator.rb
|
57
|
+
- lib/aws-sdk-resources/validator/operation_validator.rb
|
58
|
+
- lib/aws-sdk-resources/validator/rule.rb
|
59
|
+
- lib/aws-sdk-resources/validator/shape_validator.rb
|
60
|
+
- lib/aws-sdk-resources/validator.rb
|
61
|
+
- lib/aws-sdk-resources.rb
|
62
|
+
homepage: http://github.com/aws/aws-sdk-core-ruby
|
63
|
+
licenses:
|
64
|
+
- Apache 2.0
|
65
|
+
metadata: {}
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options: []
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - '>'
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: 1.3.1
|
80
|
+
requirements: []
|
81
|
+
rubyforge_project:
|
82
|
+
rubygems_version: 2.1.11
|
83
|
+
signing_key:
|
84
|
+
specification_version: 4
|
85
|
+
summary: AWS SDK for Ruby - Resources
|
86
|
+
test_files: []
|
87
|
+
has_rdoc:
|