apipierails3 0.0.1

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 (171) hide show
  1. checksums.yaml +17 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +27 -0
  5. data/APACHE-LICENSE-2.0 +202 -0
  6. data/CHANGELOG.md +469 -0
  7. data/Gemfile +1 -0
  8. data/Gemfile.rails32 +6 -0
  9. data/Gemfile.rails41 +6 -0
  10. data/Gemfile.rails42 +11 -0
  11. data/Gemfile.rails50 +6 -0
  12. data/Gemfile.rails51 +7 -0
  13. data/MIT-LICENSE +20 -0
  14. data/NOTICE +4 -0
  15. data/PROPOSAL_FOR_RESPONSE_DESCRIPTIONS.md +244 -0
  16. data/README.rst +1874 -0
  17. data/Rakefile +13 -0
  18. data/apipierails3.gemspec +27 -0
  19. data/app/controllers/apipie/apipies_controller.rb +199 -0
  20. data/app/helpers/apipie_helper.rb +10 -0
  21. data/app/public/apipie/javascripts/apipie.js +6 -0
  22. data/app/public/apipie/javascripts/bundled/bootstrap-collapse.js +138 -0
  23. data/app/public/apipie/javascripts/bundled/bootstrap.js +1726 -0
  24. data/app/public/apipie/javascripts/bundled/jquery.js +5 -0
  25. data/app/public/apipie/javascripts/bundled/prettify.js +28 -0
  26. data/app/public/apipie/stylesheets/application.css +7 -0
  27. data/app/public/apipie/stylesheets/bundled/bootstrap-responsive.min.css +12 -0
  28. data/app/public/apipie/stylesheets/bundled/bootstrap.min.css +689 -0
  29. data/app/public/apipie/stylesheets/bundled/prettify.css +30 -0
  30. data/app/views/apipie/apipies/_disqus.html.erb +13 -0
  31. data/app/views/apipie/apipies/_errors.html.erb +23 -0
  32. data/app/views/apipie/apipies/_headers.html.erb +26 -0
  33. data/app/views/apipie/apipies/_languages.erb +6 -0
  34. data/app/views/apipie/apipies/_metadata.erb +1 -0
  35. data/app/views/apipie/apipies/_method_detail.erb +61 -0
  36. data/app/views/apipie/apipies/_params.html.erb +42 -0
  37. data/app/views/apipie/apipies/_params_plain.html.erb +20 -0
  38. data/app/views/apipie/apipies/apipie_404.html.erb +17 -0
  39. data/app/views/apipie/apipies/apipie_checksum.json.erb +1 -0
  40. data/app/views/apipie/apipies/getting_started.html.erb +6 -0
  41. data/app/views/apipie/apipies/index.html.erb +56 -0
  42. data/app/views/apipie/apipies/method.html.erb +41 -0
  43. data/app/views/apipie/apipies/plain.html.erb +77 -0
  44. data/app/views/apipie/apipies/resource.html.erb +80 -0
  45. data/app/views/apipie/apipies/static.html.erb +103 -0
  46. data/app/views/layouts/apipie/apipie.html.erb +27 -0
  47. data/config/locales/de.yml +28 -0
  48. data/config/locales/en.yml +32 -0
  49. data/config/locales/es.yml +28 -0
  50. data/config/locales/fr.yml +31 -0
  51. data/config/locales/it.yml +31 -0
  52. data/config/locales/ja.yml +31 -0
  53. data/config/locales/pl.yml +28 -0
  54. data/config/locales/pt-BR.yml +28 -0
  55. data/config/locales/ru.yml +28 -0
  56. data/config/locales/tr.yml +28 -0
  57. data/config/locales/zh-CN.yml +28 -0
  58. data/config/locales/zh-TW.yml +28 -0
  59. data/images/screenshot-1.png +0 -0
  60. data/images/screenshot-2.png +0 -0
  61. data/lib/apipie/apipie_module.rb +83 -0
  62. data/lib/apipie/application.rb +462 -0
  63. data/lib/apipie/configuration.rb +186 -0
  64. data/lib/apipie/dsl_definition.rb +607 -0
  65. data/lib/apipie/error_description.rb +44 -0
  66. data/lib/apipie/errors.rb +86 -0
  67. data/lib/apipie/extractor.rb +177 -0
  68. data/lib/apipie/extractor/collector.rb +117 -0
  69. data/lib/apipie/extractor/recorder.rb +166 -0
  70. data/lib/apipie/extractor/writer.rb +454 -0
  71. data/lib/apipie/helpers.rb +73 -0
  72. data/lib/apipie/markup.rb +48 -0
  73. data/lib/apipie/method_description.rb +273 -0
  74. data/lib/apipie/middleware/checksum_in_headers.rb +35 -0
  75. data/lib/apipie/param_description.rb +280 -0
  76. data/lib/apipie/railtie.rb +9 -0
  77. data/lib/apipie/resource_description.rb +124 -0
  78. data/lib/apipie/response_description.rb +131 -0
  79. data/lib/apipie/response_description_adapter.rb +200 -0
  80. data/lib/apipie/routes_formatter.rb +33 -0
  81. data/lib/apipie/routing.rb +16 -0
  82. data/lib/apipie/rspec/response_validation_helper.rb +192 -0
  83. data/lib/apipie/see_description.rb +39 -0
  84. data/lib/apipie/static_dispatcher.rb +69 -0
  85. data/lib/apipie/swagger_generator.rb +707 -0
  86. data/lib/apipie/tag_list_description.rb +11 -0
  87. data/lib/apipie/validator.rb +526 -0
  88. data/lib/apipie/version.rb +3 -0
  89. data/lib/apipierails3.rb +25 -0
  90. data/lib/generators/apipie/install/README +6 -0
  91. data/lib/generators/apipie/install/install_generator.rb +25 -0
  92. data/lib/generators/apipie/install/templates/initializer.rb.erb +7 -0
  93. data/lib/generators/apipie/views_generator.rb +11 -0
  94. data/lib/tasks/apipie.rake +345 -0
  95. data/rel-eng/packages/.readme +3 -0
  96. data/rel-eng/packages/rubygem-apipie-rails +1 -0
  97. data/rel-eng/tito.props +5 -0
  98. data/spec/controllers/api/v1/architectures_controller_spec.rb +29 -0
  99. data/spec/controllers/api/v2/architectures_controller_spec.rb +12 -0
  100. data/spec/controllers/api/v2/nested/resources_controller_spec.rb +11 -0
  101. data/spec/controllers/apipies_controller_spec.rb +273 -0
  102. data/spec/controllers/concerns_controller_spec.rb +42 -0
  103. data/spec/controllers/extended_controller_spec.rb +11 -0
  104. data/spec/controllers/users_controller_spec.rb +740 -0
  105. data/spec/dummy/Rakefile +7 -0
  106. data/spec/dummy/app/controllers/api/base_controller.rb +4 -0
  107. data/spec/dummy/app/controllers/api/v1/architectures_controller.rb +43 -0
  108. data/spec/dummy/app/controllers/api/v1/base_controller.rb +11 -0
  109. data/spec/dummy/app/controllers/api/v2/architectures_controller.rb +30 -0
  110. data/spec/dummy/app/controllers/api/v2/base_controller.rb +11 -0
  111. data/spec/dummy/app/controllers/api/v2/nested/architectures_controller.rb +32 -0
  112. data/spec/dummy/app/controllers/api/v2/nested/resources_controller.rb +33 -0
  113. data/spec/dummy/app/controllers/application_controller.rb +18 -0
  114. data/spec/dummy/app/controllers/concerns/extending_concern.rb +11 -0
  115. data/spec/dummy/app/controllers/concerns/sample_controller.rb +41 -0
  116. data/spec/dummy/app/controllers/concerns_controller.rb +8 -0
  117. data/spec/dummy/app/controllers/extended_controller.rb +14 -0
  118. data/spec/dummy/app/controllers/files_controller.rb +5 -0
  119. data/spec/dummy/app/controllers/overridden_concerns_controller.rb +31 -0
  120. data/spec/dummy/app/controllers/pets_controller.rb +408 -0
  121. data/spec/dummy/app/controllers/pets_using_auto_views_controller.rb +73 -0
  122. data/spec/dummy/app/controllers/pets_using_self_describing_classes_controller.rb +95 -0
  123. data/spec/dummy/app/controllers/tagged_cats_controller.rb +32 -0
  124. data/spec/dummy/app/controllers/tagged_dogs_controller.rb +15 -0
  125. data/spec/dummy/app/controllers/twitter_example_controller.rb +307 -0
  126. data/spec/dummy/app/controllers/users_controller.rb +297 -0
  127. data/spec/dummy/app/views/layouts/application.html.erb +21 -0
  128. data/spec/dummy/config.ru +4 -0
  129. data/spec/dummy/config/application.rb +49 -0
  130. data/spec/dummy/config/boot.rb +10 -0
  131. data/spec/dummy/config/database.yml +21 -0
  132. data/spec/dummy/config/environment.rb +8 -0
  133. data/spec/dummy/config/environments/development.rb +28 -0
  134. data/spec/dummy/config/environments/production.rb +52 -0
  135. data/spec/dummy/config/environments/test.rb +38 -0
  136. data/spec/dummy/config/initializers/apipie.rb +110 -0
  137. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  138. data/spec/dummy/config/initializers/inflections.rb +10 -0
  139. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  140. data/spec/dummy/config/initializers/secret_token.rb +8 -0
  141. data/spec/dummy/config/initializers/session_store.rb +8 -0
  142. data/spec/dummy/config/locales/en.yml +5 -0
  143. data/spec/dummy/config/routes.rb +51 -0
  144. data/spec/dummy/db/.gitkeep +0 -0
  145. data/spec/dummy/doc/apipie_examples.json +1 -0
  146. data/spec/dummy/doc/users/desc_from_file.md +1 -0
  147. data/spec/dummy/public/404.html +26 -0
  148. data/spec/dummy/public/422.html +26 -0
  149. data/spec/dummy/public/500.html +26 -0
  150. data/spec/dummy/public/favicon.ico +0 -0
  151. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  152. data/spec/dummy/script/rails +6 -0
  153. data/spec/lib/application_spec.rb +49 -0
  154. data/spec/lib/extractor/extractor_spec.rb +9 -0
  155. data/spec/lib/extractor/middleware_spec.rb +44 -0
  156. data/spec/lib/extractor/writer_spec.rb +110 -0
  157. data/spec/lib/file_handler_spec.rb +18 -0
  158. data/spec/lib/method_description_spec.rb +98 -0
  159. data/spec/lib/param_description_spec.rb +345 -0
  160. data/spec/lib/param_group_spec.rb +60 -0
  161. data/spec/lib/rake_spec.rb +71 -0
  162. data/spec/lib/resource_description_spec.rb +48 -0
  163. data/spec/lib/swagger/openapi_2_0_schema.json +1607 -0
  164. data/spec/lib/swagger/rake_swagger_spec.rb +139 -0
  165. data/spec/lib/swagger/response_validation_spec.rb +104 -0
  166. data/spec/lib/swagger/swagger_dsl_spec.rb +658 -0
  167. data/spec/lib/validator_spec.rb +113 -0
  168. data/spec/lib/validators/array_validator_spec.rb +85 -0
  169. data/spec/spec_helper.rb +109 -0
  170. data/spec/support/rake.rb +21 -0
  171. metadata +415 -0
