grape 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of grape might be problematic. Click here for more details.

Files changed (79) hide show
  1. checksums.yaml +9 -9
  2. data/.gitignore +7 -0
  3. data/.rubocop.yml +5 -0
  4. data/.travis.yml +3 -3
  5. data/CHANGELOG.md +42 -3
  6. data/CONTRIBUTING.md +118 -0
  7. data/Gemfile +4 -4
  8. data/README.md +312 -52
  9. data/Rakefile +6 -1
  10. data/UPGRADING.md +124 -0
  11. data/lib/grape.rb +2 -0
  12. data/lib/grape/api.rb +95 -44
  13. data/lib/grape/cookies.rb +0 -2
  14. data/lib/grape/endpoint.rb +63 -39
  15. data/lib/grape/error_formatter/base.rb +0 -3
  16. data/lib/grape/error_formatter/json.rb +0 -2
  17. data/lib/grape/error_formatter/txt.rb +0 -2
  18. data/lib/grape/error_formatter/xml.rb +0 -2
  19. data/lib/grape/exceptions/base.rb +0 -2
  20. data/lib/grape/exceptions/incompatible_option_values.rb +0 -3
  21. data/lib/grape/exceptions/invalid_formatter.rb +0 -3
  22. data/lib/grape/exceptions/invalid_versioner_option.rb +0 -4
  23. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +0 -2
  24. data/lib/grape/exceptions/missing_mime_type.rb +0 -4
  25. data/lib/grape/exceptions/missing_option.rb +0 -3
  26. data/lib/grape/exceptions/missing_vendor_option.rb +0 -3
  27. data/lib/grape/exceptions/unknown_options.rb +0 -4
  28. data/lib/grape/exceptions/unknown_validator.rb +0 -2
  29. data/lib/grape/exceptions/validation_errors.rb +6 -5
  30. data/lib/grape/formatter/base.rb +0 -3
  31. data/lib/grape/formatter/json.rb +0 -2
  32. data/lib/grape/formatter/serializable_hash.rb +15 -16
  33. data/lib/grape/formatter/txt.rb +0 -2
  34. data/lib/grape/formatter/xml.rb +0 -2
  35. data/lib/grape/http/request.rb +2 -4
  36. data/lib/grape/locale/en.yml +1 -1
  37. data/lib/grape/middleware/auth/oauth2.rb +15 -6
  38. data/lib/grape/middleware/base.rb +7 -7
  39. data/lib/grape/middleware/error.rb +11 -6
  40. data/lib/grape/middleware/formatter.rb +80 -78
  41. data/lib/grape/middleware/globals.rb +13 -0
  42. data/lib/grape/middleware/versioner/accept_version_header.rb +0 -2
  43. data/lib/grape/middleware/versioner/header.rb +5 -3
  44. data/lib/grape/middleware/versioner/param.rb +2 -4
  45. data/lib/grape/middleware/versioner/path.rb +3 -4
  46. data/lib/grape/namespace.rb +0 -1
  47. data/lib/grape/parser/base.rb +0 -3
  48. data/lib/grape/parser/json.rb +0 -2
  49. data/lib/grape/parser/xml.rb +0 -2
  50. data/lib/grape/path.rb +1 -3
  51. data/lib/grape/route.rb +0 -3
  52. data/lib/grape/util/hash_stack.rb +1 -1
  53. data/lib/grape/validations.rb +72 -22
  54. data/lib/grape/validations/coerce.rb +5 -4
  55. data/lib/grape/validations/default.rb +5 -3
  56. data/lib/grape/validations/presence.rb +1 -1
  57. data/lib/grape/validations/regexp.rb +0 -2
  58. data/lib/grape/validations/values.rb +2 -1
  59. data/lib/grape/version.rb +1 -1
  60. data/spec/grape/api_spec.rb +385 -96
  61. data/spec/grape/endpoint_spec.rb +162 -15
  62. data/spec/grape/entity_spec.rb +25 -0
  63. data/spec/grape/exceptions/validation_errors_spec.rb +19 -0
  64. data/spec/grape/middleware/auth/oauth2_spec.rb +60 -15
  65. data/spec/grape/middleware/base_spec.rb +3 -8
  66. data/spec/grape/middleware/error_spec.rb +2 -2
  67. data/spec/grape/middleware/exception_spec.rb +4 -4
  68. data/spec/grape/middleware/formatter_spec.rb +7 -4
  69. data/spec/grape/middleware/versioner/param_spec.rb +8 -7
  70. data/spec/grape/path_spec.rb +24 -14
  71. data/spec/grape/util/hash_stack_spec.rb +8 -8
  72. data/spec/grape/validations/coerce_spec.rb +75 -33
  73. data/spec/grape/validations/default_spec.rb +57 -0
  74. data/spec/grape/validations/presence_spec.rb +13 -11
  75. data/spec/grape/validations/values_spec.rb +76 -2
  76. data/spec/grape/validations_spec.rb +443 -20
  77. data/spec/spec_helper.rb +2 -2
  78. data/spec/support/content_type_helpers.rb +11 -0
  79. metadata +9 -38
