sinatra-param2 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +77 -0
  4. data/LICENSE +19 -0
  5. data/README.md +190 -0
  6. data/Rakefile +18 -0
  7. data/build/reports/assets/0.10.1/application.css +799 -0
  8. data/build/reports/assets/0.10.1/application.js +1707 -0
  9. data/build/reports/assets/0.10.1/colorbox/border.png +0 -0
  10. data/build/reports/assets/0.10.1/colorbox/controls.png +0 -0
  11. data/build/reports/assets/0.10.1/colorbox/loading.gif +0 -0
  12. data/build/reports/assets/0.10.1/colorbox/loading_background.png +0 -0
  13. data/build/reports/assets/0.10.1/favicon_green.png +0 -0
  14. data/build/reports/assets/0.10.1/favicon_red.png +0 -0
  15. data/build/reports/assets/0.10.1/favicon_yellow.png +0 -0
  16. data/build/reports/assets/0.10.1/loading.gif +0 -0
  17. data/build/reports/assets/0.10.1/magnify.png +0 -0
  18. data/build/reports/assets/0.10.1/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  19. data/build/reports/assets/0.10.1/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  20. data/build/reports/assets/0.10.1/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  21. data/build/reports/assets/0.10.1/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  22. data/build/reports/assets/0.10.1/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  23. data/build/reports/assets/0.10.1/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  24. data/build/reports/assets/0.10.1/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  25. data/build/reports/assets/0.10.1/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  26. data/build/reports/assets/0.10.1/smoothness/images/ui-icons_222222_256x240.png +0 -0
  27. data/build/reports/assets/0.10.1/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  28. data/build/reports/assets/0.10.1/smoothness/images/ui-icons_454545_256x240.png +0 -0
  29. data/build/reports/assets/0.10.1/smoothness/images/ui-icons_888888_256x240.png +0 -0
  30. data/build/reports/assets/0.10.1/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  31. data/build/reports/coverage.xml +165 -0
  32. data/build/reports/index.html +1643 -0
  33. data/example/Gemfile +7 -0
  34. data/example/Gemfile.lock +33 -0
  35. data/example/Procfile +1 -0
  36. data/example/app.rb +52 -0
  37. data/example/config.ru +6 -0
  38. data/lib/sinatra/param.rb +245 -0
  39. data/lib/sinatra/param/version.rb +5 -0
  40. data/sinatra-param2.gemspec +27 -0
  41. data/spec/dummy/app.rb +346 -0
  42. data/spec/parameter_conjunctivity_spec.rb +34 -0
  43. data/spec/parameter_exclusivity_spec.rb +55 -0
  44. data/spec/parameter_inclusivity_spec.rb +30 -0
  45. data/spec/parameter_nested_validations_spec.rb +151 -0
  46. data/spec/parameter_raise_spec.rb +25 -0
  47. data/spec/parameter_spec.rb +19 -0
  48. data/spec/parameter_transformations_spec.rb +42 -0
  49. data/spec/parameter_type_coercion_spec.rb +211 -0
  50. data/spec/parameter_validations_spec.rb +247 -0
  51. data/spec/spec_helper.rb +29 -0
  52. metadata +190 -0
