grape 0.6.0 → 0.6.1
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.
- checksums.yaml +8 -8
- data/.rubocop.yml +65 -0
- data/.travis.yml +4 -0
- data/CHANGELOG.md +17 -1
- data/Gemfile +1 -0
- data/README.md +16 -8
- data/RELEASING.md +105 -0
- data/grape.gemspec +1 -1
- data/lib/grape.rb +1 -0
- data/lib/grape/api.rb +88 -54
- data/lib/grape/cookies.rb +4 -6
- data/lib/grape/endpoint.rb +81 -69
- data/lib/grape/error_formatter/base.rb +5 -4
- data/lib/grape/error_formatter/json.rb +3 -3
- data/lib/grape/error_formatter/txt.rb +1 -1
- data/lib/grape/error_formatter/xml.rb +4 -4
- data/lib/grape/exceptions/base.rb +7 -7
- data/lib/grape/exceptions/incompatible_option_values.rb +13 -0
- data/lib/grape/exceptions/invalid_formatter.rb +1 -1
- data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +1 -4
- data/lib/grape/exceptions/missing_mime_type.rb +1 -1
- data/lib/grape/exceptions/missing_option.rb +1 -1
- data/lib/grape/exceptions/missing_vendor_option.rb +1 -1
- data/lib/grape/exceptions/unknown_options.rb +1 -1
- data/lib/grape/exceptions/unknown_validator.rb +1 -1
- data/lib/grape/exceptions/validation.rb +2 -2
- data/lib/grape/exceptions/validation_errors.rb +1 -1
- data/lib/grape/formatter/base.rb +5 -4
- data/lib/grape/formatter/serializable_hash.rb +7 -6
- data/lib/grape/http/request.rb +2 -2
- data/lib/grape/locale/en.yml +2 -0
- data/lib/grape/middleware/auth/base.rb +3 -3
- data/lib/grape/middleware/auth/basic.rb +1 -1
- data/lib/grape/middleware/auth/oauth2.rb +18 -20
- data/lib/grape/middleware/base.rb +1 -1
- data/lib/grape/middleware/error.rb +19 -19
- data/lib/grape/middleware/filter.rb +3 -3
- data/lib/grape/middleware/formatter.rb +29 -23
- data/lib/grape/middleware/versioner.rb +1 -1
- data/lib/grape/middleware/versioner/accept_version_header.rb +8 -6
- data/lib/grape/middleware/versioner/header.rb +16 -14
- data/lib/grape/middleware/versioner/param.rb +7 -7
- data/lib/grape/middleware/versioner/path.rb +7 -9
- data/lib/grape/parser/base.rb +3 -2
- data/lib/grape/path.rb +1 -1
- data/lib/grape/route.rb +6 -4
- data/lib/grape/util/content_types.rb +2 -1
- data/lib/grape/util/deep_merge.rb +5 -5
- data/lib/grape/util/hash_stack.rb +2 -2
- data/lib/grape/validations.rb +34 -30
- data/lib/grape/validations/coerce.rb +6 -5
- data/lib/grape/validations/default.rb +0 -1
- data/lib/grape/validations/presence.rb +1 -1
- data/lib/grape/validations/regexp.rb +2 -2
- data/lib/grape/validations/values.rb +16 -0
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api_spec.rb +229 -210
- data/spec/grape/endpoint_spec.rb +56 -54
- data/spec/grape/entity_spec.rb +31 -33
- data/spec/grape/exceptions/missing_mime_type_spec.rb +3 -9
- data/spec/grape/middleware/auth/basic_spec.rb +8 -8
- data/spec/grape/middleware/auth/digest_spec.rb +5 -5
- data/spec/grape/middleware/auth/oauth2_spec.rb +23 -23
- data/spec/grape/middleware/base_spec.rb +6 -6
- data/spec/grape/middleware/error_spec.rb +11 -15
- data/spec/grape/middleware/exception_spec.rb +45 -25
- data/spec/grape/middleware/formatter_spec.rb +56 -45
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +25 -25
- data/spec/grape/middleware/versioner/header_spec.rb +54 -54
- data/spec/grape/middleware/versioner/param_spec.rb +17 -18
- data/spec/grape/middleware/versioner/path_spec.rb +6 -6
- data/spec/grape/middleware/versioner_spec.rb +1 -1
- data/spec/grape/util/hash_stack_spec.rb +26 -27
- data/spec/grape/validations/coerce_spec.rb +39 -34
- data/spec/grape/validations/default_spec.rb +12 -13
- data/spec/grape/validations/presence_spec.rb +18 -22
- data/spec/grape/validations/regexp_spec.rb +9 -9
- data/spec/grape/validations/values_spec.rb +64 -0
- data/spec/grape/validations_spec.rb +127 -70
- data/spec/shared/versioning_examples.rb +5 -5
- data/spec/support/basic_auth_encode_helpers.rb +0 -1
- data/spec/support/versioned_helpers.rb +5 -6
- metadata +10 -4
@@ -1,7 +1,7 @@
|
|
1
1
|
module Grape
|
2
2
|
|
3
3
|
class API
|
4
|
-
Boolean = Virtus::Attribute::Boolean
|
4
|
+
Boolean = Virtus::Attribute::Boolean # rubocop:disable ConstantName
|
5
5
|
end
|
6
6
|
|
7
7
|
module Validations
|
@@ -12,12 +12,13 @@ module Grape
|
|
12
12
|
if valid_type?(new_value)
|
13
13
|
params[attr_name] = new_value
|
14
14
|
else
|
15
|
-
raise Grape::Exceptions::Validation, :
|
15
|
+
raise Grape::Exceptions::Validation, param: @scope.full_name(attr_name), message_key: :coerce
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
class InvalidValue; end
|
20
|
-
|
20
|
+
|
21
|
+
private
|
21
22
|
|
22
23
|
def _valid_array_type?(type, values)
|
23
24
|
values.all? do |val|
|
@@ -46,11 +47,11 @@ module Grape
|
|
46
47
|
end
|
47
48
|
|
48
49
|
def coerce_value(type, val)
|
49
|
-
converter = Virtus::Attribute.build(
|
50
|
+
converter = Virtus::Attribute.build(type)
|
50
51
|
converter.coerce(val)
|
51
52
|
|
52
53
|
# not the prettiest but some invalid coercion can currently trigger
|
53
|
-
# errors in Virtus (see coerce_spec.rb)
|
54
|
+
# errors in Virtus (see coerce_spec.rb:75)
|
54
55
|
rescue
|
55
56
|
InvalidValue.new
|
56
57
|
end
|
@@ -8,7 +8,7 @@ module Grape
|
|
8
8
|
|
9
9
|
def validate_param!(attr_name, params)
|
10
10
|
unless params.has_key?(attr_name)
|
11
|
-
raise Grape::Exceptions::Validation, :
|
11
|
+
raise Grape::Exceptions::Validation, param: @scope.full_name(attr_name), message_key: :presence
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
@@ -3,8 +3,8 @@ module Grape
|
|
3
3
|
|
4
4
|
class RegexpValidator < SingleOptionValidator
|
5
5
|
def validate_param!(attr_name, params)
|
6
|
-
if params[attr_name] && !(
|
7
|
-
raise Grape::Exceptions::Validation, :
|
6
|
+
if params[attr_name] && !(params[attr_name].to_s =~ @option)
|
7
|
+
raise Grape::Exceptions::Validation, param: @scope.full_name(attr_name), message_key: :regexp
|
8
8
|
end
|
9
9
|
end
|
10
10
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Grape
|
2
|
+
module Validations
|
3
|
+
class ValuesValidator < Validator
|
4
|
+
def initialize(attrs, options, required, scope)
|
5
|
+
@values = options
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def validate_param!(attr_name, params)
|
10
|
+
if params[attr_name] && !@values.include?(params[attr_name])
|
11
|
+
raise Grape::Exceptions::Validation, param: @scope.full_name(attr_name), message_key: :values
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/grape/version.rb
CHANGED
data/spec/grape/api_spec.rb
CHANGED
@@ -4,7 +4,9 @@ require 'shared/versioning_examples'
|
|
4
4
|
describe Grape::API do
|
5
5
|
subject { Class.new(Grape::API) }
|
6
6
|
|
7
|
-
def app
|
7
|
+
def app
|
8
|
+
subject
|
9
|
+
end
|
8
10
|
|
9
11
|
describe '.prefix' do
|
10
12
|
|
@@ -52,7 +54,7 @@ describe Grape::API do
|
|
52
54
|
it_should_behave_like 'versioning' do
|
53
55
|
let(:macro_options) do
|
54
56
|
{
|
55
|
-
:
|
57
|
+
using: :path
|
56
58
|
}
|
57
59
|
end
|
58
60
|
end
|
@@ -62,8 +64,8 @@ describe Grape::API do
|
|
62
64
|
it_should_behave_like 'versioning' do
|
63
65
|
let(:macro_options) do
|
64
66
|
{
|
65
|
-
:
|
66
|
-
:
|
67
|
+
using: :param,
|
68
|
+
parameter: "apiver"
|
67
69
|
}
|
68
70
|
end
|
69
71
|
end
|
@@ -73,9 +75,9 @@ describe Grape::API do
|
|
73
75
|
it_should_behave_like 'versioning' do
|
74
76
|
let(:macro_options) do
|
75
77
|
{
|
76
|
-
:
|
77
|
-
:
|
78
|
-
:
|
78
|
+
using: :header,
|
79
|
+
vendor: 'mycompany',
|
80
|
+
format: 'json'
|
79
81
|
}
|
80
82
|
end
|
81
83
|
end
|
@@ -83,7 +85,7 @@ describe Grape::API do
|
|
83
85
|
# Behavior as defined by rfc2616 when no header is defined
|
84
86
|
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
85
87
|
describe 'no specified accept header' do
|
86
|
-
# subject.version 'v1', :
|
88
|
+
# subject.version 'v1', using: :header
|
87
89
|
# subject.get '/hello' do
|
88
90
|
# 'hello'
|
89
91
|
# end
|
@@ -101,7 +103,7 @@ describe Grape::API do
|
|
101
103
|
it_should_behave_like 'versioning' do
|
102
104
|
let(:macro_options) do
|
103
105
|
{
|
104
|
-
:
|
106
|
+
using: :accept_version_header
|
105
107
|
}
|
106
108
|
end
|
107
109
|
end
|
@@ -109,12 +111,12 @@ describe Grape::API do
|
|
109
111
|
|
110
112
|
describe '.represent' do
|
111
113
|
it 'requires a :with option' do
|
112
|
-
expect{ subject.represent Object, {} }.to raise_error(Grape::Exceptions::InvalidWithOptionForRepresent)
|
114
|
+
expect { subject.represent Object, {} }.to raise_error(Grape::Exceptions::InvalidWithOptionForRepresent)
|
113
115
|
end
|
114
116
|
|
115
117
|
it 'adds the association to the :representations setting' do
|
116
118
|
klass = Class.new
|
117
|
-
subject.represent Object, :
|
119
|
+
subject.represent Object, with: klass
|
118
120
|
subject.settings[:representations][Object].should == klass
|
119
121
|
end
|
120
122
|
end
|
@@ -128,10 +130,10 @@ describe Grape::API do
|
|
128
130
|
|
129
131
|
it 'comes after the prefix and version' do
|
130
132
|
subject.prefix :rad
|
131
|
-
subject.version 'v1', :
|
133
|
+
subject.version 'v1', using: :path
|
132
134
|
|
133
135
|
subject.namespace :awesome do
|
134
|
-
get('/hello'){ "worked" }
|
136
|
+
get('/hello') { "worked" }
|
135
137
|
end
|
136
138
|
|
137
139
|
get "/rad/v1/awesome/hello"
|
@@ -171,10 +173,10 @@ describe Grape::API do
|
|
171
173
|
|
172
174
|
it 'is callable with nil just to push onto the stack' do
|
173
175
|
subject.namespace do
|
174
|
-
version 'v2', :
|
175
|
-
get('/hello'){ "inner" }
|
176
|
+
version 'v2', using: :path
|
177
|
+
get('/hello') { "inner" }
|
176
178
|
end
|
177
|
-
subject.get('/hello'){ "outer" }
|
179
|
+
subject.get('/hello') { "outer" }
|
178
180
|
|
179
181
|
get '/v2/hello'
|
180
182
|
last_response.body.should == "inner"
|
@@ -207,7 +209,7 @@ describe Grape::API do
|
|
207
209
|
|
208
210
|
it 'should be able to define requirements with a single hash' do
|
209
211
|
subject.namespace :users do
|
210
|
-
route_param :id, :
|
212
|
+
route_param :id, requirements: /[0-9]+/ do
|
211
213
|
get do
|
212
214
|
params[:id]
|
213
215
|
end
|
@@ -248,7 +250,7 @@ describe Grape::API do
|
|
248
250
|
before do
|
249
251
|
subject.format :txt
|
250
252
|
def subject.enable_root_route!
|
251
|
-
|
253
|
+
get("/") { "root" }
|
252
254
|
end
|
253
255
|
end
|
254
256
|
|
@@ -258,12 +260,12 @@ describe Grape::API do
|
|
258
260
|
|
259
261
|
describe 'path versioned APIs' do
|
260
262
|
before do
|
261
|
-
subject.version 'v1', :
|
263
|
+
subject.version 'v1', using: :path
|
262
264
|
subject.enable_root_route!
|
263
265
|
end
|
264
266
|
|
265
267
|
it 'without a format' do
|
266
|
-
versioned_get "/", "v1", :
|
268
|
+
versioned_get "/", "v1", using: :path
|
267
269
|
end
|
268
270
|
|
269
271
|
it 'with a format' do
|
@@ -272,32 +274,32 @@ describe Grape::API do
|
|
272
274
|
end
|
273
275
|
|
274
276
|
it 'header versioned APIs' do
|
275
|
-
subject.version 'v1', :
|
277
|
+
subject.version 'v1', using: :header, vendor: 'test'
|
276
278
|
subject.enable_root_route!
|
277
279
|
|
278
|
-
versioned_get "/", "v1", :
|
280
|
+
versioned_get "/", "v1", using: :header, vendor: 'test'
|
279
281
|
end
|
280
282
|
|
281
283
|
it 'header versioned APIs with multiple headers' do
|
282
|
-
subject.version ['v1', 'v2'], :
|
284
|
+
subject.version ['v1', 'v2'], using: :header, vendor: 'test'
|
283
285
|
subject.enable_root_route!
|
284
286
|
|
285
|
-
versioned_get "/", "v1", :
|
286
|
-
versioned_get "/", "v2", :
|
287
|
+
versioned_get "/", "v1", using: :header, vendor: 'test'
|
288
|
+
versioned_get "/", "v2", using: :header, vendor: 'test'
|
287
289
|
end
|
288
290
|
|
289
291
|
it 'param versioned APIs' do
|
290
|
-
subject.version 'v1', :
|
292
|
+
subject.version 'v1', using: :param
|
291
293
|
subject.enable_root_route!
|
292
294
|
|
293
|
-
versioned_get "/", "v1", :
|
295
|
+
versioned_get "/", "v1", using: :param
|
294
296
|
end
|
295
297
|
|
296
298
|
it 'Accept-Version header versioned APIs' do
|
297
|
-
subject.version 'v1', :
|
299
|
+
subject.version 'v1', using: :accept_version_header
|
298
300
|
subject.enable_root_route!
|
299
301
|
|
300
|
-
versioned_get "/", "v1", :
|
302
|
+
versioned_get "/", "v1", using: :accept_version_header
|
301
303
|
end
|
302
304
|
|
303
305
|
it 'unversioned APIs' do
|
@@ -321,7 +323,7 @@ describe Grape::API do
|
|
321
323
|
context 'format' do
|
322
324
|
before(:each) do
|
323
325
|
subject.get("/abc") do
|
324
|
-
RSpec::Mocks::Mock.new(:
|
326
|
+
RSpec::Mocks::Mock.new(to_json: 'abc', to_txt: 'def')
|
325
327
|
end
|
326
328
|
end
|
327
329
|
|
@@ -340,7 +342,7 @@ describe Grape::API do
|
|
340
342
|
|
341
343
|
it 'allows for format without corrupting a param' do
|
342
344
|
subject.get('/:id') do
|
343
|
-
{"id" => params[:id]}
|
345
|
+
{ "id" => params[:id] }
|
344
346
|
end
|
345
347
|
|
346
348
|
get '/awesome.json'
|
@@ -373,9 +375,9 @@ describe Grape::API do
|
|
373
375
|
last_response.body.should eql 'hiya'
|
374
376
|
end
|
375
377
|
|
376
|
-
[
|
378
|
+
[:put, :post].each do |verb|
|
377
379
|
context verb do
|
378
|
-
[
|
380
|
+
['string', :symbol, 1, -1.1, {}, [], true, false, nil].each do |object|
|
379
381
|
it "allows a(n) #{object.class} json object in params" do
|
380
382
|
subject.format :json
|
381
383
|
subject.send(verb) do
|
@@ -457,7 +459,7 @@ describe Grape::API do
|
|
457
459
|
last_response.body.should eql verb == 'head' ? '' : verb
|
458
460
|
# Call it with a method other than the properly constrained one.
|
459
461
|
send(used_verb = verbs[(verbs.index(verb) + 2) % verbs.size], '/example')
|
460
|
-
last_response.status.should eql used_verb == 'options' ? 204 :405
|
462
|
+
last_response.status.should eql used_verb == 'options' ? 204 : 405
|
461
463
|
end
|
462
464
|
end
|
463
465
|
|
@@ -578,16 +580,16 @@ describe Grape::API do
|
|
578
580
|
end
|
579
581
|
|
580
582
|
it 'adds a after_validation filter' do
|
581
|
-
subject.after_validation { @foo = "first #{params[:id]}:#{params[:id].class}" }
|
583
|
+
subject.after_validation { @foo = "first #{params[:id] }:#{params[:id].class}" }
|
582
584
|
subject.after_validation { @bar = 'second' }
|
583
585
|
subject.params do
|
584
|
-
requires :id, :
|
586
|
+
requires :id, type: Integer
|
585
587
|
end
|
586
588
|
subject.get '/' do
|
587
589
|
"#{@foo} #{@bar}"
|
588
590
|
end
|
589
591
|
|
590
|
-
get '/', :
|
592
|
+
get '/', id: "32"
|
591
593
|
last_response.body.should eql 'first 32:Fixnum second'
|
592
594
|
end
|
593
595
|
|
@@ -662,7 +664,7 @@ describe Grape::API do
|
|
662
664
|
describe '.middleware' do
|
663
665
|
it 'includes middleware arguments from settings' do
|
664
666
|
settings = Grape::Util::HashStack.new
|
665
|
-
settings.stub(:stack).and_return([{:
|
667
|
+
settings.stub(:stack).and_return([{ middleware: [[ApiSpec::PhonyMiddleware, 'abc', 123]] }])
|
666
668
|
subject.stub(:settings).and_return(settings)
|
667
669
|
subject.middleware.should eql [[ApiSpec::PhonyMiddleware, 'abc', 123]]
|
668
670
|
end
|
@@ -670,9 +672,10 @@ describe Grape::API do
|
|
670
672
|
it 'includes all middleware from stacked settings' do
|
671
673
|
settings = Grape::Util::HashStack.new
|
672
674
|
settings.stub(:stack).and_return [
|
673
|
-
{:
|
674
|
-
{:
|
675
|
+
{ middleware: [[ApiSpec::PhonyMiddleware, 123], [ApiSpec::PhonyMiddleware, 'abc']] },
|
676
|
+
{ middleware: [[ApiSpec::PhonyMiddleware, 'foo']] }
|
675
677
|
]
|
678
|
+
|
676
679
|
subject.stub(:settings).and_return(settings)
|
677
680
|
|
678
681
|
subject.middleware.should eql [
|
@@ -693,7 +696,7 @@ describe Grape::API do
|
|
693
696
|
subject.use ApiSpec::PhonyMiddleware, 123
|
694
697
|
subject.namespace :awesome do
|
695
698
|
use ApiSpec::PhonyMiddleware, 'abc'
|
696
|
-
middleware.should == [[ApiSpec::PhonyMiddleware, 123],[ApiSpec::PhonyMiddleware, 'abc']]
|
699
|
+
middleware.should == [[ApiSpec::PhonyMiddleware, 123], [ApiSpec::PhonyMiddleware, 'abc']]
|
697
700
|
end
|
698
701
|
|
699
702
|
subject.middleware.should eql [[ApiSpec::PhonyMiddleware, 123]]
|
@@ -710,13 +713,13 @@ describe Grape::API do
|
|
710
713
|
end
|
711
714
|
|
712
715
|
it 'adds a block if one is given' do
|
713
|
-
block = lambda{ }
|
716
|
+
block = lambda { }
|
714
717
|
subject.use ApiSpec::PhonyMiddleware, &block
|
715
718
|
subject.middleware.should eql [[ApiSpec::PhonyMiddleware, block]]
|
716
719
|
end
|
717
720
|
|
718
721
|
it 'uses a block if one is given' do
|
719
|
-
block = lambda{ }
|
722
|
+
block = lambda { }
|
720
723
|
subject.use ApiSpec::PhonyMiddleware, &block
|
721
724
|
subject.get '/' do
|
722
725
|
env['phony.block'].inspect
|
@@ -727,7 +730,7 @@ describe Grape::API do
|
|
727
730
|
end
|
728
731
|
|
729
732
|
it 'does not destroy the middleware settings on multiple runs' do
|
730
|
-
block = lambda{ }
|
733
|
+
block = lambda { }
|
731
734
|
subject.use ApiSpec::PhonyMiddleware, &block
|
732
735
|
subject.get '/' do
|
733
736
|
env['phony.block'].inspect
|
@@ -742,7 +745,7 @@ describe Grape::API do
|
|
742
745
|
it 'mounts behind error middleware' do
|
743
746
|
m = Class.new(Grape::Middleware::Base) do
|
744
747
|
def before
|
745
|
-
throw :error, :
|
748
|
+
throw :error, message: "Caught in the Net", status: 400
|
746
749
|
end
|
747
750
|
end
|
748
751
|
subject.use m
|
@@ -756,24 +759,24 @@ describe Grape::API do
|
|
756
759
|
end
|
757
760
|
describe '.basic' do
|
758
761
|
it 'protects any resources on the same scope' do
|
759
|
-
subject.http_basic do |u,p|
|
762
|
+
subject.http_basic do |u, p|
|
760
763
|
u == 'allow'
|
761
764
|
end
|
762
|
-
subject.get(:hello){ "Hello, world."}
|
765
|
+
subject.get(:hello) { "Hello, world." }
|
763
766
|
get '/hello'
|
764
767
|
last_response.status.should eql 401
|
765
|
-
get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('allow','whatever')
|
768
|
+
get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('allow', 'whatever')
|
766
769
|
last_response.status.should eql 200
|
767
770
|
end
|
768
771
|
|
769
772
|
it 'is scopable' do
|
770
|
-
subject.get(:hello){ "Hello, world."}
|
773
|
+
subject.get(:hello) { "Hello, world." }
|
771
774
|
subject.namespace :admin do
|
772
|
-
http_basic do |u,p|
|
775
|
+
http_basic do |u, p|
|
773
776
|
u == 'allow'
|
774
777
|
end
|
775
778
|
|
776
|
-
get(:hello){ "Hello, world." }
|
779
|
+
get(:hello) { "Hello, world." }
|
777
780
|
end
|
778
781
|
|
779
782
|
get '/hello'
|
@@ -783,14 +786,14 @@ describe Grape::API do
|
|
783
786
|
end
|
784
787
|
|
785
788
|
it 'is callable via .auth as well' do
|
786
|
-
subject.auth :http_basic do |u,p|
|
789
|
+
subject.auth :http_basic do |u, p|
|
787
790
|
u == 'allow'
|
788
791
|
end
|
789
792
|
|
790
|
-
subject.get(:hello){ "Hello, world."}
|
793
|
+
subject.get(:hello) { "Hello, world." }
|
791
794
|
get '/hello'
|
792
795
|
last_response.status.should eql 401
|
793
|
-
get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('allow','whatever')
|
796
|
+
get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('allow', 'whatever')
|
794
797
|
last_response.status.should eql 200
|
795
798
|
end
|
796
799
|
end
|
@@ -877,7 +880,7 @@ describe Grape::API do
|
|
877
880
|
[one, two]
|
878
881
|
end
|
879
882
|
|
880
|
-
lambda{get '/howdy'}.should_not raise_error
|
883
|
+
lambda { get '/howdy' }.should_not raise_error
|
881
884
|
end
|
882
885
|
|
883
886
|
it 'allows for modules' do
|
@@ -915,7 +918,7 @@ describe Grape::API do
|
|
915
918
|
subject.get 'howdy' do
|
916
919
|
[one, two, three]
|
917
920
|
end
|
918
|
-
lambda{get '/howdy'}.should_not raise_error
|
921
|
+
lambda { get '/howdy' }.should_not raise_error
|
919
922
|
end
|
920
923
|
end
|
921
924
|
|
@@ -966,20 +969,20 @@ describe Grape::API do
|
|
966
969
|
|
967
970
|
it 'rescues only certain errors if rescue_from is called with specific errors' do
|
968
971
|
subject.rescue_from ArgumentError
|
969
|
-
subject.get('/rescued'){ raise ArgumentError }
|
970
|
-
subject.get('/unrescued'){ raise "beefcake" }
|
972
|
+
subject.get('/rescued') { raise ArgumentError }
|
973
|
+
subject.get('/unrescued') { raise "beefcake" }
|
971
974
|
|
972
975
|
get '/rescued'
|
973
976
|
last_response.status.should eql 403
|
974
977
|
|
975
|
-
lambda{ get '/unrescued' }.should raise_error
|
978
|
+
lambda { get '/unrescued' }.should raise_error
|
976
979
|
end
|
977
980
|
|
978
981
|
it 'does not re-raise exceptions of type Grape::Exception::Base' do
|
979
982
|
class CustomError < Grape::Exceptions::Base; end
|
980
|
-
subject.get('/custom_exception'){ raise CustomError }
|
983
|
+
subject.get('/custom_exception') { raise CustomError }
|
981
984
|
|
982
|
-
lambda{ get '/custom_exception' }.should_not raise_error
|
985
|
+
lambda { get '/custom_exception' }.should_not raise_error
|
983
986
|
end
|
984
987
|
|
985
988
|
it 'rescues custom grape exceptions' do
|
@@ -988,7 +991,7 @@ describe Grape::API do
|
|
988
991
|
rack_response('New Error', e.status)
|
989
992
|
end
|
990
993
|
subject.get '/custom_error' do
|
991
|
-
raise CustomError, :
|
994
|
+
raise CustomError, status: 400, message: 'Custom Error'
|
992
995
|
end
|
993
996
|
|
994
997
|
get '/custom_error'
|
@@ -1095,7 +1098,7 @@ describe Grape::API do
|
|
1095
1098
|
end
|
1096
1099
|
|
1097
1100
|
it 'can execute the lambda with an argument' do
|
1098
|
-
subject.rescue_from ArgumentError, lambda {|e|
|
1101
|
+
subject.rescue_from ArgumentError, lambda { |e|
|
1099
1102
|
rack_response(e.message, 400)
|
1100
1103
|
}
|
1101
1104
|
subject.get('/rescue_lambda') { raise ArgumentError, 'lambda takes an argument' }
|
@@ -1108,7 +1111,10 @@ describe Grape::API do
|
|
1108
1111
|
|
1109
1112
|
describe '.rescue_from klass, with: method' do
|
1110
1113
|
it 'rescues an error with the specified message' do
|
1111
|
-
def rescue_arg_error
|
1114
|
+
def rescue_arg_error
|
1115
|
+
Rack::Response.new('rescued with a method', 400)
|
1116
|
+
end
|
1117
|
+
|
1112
1118
|
subject.rescue_from ArgumentError, with: rescue_arg_error
|
1113
1119
|
subject.get('/rescue_method') { raise ArgumentError }
|
1114
1120
|
|
@@ -1130,7 +1136,7 @@ describe Grape::API do
|
|
1130
1136
|
end
|
1131
1137
|
|
1132
1138
|
it 'rescues all errors and return :txt with backtrace' do
|
1133
|
-
subject.rescue_from :all, :
|
1139
|
+
subject.rescue_from :all, backtrace: true
|
1134
1140
|
subject.format :txt
|
1135
1141
|
subject.get '/exception' do
|
1136
1142
|
raise "rain!"
|
@@ -1173,7 +1179,7 @@ describe Grape::API do
|
|
1173
1179
|
end
|
1174
1180
|
end
|
1175
1181
|
it 'returns a custom error format' do
|
1176
|
-
subject.rescue_from :all, :
|
1182
|
+
subject.rescue_from :all, backtrace: true
|
1177
1183
|
subject.error_formatter :txt, CustomErrorFormatter
|
1178
1184
|
subject.get '/exception' do
|
1179
1185
|
raise "rain!"
|
@@ -1214,7 +1220,7 @@ describe Grape::API do
|
|
1214
1220
|
last_response.body.should eql '{"error":"rain!"}'
|
1215
1221
|
end
|
1216
1222
|
it 'rescues all errors and return :json with backtrace' do
|
1217
|
-
subject.rescue_from :all, :
|
1223
|
+
subject.rescue_from :all, backtrace: true
|
1218
1224
|
subject.format :json
|
1219
1225
|
subject.get '/exception' do
|
1220
1226
|
raise "rain!"
|
@@ -1273,10 +1279,10 @@ describe Grape::API do
|
|
1273
1279
|
describe '.formatter' do
|
1274
1280
|
context 'multiple formatters' do
|
1275
1281
|
before :each do
|
1276
|
-
subject.formatter :json, lambda { |object, env| "{\"custom_formatter\":\"#{object[:some]}\"}" }
|
1277
|
-
subject.formatter :txt, lambda { |object, env| "custom_formatter: #{object[:some]}" }
|
1282
|
+
subject.formatter :json, lambda { |object, env| "{\"custom_formatter\":\"#{object[:some] }\"}" }
|
1283
|
+
subject.formatter :txt, lambda { |object, env| "custom_formatter: #{object[:some] }" }
|
1278
1284
|
subject.get :simple do
|
1279
|
-
{:
|
1285
|
+
{ some: 'hash' }
|
1280
1286
|
end
|
1281
1287
|
end
|
1282
1288
|
it 'sets one formatter' do
|
@@ -1292,9 +1298,9 @@ describe Grape::API do
|
|
1292
1298
|
before :each do
|
1293
1299
|
subject.content_type :json, 'application/json'
|
1294
1300
|
subject.content_type :custom, 'application/custom'
|
1295
|
-
subject.formatter :custom, lambda { |object, env| "{\"custom_formatter\":\"#{object[:some]}\"}" }
|
1301
|
+
subject.formatter :custom, lambda { |object, env| "{\"custom_formatter\":\"#{object[:some] }\"}" }
|
1296
1302
|
subject.get :simple do
|
1297
|
-
{:
|
1303
|
+
{ some: 'hash' }
|
1298
1304
|
end
|
1299
1305
|
end
|
1300
1306
|
it 'uses json' do
|
@@ -1309,7 +1315,7 @@ describe Grape::API do
|
|
1309
1315
|
context 'custom formatter class' do
|
1310
1316
|
module CustomFormatter
|
1311
1317
|
def self.call(object, env)
|
1312
|
-
"{\"custom_formatter\":\"#{object[:some]}\"}"
|
1318
|
+
"{\"custom_formatter\":\"#{object[:some] }\"}"
|
1313
1319
|
end
|
1314
1320
|
end
|
1315
1321
|
before :each do
|
@@ -1317,7 +1323,7 @@ describe Grape::API do
|
|
1317
1323
|
subject.content_type :custom, 'application/custom'
|
1318
1324
|
subject.formatter :custom, CustomFormatter
|
1319
1325
|
subject.get :simple do
|
1320
|
-
{:
|
1326
|
+
{ some: 'hash' }
|
1321
1327
|
end
|
1322
1328
|
end
|
1323
1329
|
it 'uses json' do
|
@@ -1335,7 +1341,7 @@ describe Grape::API do
|
|
1335
1341
|
it 'parses data in format requested by content-type' do
|
1336
1342
|
subject.format :json
|
1337
1343
|
subject.post '/data' do
|
1338
|
-
{ :
|
1344
|
+
{ x: params[:x] }
|
1339
1345
|
end
|
1340
1346
|
post "/data", '{"x":42}', { 'CONTENT_TYPE' => 'application/json' }
|
1341
1347
|
last_response.status.should == 201
|
@@ -1350,7 +1356,7 @@ describe Grape::API do
|
|
1350
1356
|
params[:simple]
|
1351
1357
|
end
|
1352
1358
|
end
|
1353
|
-
[
|
1359
|
+
["text/custom", "text/custom; charset=UTF-8"].each do |content_type|
|
1354
1360
|
it "uses parser for #{content_type}" do
|
1355
1361
|
put '/simple', "simple", "CONTENT_TYPE" => content_type
|
1356
1362
|
last_response.status.should == 200
|
@@ -1392,7 +1398,7 @@ describe Grape::API do
|
|
1392
1398
|
before :each do
|
1393
1399
|
subject.parser :json, nil
|
1394
1400
|
subject.put "data" do
|
1395
|
-
"body: #{env['api.request.body']}"
|
1401
|
+
"body: #{env['api.request.body'] }"
|
1396
1402
|
end
|
1397
1403
|
end
|
1398
1404
|
it "does not parse data" do
|
@@ -1410,7 +1416,7 @@ describe Grape::API do
|
|
1410
1416
|
end
|
1411
1417
|
it 'returns data in default format' do
|
1412
1418
|
subject.get '/data' do
|
1413
|
-
{ :
|
1419
|
+
{ x: 42 }
|
1414
1420
|
end
|
1415
1421
|
get "/data"
|
1416
1422
|
last_response.status.should == 200
|
@@ -1418,7 +1424,7 @@ describe Grape::API do
|
|
1418
1424
|
end
|
1419
1425
|
it 'parses data in default format' do
|
1420
1426
|
subject.post '/data' do
|
1421
|
-
{ :
|
1427
|
+
{ x: params[:x] }
|
1422
1428
|
end
|
1423
1429
|
post "/data", '{"x":42}', "CONTENT_TYPE" => ""
|
1424
1430
|
last_response.status.should == 201
|
@@ -1455,76 +1461,76 @@ describe Grape::API do
|
|
1455
1461
|
describe 'single method api structure' do
|
1456
1462
|
before(:each) do
|
1457
1463
|
subject.get :ping do
|
1458
|
-
|
1464
|
+
'pong'
|
1459
1465
|
end
|
1460
1466
|
end
|
1461
1467
|
it 'returns one route' do
|
1462
|
-
|
1463
|
-
|
1464
|
-
|
1465
|
-
|
1466
|
-
|
1468
|
+
subject.routes.size.should == 1
|
1469
|
+
route = subject.routes[0]
|
1470
|
+
route.route_version.should be_nil
|
1471
|
+
route.route_path.should == "/ping(.:format)"
|
1472
|
+
route.route_method.should == "GET"
|
1467
1473
|
end
|
1468
1474
|
end
|
1469
1475
|
describe 'api structure with two versions and a namespace' do
|
1470
1476
|
before :each do
|
1471
|
-
subject.version 'v1', :
|
1477
|
+
subject.version 'v1', using: :path
|
1472
1478
|
subject.get 'version' do
|
1473
|
-
|
1479
|
+
api.version
|
1474
1480
|
end
|
1475
1481
|
# version v2
|
1476
|
-
subject.version 'v2', :
|
1482
|
+
subject.version 'v2', using: :path
|
1477
1483
|
subject.prefix 'p'
|
1478
1484
|
subject.namespace 'n1' do
|
1479
1485
|
namespace 'n2' do
|
1480
1486
|
get 'version' do
|
1481
|
-
|
1487
|
+
api.version
|
1482
1488
|
end
|
1483
1489
|
end
|
1484
1490
|
end
|
1485
1491
|
end
|
1486
1492
|
it 'returns the latest version set' do
|
1487
|
-
|
1493
|
+
subject.version.should == 'v2'
|
1488
1494
|
end
|
1489
1495
|
it 'returns versions' do
|
1490
|
-
|
1496
|
+
subject.versions.should == ['v1', 'v2']
|
1491
1497
|
end
|
1492
1498
|
it 'sets route paths' do
|
1493
|
-
|
1494
|
-
|
1495
|
-
|
1499
|
+
subject.routes.size.should >= 2
|
1500
|
+
subject.routes[0].route_path.should == "/:version/version(.:format)"
|
1501
|
+
subject.routes[1].route_path.should == "/p/:version/n1/n2/version(.:format)"
|
1496
1502
|
end
|
1497
1503
|
it 'sets route versions' do
|
1498
|
-
|
1499
|
-
|
1504
|
+
subject.routes[0].route_version.should == 'v1'
|
1505
|
+
subject.routes[1].route_version.should == 'v2'
|
1500
1506
|
end
|
1501
1507
|
it 'sets a nested namespace' do
|
1502
|
-
|
1508
|
+
subject.routes[1].route_namespace.should == "/n1/n2"
|
1503
1509
|
end
|
1504
1510
|
it 'sets prefix' do
|
1505
|
-
|
1511
|
+
subject.routes[1].route_prefix.should == 'p'
|
1506
1512
|
end
|
1507
1513
|
end
|
1508
1514
|
describe 'api structure with additional parameters' do
|
1509
1515
|
before(:each) do
|
1510
|
-
subject.get 'split/:string', { :
|
1516
|
+
subject.get 'split/:string', { params: { "token" => "a token" }, optional_params: { "limit" => "the limit" } } do
|
1511
1517
|
params[:string].split(params[:token], (params[:limit] || 0).to_i)
|
1512
1518
|
end
|
1513
1519
|
end
|
1514
1520
|
it 'splits a string' do
|
1515
|
-
get "/split/a,b,c.json", :
|
1521
|
+
get "/split/a,b,c.json", token: ','
|
1516
1522
|
last_response.body.should == '["a","b","c"]'
|
1517
1523
|
end
|
1518
1524
|
it 'splits a string with limit' do
|
1519
|
-
get "/split/a,b,c.json", :
|
1525
|
+
get "/split/a,b,c.json", token: ',', limit: '2'
|
1520
1526
|
last_response.body.should == '["a","b,c"]'
|
1521
1527
|
end
|
1522
1528
|
it 'sets route_params' do
|
1523
1529
|
subject.routes.map { |route|
|
1524
|
-
{ :
|
1530
|
+
{ params: route.route_params, optional_params: route.route_optional_params }
|
1525
1531
|
}.should eq [
|
1526
|
-
{ :
|
1527
|
-
|
1532
|
+
{ params: { "string" => "", "token" => "a token" }, optional_params: { "limit" => "the limit" } }
|
1533
|
+
]
|
1528
1534
|
end
|
1529
1535
|
end
|
1530
1536
|
end
|
@@ -1553,122 +1559,122 @@ describe Grape::API do
|
|
1553
1559
|
subject.get :second do ; end
|
1554
1560
|
subject.routes.count.should == 2
|
1555
1561
|
subject.routes.map { |route|
|
1556
|
-
{ :
|
1562
|
+
{ description: route.route_description, params: route.route_params }
|
1557
1563
|
}.should eq [
|
1558
|
-
{ :
|
1559
|
-
{ :
|
1560
|
-
|
1564
|
+
{ description: "first method", params: {} },
|
1565
|
+
{ description: "second method", params: {} }
|
1566
|
+
]
|
1561
1567
|
end
|
1562
1568
|
it 'resets desc' do
|
1563
1569
|
subject.desc "first method"
|
1564
1570
|
subject.get :first do ; end
|
1565
1571
|
subject.get :second do ; end
|
1566
1572
|
subject.routes.map { |route|
|
1567
|
-
{ :
|
1573
|
+
{ description: route.route_description, params: route.route_params }
|
1568
1574
|
}.should eq [
|
1569
|
-
{ :
|
1570
|
-
{ :
|
1571
|
-
|
1575
|
+
{ description: "first method", params: {} },
|
1576
|
+
{ description: nil, params: {} }
|
1577
|
+
]
|
1572
1578
|
end
|
1573
1579
|
it 'namespaces and describe arbitrary parameters' do
|
1574
1580
|
subject.namespace 'ns' do
|
1575
|
-
desc "ns second", :
|
1581
|
+
desc "ns second", foo: "bar"
|
1576
1582
|
get 'second' do ; end
|
1577
1583
|
end
|
1578
1584
|
subject.routes.map { |route|
|
1579
|
-
{ :
|
1585
|
+
{ description: route.route_description, foo: route.route_foo, params: route.route_params }
|
1580
1586
|
}.should eq [
|
1581
|
-
{ :
|
1582
|
-
|
1587
|
+
{ description: "ns second", foo: "bar", params: {} },
|
1588
|
+
]
|
1583
1589
|
end
|
1584
1590
|
it 'includes details' do
|
1585
|
-
subject.desc "method", :
|
1591
|
+
subject.desc "method", details: "method details"
|
1586
1592
|
subject.get 'method' do ; end
|
1587
1593
|
subject.routes.map { |route|
|
1588
|
-
{ :
|
1594
|
+
{ description: route.route_description, details: route.route_details, params: route.route_params }
|
1589
1595
|
}.should eq [
|
1590
|
-
{ :
|
1591
|
-
|
1596
|
+
{ description: "method", details: "method details", params: {} },
|
1597
|
+
]
|
1592
1598
|
end
|
1593
1599
|
it 'describes a method with parameters' do
|
1594
|
-
subject.desc "Reverses a string.", { :
|
1595
|
-
{ "s" => { :
|
1600
|
+
subject.desc "Reverses a string.", { params:
|
1601
|
+
{ "s" => { desc: "string to reverse", type: "string" } }
|
1596
1602
|
}
|
1597
1603
|
subject.get 'reverse' do
|
1598
1604
|
params[:s].reverse
|
1599
1605
|
end
|
1600
1606
|
subject.routes.map { |route|
|
1601
|
-
{ :
|
1607
|
+
{ description: route.route_description, params: route.route_params }
|
1602
1608
|
}.should eq [
|
1603
|
-
{ :
|
1604
|
-
|
1609
|
+
{ description: "Reverses a string.", params: { "s" => { desc: "string to reverse", type: "string" } } }
|
1610
|
+
]
|
1605
1611
|
end
|
1606
1612
|
it 'merges the parameters of the namespace with the parameters of the method' do
|
1607
1613
|
subject.desc "namespace"
|
1608
1614
|
subject.params do
|
1609
|
-
requires :ns_param, :
|
1615
|
+
requires :ns_param, desc: "namespace parameter"
|
1610
1616
|
end
|
1611
1617
|
subject.namespace 'ns' do
|
1612
1618
|
desc "method"
|
1613
1619
|
params do
|
1614
|
-
optional :method_param, :
|
1620
|
+
optional :method_param, desc: "method parameter"
|
1615
1621
|
end
|
1616
1622
|
get 'method' do ; end
|
1617
1623
|
end
|
1618
1624
|
subject.routes.map { |route|
|
1619
|
-
{ :
|
1625
|
+
{ description: route.route_description, params: route.route_params }
|
1620
1626
|
}.should eq [
|
1621
|
-
{ :
|
1622
|
-
:
|
1623
|
-
"ns_param" => { :
|
1624
|
-
"method_param" => { :
|
1627
|
+
{ description: "method",
|
1628
|
+
params: {
|
1629
|
+
"ns_param" => { required: true, desc: "namespace parameter" },
|
1630
|
+
"method_param" => { required: false, desc: "method parameter" }
|
1625
1631
|
}
|
1626
1632
|
}
|
1627
|
-
|
1633
|
+
]
|
1628
1634
|
end
|
1629
1635
|
it 'merges the parameters of nested namespaces' do
|
1630
1636
|
subject.desc "ns1"
|
1631
1637
|
subject.params do
|
1632
|
-
optional :ns_param, :
|
1633
|
-
requires :ns1_param, :
|
1638
|
+
optional :ns_param, desc: "ns param 1"
|
1639
|
+
requires :ns1_param, desc: "ns1 param"
|
1634
1640
|
end
|
1635
1641
|
subject.namespace 'ns1' do
|
1636
1642
|
desc "ns2"
|
1637
1643
|
params do
|
1638
|
-
requires :ns_param, :
|
1639
|
-
requires :ns2_param, :
|
1644
|
+
requires :ns_param, desc: "ns param 2"
|
1645
|
+
requires :ns2_param, desc: "ns2 param"
|
1640
1646
|
end
|
1641
1647
|
namespace 'ns2' do
|
1642
1648
|
desc "method"
|
1643
1649
|
params do
|
1644
|
-
optional :method_param, :
|
1650
|
+
optional :method_param, desc: "method param"
|
1645
1651
|
end
|
1646
1652
|
get 'method' do ; end
|
1647
1653
|
end
|
1648
1654
|
end
|
1649
1655
|
subject.routes.map { |route|
|
1650
|
-
{ :
|
1656
|
+
{ description: route.route_description, params: route.route_params }
|
1651
1657
|
}.should eq [
|
1652
|
-
{ :
|
1653
|
-
:
|
1654
|
-
"ns_param" => { :
|
1655
|
-
"ns1_param" => { :
|
1656
|
-
"ns2_param" => { :
|
1657
|
-
"method_param" => { :
|
1658
|
+
{ description: "method",
|
1659
|
+
params: {
|
1660
|
+
"ns_param" => { required: true, desc: "ns param 2" },
|
1661
|
+
"ns1_param" => { required: true, desc: "ns1 param" },
|
1662
|
+
"ns2_param" => { required: true, desc: "ns2 param" },
|
1663
|
+
"method_param" => { required: false, desc: "method param" }
|
1658
1664
|
}
|
1659
1665
|
}
|
1660
|
-
|
1666
|
+
]
|
1661
1667
|
end
|
1662
1668
|
it "groups nested params and prevents overwriting of params with same name in different groups" do
|
1663
1669
|
subject.desc "method"
|
1664
1670
|
subject.params do
|
1665
1671
|
group :group1 do
|
1666
|
-
optional :param1, :
|
1667
|
-
requires :param2, :
|
1672
|
+
optional :param1, desc: "group1 param1 desc"
|
1673
|
+
requires :param2, desc: "group1 param2 desc"
|
1668
1674
|
end
|
1669
1675
|
group :group2 do
|
1670
|
-
optional :param1, :
|
1671
|
-
requires :param2, :
|
1676
|
+
optional :param1, desc: "group2 param1 desc"
|
1677
|
+
requires :param2, desc: "group2 param2 desc"
|
1672
1678
|
end
|
1673
1679
|
end
|
1674
1680
|
subject.get "method" do ; end
|
@@ -1676,60 +1682,60 @@ describe Grape::API do
|
|
1676
1682
|
subject.routes.map { |route|
|
1677
1683
|
route.route_params
|
1678
1684
|
}.should eq [{
|
1679
|
-
"group1[param1]" => { :
|
1680
|
-
"group1[param2]" => { :
|
1681
|
-
"group2[param1]" => { :
|
1682
|
-
"group2[param2]" => { :
|
1685
|
+
"group1[param1]" => { required: false, desc: "group1 param1 desc" },
|
1686
|
+
"group1[param2]" => { required: true, desc: "group1 param2 desc" },
|
1687
|
+
"group2[param1]" => { required: false, desc: "group2 param1 desc" },
|
1688
|
+
"group2[param2]" => { required: true, desc: "group2 param2 desc" }
|
1683
1689
|
}]
|
1684
1690
|
end
|
1685
1691
|
it 'uses full name of parameters in nested groups' do
|
1686
1692
|
subject.desc "nesting"
|
1687
1693
|
subject.params do
|
1688
|
-
requires :root_param, :
|
1694
|
+
requires :root_param, desc: "root param"
|
1689
1695
|
group :nested do
|
1690
|
-
requires :nested_param, :
|
1696
|
+
requires :nested_param, desc: "nested param"
|
1691
1697
|
end
|
1692
1698
|
end
|
1693
1699
|
subject.get 'method' do ; end
|
1694
1700
|
subject.routes.map { |route|
|
1695
|
-
{ :
|
1701
|
+
{ description: route.route_description, params: route.route_params }
|
1696
1702
|
}.should eq [
|
1697
|
-
{ :
|
1698
|
-
:
|
1699
|
-
"root_param" => { :
|
1700
|
-
"nested[nested_param]" => { :
|
1703
|
+
{ description: "nesting",
|
1704
|
+
params: {
|
1705
|
+
"root_param" => { required: true, desc: "root param" },
|
1706
|
+
"nested[nested_param]" => { required: true, desc: "nested param" }
|
1701
1707
|
}
|
1702
1708
|
}
|
1703
|
-
|
1709
|
+
]
|
1704
1710
|
end
|
1705
1711
|
it 'parses parameters when no description is given' do
|
1706
1712
|
subject.params do
|
1707
|
-
requires :one_param, :
|
1713
|
+
requires :one_param, desc: "one param"
|
1708
1714
|
end
|
1709
1715
|
subject.get 'method' do ; end
|
1710
1716
|
subject.routes.map { |route|
|
1711
|
-
{ :
|
1717
|
+
{ description: route.route_description, params: route.route_params }
|
1712
1718
|
}.should eq [
|
1713
|
-
{ :
|
1714
|
-
|
1719
|
+
{ description: nil, params: { "one_param" => { required: true, desc: "one param" } } }
|
1720
|
+
]
|
1715
1721
|
end
|
1716
1722
|
it 'does not symbolize params' do
|
1717
|
-
subject.desc "Reverses a string.", { :
|
1718
|
-
{ "s" => { :
|
1723
|
+
subject.desc "Reverses a string.", { params:
|
1724
|
+
{ "s" => { desc: "string to reverse", type: "string" } }
|
1719
1725
|
}
|
1720
1726
|
subject.get 'reverse/:s' do
|
1721
1727
|
params[:s].reverse
|
1722
1728
|
end
|
1723
1729
|
subject.routes.map { |route|
|
1724
|
-
{ :
|
1730
|
+
{ description: route.route_description, params: route.route_params }
|
1725
1731
|
}.should eq [
|
1726
|
-
{ :
|
1727
|
-
|
1732
|
+
{ description: "Reverses a string.", params: { "s" => { desc: "string to reverse", type: "string" } } }
|
1733
|
+
]
|
1728
1734
|
end
|
1729
1735
|
end
|
1730
1736
|
|
1731
1737
|
describe '.mount' do
|
1732
|
-
let(:mounted_app){ lambda{|env| [200, {}, ["MOUNTED"]]} }
|
1738
|
+
let(:mounted_app) { lambda { |env| [200, { }, ["MOUNTED"]] } }
|
1733
1739
|
|
1734
1740
|
context 'with a bare rack app' do
|
1735
1741
|
before do
|
@@ -1747,7 +1753,7 @@ describe Grape::API do
|
|
1747
1753
|
end
|
1748
1754
|
|
1749
1755
|
it 'is able to cascade' do
|
1750
|
-
subject.mount lambda{ |env|
|
1756
|
+
subject.mount lambda { |env|
|
1751
1757
|
headers = {}
|
1752
1758
|
headers['X-Cascade'] == 'pass' unless env['PATH_INFO'].include?('boo')
|
1753
1759
|
[200, headers, ["Farfegnugen"]]
|
@@ -1770,7 +1776,7 @@ describe Grape::API do
|
|
1770
1776
|
|
1771
1777
|
context 'mounting an API' do
|
1772
1778
|
it 'applies the settings of the mounting api' do
|
1773
|
-
subject.version 'v1', :
|
1779
|
+
subject.version 'v1', using: :path
|
1774
1780
|
|
1775
1781
|
subject.namespace :cool do
|
1776
1782
|
app = Class.new(Grape::API)
|
@@ -1786,7 +1792,7 @@ describe Grape::API do
|
|
1786
1792
|
end
|
1787
1793
|
|
1788
1794
|
it 'applies the settings to nested mounted apis' do
|
1789
|
-
subject.version 'v1', :
|
1795
|
+
subject.version 'v1', using: :path
|
1790
1796
|
|
1791
1797
|
subject.namespace :cool do
|
1792
1798
|
inner_app = Class.new(Grape::API)
|
@@ -1821,13 +1827,13 @@ describe Grape::API do
|
|
1821
1827
|
it 'collects the routes of the mounted api' do
|
1822
1828
|
subject.namespace :cool do
|
1823
1829
|
app = Class.new(Grape::API)
|
1824
|
-
app.get('/awesome') {}
|
1825
|
-
app.post('/sauce') {}
|
1830
|
+
app.get('/awesome') { }
|
1831
|
+
app.post('/sauce') { }
|
1826
1832
|
mount app
|
1827
1833
|
end
|
1828
1834
|
subject.routes.size.should == 2
|
1829
|
-
subject.routes.first.route_path.should =~
|
1830
|
-
subject.routes.last.route_path.should =~
|
1835
|
+
subject.routes.first.route_path.should =~ %r{\/cool\/awesome}
|
1836
|
+
subject.routes.last.route_path.should =~ %r{\/cool\/sauce}
|
1831
1837
|
end
|
1832
1838
|
|
1833
1839
|
it 'mounts on a path' do
|
@@ -1889,8 +1895,8 @@ describe Grape::API do
|
|
1889
1895
|
subject.format :json
|
1890
1896
|
subject.get '/endpoint/options' do
|
1891
1897
|
{
|
1892
|
-
:
|
1893
|
-
:
|
1898
|
+
path: options[:path],
|
1899
|
+
source_location: source.source_location
|
1894
1900
|
}
|
1895
1901
|
end
|
1896
1902
|
end
|
@@ -1926,7 +1932,7 @@ describe Grape::API do
|
|
1926
1932
|
subject.get '/description' do
|
1927
1933
|
route.route_description
|
1928
1934
|
end
|
1929
|
-
subject.desc 'returns parameters', { :
|
1935
|
+
subject.desc 'returns parameters', { params: { "x" => "y" } }
|
1930
1936
|
subject.get '/params/:id' do
|
1931
1937
|
route.route_params[params[:id]]
|
1932
1938
|
end
|
@@ -1947,40 +1953,40 @@ describe Grape::API do
|
|
1947
1953
|
subject.format :txt
|
1948
1954
|
subject.content_type :json, "application/json"
|
1949
1955
|
subject.get '/meaning_of_life' do
|
1950
|
-
{ :
|
1956
|
+
{ meaning_of_life: 42 }
|
1951
1957
|
end
|
1952
1958
|
end
|
1953
1959
|
it 'forces txt without an extension' do
|
1954
1960
|
get '/meaning_of_life'
|
1955
|
-
last_response.body.should == { :
|
1961
|
+
last_response.body.should == { meaning_of_life: 42 }.to_s
|
1956
1962
|
end
|
1957
1963
|
it 'does not force txt with an extension' do
|
1958
1964
|
get '/meaning_of_life.json'
|
1959
|
-
last_response.body.should == { :
|
1965
|
+
last_response.body.should == { meaning_of_life: 42 }.to_json
|
1960
1966
|
end
|
1961
1967
|
it 'forces txt from a non-accepting header' do
|
1962
1968
|
get '/meaning_of_life', {}, { 'HTTP_ACCEPT' => 'application/json' }
|
1963
|
-
last_response.body.should == { :
|
1969
|
+
last_response.body.should == { meaning_of_life: 42 }.to_s
|
1964
1970
|
end
|
1965
1971
|
end
|
1966
1972
|
context ':txt only' do
|
1967
1973
|
before(:each) do
|
1968
1974
|
subject.format :txt
|
1969
1975
|
subject.get '/meaning_of_life' do
|
1970
|
-
{ :
|
1976
|
+
{ meaning_of_life: 42 }
|
1971
1977
|
end
|
1972
1978
|
end
|
1973
1979
|
it 'forces txt without an extension' do
|
1974
1980
|
get '/meaning_of_life'
|
1975
|
-
last_response.body.should == { :
|
1981
|
+
last_response.body.should == { meaning_of_life: 42 }.to_s
|
1976
1982
|
end
|
1977
1983
|
it 'forces txt with the wrong extension' do
|
1978
1984
|
get '/meaning_of_life.json'
|
1979
|
-
last_response.body.should == { :
|
1985
|
+
last_response.body.should == { meaning_of_life: 42 }.to_s
|
1980
1986
|
end
|
1981
1987
|
it 'forces txt from a non-accepting header' do
|
1982
1988
|
get '/meaning_of_life', {}, { 'HTTP_ACCEPT' => 'application/json' }
|
1983
|
-
last_response.body.should == { :
|
1989
|
+
last_response.body.should == { meaning_of_life: 42 }.to_s
|
1984
1990
|
end
|
1985
1991
|
end
|
1986
1992
|
context ':json' do
|
@@ -1988,33 +1994,33 @@ describe Grape::API do
|
|
1988
1994
|
subject.format :json
|
1989
1995
|
subject.content_type :txt, "text/plain"
|
1990
1996
|
subject.get '/meaning_of_life' do
|
1991
|
-
{ :
|
1997
|
+
{ meaning_of_life: 42 }
|
1992
1998
|
end
|
1993
1999
|
end
|
1994
2000
|
it 'forces json without an extension' do
|
1995
2001
|
get '/meaning_of_life'
|
1996
|
-
last_response.body.should == { :
|
2002
|
+
last_response.body.should == { meaning_of_life: 42 }.to_json
|
1997
2003
|
end
|
1998
2004
|
it 'does not force json with an extension' do
|
1999
2005
|
get '/meaning_of_life.txt'
|
2000
|
-
last_response.body.should == { :
|
2006
|
+
last_response.body.should == { meaning_of_life: 42 }.to_s
|
2001
2007
|
end
|
2002
2008
|
it 'forces json from a non-accepting header' do
|
2003
2009
|
get '/meaning_of_life', {}, { 'HTTP_ACCEPT' => 'text/html' }
|
2004
|
-
last_response.body.should == { :
|
2010
|
+
last_response.body.should == { meaning_of_life: 42 }.to_json
|
2005
2011
|
end
|
2006
2012
|
it 'can be overwritten with an explicit content type' do
|
2007
2013
|
subject.get '/meaning_of_life_with_content_type' do
|
2008
2014
|
content_type "text/plain"
|
2009
|
-
{ :
|
2015
|
+
{ meaning_of_life: 42 }.to_s
|
2010
2016
|
end
|
2011
2017
|
get '/meaning_of_life_with_content_type'
|
2012
|
-
last_response.body.should == { :
|
2018
|
+
last_response.body.should == { meaning_of_life: 42 }.to_s
|
2013
2019
|
end
|
2014
2020
|
it 'raised :error from middleware' do
|
2015
2021
|
middleware = Class.new(Grape::Middleware::Base) do
|
2016
2022
|
def before
|
2017
|
-
throw :error, :
|
2023
|
+
throw :error, message: "Unauthorized", status: 42
|
2018
2024
|
end
|
2019
2025
|
end
|
2020
2026
|
subject.use middleware
|
@@ -2023,7 +2029,7 @@ describe Grape::API do
|
|
2023
2029
|
end
|
2024
2030
|
get "/"
|
2025
2031
|
last_response.status.should == 42
|
2026
|
-
last_response.body.should == { :
|
2032
|
+
last_response.body.should == { error: "Unauthorized" }.to_json
|
2027
2033
|
end
|
2028
2034
|
|
2029
2035
|
end
|
@@ -2031,7 +2037,7 @@ describe Grape::API do
|
|
2031
2037
|
before(:each) do
|
2032
2038
|
class SerializableHashExample
|
2033
2039
|
def serializable_hash
|
2034
|
-
{ :
|
2040
|
+
{ abc: 'def' }
|
2035
2041
|
end
|
2036
2042
|
end
|
2037
2043
|
subject.format :serializable_hash
|
@@ -2052,7 +2058,7 @@ describe Grape::API do
|
|
2052
2058
|
end
|
2053
2059
|
it 'array' do
|
2054
2060
|
subject.get '/examples' do
|
2055
|
-
[
|
2061
|
+
[SerializableHashExample.new, SerializableHashExample.new]
|
2056
2062
|
end
|
2057
2063
|
get '/examples'
|
2058
2064
|
last_response.body.should == '[{"abc":"def"},{"abc":"def"}]'
|
@@ -2080,7 +2086,7 @@ XML
|
|
2080
2086
|
ActiveSupport::OrderedHash[
|
2081
2087
|
:example1, "example1",
|
2082
2088
|
:example2, "example2"
|
2083
|
-
|
2089
|
+
]
|
2084
2090
|
end
|
2085
2091
|
get '/example'
|
2086
2092
|
last_response.status.should == 200
|
@@ -2094,7 +2100,7 @@ XML
|
|
2094
2100
|
end
|
2095
2101
|
it 'array' do
|
2096
2102
|
subject.get "/example" do
|
2097
|
-
[
|
2103
|
+
["example1", "example2"]
|
2098
2104
|
end
|
2099
2105
|
get '/example'
|
2100
2106
|
last_response.status.should == 200
|
@@ -2109,7 +2115,7 @@ XML
|
|
2109
2115
|
it 'raised :error from middleware' do
|
2110
2116
|
middleware = Class.new(Grape::Middleware::Base) do
|
2111
2117
|
def before
|
2112
|
-
throw :error, :
|
2118
|
+
throw :error, message: "Unauthorized", status: 42
|
2113
2119
|
end
|
2114
2120
|
end
|
2115
2121
|
subject.use middleware
|
@@ -2131,22 +2137,22 @@ XML
|
|
2131
2137
|
context "catch-all" do
|
2132
2138
|
before do
|
2133
2139
|
api1 = Class.new(Grape::API)
|
2134
|
-
api1.version 'v1', :
|
2140
|
+
api1.version 'v1', using: :path
|
2135
2141
|
api1.get "hello" do
|
2136
2142
|
"v1"
|
2137
2143
|
end
|
2138
2144
|
api2 = Class.new(Grape::API)
|
2139
|
-
api2.version 'v2', :
|
2145
|
+
api2.version 'v2', using: :path
|
2140
2146
|
api2.get "hello" do
|
2141
2147
|
"v2"
|
2142
2148
|
end
|
2143
2149
|
subject.mount api1
|
2144
2150
|
subject.mount api2
|
2145
2151
|
end
|
2146
|
-
[
|
2152
|
+
[true, false].each do |anchor|
|
2147
2153
|
it "anchor=#{anchor}" do
|
2148
|
-
subject.route :any, '*path', :
|
2149
|
-
error!("Unrecognized request path: #{params[:path]} - #{env['PATH_INFO']}#{env['SCRIPT_NAME']}", 404)
|
2154
|
+
subject.route :any, '*path', anchor: anchor do
|
2155
|
+
error!("Unrecognized request path: #{params[:path] } - #{env['PATH_INFO'] }#{env['SCRIPT_NAME'] }", 404)
|
2150
2156
|
end
|
2151
2157
|
get "/v1/hello"
|
2152
2158
|
last_response.status.should == 200
|
@@ -2164,13 +2170,13 @@ XML
|
|
2164
2170
|
context "cascading" do
|
2165
2171
|
context "via version" do
|
2166
2172
|
it "cascades" do
|
2167
|
-
subject.version 'v1', :
|
2173
|
+
subject.version 'v1', using: :path, cascade: true
|
2168
2174
|
get "/v1/hello"
|
2169
2175
|
last_response.status.should == 404
|
2170
2176
|
last_response.headers["X-Cascade"].should == "pass"
|
2171
2177
|
end
|
2172
2178
|
it "does not cascade" do
|
2173
|
-
subject.version 'v2', :
|
2179
|
+
subject.version 'v2', using: :path, cascade: false
|
2174
2180
|
get "/v2/hello"
|
2175
2181
|
last_response.status.should == 404
|
2176
2182
|
last_response.headers.keys.should_not include "X-Cascade"
|
@@ -2191,4 +2197,17 @@ XML
|
|
2191
2197
|
end
|
2192
2198
|
end
|
2193
2199
|
end
|
2200
|
+
|
2201
|
+
context 'with json default_error_formatter' do
|
2202
|
+
it 'returns json error' do
|
2203
|
+
subject.content_type :json, "application/json"
|
2204
|
+
subject.default_error_formatter :json
|
2205
|
+
subject.get '/something' do
|
2206
|
+
'foo'
|
2207
|
+
end
|
2208
|
+
get '/something'
|
2209
|
+
last_response.status.should == 406
|
2210
|
+
last_response.body.should == "{\"error\":\"The requested format 'txt' is not supported.\"}"
|
2211
|
+
end
|
2212
|
+
end
|
2194
2213
|
end
|