@@ -13,11 +13,6 @@ describe Grape::Middleware::Base do
13
13
  subject.app.should == blank_app
14
14
  end
15
15
 
16
- it 'is able to access the request' do
17
- subject.call({})
18
- subject.request.should be_kind_of(Rack::Request)
19
- end
20
-
21
16
  it 'calls through to the app' do
22
17
  subject.call({}).should == [200, {}, 'Hi there.']
23
18
  end
@@ -41,7 +36,7 @@ describe Grape::Middleware::Base do
41
36
 
42
37
  context 'options' do
43
38
  it 'persists options passed at initialization' do
44
- Grape::Middleware::Base.new(blank_app, abc: true).options[:abc].should be_true
39
+ Grape::Middleware::Base.new(blank_app, abc: true).options[:abc].should be true
45
40
  end
46
41
 
47
42
  context 'defaults' do
@@ -52,11 +47,11 @@ describe Grape::Middleware::Base do
52
47
  end
53
48
 
54
49
  it 'persists the default options' do
55
- ExampleWare.new(blank_app).options[:monkey].should be_true
50
+ ExampleWare.new(blank_app).options[:monkey].should be true
56
51
  end
57
52
 
58
53
  it 'overrides default options when provided' do
59
- ExampleWare.new(blank_app, monkey: false).options[:monkey].should be_false
54
+ ExampleWare.new(blank_app, monkey: false).options[:monkey].should be false
60
55
  end
61
56
  end
62
57
  end
@@ -31,10 +31,10 @@ describe Grape::Middleware::Error do
31
31
  last_response.body.should == 'Awesome stuff.'
32
32
  end
33
33
 
34
- it 'defaults to a 403 status' do
34
+ it 'defaults to a 500 status' do
35
35
  ErrApp.error = {}
36
36
  get '/'
37
- last_response.status.should == 403
37
+ last_response.status.should == 500
38
38
  end
39
39
 
40
40
  it 'has a default message' do
@@ -15,7 +15,7 @@ describe Grape::Middleware::Error do
15
15
  # raises a hash error
16
16
  class ErrorHashApp
17
17
  class << self
18
- def error!(message, status = 403)
18
+ def error!(message, status)
19
19
  throw :error, message: { error: message, detail: "missing widget" }, status: status
20
20
  end
21
21
 
@@ -28,7 +28,7 @@ describe Grape::Middleware::Error do
28
28
  # raises an error!
29
29
  class AccessDeniedApp
30
30
  class << self
31
- def error!(message, status = 403)
31
+ def error!(message, status)
32
32
  throw :error, message: message, status: status
33
33
  end
34
34
 
@@ -70,13 +70,13 @@ describe Grape::Middleware::Error do
70
70
  last_response.body.should == "rain!"
71
71
  end
72
72
 
73
- it 'defaults to a 403 status' do
73
+ it 'defaults to a 500 status' do
74
74
  @app ||= Rack::Builder.app do
75
75
  use Grape::Middleware::Error, rescue_all: true
76
76
  run ExceptionApp
77
77
  end
78
78
  get '/'
79
- last_response.status.should == 403
79
+ last_response.status.should == 500
80
80
  end
81
81
 
82
82
  it 'is possible to specify a different default status code' do
@@ -72,9 +72,12 @@ describe Grape::Middleware::Formatter do
72
72
 
73
73
  context 'detection' do
74
74
 
75
- it 'uses the extension if one is provided' do
75
+ it 'uses the xml extension if one is provided' do
76
76
  subject.call('PATH_INFO' => '/info.xml')
77
77
  subject.env['api.format'].should == :xml
78
+ end
79
+
80
+ it 'uses the json extension if one is provided' do
78
81
  subject.call('PATH_INFO' => '/info.json')