@@ -0,0 +1,11 @@
1
+ module Apipie
2
+
3
+ class TagListDescription
4
+
5
+ attr_reader :tags
6
+
7
+ def initialize(tags); @tags = tags; end
8
+
9
+ end
10
+
11
+ end
@@ -0,0 +1,526 @@
1
+ # -*- coding: utf-8 -*-
2
+ module Apipie
3
+
4
+ module Validator
5
+
6
+ # to create new validator, inherit from Apipie::Validator::Base
7
+ # and implement class method build and instance method validate
8
+ class BaseValidator
9
+
10
+ attr_accessor :param_description
11
+
12
+ def initialize(param_description)
13
+ @param_description = param_description
14
+ end
15
+
16
+ def inspected_fields
17
+ [:param_description]
18
+ end
19
+
20
+ def inspect
21
+ string = "#<#{self.class.name}:#{self.object_id} "
22
+ fields = inspected_fields.map {|field| "#{field}: #{self.send(field)}"}
23
+ string << fields.join(", ") << ">"
24
+ end
25
+
26
+ def self.inherited(subclass)
27
+ @validators ||= []
28
+ @validators.insert 0, subclass
29
+ end
30
+
31
+ # find the right validator for given options
32
+ def self.find(param_description, argument, options, block)
33
+ @validators.each do |validator_type|
34
+ validator = validator_type.build(param_description, argument, options, block)
35
+ return validator if validator
36
+ end
37
+ return nil
38
+ end
39
+
40
+ # check if value is valid
41
+ def valid?(value)
42
+ if self.validate(value)
43
+ @error_value = nil
44
+ true
45
+ else
46
+ @error_value = value
47
+ false
48
+ end
49
+ end
50
+
51
+ def param_name
52
+ @param_description.name
53
+ end
54
+
55
+ # validator description
56
+ def description
57
+ "TODO: validator description"
58
+ end
59
+
60
+ def format_description_value(value)
61
+ "<code>#{CGI::escapeHTML(value.to_s)}</code>"
62
+ end
63
+
64
+ def error
65
+ ParamInvalid.new(param_name, @error_value, description)
66
+ end
67
+
68
+ def to_s
69
+ self.description
70
+ end
71
+
72
+ def to_json
73
+ self.description
74
+ end
75
+
76
+ # what type is expected, mostly string
77
+ # this information is used in cli client
78
+ # thor supported types :string, :hash, :array, :numeric, or :boolean
79
+ def expected_type
80
+ 'string'
81
+ end
82
+
83
+ def merge_with(other_validator)
84
+ return self if self == other_validator
85
+ raise NotImplementedError, "Don't know how to merge #{self.inspect} with #{other_validator.inspect}"
86
+ end
87
+
88
+ def params_ordered
89
+ nil
90
+ end
91
+
92
+ def ==(other)
93
+ return false unless self.class == other.class
94
+ if param_description == other.param_description
95
+ true
96
+ else
97
+ false
98
+ end
99
+ end
100
+ end
101
+
102
+ # validate arguments type
103
+ class TypeValidator < BaseValidator
104
+
105
+ def initialize(param_description, argument)
106
+ super(param_description)
107
+ @type = argument
108
+ end
109
+
110
+ def validate(value)
111
+ return false if value.nil?
112
+ value.is_a? @type
113
+ end
114
+
115
+ def self.build(param_description, argument, options, block)
116
+ if argument.is_a?(Class) && (argument != Hash || block.nil?)
117
+ self.new(param_description, argument)
118
+ end
119
+ end
120
+
121
+ def description
122
+ "Must be a #{@type}"
123
+ end
124
+
125
+ def expected_type
126
+ if @type.ancestors.include? Hash
127
+ 'hash'
128
+ elsif @type.ancestors.include? Array
129
+ 'array'
130
+ elsif @type.ancestors.include? Numeric
131
+ 'numeric'
132
+ else
133
+ 'string'
134
+ end
135
+ end
136
+ end
137
+
138
+ # validate arguments value with regular expression
139
+ class RegexpValidator < BaseValidator
140
+
141
+ def initialize(param_description, argument)
142
+ super(param_description)
143
+ @regexp = argument
144
+ end
145
+
146
+ def validate(value)
147
+ value =~ @regexp
148
+ end
149
+
150
+ def self.build(param_description, argument, options, proc)
151
+ self.new(param_description, argument) if argument.is_a? Regexp
152
+ end
153
+
154
+ def description
155
+ "Must match regular expression #{format_description_value("/#{@regexp.source}/")}."
156
+ end
157
+ end
158
+
159
+ # arguments value must be one of given in array
160
+ class EnumValidator < BaseValidator
161
+ def initialize(param_description, argument)
162
+ super(param_description)
163
+ @array = argument
164
+ end
165
+
166
+ def validate(value)
167
+ @array.include?(value)
168
+ end
169
+
170
+ def self.build(param_description, argument, options, proc)
171
+ self.new(param_description, argument) if argument.is_a?(Array)
172
+ end
173
+
174
+ def values
175
+ @array
176
+ end
177
+
178
+ def description
179
+ string = @array.map { |value| format_description_value(value) }.join(', ')
180
+ "Must be one of: #{string}."
181
+ end
182
+ end
183
+
184
+ # arguments value must be an array
185
+ class ArrayValidator < Apipie::Validator::BaseValidator
186
+ def initialize(param_description, argument, options={})
187
+ super(param_description)
188
+ @type = argument
189
+ @items_type = options[:of]
190
+ @items_enum = options[:in]
191
+ end
192
+
193
+ def validate(values)
194
+ return false unless process_value(values).respond_to?(:each) && !process_value(values).is_a?(String)
195
+ process_value(values).all? { |v| validate_item(v)}
196
+ end
197
+
198
+ def process_value(values)
199
+ values || []
200
+ end
201
+
202
+ def description
203
+ "Must be an array of #{items}"
204
+ end
205
+
206
+ def expected_type
207
+ "array"
208
+ end
209
+
210
+ def self.build(param_description, argument, options, block)
211
+ if argument == Array && !block.is_a?(Proc)
212
+ self.new(param_description, argument, options)
213
+ end
214
+ end
215
+
216
+ private
217
+
218
+ def enum
219
+ if @items_enum.kind_of?(Proc)
220
+ @items_enum = Array(@items_enum.call)
221
+ end
222
+ @items_enum
223
+ end
224
+
225
+ def validate_item(value)
226
+ has_valid_type?(value) &&
227
+ is_valid_value?(value)
228
+ end
229
+
230
+ def has_valid_type?(value)
231
+ if @items_type
232
+ item_validator = BaseValidator.find('', @items_type, nil, nil)
233
+
234
+ if item_validator
235
+ item_validator.valid?(value)
236
+ else
237
+ value.kind_of?(@items_type)
238
+ end
239
+ else
240
+ true
241
+ end
242
+ end
243
+
244
+ def is_valid_value?(value)
245
+ if enum
246
+ enum.include?(value)
247
+ else
248
+ true
249
+ end
250
+ end
251
+
252
+ def items
253
+ unless enum
254
+ @items_type || "any type"
255
+ else
256
+ enum.inspect
257
+ end
258
+ end
259
+ end
260
+
261
+ class ArrayClassValidator < BaseValidator
262
+
263
+ def initialize(param_description, argument)
264
+ super(param_description)
265
+ @array = argument
266
+ end
267
+
268
+ def validate(value)
269
+ @array.include?(value.class)
270
+ end
271
+
272
+ def self.build(param_description, argument, options, block)
273
+ if argument.is_a?(Array) && argument.first.class == Class && !block.is_a?(Proc)
274
+ self.new(param_description, argument)
275
+ end
276
+ end
277
+
278
+ def description
279
+ string = @array.map { |value| format_description_value(value) }.join(', ')
280
+ "Must be one of: #{string}."
281
+ end
282
+ end
283
+
284
+ class ProcValidator < BaseValidator
285
+
286
+ def initialize(param_description, argument)
287
+ super(param_description)
288
+ @proc = argument
289
+ end
290
+
291
+ def validate(value)
292
+ (@help = @proc.call(value)) === true
293
+ end
294
+
295
+ def self.build(param_description, argument, options, proc)
296
+ self.new(param_description, argument) if argument.is_a?(Proc) && argument.arity == 1
297
+ end
298
+
299
+ def error
300
+ ParamInvalid.new(param_name, @error_value, @help)
301
+ end
302
+
303
+ def description
304
+ ""
305
+ end
306
+ end
307
+
308
+ class HashValidator < BaseValidator
309
+ include Apipie::DSL::Base
310
+ include Apipie::DSL::Param
311
+
312
+ def self.build(param_description, argument, options, block)
313
+ self.new(param_description, block, options[:param_group]) if block.is_a?(Proc) && block.arity <= 0 && argument == Hash
314
+ end
315
+
316
+ def initialize(param_description, argument, param_group)
317
+ super(param_description)
318
+ @proc = argument
319
+ @param_group = param_group
320
+ self.instance_exec(&@proc)
321
+ # specifying action_aware on Hash influences the child params,
322
+ # not the hash param itself: assuming it's required when
323
+ # updating as well
324
+ if param_description.options[:action_aware] && param_description.options[:required]
325
+ param_description.required = true
326
+ end
327
+ prepare_hash_params
328
+ end
329
+
330
+ def params_ordered
331
+ @params_ordered ||= _apipie_dsl_data[:params].map do |args|
332
+ options = args.find { |arg| arg.is_a? Hash }
333
+ options[:parent] = self.param_description
334
+ Apipie::ParamDescription.from_dsl_data(param_description.method_description, args)
335
+ end
336
+ end
337
+
338
+ def validate(value)
339
+ return false if !value.is_a? Hash
340
+ if @hash_params
341
+ @hash_params.each do |k, p|
342
+ if Apipie.configuration.validate_presence?
343
+ raise ParamMissing.new(p) if p.required && !value.has_key?(k)
344
+ end
345
+ if Apipie.configuration.validate_value?
346
+ p.validate(value[k]) if value.has_key?(k)
347
+ end
348
+ end
349
+ end
350
+ return true
351
+ end
352
+
353
+ def process_value(value)
354
+ if @hash_params && value
355
+ return @hash_params.each_with_object({}) do |(key, param), api_params|
356
+ if value.has_key?(key)
357
+ api_params[param.as] = param.process_value(value[key])
358
+ end
359
+ end
360
+ end
361
+ end
362
+
363
+ def description
364
+ "Must be a Hash"
365
+ end
366
+
367
+ def expected_type
368
+ 'hash'
369
+ end
370
+
371
+ # where the group definition should be looked up when no scope
372
+ # given. This is expected to return a controller.
373
+ def _default_param_group_scope
374
+ @param_group && @param_group[:scope]
375
+ end
376
+
377
+ def merge_with(other_validator)
378
+ if other_validator.is_a? HashValidator
379
+ @params_ordered = ParamDescription.unify(self.params_ordered + other_validator.params_ordered)
380
+ prepare_hash_params
381
+ else
382
+ super
383
+ end
384
+ end
385
+
386
+ def prepare_hash_params
387
+ @hash_params = params_ordered.reduce({}) do |h, param|
388
+ h.update(param.name.to_sym => param)
389
+ end
390
+ end
391
+ end
392
+
393
+
394
+ # special type of validator: we say that it's not specified
395
+ class UndefValidator < BaseValidator
396
+
397
+ def validate(value)
398
+ true
399
+ end
400
+
401
+ def self.build(param_description, argument, options, block)
402
+ if argument == :undef
403
+ self.new(param_description)
404
+ end
405
+ end
406
+
407
+ def description
408
+ nil
409
+ end
410
+ end
411
+
412
+ class DecimalValidator < BaseValidator
413
+
414
+ def validate(value)
415
+ self.class.validate(value)
416
+ end
417
+
418
+ def self.build(param_description, argument, options, block)
419
+ if argument == :decimal
420
+ self.new(param_description)
421
+ end
422
+ end
423
+
424
+ def description
425
+ "Must be a decimal number."
426
+ end
427
+
428
+ def self.validate(value)
429
+ value.to_s =~ /\A^[-+]?[0-9]+([,.][0-9]+)?\Z$/
430
+ end
431
+ end
432
+
433
+ class NumberValidator < BaseValidator
434
+
435
+ def validate(value)
436
+ self.class.validate(value)
437
+ end
438
+
439
+ def self.build(param_description, argument, options, block)
440
+ if argument == :number
441
+ self.new(param_description)
442
+ end
443
+ end
444
+
445
+ def description
446
+ "Must be a number."
447
+ end
448
+
449
+ def expected_type
450
+ 'numeric'
451
+ end
452
+
453
+ def self.validate(value)
454
+ value.to_s =~ /\A(0|[1-9]\d*)\Z$/
455
+ end
456
+ end
457
+
458
+ class BooleanValidator < BaseValidator
459
+
460
+ def validate(value)
461
+ %w[true false 1 0].include?(value.to_s)
462
+ end
463
+
464
+ def self.build(param_description, argument, options, block)
465
+ if argument == :bool || argument == :boolean
466
+ self.new(param_description)
467
+ end
468
+ end
469
+
470
+ def expected_type
471
+ 'boolean'
472
+ end
473
+
474
+ def description
475
+ string = %w(true false 1 0).map { |value| format_description_value(value) }.join(', ')
476
+ "Must be one of: #{string}."
477
+ end
478
+ end
479
+
480
+ class NestedValidator < BaseValidator
481
+
482
+ def initialize(param_description, argument, param_group)
483
+ super(param_description)
484
+ @validator = Apipie::Validator:: HashValidator.new(param_description, argument, param_group)
485
+ @type = argument
486
+ end
487
+
488
+ def validate(value)
489
+ value ||= [] # Rails convert empty array to nil
490
+ return false if value.class != Array
491
+ value.each do |child|
492
+ return false unless @validator.validate(child)
493
+ end
494
+ true
495
+ end
496
+
497
+ def process_value(value)
498
+ value ||= [] # Rails convert empty array to nil
499
+ @values = []
500
+ value.each do |child|
501
+ @values << @validator.process_value(child)
502
+ end
503
+ @values
504
+ end
505
+
506
+ def self.build(param_description, argument, options, block)
507
+ # in Ruby 1.8.x the arity on block without args is -1
508
+ # while in Ruby 1.9+ it is 0
509
+ self.new(param_description, block, options[:param_group]) if block.is_a?(Proc) && block.arity <= 0 && argument == Array
510
+ end
511
+
512
+ def expected_type
513
+ 'array'
514
+ end
515
+
516
+ def description
517
+ "Must be an Array of nested elements"
518
+ end
519
+
520
+ def params_ordered
521
+ @validator.params_ordered
522
+ end
523
+ end
524
+
525
+ end
526
+ end