grape 0.16.2 → 0.17.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.
Potentially problematic release.
This version of grape might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Appraisals +4 -0
- data/CHANGELOG.md +54 -27
- data/Dangerfile +80 -0
- data/Gemfile +23 -0
- data/Gemfile.lock +61 -27
- data/README.md +135 -7
- data/Rakefile +34 -30
- data/UPGRADING.md +21 -0
- data/gemfiles/rack_1.5.2.gemfile +21 -0
- data/gemfiles/rails_3.gemfile +22 -1
- data/gemfiles/rails_4.gemfile +21 -0
- data/gemfiles/rails_5.gemfile +34 -0
- data/grape.gemspec +0 -14
- data/lib/grape.rb +2 -0
- data/lib/grape/api.rb +9 -2
- data/lib/grape/dsl/headers.rb +1 -1
- data/lib/grape/dsl/inside_route.rb +15 -17
- data/lib/grape/dsl/middleware.rb +15 -1
- data/lib/grape/dsl/parameters.rb +16 -14
- data/lib/grape/dsl/request_response.rb +24 -20
- data/lib/grape/dsl/routing.rb +11 -10
- data/lib/grape/dsl/settings.rb +16 -0
- data/lib/grape/endpoint.rb +77 -60
- data/lib/grape/exceptions/validation.rb +5 -2
- data/lib/grape/exceptions/validation_array_errors.rb +11 -0
- data/lib/grape/formatter/xml.rb +1 -1
- data/lib/grape/middleware/error.rb +34 -25
- data/lib/grape/middleware/formatter.rb +9 -9
- data/lib/grape/middleware/stack.rb +110 -0
- data/lib/grape/middleware/versioner.rb +1 -1
- data/lib/grape/middleware/versioner/accept_version_header.rb +1 -1
- data/lib/grape/middleware/versioner/header.rb +3 -3
- data/lib/grape/path.rb +10 -2
- data/lib/grape/request.rb +1 -1
- data/lib/grape/router.rb +10 -19
- data/lib/grape/router/pattern.rb +2 -2
- data/lib/grape/router/route.rb +3 -3
- data/lib/grape/util/content_types.rb +1 -1
- data/lib/grape/util/inheritable_setting.rb +7 -2
- data/lib/grape/util/reverse_stackable_values.rb +45 -0
- data/lib/grape/util/stackable_values.rb +10 -11
- data/lib/grape/validations/attributes_iterator.rb +32 -7
- data/lib/grape/validations/params_scope.rb +33 -21
- data/lib/grape/validations/types.rb +4 -4
- data/lib/grape/validations/types/build_coercer.rb +9 -1
- data/lib/grape/validations/validators/all_or_none.rb +2 -2
- data/lib/grape/validations/validators/allow_blank.rb +10 -11
- data/lib/grape/validations/validators/at_least_one_of.rb +1 -1
- data/lib/grape/validations/validators/base.rb +16 -6
- data/lib/grape/validations/validators/coerce.rb +3 -6
- data/lib/grape/validations/validators/default.rb +26 -1
- data/lib/grape/validations/validators/exactly_one_of.rb +1 -1
- data/lib/grape/validations/validators/mutual_exclusion.rb +1 -1
- data/lib/grape/validations/validators/presence.rb +1 -1
- data/lib/grape/validations/validators/regexp.rb +1 -1
- data/lib/grape/validations/validators/values.rb +1 -1
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api/custom_validations_spec.rb +3 -3
- data/spec/grape/api/parameters_modification_spec.rb +41 -0
- data/spec/grape/api_spec.rb +335 -108
- data/spec/grape/dsl/logger_spec.rb +1 -1
- data/spec/grape/dsl/middleware_spec.rb +25 -5
- data/spec/grape/dsl/request_response_spec.rb +20 -6
- data/spec/grape/dsl/validations_spec.rb +1 -1
- data/spec/grape/endpoint_spec.rb +166 -23
- data/spec/grape/entity_spec.rb +0 -2
- data/spec/grape/exceptions/body_parse_errors_spec.rb +37 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +5 -5
- data/spec/grape/exceptions/validation_spec.rb +10 -0
- data/spec/grape/integration/global_namespace_function_spec.rb +1 -1
- data/spec/grape/integration/rack_spec.rb +1 -1
- data/spec/grape/middleware/base_spec.rb +1 -1
- data/spec/grape/middleware/exception_spec.rb +2 -2
- data/spec/grape/middleware/formatter_spec.rb +4 -4
- data/spec/grape/middleware/stack_spec.rb +123 -0
- data/spec/grape/middleware/versioner/header_spec.rb +6 -6
- data/spec/grape/request_spec.rb +22 -22
- data/spec/grape/util/inheritable_setting_spec.rb +23 -0
- data/spec/grape/util/reverse_stackable_values_spec.rb +131 -0
- data/spec/grape/validations/params_scope_spec.rb +88 -1
- data/spec/grape/validations/validators/allow_blank_spec.rb +5 -0
- data/spec/grape/validations/validators/coerce_spec.rb +5 -5
- data/spec/grape/validations/validators/default_spec.rb +44 -0
- data/spec/grape/validations/validators/values_spec.rb +1 -1
- data/spec/grape/validations_spec.rb +36 -17
- data/spec/spec_helper.rb +1 -8
- data/spec/support/versioned_helpers.rb +3 -3
- metadata +13 -188
- data/gemfiles/rails_3.gemfile.lock +0 -225
- data/pkg/grape-0.16.1.gem +0 -0
- data/pkg/patch.diff +0 -24
- data/tmp/Gemfile.lock +0 -63
@@ -22,7 +22,7 @@ describe Rack do
|
|
22
22
|
unless RUBY_PLATFORM == 'java'
|
23
23
|
major, minor, patch = Rack.release.split('.').map(&:to_i)
|
24
24
|
patch ||= 0 # rack <= 1.5.2 does not specify patch version
|
25
|
-
pending 'Rack 1.5.3 or 1.6.1 required' unless major >= 1 && ((minor == 5 && patch >= 3) || (minor >= 6))
|
25
|
+
pending 'Rack 1.5.3 or 1.6.1 required' unless major >= 2 || (major >= 1 && ((minor == 5 && patch >= 3) || (minor >= 6)))
|
26
26
|
end
|
27
27
|
|
28
28
|
expect(JSON.parse(app.call(env)[2].body.first)['params_keys']).to match_array('test')
|
@@ -6,7 +6,7 @@ describe Grape::Middleware::Error do
|
|
6
6
|
class ExceptionApp
|
7
7
|
class << self
|
8
8
|
def call(_env)
|
9
|
-
|
9
|
+
raise 'rain!'
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
@@ -44,7 +44,7 @@ describe Grape::Middleware::Error do
|
|
44
44
|
class CustomErrorApp
|
45
45
|
class << self
|
46
46
|
def call(_env)
|
47
|
-
|
47
|
+
raise CustomError, status: 400, message: 'failed validation'
|
48
48
|
end
|
49
49
|
end
|
50
50
|
end
|
@@ -17,7 +17,7 @@ describe Grape::Middleware::Formatter do
|
|
17
17
|
@body = ['foo']
|
18
18
|
@body.instance_eval do
|
19
19
|
def to_json
|
20
|
-
"
|
20
|
+
'"bar"'
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
@@ -28,7 +28,7 @@ describe Grape::Middleware::Formatter do
|
|
28
28
|
@body = { 'foos' => [{ 'bar' => 'baz' }] }
|
29
29
|
@body.instance_eval do
|
30
30
|
def to_json
|
31
|
-
|
31
|
+
'{"foos":[{"bar":"baz"}] }'
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
@@ -54,7 +54,7 @@ describe Grape::Middleware::Formatter do
|
|
54
54
|
end
|
55
55
|
|
56
56
|
it 'rescues formatter-specific exceptions' do
|
57
|
-
allow(formatter).to receive(:call) {
|
57
|
+
allow(formatter).to receive(:call) { raise Grape::Exceptions::InvalidFormatter.new(String, 'xml') }
|
58
58
|
|
59
59
|
expect do
|
60
60
|
catch(:error) { subject.call('PATH_INFO' => '/somewhere.xml', 'HTTP_ACCEPT' => 'application/json') }
|
@@ -62,7 +62,7 @@ describe Grape::Middleware::Formatter do
|
|
62
62
|
end
|
63
63
|
|
64
64
|
it 'does not rescue other exceptions' do
|
65
|
-
allow(formatter).to receive(:call) {
|
65
|
+
allow(formatter).to receive(:call) { raise StandardError }
|
66
66
|
|
67
67
|
expect do
|
68
68
|
catch(:error) { subject.call('PATH_INFO' => '/somewhere.xml', 'HTTP_ACCEPT' => 'application/json') }
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Grape::Middleware::Stack do
|
4
|
+
module StackSpec
|
5
|
+
class FooMiddleware; end
|
6
|
+
class BarMiddleware; end
|
7
|
+
class BlockMiddleware
|
8
|
+
attr_reader :block
|
9
|
+
def initialize(&block)
|
10
|
+
@block = block
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:proc) { ->() {} }
|
16
|
+
let(:others) { [[:use, StackSpec::BarMiddleware], [:insert_before, StackSpec::BarMiddleware, StackSpec::BlockMiddleware, proc]] }
|
17
|
+
|
18
|
+
subject { Grape::Middleware::Stack.new }
|
19
|
+
|
20
|
+
before do
|
21
|
+
subject.use StackSpec::FooMiddleware
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#use' do
|
25
|
+
it 'pushes a middleware class onto the stack' do
|
26
|
+
expect { subject.use StackSpec::BarMiddleware }
|
27
|
+
.to change { subject.size }.by(1)
|
28
|
+
expect(subject.last).to eq(StackSpec::BarMiddleware)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'pushes a middleware class with arguments onto the stack' do
|
32
|
+
expect { subject.use StackSpec::BarMiddleware, false, my_arg: 42 }
|
33
|
+
.to change { subject.size }.by(1)
|
34
|
+
expect(subject.last).to eq(StackSpec::BarMiddleware)
|
35
|
+
expect(subject.last.args).to eq([false, { my_arg: 42 }])
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'pushes a middleware class with block arguments onto the stack' do
|
39
|
+
expect { subject.use StackSpec::BlockMiddleware, &proc }
|
40
|
+
.to change { subject.size }.by(1)
|
41
|
+
expect(subject.last).to eq(StackSpec::BlockMiddleware)
|
42
|
+
expect(subject.last.args).to eq([])
|
43
|
+
expect(subject.last.block).to eq(proc)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#insert' do
|
48
|
+
it 'inserts a middleware class at the integer index' do
|
49
|
+
expect { subject.insert 0, StackSpec::BarMiddleware }
|
50
|
+
.to change { subject.size }.by(1)
|
51
|
+
expect(subject[0]).to eq(StackSpec::BarMiddleware)
|
52
|
+
expect(subject[1]).to eq(StackSpec::FooMiddleware)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#insert_before' do
|
57
|
+
it 'inserts a middleware before another middleware class' do
|
58
|
+
expect { subject.insert_before StackSpec::FooMiddleware, StackSpec::BarMiddleware }
|
59
|
+
.to change { subject.size }.by(1)
|
60
|
+
expect(subject[0]).to eq(StackSpec::BarMiddleware)
|
61
|
+
expect(subject[1]).to eq(StackSpec::FooMiddleware)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'raises an error on an invalid index' do
|
65
|
+
expect { subject.insert_before StackSpec::BlockMiddleware, StackSpec::BarMiddleware }
|
66
|
+
.to raise_error(RuntimeError, 'No such middleware to insert before: StackSpec::BlockMiddleware')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#insert_after' do
|
71
|
+
it 'inserts a middleware after another middleware class' do
|
72
|
+
expect { subject.insert_after StackSpec::FooMiddleware, StackSpec::BarMiddleware }
|
73
|
+
.to change { subject.size }.by(1)
|
74
|
+
expect(subject[1]).to eq(StackSpec::BarMiddleware)
|
75
|
+
expect(subject[0]).to eq(StackSpec::FooMiddleware)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'raises an error on an invalid index' do
|
79
|
+
expect { subject.insert_after StackSpec::BlockMiddleware, StackSpec::BarMiddleware }
|
80
|
+
.to raise_error(RuntimeError, 'No such middleware to insert after: StackSpec::BlockMiddleware')
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe '#merge_with' do
|
85
|
+
it 'applies a collection of operations and middlewares' do
|
86
|
+
expect { subject.merge_with(others) }
|
87
|
+
.to change { subject.size }.by(2)
|
88
|
+
expect(subject[0]).to eq(StackSpec::FooMiddleware)
|
89
|
+
expect(subject[1]).to eq(StackSpec::BlockMiddleware)
|
90
|
+
expect(subject[2]).to eq(StackSpec::BarMiddleware)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe '#build' do
|
95
|
+
it 'returns a rack builder instance' do
|
96
|
+
expect(subject.build).to be_instance_of(Rack::Builder)
|
97
|
+
end
|
98
|
+
|
99
|
+
context 'when @others are present' do
|
100
|
+
let(:others) { [[:insert_after, Grape::Middleware::Formatter, StackSpec::BarMiddleware]] }
|
101
|
+
|
102
|
+
it 'applies the middleware specs stored in @others' do
|
103
|
+
subject.concat others
|
104
|
+
subject.use Grape::Middleware::Formatter
|
105
|
+
subject.build
|
106
|
+
expect(subject[0]).to eq StackSpec::FooMiddleware
|
107
|
+
expect(subject[1]).to eq Grape::Middleware::Formatter
|
108
|
+
expect(subject[2]).to eq StackSpec::BarMiddleware
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe '#concat' do
|
114
|
+
it 'adds non :use specs to @others' do
|
115
|
+
expect { subject.concat others }.to change(subject, :others).from([]).to([[others.last]])
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'calls +merge_with+ with the :use specs' do
|
119
|
+
expect(subject).to receive(:merge_with).with [[:use, StackSpec::BarMiddleware]]
|
120
|
+
subject.concat others
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -263,13 +263,13 @@ describe Grape::Middleware::Versioner::Header do
|
|
263
263
|
end
|
264
264
|
|
265
265
|
context 'when there are multiple versions with complex vendor specified with rescue_from :all' do
|
266
|
-
subject
|
266
|
+
subject do
|
267
267
|
Class.new(Grape::API) do
|
268
268
|
rescue_from :all
|
269
269
|
end
|
270
|
-
|
270
|
+
end
|
271
271
|
|
272
|
-
let(:v1_app)
|
272
|
+
let(:v1_app) do
|
273
273
|
Class.new(Grape::API) do
|
274
274
|
version 'v1', using: :header, vendor: 'test.a-cool_resource', cascade: false, strict: true
|
275
275
|
content_type :v1_test, 'application/vnd.test.a-cool_resource-v1+json'
|
@@ -282,9 +282,9 @@ describe Grape::Middleware::Versioner::Header do
|
|
282
282
|
end
|
283
283
|
end
|
284
284
|
end
|
285
|
-
|
285
|
+
end
|
286
286
|
|
287
|
-
let(:v2_app)
|
287
|
+
let(:v2_app) do
|
288
288
|
Class.new(Grape::API) do
|
289
289
|
version 'v2', using: :header, vendor: 'test.a-cool_resource', strict: true
|
290
290
|
content_type :v2_test, 'application/vnd.test.a-cool_resource-v2+json'
|
@@ -297,7 +297,7 @@ describe Grape::Middleware::Versioner::Header do
|
|
297
297
|
end
|
298
298
|
end
|
299
299
|
end
|
300
|
-
|
300
|
+
end
|
301
301
|
|
302
302
|
def app
|
303
303
|
subject.mount v2_app
|
data/spec/grape/request_spec.rb
CHANGED
@@ -4,47 +4,47 @@ module Grape
|
|
4
4
|
describe Request do
|
5
5
|
let(:default_method) { 'GET' }
|
6
6
|
let(:default_params) { {} }
|
7
|
-
let(:default_options)
|
7
|
+
let(:default_options) do
|
8
8
|
{
|
9
9
|
method: method,
|
10
10
|
params: params
|
11
11
|
}
|
12
|
-
|
13
|
-
let(:default_env)
|
12
|
+
end
|
13
|
+
let(:default_env) do
|
14
14
|
Rack::MockRequest.env_for('/', options)
|
15
|
-
|
15
|
+
end
|
16
16
|
let(:method) { default_method }
|
17
17
|
let(:params) { default_params }
|
18
18
|
let(:options) { default_options }
|
19
19
|
let(:env) { default_env }
|
20
20
|
|
21
|
-
let(:request)
|
21
|
+
let(:request) do
|
22
22
|
Grape::Request.new(env)
|
23
|
-
|
23
|
+
end
|
24
24
|
|
25
25
|
describe '#params' do
|
26
|
-
let(:params)
|
26
|
+
let(:params) do
|
27
27
|
{
|
28
28
|
a: '123',
|
29
29
|
b: 'xyz'
|
30
30
|
}
|
31
|
-
|
31
|
+
end
|
32
32
|
|
33
33
|
it 'returns params' do
|
34
34
|
expect(request.params).to eq('a' => '123', 'b' => 'xyz')
|
35
35
|
end
|
36
36
|
|
37
37
|
describe 'with grape.routing_args' do
|
38
|
-
let(:options)
|
38
|
+
let(:options) do
|
39
39
|
default_options.merge('grape.routing_args' => routing_args)
|
40
|
-
|
41
|
-
let(:routing_args)
|
40
|
+
end
|
41
|
+
let(:routing_args) do
|
42
42
|
{
|
43
43
|
version: '123',
|
44
44
|
route_info: '456',
|
45
45
|
c: 'ccc'
|
46
46
|
}
|
47
|
-
|
47
|
+
end
|
48
48
|
|
49
49
|
it 'cuts version and route_info' do
|
50
50
|
expect(request.params).to eq('a' => '123', 'b' => 'xyz', 'c' => 'ccc')
|
@@ -53,16 +53,16 @@ module Grape
|
|
53
53
|
end
|
54
54
|
|
55
55
|
describe '#headers' do
|
56
|
-
let(:options)
|
56
|
+
let(:options) do
|
57
57
|
default_options.merge(request_headers)
|
58
|
-
|
58
|
+
end
|
59
59
|
|
60
60
|
describe 'with http headers in env' do
|
61
|
-
let(:request_headers)
|
61
|
+
let(:request_headers) do
|
62
62
|
{
|
63
63
|
'HTTP_X_GRAPE_IS_COOL' => 'yeah'
|
64
64
|
}
|
65
|
-
|
65
|
+
end
|
66
66
|
|
67
67
|
it 'cuts HTTP_ prefix and capitalizes header name words' do
|
68
68
|
expect(request.headers).to eq('X-Grape-Is-Cool' => 'yeah')
|
@@ -70,11 +70,11 @@ module Grape
|
|
70
70
|
end
|
71
71
|
|
72
72
|
describe 'with non-HTTP_* stuff in env' do
|
73
|
-
let(:request_headers)
|
73
|
+
let(:request_headers) do
|
74
74
|
{
|
75
75
|
'HTP_X_GRAPE_ENTITY_TOO' => 'but now we are testing Grape'
|
76
76
|
}
|
77
|
-
|
77
|
+
end
|
78
78
|
|
79
79
|
it 'does not include them' do
|
80
80
|
expect(request.headers).to eq({})
|
@@ -82,14 +82,14 @@ module Grape
|
|
82
82
|
end
|
83
83
|
|
84
84
|
describe 'with symbolic header names' do
|
85
|
-
let(:request_headers)
|
85
|
+
let(:request_headers) do
|
86
86
|
{
|
87
87
|
HTTP_GRAPE_LIKES_SYMBOLIC: 'it is true'
|
88
88
|
}
|
89
|
-
|
90
|
-
let(:env)
|
89
|
+
end
|
90
|
+
let(:env) do
|
91
91
|
default_env.merge(request_headers)
|
92
|
-
|
92
|
+
end
|
93
93
|
|
94
94
|
it 'converts them to string' do
|
95
95
|
expect(request.headers).to eq('Grape-Likes-Symbolic' => 'it is true')
|
@@ -12,6 +12,7 @@ module Grape
|
|
12
12
|
settings.namespace[:namespace_thing] = :namespace_foo_bar
|
13
13
|
settings.namespace_inheritable[:namespace_inheritable_thing] = :namespace_inheritable_foo_bar
|
14
14
|
settings.namespace_stackable[:namespace_stackable_thing] = :namespace_stackable_foo_bar
|
15
|
+
settings.namespace_reverse_stackable[:namespace_reverse_stackable_thing] = :namespace_reverse_stackable_foo_bar
|
15
16
|
settings.route[:route_thing] = :route_foo_bar
|
16
17
|
end
|
17
18
|
end
|
@@ -21,6 +22,7 @@ module Grape
|
|
21
22
|
settings.namespace[:namespace_thing] = :namespace_foo_bar_other
|
22
23
|
settings.namespace_inheritable[:namespace_inheritable_thing] = :namespace_inheritable_foo_bar_other
|
23
24
|
settings.namespace_stackable[:namespace_stackable_thing] = :namespace_stackable_foo_bar_other
|
25
|
+
settings.namespace_reverse_stackable[:namespace_reverse_stackable_thing] = :namespace_reverse_stackable_foo_bar_other
|
24
26
|
settings.route[:route_thing] = :route_foo_bar_other
|
25
27
|
end
|
26
28
|
end
|
@@ -118,6 +120,16 @@ module Grape
|
|
118
120
|
end
|
119
121
|
end
|
120
122
|
|
123
|
+
describe '#namespace_reverse_stackable' do
|
124
|
+
it 'works with reverse stackable values' do
|
125
|
+
expect(subject.namespace_reverse_stackable[:namespace_reverse_stackable_thing]).to eq [:namespace_reverse_stackable_foo_bar]
|
126
|
+
|
127
|
+
subject.inherit_from other_parent
|
128
|
+
|
129
|
+
expect(subject.namespace_reverse_stackable[:namespace_reverse_stackable_thing]).to eq [:namespace_reverse_stackable_foo_bar_other]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
121
133
|
describe '#route' do
|
122
134
|
it 'sets a value until the next route' do
|
123
135
|
subject.route[:some_thing] = :foo_bar
|
@@ -184,6 +196,14 @@ module Grape
|
|
184
196
|
expect(cloned_obj.namespace_stackable[:namespace_stackable_thing]).to eq [:namespace_stackable_foo_bar]
|
185
197
|
end
|
186
198
|
|
199
|
+
it 'decouples namespace reverse stackable values' do
|
200
|
+
expect(cloned_obj.namespace_reverse_stackable[:namespace_reverse_stackable_thing]).to eq [:namespace_reverse_stackable_foo_bar]
|
201
|
+
|
202
|
+
subject.namespace_reverse_stackable[:namespace_reverse_stackable_thing] = :other_thing
|
203
|
+
expect(subject.namespace_reverse_stackable[:namespace_reverse_stackable_thing]).to eq [:other_thing, :namespace_reverse_stackable_foo_bar]
|
204
|
+
expect(cloned_obj.namespace_reverse_stackable[:namespace_reverse_stackable_thing]).to eq [:namespace_reverse_stackable_foo_bar]
|
205
|
+
end
|
206
|
+
|
187
207
|
it 'decouples route values' do
|
188
208
|
expect(cloned_obj.route[:route_thing]).to eq :route_foo_bar
|
189
209
|
|
@@ -202,6 +222,7 @@ module Grape
|
|
202
222
|
subject.namespace[:namespace_thing] = :namespace_foo_bar
|
203
223
|
subject.namespace_inheritable[:namespace_inheritable_thing] = :namespace_inheritable_foo_bar
|
204
224
|
subject.namespace_stackable[:namespace_stackable_thing] = [:namespace_stackable_foo_bar]
|
225
|
+
subject.namespace_reverse_stackable[:namespace_reverse_stackable_thing] = [:namespace_reverse_stackable_foo_bar]
|
205
226
|
subject.route[:route_thing] = :route_foo_bar
|
206
227
|
|
207
228
|
expect(subject.to_hash).to include(global: { global_thing: :global_foo_bar })
|
@@ -209,6 +230,8 @@ module Grape
|
|
209
230
|
expect(subject.to_hash).to include(namespace_inheritable: {
|
210
231
|
namespace_inheritable_thing: :namespace_inheritable_foo_bar })
|
211
232
|
expect(subject.to_hash).to include(namespace_stackable: { namespace_stackable_thing: [:namespace_stackable_foo_bar, [:namespace_stackable_foo_bar]] })
|
233
|
+
expect(subject.to_hash).to include(namespace_reverse_stackable:
|
234
|
+
{ namespace_reverse_stackable_thing: [[:namespace_reverse_stackable_foo_bar], :namespace_reverse_stackable_foo_bar] })
|
212
235
|
expect(subject.to_hash).to include(route: { route_thing: :route_foo_bar })
|
213
236
|
end
|
214
237
|
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
module Grape
|
3
|
+
module Util
|
4
|
+
describe ReverseStackableValues do
|
5
|
+
let(:parent) { described_class.new }
|
6
|
+
subject { described_class.new(parent) }
|
7
|
+
|
8
|
+
describe '#keys' do
|
9
|
+
it 'returns all keys' do
|
10
|
+
subject[:some_thing] = :foo_bar
|
11
|
+
subject[:some_thing_else] = :foo_bar
|
12
|
+
expect(subject.keys).to eq [:some_thing, :some_thing_else].sort
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'returns merged keys with parent' do
|
16
|
+
parent[:some_thing] = :foo
|
17
|
+
parent[:some_thing_else] = :foo
|
18
|
+
|
19
|
+
subject[:some_thing] = :foo_bar
|
20
|
+
subject[:some_thing_more] = :foo_bar
|
21
|
+
|
22
|
+
expect(subject.keys).to eq [:some_thing, :some_thing_else, :some_thing_more].sort
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#delete' do
|
27
|
+
it 'deletes a key' do
|
28
|
+
subject[:some_thing] = :new_foo_bar
|
29
|
+
subject.delete :some_thing
|
30
|
+
expect(subject[:some_thing]).to eq []
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'does not delete parent values' do
|
34
|
+
parent[:some_thing] = :foo
|
35
|
+
subject[:some_thing] = :new_foo_bar
|
36
|
+
subject.delete :some_thing
|
37
|
+
expect(subject[:some_thing]).to eq [:foo]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#[]' do
|
42
|
+
it 'returns an array of values' do
|
43
|
+
subject[:some_thing] = :foo
|
44
|
+
expect(subject[:some_thing]).to eq [:foo]
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'returns parent value when no value is set' do
|
48
|
+
parent[:some_thing] = :foo
|
49
|
+
expect(subject[:some_thing]).to eq [:foo]
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'combines parent and actual values (actual first)' do
|
53
|
+
parent[:some_thing] = :foo
|
54
|
+
subject[:some_thing] = :foo_bar
|
55
|
+
expect(subject[:some_thing]).to eq [:foo_bar, :foo]
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'parent values are not changed' do
|
59
|
+
parent[:some_thing] = :foo
|
60
|
+
subject[:some_thing] = :foo_bar
|
61
|
+
expect(parent[:some_thing]).to eq [:foo]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#[]=' do
|
66
|
+
it 'sets a value' do
|
67
|
+
subject[:some_thing] = :foo
|
68
|
+
expect(subject[:some_thing]).to eq [:foo]
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'pushes further values' do
|
72
|
+
subject[:some_thing] = :foo
|
73
|
+
subject[:some_thing] = :bar
|
74
|
+
expect(subject[:some_thing]).to eq [:foo, :bar]
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'can handle array values' do
|
78
|
+
subject[:some_thing] = :foo
|
79
|
+
subject[:some_thing] = [:bar, :more]
|
80
|
+
expect(subject[:some_thing]).to eq [:foo, [:bar, :more]]
|
81
|
+
|
82
|
+
parent[:some_thing_else] = [:foo, :bar]
|
83
|
+
subject[:some_thing_else] = [:some, :bar, :foo]
|
84
|
+
|
85
|
+
expect(subject[:some_thing_else]).to eq [[:some, :bar, :foo], [:foo, :bar]]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe '#to_hash' do
|
90
|
+
it 'returns a Hash representation' do
|
91
|
+
parent[:some_thing] = :foo
|
92
|
+
subject[:some_thing] = [:bar, :more]
|
93
|
+
subject[:some_thing_more] = :foo_bar
|
94
|
+
expect(subject.to_hash).to eq(
|
95
|
+
some_thing: [[:bar, :more], :foo],
|
96
|
+
some_thing_more: [:foo_bar]
|
97
|
+
)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe '#clone' do
|
102
|
+
let(:obj_cloned) { subject.clone }
|
103
|
+
it 'copies all values' do
|
104
|
+
parent = described_class.new
|
105
|
+
child = described_class.new parent
|
106
|
+
grandchild = described_class.new child
|
107
|
+
|
108
|
+
parent[:some_thing] = :foo
|
109
|
+
child[:some_thing] = [:bar, :more]
|
110
|
+
grandchild[:some_thing] = :grand_foo_bar
|
111
|
+
grandchild[:some_thing_more] = :foo_bar
|
112
|
+
|
113
|
+
expect(grandchild.clone.to_hash).to eq(
|
114
|
+
some_thing: [:grand_foo_bar, [:bar, :more], :foo],
|
115
|
+
some_thing_more: [:foo_bar]
|
116
|
+
)
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'complex (i.e. not primitive) data types (ex. middleware, please see bug #930)' do
|
120
|
+
let(:middleware) { double }
|
121
|
+
|
122
|
+
before { subject[:middleware] = middleware }
|
123
|
+
|
124
|
+
it 'copies values; does not duplicate them' do
|
125
|
+
expect(obj_cloned[:middleware]).to eq [middleware]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|