79
82
  subject.env['api.format'].should == :json
80
83
  end
@@ -195,7 +198,7 @@ describe Grape::Middleware::Formatter do
195
198
  'rack.input' => io,
196
199
  'CONTENT_LENGTH' => io.length
197
200
  )
198
- subject.env['rack.request.form_hash']['is_boolean'].should be_true
201
+ subject.env['rack.request.form_hash']['is_boolean'].should be true
199
202
  subject.env['rack.request.form_hash']['string'].should == 'thing'
200
203
  end
201
204
  end
@@ -209,7 +212,7 @@ describe Grape::Middleware::Formatter do
209
212
  'rack.input' => io,
210
213
  'HTTP_TRANSFER_ENCODING' => 'chunked'
211
214
  )
212
- subject.env['rack.request.form_hash']['is_boolean'].should be_true
215
+ subject.env['rack.request.form_hash']['is_boolean'].should be true
213
216
  subject.env['rack.request.form_hash']['string'].should == 'thing'
214
217
  end
215
218
  it "rewinds IO" do
@@ -222,7 +225,7 @@ describe Grape::Middleware::Formatter do
222
225
  'rack.input' => io,
223
226
  'HTTP_TRANSFER_ENCODING' => 'chunked'
224
227
  )
225
- subject.env['rack.request.form_hash']['is_boolean'].should be_true
228
+ subject.env['rack.request.form_hash']['is_boolean'].should be true
226
229
  subject.env['rack.request.form_hash']['string'].should == 'thing'
227
230
  end
228
231
  it 'parses the body from an xml #{method} and copies values into rack.request.from_hash' do
@@ -6,12 +6,13 @@ describe Grape::Middleware::Versioner::Param do
6
6
  subject { Grape::Middleware::Versioner::Param.new(app, @options || {}) }
7
7
 
8
8
  it 'sets the API version based on the default param (apiver)' do
9
- env = Rack::MockRequest.env_for("/awesome", { params: { "apiver" => "v1" } })
9
+ env = Rack::MockRequest.env_for("/awesome", params: { "apiver" => "v1" })
10
10
  subject.call(env)[1]["api.version"].should == 'v1'
11
11
  end
12
12
 
13
13
  it 'cuts (only) the version out of the params', focus: true do
14
- env = Rack::MockRequest.env_for("/awesome", { params: { "apiver" => "v1", "other_param" => "5" } })
14
+ env = Rack::MockRequest.env_for("/awesome", params: { "apiver" => "v1", "other_param" => "5" })
15
+ env['rack.request.query_hash'] = Rack::Utils.parse_nested_query(env['QUERY_STRING'])
15
16
  subject.call(env)[1]['rack.request.query_hash']["apiver"].should be_nil
16
17
  subject.call(env)[1]['rack.request.query_hash']["other_param"].should == "5"
17
18
  end
@@ -24,11 +25,11 @@ describe Grape::Middleware::Versioner::Param do
24
25
  context 'with specified parameter name' do
25
26
  before { @options = { parameter: 'v' } }
26
27
  it 'sets the API version based on the custom parameter name' do
27
- env = Rack::MockRequest.env_for("/awesome", { params: { "v" => "v1" } })
28
+ env = Rack::MockRequest.env_for("/awesome", params: { "v" => "v1" })
28
29
  subject.call(env)[1]["api.version"].should == "v1"
29
30
  end
30
31
  it 'does not set the API version based on the default param' do
31
- env = Rack::MockRequest.env_for("/awesome", { params: { "apiver" => "v1" } })
32
+ env = Rack::MockRequest.env_for("/awesome", params: { "apiver" => "v1" })
32
33
  subject.call(env)[1]["api.version"].should be_nil
33
34
  end
34
35
  end
@@ -36,11 +37,11 @@ describe Grape::Middleware::Versioner::Param do
36
37
  context 'with specified versions' do
37
38
  before { @options = { versions: ['v1', 'v2'] } }
38
39
  it 'throws an error if a non-allowed version is specified' do
39
- env = Rack::MockRequest.env_for("/awesome", { params: { "apiver" => "v3" } })
40
+ env = Rack::MockRequest.env_for("/awesome", params: { "apiver" => "v3" })
40
41
  catch(:error) { subject.call(env) }[:status].should == 404
41
42
  end
42
43
  it 'allows versions that have been specified' do
