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.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +485 -0
  4. data/app/controllers/apipie_dsl/apipie_dsls_controller.rb +190 -0
  5. data/app/helpers/apipie_dsl_helper.rb +110 -0
  6. data/app/public/apipie_dsl/javascripts/apipie_dsl.js +6 -0
  7. data/app/public/apipie_dsl/javascripts/bundled/bootstrap-collapse.js +138 -0
  8. data/app/public/apipie_dsl/javascripts/bundled/bootstrap.js +1726 -0
  9. data/app/public/apipie_dsl/javascripts/bundled/jquery.js +5 -0
  10. data/app/public/apipie_dsl/javascripts/bundled/prettify.js +28 -0
  11. data/app/public/apipie_dsl/stylesheets/application.css +7 -0
  12. data/app/public/apipie_dsl/stylesheets/bundled/bootstrap-responsive.min.css +12 -0
  13. data/app/public/apipie_dsl/stylesheets/bundled/bootstrap.min.css +689 -0
  14. data/app/public/apipie_dsl/stylesheets/bundled/prettify.css +30 -0
  15. data/app/views/apipie_dsl/apipie_dsls/_index_class_meth.erb +11 -0
  16. data/app/views/apipie_dsl/apipie_dsls/_index_class_prop.erb +13 -0
  17. data/app/views/apipie_dsl/apipie_dsls/_languages.erb +6 -0
  18. data/app/views/apipie_dsl/apipie_dsls/_metadata.erb +1 -0
  19. data/app/views/apipie_dsl/apipie_dsls/_method.erb +34 -0
  20. data/app/views/apipie_dsl/apipie_dsls/_method_detail.erb +58 -0
  21. data/app/views/apipie_dsl/apipie_dsls/_params.html.erb +47 -0
  22. data/app/views/apipie_dsl/apipie_dsls/_params_plain.html.erb +19 -0
  23. data/app/views/apipie_dsl/apipie_dsls/_property.erb +24 -0
  24. data/app/views/apipie_dsl/apipie_dsls/_property_detail.erb +35 -0
  25. data/app/views/apipie_dsl/apipie_dsls/_raises.html.erb +23 -0
  26. data/app/views/apipie_dsl/apipie_dsls/_returns.html.erb +53 -0
  27. data/app/views/apipie_dsl/apipie_dsls/apipie_dsl_404.html.erb +17 -0
  28. data/app/views/apipie_dsl/apipie_dsls/class.html.erb +75 -0
  29. data/app/views/apipie_dsl/apipie_dsls/custom_help.html.erb +10 -0
  30. data/app/views/apipie_dsl/apipie_dsls/getting_started.html.erb +4 -0
  31. data/app/views/apipie_dsl/apipie_dsls/index.html.erb +72 -0
  32. data/app/views/apipie_dsl/apipie_dsls/method.html.erb +52 -0
  33. data/app/views/apipie_dsl/apipie_dsls/plain.html.erb +116 -0
  34. data/app/views/apipie_dsl/apipie_dsls/static.html.erb +158 -0
  35. data/app/views/layouts/apipie_dsl/apipie_dsl.html.erb +26 -0
  36. data/lib/apipie-dsl.rb +3 -0
  37. data/lib/apipie_dsl.rb +28 -0
  38. data/lib/apipie_dsl/Rakefile +6 -0
  39. data/lib/apipie_dsl/apipie_dsl_module.rb +51 -0
  40. data/lib/apipie_dsl/application.rb +321 -0
  41. data/lib/apipie_dsl/class_description.rb +107 -0
  42. data/lib/apipie_dsl/configuration.rb +87 -0
  43. data/lib/apipie_dsl/dsl.rb +596 -0
  44. data/lib/apipie_dsl/errors.rb +68 -0
  45. data/lib/apipie_dsl/exception_description.rb +39 -0
  46. data/lib/apipie_dsl/markup.rb +41 -0
  47. data/lib/apipie_dsl/method_description.rb +112 -0
  48. data/lib/apipie_dsl/parameter_description.rb +152 -0
  49. data/lib/apipie_dsl/railtie.rb +15 -0
  50. data/lib/apipie_dsl/return_description.rb +71 -0
  51. data/lib/apipie_dsl/routing.rb +17 -0
  52. data/lib/apipie_dsl/see_description.rb +35 -0
  53. data/lib/apipie_dsl/static_dispatcher.rb +70 -0
  54. data/lib/apipie_dsl/tag_list_description.rb +11 -0
  55. data/lib/apipie_dsl/tasks/apipie_dsl/cache.rake +43 -0
  56. data/lib/apipie_dsl/tasks/apipie_dsl/static.rake +43 -0
  57. data/lib/apipie_dsl/tasks/apipie_dsl/static_json.rake +29 -0
  58. data/lib/apipie_dsl/tasks_utils.rb +137 -0
  59. data/lib/apipie_dsl/utils.rb +83 -0
  60. data/lib/apipie_dsl/validator.rb +479 -0
  61. data/lib/apipie_dsl/version.rb +5 -0
  62. data/lib/generators/apipie_dsl/install/install_generator.rb +21 -0
  63. data/lib/generators/apipie_dsl/install/templates/initializer.rb.erb +9 -0
  64. data/lib/generators/apipie_dsl/views_generator.rb +14 -0
  65. data/test/test_helper.rb +6 -0
  66. 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