apipie-dsl 2.0.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +485 -0
- data/app/controllers/apipie_dsl/apipie_dsls_controller.rb +190 -0
- data/app/helpers/apipie_dsl_helper.rb +110 -0
- data/app/public/apipie_dsl/javascripts/apipie_dsl.js +6 -0
- data/app/public/apipie_dsl/javascripts/bundled/bootstrap-collapse.js +138 -0
- data/app/public/apipie_dsl/javascripts/bundled/bootstrap.js +1726 -0
- data/app/public/apipie_dsl/javascripts/bundled/jquery.js +5 -0
- data/app/public/apipie_dsl/javascripts/bundled/prettify.js +28 -0
- data/app/public/apipie_dsl/stylesheets/application.css +7 -0
- data/app/public/apipie_dsl/stylesheets/bundled/bootstrap-responsive.min.css +12 -0
- data/app/public/apipie_dsl/stylesheets/bundled/bootstrap.min.css +689 -0
- data/app/public/apipie_dsl/stylesheets/bundled/prettify.css +30 -0
- data/app/views/apipie_dsl/apipie_dsls/_index_class_meth.erb +11 -0
- data/app/views/apipie_dsl/apipie_dsls/_index_class_prop.erb +13 -0
- data/app/views/apipie_dsl/apipie_dsls/_languages.erb +6 -0
- data/app/views/apipie_dsl/apipie_dsls/_metadata.erb +1 -0
- data/app/views/apipie_dsl/apipie_dsls/_method.erb +34 -0
- data/app/views/apipie_dsl/apipie_dsls/_method_detail.erb +58 -0
- data/app/views/apipie_dsl/apipie_dsls/_params.html.erb +47 -0
- data/app/views/apipie_dsl/apipie_dsls/_params_plain.html.erb +19 -0
- data/app/views/apipie_dsl/apipie_dsls/_property.erb +24 -0
- data/app/views/apipie_dsl/apipie_dsls/_property_detail.erb +35 -0
- data/app/views/apipie_dsl/apipie_dsls/_raises.html.erb +23 -0
- data/app/views/apipie_dsl/apipie_dsls/_returns.html.erb +53 -0
- data/app/views/apipie_dsl/apipie_dsls/apipie_dsl_404.html.erb +17 -0
- data/app/views/apipie_dsl/apipie_dsls/class.html.erb +75 -0
- data/app/views/apipie_dsl/apipie_dsls/custom_help.html.erb +10 -0
- data/app/views/apipie_dsl/apipie_dsls/getting_started.html.erb +4 -0
- data/app/views/apipie_dsl/apipie_dsls/index.html.erb +72 -0
- data/app/views/apipie_dsl/apipie_dsls/method.html.erb +52 -0
- data/app/views/apipie_dsl/apipie_dsls/plain.html.erb +116 -0
- data/app/views/apipie_dsl/apipie_dsls/static.html.erb +158 -0
- data/app/views/layouts/apipie_dsl/apipie_dsl.html.erb +26 -0
- data/lib/apipie-dsl.rb +3 -0
- data/lib/apipie_dsl.rb +28 -0
- data/lib/apipie_dsl/Rakefile +6 -0
- data/lib/apipie_dsl/apipie_dsl_module.rb +51 -0
- data/lib/apipie_dsl/application.rb +321 -0
- data/lib/apipie_dsl/class_description.rb +107 -0
- data/lib/apipie_dsl/configuration.rb +87 -0
- data/lib/apipie_dsl/dsl.rb +596 -0
- data/lib/apipie_dsl/errors.rb +68 -0
- data/lib/apipie_dsl/exception_description.rb +39 -0
- data/lib/apipie_dsl/markup.rb +41 -0
- data/lib/apipie_dsl/method_description.rb +112 -0
- data/lib/apipie_dsl/parameter_description.rb +152 -0
- data/lib/apipie_dsl/railtie.rb +15 -0
- data/lib/apipie_dsl/return_description.rb +71 -0
- data/lib/apipie_dsl/routing.rb +17 -0
- data/lib/apipie_dsl/see_description.rb +35 -0
- data/lib/apipie_dsl/static_dispatcher.rb +70 -0
- data/lib/apipie_dsl/tag_list_description.rb +11 -0
- data/lib/apipie_dsl/tasks/apipie_dsl/cache.rake +43 -0
- data/lib/apipie_dsl/tasks/apipie_dsl/static.rake +43 -0
- data/lib/apipie_dsl/tasks/apipie_dsl/static_json.rake +29 -0
- data/lib/apipie_dsl/tasks_utils.rb +137 -0
- data/lib/apipie_dsl/utils.rb +83 -0
- data/lib/apipie_dsl/validator.rb +479 -0
- data/lib/apipie_dsl/version.rb +5 -0
- data/lib/generators/apipie_dsl/install/install_generator.rb +21 -0
- data/lib/generators/apipie_dsl/install/templates/initializer.rb.erb +9 -0
- data/lib/generators/apipie_dsl/views_generator.rb +14 -0
- data/test/test_helper.rb +6 -0
- metadata +220 -0
@@ -0,0 +1,479 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ApipieDSL
|
4
|
+
module Validator
|
5
|
+
class Lazy
|
6
|
+
def initialize(param_description, argument, options, block)
|
7
|
+
@param_description = param_description
|
8
|
+
@argument = argument
|
9
|
+
@options = options
|
10
|
+
@block = block
|
11
|
+
end
|
12
|
+
|
13
|
+
def build
|
14
|
+
# TODO support for plain Ruby
|
15
|
+
return unless defined? Rails
|
16
|
+
|
17
|
+
BaseValidator.find(@param_description, @argument.constantize, @options, @block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
# To create a new validator, inherit from ApipieDSL::Validator::BaseValidator
|
21
|
+
# and implement class method 'build' and instance method 'validate'
|
22
|
+
class BaseValidator
|
23
|
+
attr_reader :param_description
|
24
|
+
|
25
|
+
def initialize(param_description)
|
26
|
+
@param_description = param_description
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.build(_param_description, _argument, _options, &_block)
|
30
|
+
raise NotImplementedError
|
31
|
+
end
|
32
|
+
|
33
|
+
def validate(_value)
|
34
|
+
raise NotImplementedError
|
35
|
+
end
|
36
|
+
|
37
|
+
def description
|
38
|
+
raise NotImplementedError
|
39
|
+
end
|
40
|
+
|
41
|
+
def inspected_fields
|
42
|
+
[:param_description]
|
43
|
+
end
|
44
|
+
|
45
|
+
def inspect
|
46
|
+
string = "#<#{self.class.name}:#{object_id} "
|
47
|
+
fields = inspected_fields.map { |field| "#{field}: #{send(field)}" }
|
48
|
+
string << fields.join(', ') << '>'
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.inherited(subclass)
|
52
|
+
@validators ||= []
|
53
|
+
@validators.unshift(subclass)
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.find(param_description, argument, options, block)
|
57
|
+
@validators.each do |type|
|
58
|
+
validator = type.build(param_description, argument, options, block)
|
59
|
+
return validator if validator
|
60
|
+
end
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def valid?(value)
|
65
|
+
return true if validate(value)
|
66
|
+
|
67
|
+
raise ParamInvalid.new(@param_description.name, value, description)
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_s
|
71
|
+
description
|
72
|
+
end
|
73
|
+
|
74
|
+
def docs
|
75
|
+
raise NotImplementedError
|
76
|
+
end
|
77
|
+
|
78
|
+
def expected_type
|
79
|
+
'string'
|
80
|
+
end
|
81
|
+
|
82
|
+
def sub_params
|
83
|
+
nil
|
84
|
+
end
|
85
|
+
|
86
|
+
def merge_with(other_validator)
|
87
|
+
return self if self == other_validator
|
88
|
+
|
89
|
+
raise NotImplementedError, "Don't know how to merge #{inspect} with #{other_validator.inspect}"
|
90
|
+
end
|
91
|
+
|
92
|
+
def ==(other)
|
93
|
+
return false unless self.class == other.class
|
94
|
+
|
95
|
+
param_description == other.param_description
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class TypeValidator < BaseValidator
|
100
|
+
def initialize(param_description, argument)
|
101
|
+
super(param_description)
|
102
|
+
@type = argument
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.build(param_description, argument, _options, block)
|
106
|
+
return unless argument.is_a?(::Class)
|
107
|
+
return if argument == Hash && !block.nil?
|
108
|
+
|
109
|
+
new(param_description, argument)
|
110
|
+
end
|
111
|
+
|
112
|
+
def validate(value)
|
113
|
+
return false if value.nil?
|
114
|
+
|
115
|
+
value.is_a?(@type)
|
116
|
+
end
|
117
|
+
|
118
|
+
def description
|
119
|
+
"Must be a #{@type}"
|
120
|
+
end
|
121
|
+
|
122
|
+
def expected_type
|
123
|
+
if @type.ancestors.include?(Hash)
|
124
|
+
'hash'
|
125
|
+
elsif @type.ancestors.include?(Array)
|
126
|
+
'array'
|
127
|
+
elsif @type.ancestors.include?(Numeric)
|
128
|
+
'numeric'
|
129
|
+
else
|
130
|
+
'string'
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
class RegexpValidator < BaseValidator
|
136
|
+
def initialize(param_description, argument)
|
137
|
+
super(param_description)
|
138
|
+
@regexp = argument
|
139
|
+
end
|
140
|
+
|
141
|
+
def self.build(param_description, argument, _options, _block)
|
142
|
+
new(param_description, argument) if argument.is_a?(Regexp)
|
143
|
+
end
|
144
|
+
|
145
|
+
def validate(value)
|
146
|
+
value =~ @regexp
|
147
|
+
end
|
148
|
+
|
149
|
+
def description
|
150
|
+
"Must match regular expression <code>/#{@regexp.source}/</code>."
|
151
|
+
end
|
152
|
+
|
153
|
+
def expected_type
|
154
|
+
'regexp'
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# Arguments value must be one of given in array
|
159
|
+
class EnumValidator < BaseValidator
|
160
|
+
def initialize(param_description, argument)
|
161
|
+
super(param_description)
|
162
|
+
@array = argument
|
163
|
+
end
|
164
|
+
|
165
|
+
def self.build(param_description, argument, _options, _block)
|
166
|
+
new(param_description, argument) if argument.is_a?(Array)
|
167
|
+
end
|
168
|
+
|
169
|
+
def validate(value)
|
170
|
+
@array.include?(value)
|
171
|
+
end
|
172
|
+
|
173
|
+
def values
|
174
|
+
@array
|
175
|
+
end
|
176
|
+
|
177
|
+
def description
|
178
|
+
string = @array.map { |value| "<code>#{value}</code>" }.join(', ')
|
179
|
+
"Must be one of: #{string}."
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
class ArrayValidator < BaseValidator
|
184
|
+
def initialize(param_description, argument, options = {})
|
185
|
+
super(param_description)
|
186
|
+
@type = argument
|
187
|
+
@items_type = options[:of]
|
188
|
+
@items_enum = options[:in]
|
189
|
+
end
|
190
|
+
|
191
|
+
def self.build(param_description, argument, options, block)
|
192
|
+
return if argument != Array || block.is_a?(Proc)
|
193
|
+
|
194
|
+
new(param_description, argument, options)
|
195
|
+
end
|
196
|
+
|
197
|
+
def validate(values)
|
198
|
+
return false unless process_value(values).respond_to?(:each) &&
|
199
|
+
!process_value(values).is_a?(String)
|
200
|
+
|
201
|
+
process_value(values).all? { |v| validate_item(v) }
|
202
|
+
end
|
203
|
+
|
204
|
+
def process_value(values)
|
205
|
+
values || []
|
206
|
+
end
|
207
|
+
|
208
|
+
def description
|
209
|
+
"Must be an array of #{items_type}"
|
210
|
+
end
|
211
|
+
|
212
|
+
def expected_type
|
213
|
+
'array'
|
214
|
+
end
|
215
|
+
|
216
|
+
private
|
217
|
+
|
218
|
+
def validate_item(value)
|
219
|
+
valid_type?(value) && valid_value?(value)
|
220
|
+
end
|
221
|
+
|
222
|
+
def valid_type?(value)
|
223
|
+
return true unless @items_type
|
224
|
+
|
225
|
+
item_validator = BaseValidator.find(nil, @items_type, nil, nil)
|
226
|
+
|
227
|
+
if item_validator
|
228
|
+
item_validator.valid?(value)
|
229
|
+
else
|
230
|
+
value.is_a?(@items_type)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def items_enum
|
235
|
+
@items_enum = Array(@items_enum.call) if @items_enum.is_a?(Proc)
|
236
|
+
@items_enum
|
237
|
+
end
|
238
|
+
|
239
|
+
def valid_value?(value)
|
240
|
+
if items_enum
|
241
|
+
items_enum.include?(value)
|
242
|
+
else
|
243
|
+
true
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
def items_type
|
248
|
+
return items_enum.inspect if items_enum
|
249
|
+
|
250
|
+
@items_type || 'any type'
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
class ArrayClassValidator < BaseValidator
|
255
|
+
def initialize(param_description, argument)
|
256
|
+
super(param_description)
|
257
|
+
@array = argument
|
258
|
+
end
|
259
|
+
|
260
|
+
def validate(value)
|
261
|
+
@array.include?(value.class)
|
262
|
+
end
|
263
|
+
|
264
|
+
def self.build(param_description, argument, _options, block)
|
265
|
+
return if !argument.is_a?(Array) || argument.first.class != ::Class || block.is_a?(Proc)
|
266
|
+
|
267
|
+
new(param_description, argument)
|
268
|
+
end
|
269
|
+
|
270
|
+
def description
|
271
|
+
"Must be one of: #{@array.join(', ')}."
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
class ProcValidator < BaseValidator
|
276
|
+
def initialize(param_description, argument)
|
277
|
+
super(param_description)
|
278
|
+
@proc = argument
|
279
|
+
end
|
280
|
+
|
281
|
+
def validate(value)
|
282
|
+
# The proc should return true if value is valid
|
283
|
+
# Otherwise it should return a string
|
284
|
+
!(@help = @proc.call(value)).is_a?(String)
|
285
|
+
end
|
286
|
+
|
287
|
+
def self.build(param_description, argument, _options, _block)
|
288
|
+
return if !argument.is_a?(Proc) || argument.arity != 1
|
289
|
+
|
290
|
+
new(param_description, argument)
|
291
|
+
end
|
292
|
+
|
293
|
+
def description
|
294
|
+
@help
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
class HashValidator < BaseValidator
|
299
|
+
include ApipieDSL::Base
|
300
|
+
include ApipieDSL::Parameter
|
301
|
+
include ApipieDSL::Klass
|
302
|
+
|
303
|
+
def initialize(param_description, argument, param_group)
|
304
|
+
super(param_description)
|
305
|
+
@param_group = param_group
|
306
|
+
instance_exec(&argument)
|
307
|
+
prepare_hash_params
|
308
|
+
end
|
309
|
+
|
310
|
+
def self.build(param_description, argument, options, block)
|
311
|
+
return if argument != Hash || !block.is_a?(Proc) || block.arity.positive?
|
312
|
+
|
313
|
+
new(param_description, block, options[:param_group])
|
314
|
+
end
|
315
|
+
|
316
|
+
def sub_params
|
317
|
+
@sub_params ||= dsl_data[:params].map do |args|
|
318
|
+
options = args.find { |arg| arg.is_a?(Hash) }
|
319
|
+
options[:parent] = param_description
|
320
|
+
ApipieDSL::ParameterDescription.from_dsl_data(param_description.method_description, args)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
def validate(value)
|
325
|
+
return false unless value.is_a?(Hash)
|
326
|
+
|
327
|
+
@hash_params&.each do |name, param|
|
328
|
+
if ApipieDSL.configuration.validate_value?
|
329
|
+
param.validate(value[name]) if value.key?(name)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
true
|
333
|
+
end
|
334
|
+
|
335
|
+
def description
|
336
|
+
'Must be a Hash'
|
337
|
+
end
|
338
|
+
|
339
|
+
def expected_type
|
340
|
+
'hash'
|
341
|
+
end
|
342
|
+
|
343
|
+
def default_param_group_scope
|
344
|
+
@param_group && @param_group[:scope]
|
345
|
+
end
|
346
|
+
|
347
|
+
def merge_with(other_validator)
|
348
|
+
if other_validator.is_a?(HashValidator)
|
349
|
+
@sub_params = ApipieDSL::ParameterDescription.unify(sub_params + other_validator.sub_params)
|
350
|
+
prepare_hash_params
|
351
|
+
else
|
352
|
+
super
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
private
|
357
|
+
|
358
|
+
def prepare_hash_params
|
359
|
+
@hash_params = sub_params.each_with_object({}) do |param, hash|
|
360
|
+
hash.update(param.name.to_sym => param)
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
class DecimalValidator < BaseValidator
|
366
|
+
def self.build(param_description, argument, _options, _block)
|
367
|
+
return if argument != :decimal
|
368
|
+
|
369
|
+
new(param_description)
|
370
|
+
end
|
371
|
+
|
372
|
+
def validate(value)
|
373
|
+
value.to_s =~ /\A^[-+]?[0-9]+([,.][0-9]+)?\Z$/
|
374
|
+
end
|
375
|
+
|
376
|
+
def description
|
377
|
+
'Must be a decimal number'
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
class NumberValidator < BaseValidator
|
382
|
+
def self.build(param_description, argument, _options, _block)
|
383
|
+
return if argument != :number
|
384
|
+
|
385
|
+
new(param_description)
|
386
|
+
end
|
387
|
+
|
388
|
+
def validate(value)
|
389
|
+
value.to_s =~ /\A(0|[1-9]\d*)\Z$/
|
390
|
+
end
|
391
|
+
|
392
|
+
def description
|
393
|
+
'Must be a number'
|
394
|
+
end
|
395
|
+
|
396
|
+
def expected_type
|
397
|
+
'numeric'
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
class BooleanValidator < BaseValidator
|
402
|
+
def self.build(param_description, argument, _options, _block)
|
403
|
+
return unless %i[bool boolean].include?(argument)
|
404
|
+
|
405
|
+
new(param_description)
|
406
|
+
end
|
407
|
+
|
408
|
+
def validate(value)
|
409
|
+
%w[true false 1 0].include?(value.to_s)
|
410
|
+
end
|
411
|
+
|
412
|
+
def description
|
413
|
+
string = %w[true false 1 0].map { |value| "<code>#{value}</code>" }.join(', ')
|
414
|
+
"Must be one of: #{string}"
|
415
|
+
end
|
416
|
+
|
417
|
+
def expected_type
|
418
|
+
'boolean'
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
class RestValidator < BaseValidator
|
423
|
+
def self.build(param_description, argument, _options, _block)
|
424
|
+
return unless %i[rest list splat].include?(argument)
|
425
|
+
|
426
|
+
new(param_description)
|
427
|
+
end
|
428
|
+
|
429
|
+
def validate(_value)
|
430
|
+
# In *rest param we don't care about passed values.
|
431
|
+
true
|
432
|
+
end
|
433
|
+
|
434
|
+
def description
|
435
|
+
'Must be a list of values'
|
436
|
+
end
|
437
|
+
|
438
|
+
def expected_type
|
439
|
+
'list'
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
class NestedValidator < BaseValidator
|
444
|
+
def initialize(param_description, argument, param_group)
|
445
|
+
super(param_description)
|
446
|
+
@validator = HashValidator.new(param_description, argument, param_group)
|
447
|
+
@type = argument
|
448
|
+
end
|
449
|
+
|
450
|
+
def self.build(param_description, argument, options, block)
|
451
|
+
return if argument != Array || !block.is_a?(Proc) || block.arity.positive?
|
452
|
+
|
453
|
+
new(param_description, block, options[:param_group])
|
454
|
+
end
|
455
|
+
|
456
|
+
def validate(value)
|
457
|
+
value ||= []
|
458
|
+
return false if value.class != Array
|
459
|
+
|
460
|
+
value.each do |child|
|
461
|
+
return false unless @validator.validate(child)
|
462
|
+
end
|
463
|
+
true
|
464
|
+
end
|
465
|
+
|
466
|
+
def expected_type
|
467
|
+
'array'
|
468
|
+
end
|
469
|
+
|
470
|
+
def description
|
471
|
+
'Must be an Array of nested elements'
|
472
|
+
end
|
473
|
+
|
474
|
+
def sub_params
|
475
|
+
@validator.sub_params
|
476
|
+
end
|
477
|
+
end
|
478
|
+
end
|
479
|
+
end
|