43
- env = Rack::MockRequest.env_for("/awesome", { params: { "apiver" => "v1" } })
44
+ env = Rack::MockRequest.env_for("/awesome", params: { "apiver" => "v1" })
44
45
  subject.call(env)[1]["api.version"].should == 'v1'
45
46
  end
46
47
  end
@@ -50,7 +51,7 @@ describe Grape::Middleware::Versioner::Param do
50
51
  versions: ['v1'],
51
52
  version_options: { using: :header }
52
53
  }
53
- env = Rack::MockRequest.env_for("/awesome", { params: {} })
54
+ env = Rack::MockRequest.env_for("/awesome", params: {})
54
55
  subject.call(env).first.should == 200
55
56
  end
56
57
 
@@ -57,25 +57,29 @@ module Grape
57
57
  describe "#uses_path_versioning?" do
58
58
  it "is false when the version setting is nil" do
59
59
  path = Path.new(anything, anything, version: nil)
60
- expect(path.uses_path_versioning?).to be_false
60
+ expect(path.uses_path_versioning?).to be false
61
61
  end
62
62
 
63
63
  it "is false when the version option is header" do
64
- path = Path.new(anything, anything, {
64
+ path = Path.new(
65
+ anything,
66
+ anything,
65
67
  version: 'v1',
66
68
  version_options: { using: :header }
67
- })
69
+ )
68
70
 
69
- expect(path.uses_path_versioning?).to be_false
71
+ expect(path.uses_path_versioning?).to be false
70
72
  end
71
73
 
72
74
  it "is true when the version option is path" do
73
- path = Path.new(anything, anything, {
75
+ path = Path.new(
76
+ anything,
77
+ anything,
74
78
  version: 'v1',
75
79
  version_options: { using: :path }
76
- })
80
+ )
77
81
 
78
- expect(path.uses_path_versioning?).to be_true
82
+ expect(path.uses_path_versioning?).to be true
79
83
  end
80
84
  end
81
85
 
@@ -126,7 +130,7 @@ module Grape
126
130
  describe "#path" do
127
131
  context "mount_path" do
128
132
  it "is not included when it is nil" do
129
- path = Path.new(nil, nil, { mount_path: '/foo/bar' })
133
+ path = Path.new(nil, nil, mount_path: '/foo/bar')
130
134
  expect(path.path).to eql '/foo/bar'
131
135
  end
132
136
 
@@ -143,29 +147,35 @@ module Grape
143
147
  end
144
148
 
145
149
  it "is included after the mount path" do
146
- path = Path.new(nil, nil, {
150
+ path = Path.new(
151
+ nil,
152
+ nil,
147
153
  mount_path: '/foo',
148
154
  root_prefix: '/hello'
149
- })
155
+ )
150
156
 
151
157
  expect(path.path).to eql('/foo/hello')
152
158
  end
153
159
  end
154
160
 
155
161
  it "uses the namespace after the mount path and root prefix" do
156
- path = Path.new(nil, 'namespace', {
162
+ path = Path.new(
163
+ nil,
164
+ 'namespace',
157
165
  mount_path: '/foo',
158
166
  root_prefix: '/hello'
159
- })
167
+ )
160
168
 
161
169
  expect(path.path).to eql('/foo/hello/namespace')
162
170
  end
163
171
 
164
172
  it "uses the raw path after the namespace" do
165
- path = Path.new('raw_path', 'namespace', {
173
+ path = Path.new(
174
+ 'raw_path',
175
+ 'namespace',
166
176
  mount_path: '/foo',
167
177
  root_prefix: '/hello'
168
- })
178
+ )
169
179
 
170
180
  expect(path.path).to eql('/foo/hello/namespace/raw_path')
171
181
  end
@@ -25,31 +25,31 @@ describe Grape::Util::HashStack do
25
25
  describe '#imbue' do
26
26
  it 'pushes a new value onto the end of an array' do
27
27
  subject[:abc] = []
28
- subject.imbue(:abc, [123])
29
- subject.imbue(:abc, [456])
28
+ subject.imbue :abc, [123]
29
+ subject.imbue :abc, [456]
30
30
  subject[:abc].should == [123, 456]
31
31
  end
32
32
 
33
33
  it 'merges a hash that is passed' do
34
34
  subject[:abc] = { foo: 'bar' }
