grape 0.2.6 → 0.3.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.
- data/{CHANGELOG.markdown → CHANGELOG.md} +21 -1
- data/Gemfile +1 -0
- data/{README.markdown → README.md} +178 -125
- data/grape.gemspec +1 -1
- data/lib/grape.rb +25 -3
- data/lib/grape/api.rb +43 -20
- data/lib/grape/endpoint.rb +32 -13
- data/lib/grape/exceptions/base.rb +50 -1
- data/lib/grape/exceptions/invalid_formatter.rb +13 -0
- data/lib/grape/exceptions/invalid_versioner_option.rb +14 -0
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +15 -0
- data/lib/grape/exceptions/missing_mime_type.rb +14 -0
- data/lib/grape/exceptions/missing_option.rb +13 -0
- data/lib/grape/exceptions/missing_vendor_option.rb +13 -0
- data/lib/grape/exceptions/unknown_options.rb +14 -0
- data/lib/grape/exceptions/unknown_validator.rb +12 -0
- data/lib/grape/exceptions/{validation_error.rb → validation.rb} +3 -1
- data/lib/grape/formatter/xml.rb +2 -1
- data/lib/grape/locale/en.yml +20 -0
- data/lib/grape/middleware/base.rb +0 -5
- data/lib/grape/middleware/error.rb +1 -2
- data/lib/grape/middleware/formatter.rb +9 -5
- data/lib/grape/middleware/versioner.rb +1 -1
- data/lib/grape/middleware/versioner/header.rb +16 -6
- data/lib/grape/middleware/versioner/param.rb +1 -1
- data/lib/grape/middleware/versioner/path.rb +1 -1
- data/lib/grape/util/content_types.rb +0 -2
- data/lib/grape/validations.rb +7 -14
- data/lib/grape/validations/coerce.rb +2 -1
- data/lib/grape/validations/presence.rb +2 -1
- data/lib/grape/validations/regexp.rb +2 -1
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api_spec.rb +150 -5
- data/spec/grape/endpoint_spec.rb +51 -157
- data/spec/grape/entity_spec.rb +142 -520
- data/spec/grape/exceptions/invalid_formatter_spec.rb +18 -0
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +18 -0
- data/spec/grape/exceptions/missing_mime_type_spec.rb +24 -0
- data/spec/grape/exceptions/missing_option_spec.rb +18 -0
- data/spec/grape/exceptions/unknown_options_spec.rb +18 -0
- data/spec/grape/exceptions/unknown_validator_spec.rb +18 -0
- data/spec/grape/middleware/formatter_spec.rb +40 -34
- data/spec/grape/middleware/versioner/header_spec.rb +78 -20
- data/spec/grape/middleware/versioner/path_spec.rb +12 -8
- data/spec/grape/validations/coerce_spec.rb +1 -0
- data/spec/grape/validations/presence_spec.rb +8 -8
- data/spec/grape/validations_spec.rb +26 -3
- data/spec/spec_helper.rb +3 -6
- metadata +44 -9
- data/lib/grape/entity.rb +0 -386
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Grape::Exceptions::InvalidFormatter do
|
5
|
+
describe "#message" do
|
6
|
+
let(:error) do
|
7
|
+
described_class.new(String, 'xml')
|
8
|
+
end
|
9
|
+
|
10
|
+
it "contains the problem in the message" do
|
11
|
+
error.message.should include(
|
12
|
+
"cannot convert String to xml"
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Grape::Exceptions::InvalidVersionerOption do
|
5
|
+
describe "#message" do
|
6
|
+
let(:error) do
|
7
|
+
described_class.new("headers")
|
8
|
+
end
|
9
|
+
|
10
|
+
it "contains the problem in the message" do
|
11
|
+
error.message.should include(
|
12
|
+
"Unknown :using for versioner: headers"
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
describe Grape::Exceptions::MissingMimeType do
|
4
|
+
describe "#message" do
|
5
|
+
|
6
|
+
let(:error) do
|
7
|
+
described_class.new("new_json")
|
8
|
+
end
|
9
|
+
|
10
|
+
it "contains the problem in the message" do
|
11
|
+
error.message.should include(
|
12
|
+
"missing mime type for new_json"
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "contains the resolution in the message" do
|
17
|
+
error.message.should include(
|
18
|
+
"or add your own with content_type :new_json, 'application/new_json' "
|
19
|
+
)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Grape::Exceptions::MissingOption do
|
5
|
+
describe "#message" do
|
6
|
+
let(:error) do
|
7
|
+
described_class.new(:path)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "contains the problem in the message" do
|
11
|
+
error.message.should include(
|
12
|
+
"You must specify :path options."
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Grape::Exceptions::UnknownOptions do
|
5
|
+
describe "#message" do
|
6
|
+
let(:error) do
|
7
|
+
described_class.new([:a, :b])
|
8
|
+
end
|
9
|
+
|
10
|
+
it "contains the problem in the message" do
|
11
|
+
error.message.should include(
|
12
|
+
"unknown options: "
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Grape::Exceptions::UnknownValidator do
|
5
|
+
describe "#message" do
|
6
|
+
let(:error) do
|
7
|
+
described_class.new('gt_10')
|
8
|
+
end
|
9
|
+
|
10
|
+
it "contains the problem in the message" do
|
11
|
+
error.message.should include(
|
12
|
+
"unknown validator: gt_10"
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -4,7 +4,7 @@ describe Grape::Middleware::Formatter do
|
|
4
4
|
subject{ Grape::Middleware::Formatter.new(app) }
|
5
5
|
before{ subject.stub!(:dup).and_return(subject) }
|
6
6
|
|
7
|
-
let(:app){ lambda{|env| [200, {}, [@body]]} }
|
7
|
+
let(:app){ lambda{|env| [200, {}, [@body || { "foo" => "bar" }]]} }
|
8
8
|
|
9
9
|
context 'serialization' do
|
10
10
|
it 'looks at the bodies for possibly serializable data' do
|
@@ -37,6 +37,7 @@ describe Grape::Middleware::Formatter do
|
|
37
37
|
end
|
38
38
|
|
39
39
|
context 'detection' do
|
40
|
+
|
40
41
|
it 'uses the extension if one is provided' do
|
41
42
|
subject.call({'PATH_INFO' => '/info.xml'})
|
42
43
|
subject.env['api.format'].should == :xml
|
@@ -45,9 +46,9 @@ describe Grape::Middleware::Formatter do
|
|
45
46
|
end
|
46
47
|
|
47
48
|
it 'uses the format parameter if one is provided' do
|
48
|
-
subject.call({'PATH_INFO' => '/
|
49
|
+
subject.call({'PATH_INFO' => '/info','QUERY_STRING' => 'format=json'})
|
49
50
|
subject.env['api.format'].should == :json
|
50
|
-
subject.call({'PATH_INFO' => '/
|
51
|
+
subject.call({'PATH_INFO' => '/info','QUERY_STRING' => 'format=xml'})
|
51
52
|
subject.env['api.format'].should == :xml
|
52
53
|
end
|
53
54
|
|
@@ -148,43 +149,48 @@ describe Grape::Middleware::Formatter do
|
|
148
149
|
end
|
149
150
|
|
150
151
|
context 'input' do
|
151
|
-
[ "
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
152
|
+
[ "POST", "PATCH", "PUT" ].each do |method|
|
153
|
+
[ "application/json", "application/json; charset=utf-8" ].each do |content_type|
|
154
|
+
context content_type do
|
155
|
+
it 'parses the body from #{method} and copies values into rack.request.form_hash' do
|
156
|
+
io = StringIO.new('{"is_boolean":true,"string":"thing"}')
|
157
|
+
subject.call({
|
158
|
+
'PATH_INFO' => '/info',
|
159
|
+
'REQUEST_METHOD' => method,
|
160
|
+
'CONTENT_TYPE' => content_type,
|
161
|
+
'rack.input' => io,
|
162
|
+
'CONTENT_LENGTH' => io.length
|
163
|
+
})
|
164
|
+
subject.env['rack.request.form_hash']['is_boolean'].should be_true
|
165
|
+
subject.env['rack.request.form_hash']['string'].should == 'thing'
|
166
|
+
end
|
167
|
+
end
|
163
168
|
end
|
164
|
-
|
165
|
-
|
166
|
-
io = StringIO.new('<thing><name>Test</name></thing>')
|
167
|
-
subject.call({
|
168
|
-
'PATH_INFO' => '/info.xml',
|
169
|
-
'REQUEST_METHOD' => 'POST',
|
170
|
-
'CONTENT_TYPE' => 'application/xml',
|
171
|
-
'rack.input' => io,
|
172
|
-
'CONTENT_LENGTH' => io.length
|
173
|
-
})
|
174
|
-
subject.env['rack.request.form_hash']['thing']['name'].should == 'Test'
|
175
|
-
end
|
176
|
-
[ Rack::Request::FORM_DATA_MEDIA_TYPES, Rack::Request::PARSEABLE_DATA_MEDIA_TYPES ].flatten.each do |content_type|
|
177
|
-
it "ignores #{content_type}" do
|
178
|
-
io = StringIO.new('name=Other+Test+Thing')
|
169
|
+
it 'parses the body from an xml #{method} and copies values into rack.request.from_hash' do
|
170
|
+
io = StringIO.new('<thing><name>Test</name></thing>')
|
179
171
|
subject.call({
|
180
|
-
'PATH_INFO' => '/info',
|
181
|
-
'REQUEST_METHOD' =>
|
182
|
-
'CONTENT_TYPE' =>
|
172
|
+
'PATH_INFO' => '/info.xml',
|
173
|
+
'REQUEST_METHOD' => method,
|
174
|
+
'CONTENT_TYPE' => 'application/xml',
|
183
175
|
'rack.input' => io,
|
184
176
|
'CONTENT_LENGTH' => io.length
|
185
177
|
})
|
186
|
-
subject.env['rack.request.form_hash'].should
|
178
|
+
subject.env['rack.request.form_hash']['thing']['name'].should == 'Test'
|
179
|
+
end
|
180
|
+
[ Rack::Request::FORM_DATA_MEDIA_TYPES, Rack::Request::PARSEABLE_DATA_MEDIA_TYPES ].flatten.each do |content_type|
|
181
|
+
it "ignores #{content_type}" do
|
182
|
+
io = StringIO.new('name=Other+Test+Thing')
|
183
|
+
subject.call({
|
184
|
+
'PATH_INFO' => '/info',
|
185
|
+
'REQUEST_METHOD' => method,
|
186
|
+
'CONTENT_TYPE' => content_type,
|
187
|
+
'rack.input' => io,
|
188
|
+
'CONTENT_LENGTH' => io.length
|
189
|
+
})
|
190
|
+
subject.env['rack.request.form_hash'].should be_nil
|
191
|
+
end
|
187
192
|
end
|
188
193
|
end
|
189
194
|
end
|
195
|
+
|
190
196
|
end
|
@@ -49,21 +49,23 @@ describe Grape::Middleware::Versioner::Header do
|
|
49
49
|
status.should == 200
|
50
50
|
end
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
[ 'v1', :v1 ].each do |version|
|
53
|
+
context 'when version is set to #{version{' do
|
54
|
+
before do
|
55
|
+
@options[:versions] = [ version ]
|
56
|
+
end
|
56
57
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
58
|
+
it 'is set' do
|
59
|
+
status, _, env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1+json')
|
60
|
+
env['api.format'].should eql 'json'
|
61
|
+
status.should == 200
|
62
|
+
end
|
62
63
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
64
|
+
it 'is nil if not provided' do
|
65
|
+
status, _, env = subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1')
|
66
|
+
env['api.format'].should eql nil
|
67
|
+
status.should == 200
|
68
|
+
end
|
67
69
|
end
|
68
70
|
end
|
69
71
|
end
|
@@ -88,7 +90,7 @@ describe Grape::Middleware::Versioner::Header do
|
|
88
90
|
:error,
|
89
91
|
:status => 406,
|
90
92
|
:headers => {'X-Cascade' => 'pass'},
|
91
|
-
:message => 'API vendor or version not found'
|
93
|
+
:message => 'API vendor or version not found.'
|
92
94
|
)
|
93
95
|
end
|
94
96
|
|
@@ -116,7 +118,7 @@ describe Grape::Middleware::Versioner::Header do
|
|
116
118
|
:error,
|
117
119
|
:status => 406,
|
118
120
|
:headers => {'X-Cascade' => 'pass'},
|
119
|
-
:message => 'API vendor or version not found'
|
121
|
+
:message => 'API vendor or version not found.'
|
120
122
|
)
|
121
123
|
end
|
122
124
|
end
|
@@ -146,7 +148,7 @@ describe Grape::Middleware::Versioner::Header do
|
|
146
148
|
:error,
|
147
149
|
:status => 406,
|
148
150
|
:headers => {'X-Cascade' => 'pass'},
|
149
|
-
:message => 'API vendor or version not found'
|
151
|
+
:message => 'API vendor or version not found.'
|
150
152
|
)
|
151
153
|
end
|
152
154
|
end
|
@@ -175,7 +177,7 @@ describe Grape::Middleware::Versioner::Header do
|
|
175
177
|
:error,
|
176
178
|
:status => 406,
|
177
179
|
:headers => {'X-Cascade' => 'pass'},
|
178
|
-
:message => 'Accept header must be set'
|
180
|
+
:message => 'Accept header must be set.'
|
179
181
|
)
|
180
182
|
end
|
181
183
|
|
@@ -186,7 +188,7 @@ describe Grape::Middleware::Versioner::Header do
|
|
186
188
|
:error,
|
187
189
|
:status => 406,
|
188
190
|
:headers => {'X-Cascade' => 'pass'},
|
189
|
-
:message => 'Accept header must be set'
|
191
|
+
:message => 'Accept header must be set.'
|
190
192
|
)
|
191
193
|
end
|
192
194
|
|
@@ -197,7 +199,7 @@ describe Grape::Middleware::Versioner::Header do
|
|
197
199
|
:error,
|
198
200
|
:status => 406,
|
199
201
|
:headers => {'X-Cascade' => 'pass'},
|
200
|
-
:message => 'Accept header must not contain ranges ("*")'
|
202
|
+
:message => 'Accept header must not contain ranges ("*").'
|
201
203
|
)
|
202
204
|
end
|
203
205
|
|
@@ -208,7 +210,63 @@ describe Grape::Middleware::Versioner::Header do
|
|
208
210
|
:error,
|
209
211
|
:status => 406,
|
210
212
|
:headers => {'X-Cascade' => 'pass'},
|
211
|
-
:message => 'Accept header must not contain ranges ("*")'
|
213
|
+
:message => 'Accept header must not contain ranges ("*").'
|
214
|
+
)
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'succeeds if proper header is set' do
|
218
|
+
subject.call('HTTP_ACCEPT' => 'application/vnd.vendor-v1+json').first.should == 200
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
context 'when :strict and :cascade=>false are set' do
|
223
|
+
before do
|
224
|
+
@options[:versions] = ['v1']
|
225
|
+
@options[:version_options][:strict] = true
|
226
|
+
@options[:version_options][:cascade] = false
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'fails with 406 Not Acceptable if header is not set' do
|
230
|
+
expect {
|
231
|
+
env = subject.call({}).last
|
232
|
+
}.to throw_symbol(
|
233
|
+
:error,
|
234
|
+
:status => 406,
|
235
|
+
:headers => {},
|
236
|
+
:message => 'Accept header must be set.'
|
237
|
+
)
|
238
|
+
end
|
239
|
+
|
240
|
+
it 'fails with 406 Not Acceptable if header is empty' do
|
241
|
+
expect {
|
242
|
+
env = subject.call('HTTP_ACCEPT' => '').last
|
243
|
+
}.to throw_symbol(
|
244
|
+
:error,
|
245
|
+
:status => 406,
|
246
|
+
:headers => {},
|
247
|
+
:message => 'Accept header must be set.'
|
248
|
+
)
|
249
|
+
end
|
250
|
+
|
251
|
+
it 'fails with 406 Not Acceptable if type is a range' do
|
252
|
+
expect {
|
253
|
+
env = subject.call('HTTP_ACCEPT' => '*/*').last
|
254
|
+
}.to throw_symbol(
|
255
|
+
:error,
|
256
|
+
:status => 406,
|
257
|
+
:headers => {},
|
258
|
+
:message => 'Accept header must not contain ranges ("*").'
|
259
|
+
)
|
260
|
+
end
|
261
|
+
|
262
|
+
it 'fails with 406 Not Acceptable if subtype is a range' do
|
263
|
+
expect {
|
264
|
+
env = subject.call('HTTP_ACCEPT' => 'application/*').last
|
265
|
+
}.to throw_symbol(
|
266
|
+
:error,
|
267
|
+
:status => 406,
|
268
|
+
:headers => {},
|
269
|
+
:message => 'Accept header must not contain ranges ("*").'
|
212
270
|
)
|
213
271
|
end
|
214
272
|
|
@@ -17,7 +17,7 @@ describe Grape::Middleware::Versioner::Path do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
context 'with a pattern' do
|
20
|
-
before{ @options = {:pattern => /v./i} }
|
20
|
+
before { @options = {:pattern => /v./i} }
|
21
21
|
it 'sets the version if it matches' do
|
22
22
|
subject.call('PATH_INFO' => '/v1/awesome').last.should == 'v1'
|
23
23
|
end
|
@@ -27,14 +27,18 @@ describe Grape::Middleware::Versioner::Path do
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
30
|
+
[ [ 'v1', 'v2'], [ :v1, :v2 ], [ :v1, 'v2' ], [ 'v1', :v2 ] ].each do |versions|
|
31
|
+
context 'with specified versions as #{versions}' do
|
32
|
+
before { @options = { :versions => versions } }
|
33
|
+
|
34
|
+
it 'throws an error if a non-allowed version is specified' do
|
35
|
+
catch(:error){subject.call('PATH_INFO' => '/v3/awesome')}[:status].should == 404
|
36
|
+
end
|
35
37
|
|
36
|
-
|
37
|
-
|
38
|
+
it 'allows versions that have been specified' do
|
39
|
+
subject.call('PATH_INFO' => '/v1/asoasd').last.should == 'v1'
|
40
|
+
end
|
38
41
|
end
|
39
42
|
end
|
43
|
+
|
40
44
|
end
|
@@ -8,6 +8,7 @@ describe Grape::Validations::CoerceValidator do
|
|
8
8
|
describe 'coerce' do
|
9
9
|
it "i18n error on malformed input" do
|
10
10
|
I18n.load_path << File.expand_path('../zh-CN.yml',__FILE__)
|
11
|
+
I18n.reload!
|
11
12
|
I18n.locale = :'zh-CN'
|
12
13
|
subject.params { requires :age, :type => Integer }
|
13
14
|
subject.get '/single' do 'int works'; end
|
@@ -100,11 +100,11 @@ describe Grape::Validations::PresenceValidator do
|
|
100
100
|
it 'validates nested parameters' do
|
101
101
|
get('/nested')
|
102
102
|
last_response.status.should == 400
|
103
|
-
last_response.body.should == '{"error":"missing parameter: first_name"}'
|
103
|
+
last_response.body.should == '{"error":"missing parameter: user[first_name]"}'
|
104
104
|
|
105
105
|
get('/nested', :user => {:first_name => "Billy"})
|
106
106
|
last_response.status.should == 400
|
107
|
-
last_response.body.should == '{"error":"missing parameter: last_name"}'
|
107
|
+
last_response.body.should == '{"error":"missing parameter: user[last_name]"}'
|
108
108
|
|
109
109
|
get('/nested', :user => {:first_name => "Billy", :last_name => "Bob"})
|
110
110
|
last_response.status.should == 200
|
@@ -114,27 +114,27 @@ describe Grape::Validations::PresenceValidator do
|
|
114
114
|
it 'validates triple nested parameters' do
|
115
115
|
get('/nested_triple')
|
116
116
|
last_response.status.should == 400
|
117
|
-
last_response.body.should == '{"error":"missing parameter: admin_name"}'
|
117
|
+
last_response.body.should == '{"error":"missing parameter: admin[admin_name]"}'
|
118
118
|
|
119
119
|
get('/nested_triple', :user => {:first_name => "Billy"})
|
120
120
|
last_response.status.should == 400
|
121
|
-
last_response.body.should == '{"error":"missing parameter: admin_name"}'
|
121
|
+
last_response.body.should == '{"error":"missing parameter: admin[admin_name]"}'
|
122
122
|
|
123
123
|
get('/nested_triple', :admin => {:super => {:first_name => "Billy"}})
|
124
124
|
last_response.status.should == 400
|
125
|
-
last_response.body.should == '{"error":"missing parameter: admin_name"}'
|
125
|
+
last_response.body.should == '{"error":"missing parameter: admin[admin_name]"}'
|
126
126
|
|
127
127
|
get('/nested_triple', :super => {:user => {:first_name => "Billy", :last_name => "Bob"}})
|
128
128
|
last_response.status.should == 400
|
129
|
-
last_response.body.should == '{"error":"missing parameter: admin_name"}'
|
129
|
+
last_response.body.should == '{"error":"missing parameter: admin[admin_name]"}'
|
130
130
|
|
131
131
|
get('/nested_triple', :admin => {:super => {:user => {:first_name => "Billy"}}})
|
132
132
|
last_response.status.should == 400
|
133
|
-
last_response.body.should == '{"error":"missing parameter: admin_name"}'
|
133
|
+
last_response.body.should == '{"error":"missing parameter: admin[admin_name]"}'
|
134
134
|
|
135
135
|
get('/nested_triple', :admin => { :admin_name => 'admin', :super => {:user => {:first_name => "Billy"}}})
|
136
136
|
last_response.status.should == 400
|
137
|
-
last_response.body.should == '{"error":"missing parameter: last_name"}'
|
137
|
+
last_response.body.should == '{"error":"missing parameter: admin[super][user][last_name]"}'
|
138
138
|
|
139
139
|
get('/nested_triple', :admin => { :admin_name => 'admin', :super => {:user => {:first_name => "Billy", :last_name => "Bob"}}})
|
140
140
|
last_response.status.should == 200
|