@@ -0,0 +1,7 @@
1
+ ruby '2.0.0'
2
+ source "https://rubygems.org"
3
+
4
+ gem 'sinatra', require: 'sinatra/base'
5
+ gem 'sinatra-param', path: File.join(__FILE__, "../.."), require: 'sinatra/param'
6
+ gem 'json'
7
+ gem 'unicorn'
@@ -0,0 +1,33 @@
1
+ PATH
2
+ remote: /Users/mattt/Code/Ruby/sinatra-param
3
+ specs:
4
+ sinatra-param (0.1.3)
5
+ sinatra (~> 1.3)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ json (1.8.1)
11
+ kgio (2.8.1)
12
+ rack (1.5.2)
13
+ rack-protection (1.5.0)
14
+ rack
15
+ raindrops (0.12.0)
16
+ sinatra (1.4.3)
17
+ rack (~> 1.4)
18
+ rack-protection (~> 1.4)
19
+ tilt (~> 1.3, >= 1.3.4)
20
+ tilt (1.4.1)
21
+ unicorn (4.6.3)
22
+ kgio (~> 2.6)
23
+ rack
24
+ raindrops (~> 0.7)
25
+
26
+ PLATFORMS
27
+ ruby
28
+
29
+ DEPENDENCIES
30
+ json
31
+ sinatra
32
+ sinatra-param!
33
+ unicorn
@@ -0,0 +1 @@
1
+ web: bundle exec unicorn -p $PORT
@@ -0,0 +1,52 @@
1
+ class App < Sinatra::Base
2
+ helpers Sinatra::Param
3
+
4
+ before do
5
+ content_type :json
6
+ end
7
+
8
+ # GET /messages
9
+ # GET /messages?sort=name&order=ASC
10
+ get '/messages' do
11
+ param :sort, String, default: "name"
12
+ param :order, String, in: ["ASC", "DESC"], transform: :upcase, default: "ASC"
13
+
14
+ {
15
+ sort: params[:sort],
16
+ order: params[:order]
17
+ }.to_json
18
+ end
19
+
20
+ # GET /messages/1,2,3,4,5
21
+ get '/messages/:ids' do
22
+ param :ids, Array, required: true
23
+
24
+ {
25
+ ids: params[:ids]
26
+ }.to_json
27
+ end
28
+
29
+ # POST /messages/1/response
30
+ post '/messages/:id/response' do
31
+ param :message, String, max: 1024, required: true
32
+
33
+ {
34
+ message: params[:message]
35
+ }.to_json
36
+ end
37
+
38
+ # GET /choice?a=foo
39
+ # GET /choice?b=bar
40
+ # GET /choice?c=baz
41
+ get '/choice' do
42
+ param :a, String
43
+ param :b, String
44
+ param :c, String
45
+
46
+ one_of(:a, :b, :c)
47
+
48
+ {
49
+ message: 'OK'
50
+ }.to_json
51
+ end
52
+ end
@@ -0,0 +1,6 @@
1
+ require 'bundler'
2
+ Bundler.require
3
+
4
+ require './app'
5
+
6
+ run App
@@ -0,0 +1,245 @@
1
+ require 'sinatra/base'
2
+ require 'sinatra/param/version'
3
+ require 'date'
4
+ require 'time'
5
+
6
+ module Sinatra
7
+ module Param
8
+ Boolean = :boolean
9
+
10
+ class InvalidParameterError < StandardError
11
+ attr_accessor :param, :options
12
+ end
13
+
14
+ def param(name, type, options = {})
15
+ name = name.to_s
16
+ applicable_params = @applicable_params || params
17
+
18
+ return unless applicable_params.member?(name) or options.has_key?(:default) or options[:required]
19
+
20
+ begin
21
+ applicable_params[name] = coerce(applicable_params[name], type, options)
22
+ applicable_params[name] = (options[:default].call if options[:default].respond_to?(:call)) || options[:default] if applicable_params[name].nil? and options.has_key?(:default)
23
+ applicable_params[name] = options[:transform].to_proc.call(applicable_params[name]) if applicable_params[name] and options[:transform]
24
+ validate!(applicable_params[name], options)
25
+
26
+ if block_given?
27
+ if type != Hash
28
+ raise Sinatra::Param::InvalidParameterError.new(
29
+ 'Only the Hash parameter validation can use sub hash validation method')
30
+ end
31
+ original_applicable_params = @applicable_params
32
+ original_parent_key_name = @parent_key_name
33
+ @applicable_params = applicable_params[name]
34
+ @parent_key_name = formatted_params(@parent_key_name, name)
35
+
36
+ yield
37
+
38
+ @applicable_params = original_applicable_params
39
+ @parent_key_name = original_parent_key_name
40
+ end
41
+
42
+ applicable_params[name]
43
+ rescue InvalidParameterError => exception
44
+ exception_name = formatted_params(@parent_key_name, name)
45
+ if options[:raise] or (settings.raise_sinatra_param_exceptions rescue false)
46
+ exception.param = exception_name
47
+ exception.options = options
48
+ raise exception
49
+ end
50
+
51
+ error = options[:message] || exception.to_s
52
+
53
+ if content_type and content_type.match(mime_type(:json))
54
+ error = {message: error, errors: {exception_name => exception.message}}.to_json
55
+ end
56
+
57
+ halt 400, error
58
+ end
59
+ end
60
+
61
+ def one_of(*args)
62
+ options = args.last.is_a?(Hash) ? args.pop : {}
63
+ names = args.collect(&:to_s)
64
+ applicable_params = @applicable_params || params
65
+
66
+ return unless names.length >= 2
67
+
68
+ begin
69
+ validate_one_of!(applicable_params, names, options)
70
+ rescue InvalidParameterError => exception
71
+ if options[:raise] or (settings.raise_sinatra_param_exceptions rescue false)
72
+ exception.param, exception.options = names, options
73
+ raise exception
74
+ end
75
+
76
+ error = "Invalid parameters #{formatted_params(@parent_key_name, names)}"
77
+ if content_type and content_type.match(mime_type(:json))
78
+ error = {message: error, errors: {names => exception.message}}.to_json
79
+ end
80
+
81
+ halt 400, error
82
+ end
83
+ end
84
+
85
+ def any_of(*args)
86
+ options = args.last.is_a?(Hash) ? args.pop : {}
87
+ names = args.collect(&:to_s)
88
+ applicable_params = @applicable_params || params
89
+
90
+ return unless names.length >= 2
91
+
92
+ begin
93
+ validate_any_of!(applicable_params, names, options)
94
+ rescue InvalidParameterError => exception
95
+ if options[:raise] or (settings.raise_sinatra_param_exceptions rescue false)
96
+ exception.param, exception.options = names, options
97
+ raise exception
98
+ end
99
+
100
+ formatted_params = formatted_params(@parent_key_name, names)
101
+ error = "Invalid parameters #{formatted_params}"
102
+ if content_type and content_type.match(mime_type(:json))
103
+ error = {message: error, errors: {formatted_params => exception.message}}.to_json
104
+ end
105
+
106
+ halt 400, error
107
+ end
108
+ end
109
+
110
+ def all_or_none_of(*args)
111
+ options = args.last.is_a?(Hash) ? args.pop : {}
112
+ names = args.collect(&:to_s)
113
+
114
+ begin
115
+ validate_all_or_none_of!(params, names, options)
116
+ rescue InvalidParameterError => exception
117
+ if options[:raise] or (settings.raise_sinatra_param_exceptions rescue false)
118
+ exception.param, exception.options = names, options
119
+ raise exception
120
+ end
121
+
122
+ error = "Invalid parameters [#{names.join(', ')}]"
123
+ if content_type and content_type.match(mime_type(:json))
124
+ error = {message: error, errors: {names => exception.message}}.to_json
125
+ end
126
+
127
+ halt 400, error
128
+ end
129
+ end
130
+
131
+ private
132
+
133
+ def coerce(param, type, options = {})
134
+ begin
135
+ return nil if param.nil?
136
+ return param if (param.is_a?(type) rescue false)
137
+ return Integer(param) if type == Integer
138
+ return Float(param) if type == Float
139
+ return String(param) if type == String
140
+ return Date.parse(param) if type == Date
141
+ return Time.parse(param) if type == Time
142
+ return DateTime.parse(param) if type == DateTime
143
+ return coerce_array(param, options) if type == Array
144
+ return Hash[param.to_s.split(options[:delimiter] || ",").map{|c| c.split(options[:separator] || ":")}] if type == Hash
145
+ return (/(false|f|no|n|0)$/i === param.to_s ? false : (/(true|t|yes|y|1)$/i === param.to_s ? true : nil)) if type == TrueClass || type == FalseClass || type == Boolean
146
+ return nil
147
+ rescue ArgumentError
148
+ raise InvalidParameterError, "'#{param}' is not a valid #{type}"
149
+ end
150
+ end
151
+
152
+ ###
153
+ # Array is a special case, we should convert from Hash
154
+ # or convert to string before parsing.
155
+ def coerce_array(param, options = {})
156
+ return param if param.is_a? Array
157
+ return param.to_a if param.is_a? Hash
158
+ return Array(param.to_s.split(options[:delimiter] || ","))
159
+ end
160
+
161
+ def validate!(param, options)
162
+ options.each do |key, value|
163
+ case key
164
+ when :required
165
+ raise InvalidParameterError, "Parameter is required" if value && param.nil?
166
+ when :blank
167
+ raise InvalidParameterError, "Parameter cannot be blank" if !value && case param
168
+ when String
169
+ !(/\S/ === param)
170
+ when Array, Hash
171
+ param.empty?
172
+ else
173
+ param.nil?
174
+ end
175
+ when :format
176
+ raise InvalidParameterError, "Parameter must be a string if using the format validation" unless param.kind_of?(String)
177
+ raise InvalidParameterError, "Parameter must match format #{value}" unless param =~ value
178
+ when :is
179
+ raise InvalidParameterError, "Parameter must be #{value}" unless param === value
180
+ when :in, :within, :range
181
+ raise InvalidParameterError, "Parameter must be within #{value}" unless param.nil? || case value
182
+ when Range
183
+ case param
184
+ when Array
185
+ param.all? { |element| value.include?(element) }
186
+ else
187
+ value.include?(param)
188
+ end
189
+ else
190
+ case param
191
+ when Array
192
+ (param - Array(value)).empty?
193
+ else
194
+ Array(value).include?(param)
195
+ end
196
+ end
197
+ when :min
198
+ raise InvalidParameterError, "Parameter cannot be less than #{value}" unless param.nil? || value <= param
199
+ when :max
200
+ raise InvalidParameterError, "Parameter cannot be greater than #{value}" unless param.nil? || value >= param
201
+ when :min_length
202
+ raise InvalidParameterError, "Parameter cannot have length less than #{value}" unless param.nil? || value <= param.length
203
+ when :max_length
204
+ raise InvalidParameterError, "Parameter cannot have length greater than #{value}" unless param.nil? || value >= param.length
205
+ end
206
+ end
207
+ end
208
+
209
+ def validate_one_of!(params, names, options)
210
+ if names.count{|name| present?(params[name])} > 1
211
+ raise InvalidParameterError, "Only one of #{formatted_params(@parent_key_name, names)} is allowed"
212
+ end
213
+ end
214
+
215
+ def validate_any_of!(params, names, options)
216
+ if names.count{|name| present?(params[name])} < 1
217
+ raise InvalidParameterError, "One of parameters #{formatted_params(@parent_key_name, names)} is required"
218
+ end
219
+ end
220
+
221
+ def validate_all_or_none_of!(params, names, options)
222
+ present_count = names.count{|name| present?(params[name])}
223
+ raise InvalidParameterError, "All or none of parameters [#{names.join(', ')}] are required" if present_count > 0 and present_count != names.length
224
+ end
225
+
226
+ # ActiveSupport #present? and #blank? without patching Object
227
+ def present?(object)
228
+ !blank?(object)
229
+ end
230
+
231
+ def blank?(object)
232
+ object.respond_to?(:empty?) ? object.empty? : !object
233
+ end
234
+
235
+ def formatted_params(parent_key, name)
236
+ if name.is_a?(Array)
237
+ name = "[#{name.join(', ')}]"
238
+ end
239
+
240
+ return parent_key ? "#{parent_key}[#{name}]" : name
241
+ end
242
+ end
243
+
244
+ helpers Param
245
+ end
@@ -0,0 +1,5 @@
1
+ module Sinatra
2
+ module Param
3
+ VERSION = '1.0.0'
4
+ end
5
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/sinatra/param/version', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "sinatra-param2"
6
+ s.license = "MIT"
7
+ s.authors = ["Mattt Thompson", "Adrian Bravo"]
8
+ s.email = "adrianbn@gmail.com"
9
+ s.homepage = "https://github.com/adrianbn/sinatra-param2"
10
+ s.version = Sinatra::Param::VERSION
11
+ s.platform = Gem::Platform::RUBY
12
+ s.summary = "Parameter Validation & Type Coercion for Sinatra 2."
13
+ s.description = "sinatra-param2 allows you to declare, validate, and transform endpoint parameters as you would in frameworks like ActiveModel or DataMapper."
14
+
15
+ s.add_dependency "sinatra", "~> 2.0"
16
+
17
+ s.add_development_dependency "rake"
18
+ s.add_development_dependency "rspec"
19
+ s.add_development_dependency "rack-test"
20
+ s.add_development_dependency "simplecov"
21
+ s.add_development_dependency "simplecov-cobertura"
22
+
23
+ s.files = Dir["./**/*"].reject { |file| file =~ /\.\/(bin|log|pkg|script|spec|test|vendor)/ }
24
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
25
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
26
+ s.require_paths = ["lib"]
27
+ end
@@ -0,0 +1,346 @@
1
+ require 'sinatra/base'
2
+ require 'sinatra/param'
3
+ require 'date'
4
+ require 'time'
5
+ require 'json'
6
+
7
+ class App < Sinatra::Base
8
+ helpers Sinatra::Param
9
+
10
+ set :show_exceptions, false
11
+ set :raise_errors, true
12
+
13
+ before do
14
+ content_type :json
15
+ end
16
+
17
+ get '/' do
18
+ param :a, String
19
+ param :b, String, required: true
20
+ param :c, String, default: 'test'
21
+ param :d, String
22
+
23
+ params.to_json
24
+ end
25
+
26
+ get '/keys/stringify' do
27
+ param :q, String, transform: :upcase
28
+
29
+ params['q']
30
+ end
31
+
32
+ get '/coerce/string' do
33
+ params['arg'] = params['arg'].to_i
34
+ param :arg, String
35
+ params.to_json
36
+ end
37
+
38
+ get '/coerce/integer' do
39
+ param :arg, Integer
40
+ params.to_json
41
+ end
42
+
43
+ get '/coerce/float' do
44
+ param :arg, Float
45
+ params.to_json
46
+ end
47
+
48
+ get '/coerce/time' do
49
+ param :arg, Time
50
+ params.to_json
51
+ end
52
+
53
+ get '/coerce/date' do
54
+ param :arg, Date
55
+ params.to_json
56
+ end
57
+
58
+ get '/coerce/datetime' do
59
+ param :arg, DateTime
60
+ params.to_json
61
+ end
62
+
63
+ get '/coerce/array' do
64
+ param :arg, Array
65
+ params.to_json
66
+ end
67
+
68
+ get '/coerce/hash' do
69
+ param :arg, Hash
70
+ params.to_json
71
+ end
72
+
73
+ get '/coerce/boolean' do
74
+ param :arg, Boolean
75
+ params.to_json
76
+ end
77
+
78
+ get '/default' do
79
+ param :sort, String, default: "title"
80
+ params.to_json
81
+ end
82
+
83
+ get '/default/hash' do
84
+ param :attributes, Hash, default: {}
85
+ params.to_json
86
+ end
87
+
88
+ get '/default/proc' do
89
+ param :year, Integer, default: proc { 2014 }
90
+ params.to_json
91
+ end
92
+
93
+ get '/default/boolean/true' do
94
+ param :arg, Boolean, default: true
95
+ params.to_json
96
+ end
97
+
98
+ get '/default/boolean/false' do
99
+ param :arg, Boolean, default: false
100
+ params.to_json
101
+ end
102
+
103
+ get '/transform' do
104
+ param :order, String, transform: :upcase
105
+ params.to_json
106
+ end
107
+
108
+ get '/transform/required' do
109
+ param :order, String, required: true, transform: :upcase
110
+ params.to_json
111
+ end
112
+
113
+ get '/validation/required' do
114
+ param :arg, String, required: true
115
+ params.to_json
116
+ end
117
+
118
+ get '/validation/blank/string' do
119
+ param :arg, String, blank: false
120
+ end
121
+
122
+ get '/validation/blank/array' do
123
+ param :arg, Array, blank: false
124
+ end
125
+
126
+ get '/validation/blank/hash' do
127
+ param :arg, Hash, blank: false
128
+ end
129
+
130
+ get '/validation/blank/other' do
131
+ param :arg, Class, blank: false
132
+ end
133
+
134
+ get '/validation/nonblank/string' do
135
+ param :arg, String, blank: true
136
+ end
137
+
138
+ get '/validation/format/9000' do
139
+ param :arg, Integer, format: /9000/
140
+ params.to_json
141
+ end
142
+
143
+ get '/validation/format/hello' do
144
+ param :arg, String, format: /hello/
145
+ params.to_json
146
+ end
147
+
148
+ get '/validation/is' do
149
+ param :arg, String, is: 'foo'
150
+ params.to_json
151
+ end
152
+
153
+ get '/validation/in' do
154
+ param :arg, String, in: ['ASC', 'DESC']
155
+ params.to_json
156
+ end
157
+
158
+ get '/validation/in/array' do
159
+ param :arg, Array, in: ['ASC', 'DESC']
160
+ params.to_json
161
+ end
162
+
163
+ get '/validation/within' do
164
+ param :arg, Integer, within: 1..10
165
+ params.to_json
166
+ end
167
+
168
+ get '/validation/within/array' do
169
+ param :arg, Array, within: 1..10, transform: ->(a) { a.map(&:to_i) }
170
+ params.to_json
171
+ end
172
+
173
+ get '/validation/range' do
174
+ param :arg, Integer, range: 1..10
175
+ params.to_json
176
+ end
177
+
178
+ get '/validation/range/array' do
179
+ param :arg, Array, range: 1..10, transform: ->(a) { a.map(&:to_i) }
180
+ params.to_json
181
+ end
182
+
183
+ get '/validation/min' do
184
+ param :arg, Integer, min: 12
185
+ params.to_json
186
+ end
187
+
188
+ get '/validation/max' do
189
+ param :arg, Integer, max: 20
190
+ params.to_json
191
+ end
192
+
193
+ get '/validation/min_length' do
194
+ param :arg, String, min_length: 5
195
+ params.to_json
196
+ end
197
+
198
+ get '/validation/max_length' do
199
+ param :arg, String, max_length: 10
200
+ params.to_json
201
+ end
202
+
203
+ get '/one_of/1' do
204
+ param :a, String
205
+ param :b, String
206
+ param :c, String
207
+
208
+ one_of :a
209
+
210
+ {
211
+ message: 'OK'
212
+ }.to_json
213
+ end
214
+
215
+ get '/one_of/2' do
216
+ param :a, String
217
+ param :b, String
218
+ param :c, String
219
+
220
+ one_of :a, :b
221
+
222
+ {
223
+ message: 'OK'
224
+ }.to_json
225
+ end
226
+
227
+ get '/one_of/3' do
228
+ param :a, String
229
+ param :b, String
230
+ param :c, String
231
+
232
+ one_of :a, :b, :c
233
+
234
+ {
235
+ message: 'OK'
236
+ }.to_json
237
+ end
238
+
239
+ get '/any_of' do
240
+ param :a, String
241
+ param :b, String
242
+ param :c, String
243
+
244
+ any_of :a, :b, :c
245
+
246
+ {
247
+ message: 'OK'
248
+ }.to_json
249
+ end
250
+
251
+ get '/raise/validation/required' do
252
+ param :arg, String, required: true, raise: true
253
+ params.to_json
254
+ end
255
+
256
+ get '/raise/one_of/3' do
257
+ param :a, String
258
+ param :b, String
259
+ param :c, String
260
+
261
+ one_of :a, :b, :c, raise: true
262
+
263
+ {
264
+ message: 'OK'
265
+ }.to_json
266
+ end
267
+
268
+ get '/raise/any_of' do
269
+ param :a, String
270
+ param :b, String
271
+ param :c, String
272
+
273
+ any_of :a, :b, :c, raise: true
274
+
275
+ {
276
+ message: 'OK'
277
+ }.to_json
278
+ end
279
+
280
+ get '/validation/hash/nested_values' do
281
+ param :parent, Hash do
282
+ param :required_child, Integer, :required => true
283
+ param :optional_child, String
284
+ param :nested_child, Hash do
285
+ param :required_sub_child, String, :required => true
286
+ param :optional_sub_child, Integer
287
+ end
288
+ param :default_child, Boolean, :default => true
289
+ end
290
+
291
+ {
292
+ message: 'OK'
293
+ }.to_json
294
+ end
295
+
296
+ get '/validation/hash/bad_nested_values' do
297
+ param :parent, String do
298
+ param :child, String
299
+ end
300
+ end
301
+
302
+ get '/one_of/nested' do
303
+ param :parent, Hash do
304
+ param :a, String
305
+ param :b, String
306
+ param :c, String
307
+
308
+ one_of :a, :b, :c
309
+ end
310
+
311
+ {
312
+ message: 'OK'
313
+ }.to_json
314
+ end
315
+
316
+ get '/any_of/nested' do
317
+ param :parent, Hash do
318
+ param :a, String
319
+ param :b, String
320
+ param :c, String
321
+
322
+ any_of :a, :b, :c
323
+ end
324
+
325
+ {
326
+ message: 'OK'
327
+ }.to_json
328
+ end
329
+
330
+ get '/all_or_none_of' do
331
+ param :a, String
332
+ param :b, String
333
+ param :c, String
334
+
335
+ all_or_none_of :a, :b, :c
336
+
337
+ {
338
+ message: 'OK'
339
+ }.to_json
340
+ end
341
+
342
+ get '/custommessage' do
343
+ param :a, Integer, within: 1..10, required: true,
344
+ message: "'a' must be less than 10"
345
+ end
346
+ end