35
- subject.imbue(:abc, { baz: 'wich' })
35
+ subject.imbue :abc, baz: 'wich'
36
36
  subject[:abc].should == { foo: 'bar', baz: 'wich' }
37
37
  end
38
38
 
39
39
  it 'sets the value if not a hash or array' do
40
- subject.imbue(:abc, 123)
40
+ subject.imbue :abc, 123
41
41
  subject[:abc].should == 123
42
42
  end
43
43
 
44
44
  it 'is able to imbue an array without explicit setting' do
45
- subject.imbue(:arr, [1])
46
- subject.imbue(:arr, [2])
45
+ subject.imbue :arr, [1]
46
+ subject.imbue :arr, [2]
47
47
  subject[:arr].should == [1, 2]
48
48
  end
49
49
 
50
50
  it 'is able to imbue a hash without explicit setting' do
51
- subject.imbue(:hash, foo: 'bar')
52
- subject.imbue(:hash, baz: 'wich')
51
+ subject.imbue :hash, foo: 'bar'
52
+ subject.imbue :hash, baz: 'wich'
53
53
  subject[:hash].should == { foo: 'bar', baz: 'wich' }
54
54
  end
55
55
  end
@@ -22,8 +22,12 @@ describe Grape::Validations::CoerceValidator do
22
22
  I18n.load_path << File.expand_path('../zh-CN.yml', __FILE__)
23
23
  I18n.reload!
24
24
  I18n.locale = 'zh-CN'.to_sym
25
- subject.params { requires :age, type: Integer }
26
- subject.get '/single' do 'int works'; end
25
+ subject.params do
26
+ requires :age, type: Integer
27
+ end
28
+ subject.get '/single' do
29
+ 'int works'
30
+ end
27
31
 
28
32
  get '/single', age: '43a'
29
33
  last_response.status.should == 400
@@ -32,8 +36,12 @@ describe Grape::Validations::CoerceValidator do
32
36
 
33
37
  it 'gives an english fallback error when default locale message is blank' do
34
38
  I18n.locale = 'pt-BR'.to_sym
35
- subject.params { requires :age, type: Integer }
36
- subject.get '/single' do 'int works'; end
39
+ subject.params do
40
+ requires :age, type: Integer
41
+ end
42
+ subject.get '/single' do
43
+ 'int works'
44
+ end
37
45
 
38
46
  get '/single', age: '43a'
39
47
  last_response.status.should == 400
@@ -43,8 +51,12 @@ describe Grape::Validations::CoerceValidator do
43
51
  end
44
52
 
45
53
  it 'error on malformed input' do
46
- subject.params { requires :int, type: Integer }
47
- subject.get '/single' do 'int works'; end
54
+ subject.params do
55
+ requires :int, type: Integer
56
+ end
57
+ subject.get '/single' do
58
+ 'int works'
59
+ end
48
60
 
49
61
  get '/single', int: '43a'
50
62
  last_response.status.should == 400
@@ -56,14 +68,18 @@ describe Grape::Validations::CoerceValidator do
56
68
  end
57
69
 
58
70
  it 'error on malformed input (Array)' do
59
- subject.params { requires :ids, type: Array[Integer] }
60
- subject.get '/array' do 'array int works'; end
71
+ subject.params do
72
+ requires :ids, type: Array[Integer]
73
+ end
74
+ subject.get '/array' do
75
+ 'array int works'
76
+ end
61
77
 
62
- get 'array', { ids: ['1', '2', 'az'] }
78
+ get 'array', ids: ['1', '2', 'az']
63
79
  last_response.status.should == 400
64
80
  last_response.body.should == 'ids is invalid'
65
81
 
66
- get 'array', { ids: ['1', '2', '890'] }
82
+ get 'array', ids: ['1', '2', '890']
67
83
  last_response.status.should == 200
68
84
  last_response.body.should == 'array int works'
69
85
  end
@@ -78,8 +94,12 @@ describe Grape::Validations::CoerceValidator do
78
94
  end
79
95
 
80
96
  it 'error on malformed input for complex objects' do
81
- subject.params { requires :user, type: CoerceValidatorSpec::User }
82
- subject.get '/user' do 'complex works'; end
97
+ subject.params do
98
+ requires :user, type: CoerceValidatorSpec::User
99
+ end
100
+ subject.get '/user' do
101
+ 'complex works'
102
+ end
83
103
 
84
104
  get '/user', user: "32"
85
105
  last_response.status.should == 400
