sinatra-param2 1.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 (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