@@ -93,71 +113,93 @@ describe Grape::Validations::CoerceValidator do
93
113
 
94
114
  context 'coerces' do
95
115
  it 'Integer' do
96
- subject.params { requires :int, coerce: Integer }
97
- subject.get '/int' do params[:int].class; end
116
+ subject.params do
117
+ requires :int, coerce: Integer
118
+ end
119
+ subject.get '/int' do
120
+ params[:int].class
121
+ end
98
122
 
99
- get '/int', { int: "45" }
123
+ get '/int', int: "45"
100
124
  last_response.status.should == 200
101
125
  last_response.body.should == 'Fixnum'
102
126
  end
103
127
 
104
128
  it 'Array of Integers' do
105
- subject.params { requires :arry, coerce: Array[Integer] }
106
- subject.get '/array' do params[:arry][0].class; end
129
+ subject.params do
130
+ requires :arry, coerce: Array[Integer]
131
+ end
132
+ subject.get '/array' do
133
+ params[:arry][0].class
134
+ end
107
135
 
108
- get '/array', { arry: ['1', '2', '3'] }
136
+ get '/array', arry: ['1', '2', '3']
109
137
  last_response.status.should == 200
110
138
  last_response.body.should == 'Fixnum'
111
139
  end
112
140
 
113
141
  it 'Array of Bools' do
114
- subject.params { requires :arry, coerce: Array[Virtus::Attribute::Boolean] }
115
- subject.get '/array' do params[:arry][0].class; end
142
+ subject.params do
143
+ requires :arry, coerce: Array[Virtus::Attribute::Boolean]
144
+ end
145
+ subject.get '/array' do
146
+ params[:arry][0].class
147
+ end
116
148
 
117
- get 'array', { arry: [1, 0] }
149
+ get 'array', arry: [1, 0]
118
150
  last_response.status.should == 200
119
151
  last_response.body.should == 'TrueClass'
120
152
  end
121
153
 
122
154
  it 'Bool' do
123
- subject.params { requires :bool, coerce: Virtus::Attribute::Boolean }
124
- subject.get '/bool' do params[:bool].class; end
155
+ subject.params do
156
+ requires :bool, coerce: Virtus::Attribute::Boolean
157
+ end
158
+ subject.get '/bool' do
159
+ params[:bool].class
160
+ end
125
161
 
126
- get '/bool', { bool: 1 }
162
+ get '/bool', bool: 1
127
163
  last_response.status.should == 200
128
164
  last_response.body.should == 'TrueClass'
129
165
 
130
- get '/bool', { bool: 0 }
166
+ get '/bool', bool: 0
131
167
  last_response.status.should == 200
132
168
  last_response.body.should == 'FalseClass'
133
169
 
134
- get '/bool', { bool: 'false' }
170
+ get '/bool', bool: 'false'
135
171
  last_response.status.should == 200
136
172
  last_response.body.should == 'FalseClass'
137
173
 
138
- get '/bool', { bool: 'true' }
174
+ get '/bool', bool: 'true'
139
175
  last_response.status.should == 200
140
176
  last_response.body.should == 'TrueClass'
141
177
  end
142
178
 
143
179
  it 'file' do
144
- subject.params { requires :file, coerce: Rack::Multipart::UploadedFile }
145
- subject.post '/upload' do params[:file].filename; end
180
+ subject.params do
181
+ requires :file, coerce: Rack::Multipart::UploadedFile
182
+ end
183
+ subject.post '/upload' do
184
+ params[:file].filename
185
+ end
146
186
 
147
- post '/upload', { file: Rack::Test::UploadedFile.new(__FILE__) }
187
+ post '/upload', file: Rack::Test::UploadedFile.new(__FILE__)
148
188
  last_response.status.should == 201
149
189
  last_response.body.should == File.basename(__FILE__).to_s
150
190
  end
151
191
 
152
192
  it 'Nests integers' do
153
193
  subject.params do
154
- group :integers do
194
+ requires :integers, type: Hash do
155
195
  requires :int, coerce: Integer
156
196
  end
157
197
  end
158
- subject.get '/int' do params[:integers][:int].class; end
198
+ subject.get '/int' do
199
+ params[:integers][:int].class
200
+ end
159
201
 
160
- get '/int', { integers: { int: "45" } }
202
+ get '/int', integers: { int: "45" }
161
203
  last_response.status.should == 200
162
204
  last_response.body.should == 'Fixnum'
163
205
  end