grape-security 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +45 -0
- data/.rspec +2 -0
- data/.rubocop.yml +70 -0
- data/.travis.yml +18 -0
- data/.yardopts +2 -0
- data/CHANGELOG.md +314 -0
- data/CONTRIBUTING.md +118 -0
- data/Gemfile +21 -0
- data/Guardfile +14 -0
- data/LICENSE +20 -0
- data/README.md +1777 -0
- data/RELEASING.md +105 -0
- data/Rakefile +69 -0
- data/UPGRADING.md +124 -0
- data/grape-security.gemspec +39 -0
- data/grape.png +0 -0
- data/lib/grape.rb +99 -0
- data/lib/grape/api.rb +646 -0
- data/lib/grape/cookies.rb +39 -0
- data/lib/grape/endpoint.rb +533 -0
- data/lib/grape/error_formatter/base.rb +31 -0
- data/lib/grape/error_formatter/json.rb +15 -0
- data/lib/grape/error_formatter/txt.rb +16 -0
- data/lib/grape/error_formatter/xml.rb +15 -0
- data/lib/grape/exceptions/base.rb +66 -0
- data/lib/grape/exceptions/incompatible_option_values.rb +10 -0
- data/lib/grape/exceptions/invalid_formatter.rb +10 -0
- data/lib/grape/exceptions/invalid_versioner_option.rb +10 -0
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +10 -0
- data/lib/grape/exceptions/missing_mime_type.rb +10 -0
- data/lib/grape/exceptions/missing_option.rb +10 -0
- data/lib/grape/exceptions/missing_vendor_option.rb +10 -0
- data/lib/grape/exceptions/unknown_options.rb +10 -0
- data/lib/grape/exceptions/unknown_validator.rb +10 -0
- data/lib/grape/exceptions/validation.rb +26 -0
- data/lib/grape/exceptions/validation_errors.rb +43 -0
- data/lib/grape/formatter/base.rb +31 -0
- data/lib/grape/formatter/json.rb +12 -0
- data/lib/grape/formatter/serializable_hash.rb +35 -0
- data/lib/grape/formatter/txt.rb +11 -0
- data/lib/grape/formatter/xml.rb +12 -0
- data/lib/grape/http/request.rb +26 -0
- data/lib/grape/locale/en.yml +32 -0
- data/lib/grape/middleware/auth/base.rb +30 -0
- data/lib/grape/middleware/auth/basic.rb +13 -0
- data/lib/grape/middleware/auth/digest.rb +13 -0
- data/lib/grape/middleware/auth/oauth2.rb +83 -0
- data/lib/grape/middleware/base.rb +62 -0
- data/lib/grape/middleware/error.rb +89 -0
- data/lib/grape/middleware/filter.rb +17 -0
- data/lib/grape/middleware/formatter.rb +150 -0
- data/lib/grape/middleware/globals.rb +13 -0
- data/lib/grape/middleware/versioner.rb +32 -0
- data/lib/grape/middleware/versioner/accept_version_header.rb +67 -0
- data/lib/grape/middleware/versioner/header.rb +132 -0
- data/lib/grape/middleware/versioner/param.rb +42 -0
- data/lib/grape/middleware/versioner/path.rb +52 -0
- data/lib/grape/namespace.rb +23 -0
- data/lib/grape/parser/base.rb +29 -0
- data/lib/grape/parser/json.rb +11 -0
- data/lib/grape/parser/xml.rb +11 -0
- data/lib/grape/path.rb +70 -0
- data/lib/grape/route.rb +27 -0
- data/lib/grape/util/content_types.rb +18 -0
- data/lib/grape/util/deep_merge.rb +23 -0
- data/lib/grape/util/hash_stack.rb +120 -0
- data/lib/grape/validations.rb +322 -0
- data/lib/grape/validations/coerce.rb +63 -0
- data/lib/grape/validations/default.rb +25 -0
- data/lib/grape/validations/exactly_one_of.rb +26 -0
- data/lib/grape/validations/mutual_exclusion.rb +25 -0
- data/lib/grape/validations/presence.rb +16 -0
- data/lib/grape/validations/regexp.rb +12 -0
- data/lib/grape/validations/values.rb +23 -0
- data/lib/grape/version.rb +3 -0
- data/spec/grape/api_spec.rb +2571 -0
- data/spec/grape/endpoint_spec.rb +784 -0
- data/spec/grape/entity_spec.rb +324 -0
- 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 +18 -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/exceptions/validation_errors_spec.rb +19 -0
- data/spec/grape/middleware/auth/basic_spec.rb +31 -0
- data/spec/grape/middleware/auth/digest_spec.rb +47 -0
- data/spec/grape/middleware/auth/oauth2_spec.rb +135 -0
- data/spec/grape/middleware/base_spec.rb +58 -0
- data/spec/grape/middleware/error_spec.rb +45 -0
- data/spec/grape/middleware/exception_spec.rb +184 -0
- data/spec/grape/middleware/formatter_spec.rb +258 -0
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +121 -0
- data/spec/grape/middleware/versioner/header_spec.rb +302 -0
- data/spec/grape/middleware/versioner/param_spec.rb +58 -0
- data/spec/grape/middleware/versioner/path_spec.rb +44 -0
- data/spec/grape/middleware/versioner_spec.rb +22 -0
- data/spec/grape/path_spec.rb +229 -0
- data/spec/grape/util/hash_stack_spec.rb +132 -0
- data/spec/grape/validations/coerce_spec.rb +208 -0
- data/spec/grape/validations/default_spec.rb +123 -0
- data/spec/grape/validations/exactly_one_of_spec.rb +71 -0
- data/spec/grape/validations/mutual_exclusion_spec.rb +61 -0
- data/spec/grape/validations/presence_spec.rb +142 -0
- data/spec/grape/validations/regexp_spec.rb +40 -0
- data/spec/grape/validations/values_spec.rb +152 -0
- data/spec/grape/validations/zh-CN.yml +10 -0
- data/spec/grape/validations_spec.rb +994 -0
- data/spec/shared/versioning_examples.rb +121 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/basic_auth_encode_helpers.rb +3 -0
- data/spec/support/content_type_helpers.rb +11 -0
- data/spec/support/versioned_helpers.rb +50 -0
- metadata +421 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Grape::Middleware::Versioner::Param do
|
4
|
+
|
5
|
+
let(:app) { lambda { |env| [200, env, env['api.version']] } }
|
6
|
+
subject { Grape::Middleware::Versioner::Param.new(app, @options || {}) }
|
7
|
+
|
8
|
+
it 'sets the API version based on the default param (apiver)' do
|
9
|
+
env = Rack::MockRequest.env_for("/awesome", params: { "apiver" => "v1" })
|
10
|
+
expect(subject.call(env)[1]["api.version"]).to eq('v1')
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'cuts (only) the version out of the params' do
|
14
|
+
env = Rack::MockRequest.env_for("/awesome", params: { "apiver" => "v1", "other_param" => "5" })
|
15
|
+
env['rack.request.query_hash'] = Rack::Utils.parse_nested_query(env['QUERY_STRING'])
|
16
|
+
expect(subject.call(env)[1]['rack.request.query_hash']["apiver"]).to be_nil
|
17
|
+
expect(subject.call(env)[1]['rack.request.query_hash']["other_param"]).to eq("5")
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'provides a nil version if no version is given' do
|
21
|
+
env = Rack::MockRequest.env_for("/")
|
22
|
+
expect(subject.call(env).last).to be_nil
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'with specified parameter name' do
|
26
|
+
before { @options = { parameter: 'v' } }
|
27
|
+
it 'sets the API version based on the custom parameter name' do
|
28
|
+
env = Rack::MockRequest.env_for("/awesome", params: { "v" => "v1" })
|
29
|
+
expect(subject.call(env)[1]["api.version"]).to eq("v1")
|
30
|
+
end
|
31
|
+
it 'does not set the API version based on the default param' do
|
32
|
+
env = Rack::MockRequest.env_for("/awesome", params: { "apiver" => "v1" })
|
33
|
+
expect(subject.call(env)[1]["api.version"]).to be_nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'with specified versions' do
|
38
|
+
before { @options = { versions: ['v1', 'v2'] } }
|
39
|
+
it 'throws an error if a non-allowed version is specified' do
|
40
|
+
env = Rack::MockRequest.env_for("/awesome", params: { "apiver" => "v3" })
|
41
|
+
expect(catch(:error) { subject.call(env) }[:status]).to eq(404)
|
42
|
+
end
|
43
|
+
it 'allows versions that have been specified' do
|
44
|
+
env = Rack::MockRequest.env_for("/awesome", params: { "apiver" => "v1" })
|
45
|
+
expect(subject.call(env)[1]["api.version"]).to eq('v1')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'returns a 200 when no version is set (matches the first version found)' do
|
50
|
+
@options = {
|
51
|
+
versions: ['v1'],
|
52
|
+
version_options: { using: :header }
|
53
|
+
}
|
54
|
+
env = Rack::MockRequest.env_for("/awesome", params: {})
|
55
|
+
expect(subject.call(env).first).to eq(200)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Grape::Middleware::Versioner::Path do
|
4
|
+
let(:app) { lambda { |env| [200, env, env['api.version']] } }
|
5
|
+
subject { Grape::Middleware::Versioner::Path.new(app, @options || {}) }
|
6
|
+
|
7
|
+
it 'sets the API version based on the first path' do
|
8
|
+
expect(subject.call('PATH_INFO' => '/v1/awesome').last).to eq('v1')
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'does not cut the version out of the path' do
|
12
|
+
expect(subject.call('PATH_INFO' => '/v1/awesome')[1]['PATH_INFO']).to eq('/v1/awesome')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'provides a nil version if no path is given' do
|
16
|
+
expect(subject.call('PATH_INFO' => '/').last).to be_nil
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'with a pattern' do
|
20
|
+
before { @options = { pattern: /v./i } }
|
21
|
+
it 'sets the version if it matches' do
|
22
|
+
expect(subject.call('PATH_INFO' => '/v1/awesome').last).to eq('v1')
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'ignores the version if it fails to match' do
|
26
|
+
expect(subject.call('PATH_INFO' => '/awesome/radical').last).to be_nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
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
|
+
expect(catch(:error) { subject.call('PATH_INFO' => '/v3/awesome') }[:status]).to eq(404)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'allows versions that have been specified' do
|
39
|
+
expect(subject.call('PATH_INFO' => '/v1/asoasd').last).to eq('v1')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Grape::Middleware::Versioner do
|
4
|
+
|
5
|
+
let(:klass) { Grape::Middleware::Versioner }
|
6
|
+
|
7
|
+
it 'recognizes :path' do
|
8
|
+
expect(klass.using(:path)).to eq(Grape::Middleware::Versioner::Path)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'recognizes :header' do
|
12
|
+
expect(klass.using(:header)).to eq(Grape::Middleware::Versioner::Header)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'recognizes :param' do
|
16
|
+
expect(klass.using(:param)).to eq(Grape::Middleware::Versioner::Param)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'recognizes :accept_version_header' do
|
20
|
+
expect(klass.using(:accept_version_header)).to eq(Grape::Middleware::Versioner::AcceptVersionHeader)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
describe Path do
|
5
|
+
|
6
|
+
describe "#initialize" do
|
7
|
+
it "remembers the path" do
|
8
|
+
path = Path.new('/:id', anything, anything)
|
9
|
+
expect(path.raw_path).to eql('/:id')
|
10
|
+
end
|
11
|
+
|
12
|
+
it "remembers the namespace" do
|
13
|
+
path = Path.new(anything, '/users', anything)
|
14
|
+
expect(path.namespace).to eql('/users')
|
15
|
+
end
|
16
|
+
|
17
|
+
it "remebers the settings" do
|
18
|
+
path = Path.new(anything, anything, foo: 'bar')
|
19
|
+
expect(path.settings).to eql(foo: 'bar')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#mount_path" do
|
24
|
+
it "is nil when no mount path setting exists" do
|
25
|
+
path = Path.new(anything, anything, {})
|
26
|
+
expect(path.mount_path).to be_nil
|
27
|
+
end
|
28
|
+
|
29
|
+
it "is nil when the mount path is nil" do
|
30
|
+
path = Path.new(anything, anything, mount_path: nil)
|
31
|
+
expect(path.mount_path).to be_nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it "splits the mount path" do
|
35
|
+
path = Path.new(anything, anything, mount_path: 'foo/bar')
|
36
|
+
expect(path.mount_path).to eql(['foo', 'bar'])
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#root_prefix" do
|
41
|
+
it "is nil when no root prefix setting exists" do
|
42
|
+
path = Path.new(anything, anything, {})
|
43
|
+
expect(path.root_prefix).to be_nil
|
44
|
+
end
|
45
|
+
|
46
|
+
it "is nil when the mount path is nil" do
|
47
|
+
path = Path.new(anything, anything, root_prefix: nil)
|
48
|
+
expect(path.root_prefix).to be_nil
|
49
|
+
end
|
50
|
+
|
51
|
+
it "splits the mount path" do
|
52
|
+
path = Path.new(anything, anything, root_prefix: 'hello/world')
|
53
|
+
expect(path.root_prefix).to eql(['hello', 'world'])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#uses_path_versioning?" do
|
58
|
+
it "is false when the version setting is nil" do
|
59
|
+
path = Path.new(anything, anything, version: nil)
|
60
|
+
expect(path.uses_path_versioning?).to be false
|
61
|
+
end
|
62
|
+
|
63
|
+
it "is false when the version option is header" do
|
64
|
+
path = Path.new(
|
65
|
+
anything,
|
66
|
+
anything,
|
67
|
+
version: 'v1',
|
68
|
+
version_options: { using: :header }
|
69
|
+
)
|
70
|
+
|
71
|
+
expect(path.uses_path_versioning?).to be false
|
72
|
+
end
|
73
|
+
|
74
|
+
it "is true when the version option is path" do
|
75
|
+
path = Path.new(
|
76
|
+
anything,
|
77
|
+
anything,
|
78
|
+
version: 'v1',
|
79
|
+
version_options: { using: :path }
|
80
|
+
)
|
81
|
+
|
82
|
+
expect(path.uses_path_versioning?).to be true
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#has_namespace?" do
|
87
|
+
it "is false when the namespace is nil" do
|
88
|
+
path = Path.new(anything, nil, anything)
|
89
|
+
expect(path).not_to have_namespace
|
90
|
+
end
|
91
|
+
|
92
|
+
it "is false when the namespace starts with whitespace" do
|
93
|
+
path = Path.new(anything, ' /foo', anything)
|
94
|
+
expect(path).not_to have_namespace
|
95
|
+
end
|
96
|
+
|
97
|
+
it "is false when the namespace is the root path" do
|
98
|
+
path = Path.new(anything, '/', anything)
|
99
|
+
expect(path).not_to have_namespace
|
100
|
+
end
|
101
|
+
|
102
|
+
it "is true otherwise" do
|
103
|
+
path = Path.new(anything, '/world', anything)
|
104
|
+
expect(path).to have_namespace
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "#has_path?" do
|
109
|
+
it "is false when the path is nil" do
|
110
|
+
path = Path.new(nil, anything, anything)
|
111
|
+
expect(path).not_to have_path
|
112
|
+
end
|
113
|
+
|
114
|
+
it "is false when the path starts with whitespace" do
|
115
|
+
path = Path.new(' /foo', anything, anything)
|
116
|
+
expect(path).not_to have_path
|
117
|
+
end
|
118
|
+
|
119
|
+
it "is false when the path is the root path" do
|
120
|
+
path = Path.new('/', anything, anything)
|
121
|
+
expect(path).not_to have_path
|
122
|
+
end
|
123
|
+
|
124
|
+
it "is true otherwise" do
|
125
|
+
path = Path.new('/hello', anything, anything)
|
126
|
+
expect(path).to have_path
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe "#path" do
|
131
|
+
context "mount_path" do
|
132
|
+
it "is not included when it is nil" do
|
133
|
+
path = Path.new(nil, nil, mount_path: '/foo/bar')
|
134
|
+
expect(path.path).to eql '/foo/bar'
|
135
|
+
end
|
136
|
+
|
137
|
+
it "is included when it is not nil" do
|
138
|
+
path = Path.new(nil, nil, {})
|
139
|
+
expect(path.path).to eql('/')
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context "root_prefix" do
|
144
|
+
it "is not included when it is nil" do
|
145
|
+
path = Path.new(nil, nil, {})
|
146
|
+
expect(path.path).to eql('/')
|
147
|
+
end
|
148
|
+
|
149
|
+
it "is included after the mount path" do
|
150
|
+
path = Path.new(
|
151
|
+
nil,
|
152
|
+
nil,
|
153
|
+
mount_path: '/foo',
|
154
|
+
root_prefix: '/hello'
|
155
|
+
)
|
156
|
+
|
157
|
+
expect(path.path).to eql('/foo/hello')
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
it "uses the namespace after the mount path and root prefix" do
|
162
|
+
path = Path.new(
|
163
|
+
nil,
|
164
|
+
'namespace',
|
165
|
+
mount_path: '/foo',
|
166
|
+
root_prefix: '/hello'
|
167
|
+
)
|
168
|
+
|
169
|
+
expect(path.path).to eql('/foo/hello/namespace')
|
170
|
+
end
|
171
|
+
|
172
|
+
it "uses the raw path after the namespace" do
|
173
|
+
path = Path.new(
|
174
|
+
'raw_path',
|
175
|
+
'namespace',
|
176
|
+
mount_path: '/foo',
|
177
|
+
root_prefix: '/hello'
|
178
|
+
)
|
179
|
+
|
180
|
+
expect(path.path).to eql('/foo/hello/namespace/raw_path')
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe "#suffix" do
|
185
|
+
context "when path versioning is used" do
|
186
|
+
it "includes a '/'" do
|
187
|
+
path = Path.new(nil, nil, {})
|
188
|
+
allow(path).to receive(:uses_path_versioning?) { true }
|
189
|
+
|
190
|
+
expect(path.suffix).to eql('(/.:format)')
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
context "when path versioning is not used" do
|
195
|
+
it "does not include a '/' when the path has a namespace" do
|
196
|
+
path = Path.new(nil, 'namespace', {})
|
197
|
+
allow(path).to receive(:uses_path_versioning?) { true }
|
198
|
+
|
199
|
+
expect(path.suffix).to eql('(.:format)')
|
200
|
+
end
|
201
|
+
|
202
|
+
it "does not include a '/' when the path has a path" do
|
203
|
+
path = Path.new('/path', nil, {})
|
204
|
+
allow(path).to receive(:uses_path_versioning?) { true }
|
205
|
+
|
206
|
+
expect(path.suffix).to eql('(.:format)')
|
207
|
+
end
|
208
|
+
|
209
|
+
it "includes a '/' otherwise" do
|
210
|
+
path = Path.new(nil, nil, {})
|
211
|
+
allow(path).to receive(:uses_path_versioning?) { true }
|
212
|
+
|
213
|
+
expect(path.suffix).to eql('(/.:format)')
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
describe "#path_with_suffix" do
|
219
|
+
it "combines the path and suffix" do
|
220
|
+
path = Path.new(nil, nil, {})
|
221
|
+
allow(path).to receive(:path) { '/the/path' }
|
222
|
+
allow(path).to receive(:suffix) { 'suffix' }
|
223
|
+
|
224
|
+
expect(path.path_with_suffix).to eql('/the/pathsuffix')
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
end
|
229
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Grape::Util::HashStack do
|
4
|
+
|
5
|
+
describe '#get' do
|
6
|
+
it 'finds the first available key' do
|
7
|
+
subject[:abc] = 123
|
8
|
+
subject.push(abc: 345)
|
9
|
+
expect(subject.get(:abc)).to eq(345)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'is nil if the key has not been set' do
|
13
|
+
expect(subject[:abc]).to be_nil
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#set' do
|
18
|
+
it 'sets a value on the highest frame' do
|
19
|
+
subject.push
|
20
|
+
subject.set(:abc, 123)
|
21
|
+
expect(subject.stack.last[:abc]).to eq(123)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#imbue' do
|
26
|
+
it 'pushes a new value onto the end of an array' do
|
27
|
+
subject[:abc] = []
|
28
|
+
subject.imbue :abc, [123]
|
29
|
+
subject.imbue :abc, [456]
|
30
|
+
expect(subject[:abc]).to eq([123, 456])
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'merges a hash that is passed' do
|
34
|
+
subject[:abc] = { foo: 'bar' }
|
35
|
+
subject.imbue :abc, baz: 'wich'
|
36
|
+
expect(subject[:abc]).to eq(foo: 'bar', baz: 'wich')
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'sets the value if not a hash or array' do
|
40
|
+
subject.imbue :abc, 123
|
41
|
+
expect(subject[:abc]).to eq(123)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'is able to imbue an array without explicit setting' do
|
45
|
+
subject.imbue :arr, [1]
|
46
|
+
subject.imbue :arr, [2]
|
47
|
+
expect(subject[:arr]).to eq([1, 2])
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'is able to imbue a hash without explicit setting' do
|
51
|
+
subject.imbue :hash, foo: 'bar'
|
52
|
+
subject.imbue :hash, baz: 'wich'
|
53
|
+
expect(subject[:hash]).to eq(foo: 'bar', baz: 'wich')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#push' do
|
58
|
+
it 'returns a HashStack' do
|
59
|
+
expect(subject.push(Grape::Util::HashStack.new)).to be_kind_of(Grape::Util::HashStack)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'places the passed value on the top of the stack' do
|
63
|
+
subject.push(abc: 123)
|
64
|
+
expect(subject.stack).to eq([{}, { abc: 123 }])
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'pushes an empty hash by default' do
|
68
|
+
subject[:abc] = 123
|
69
|
+
subject.push
|
70
|
+
expect(subject.stack).to eq([{ abc: 123 }, {}])
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe '#pop' do
|
75
|
+
it 'removes and return the top frame' do
|
76
|
+
subject.push(abc: 123)
|
77
|
+
expect(subject.pop).to eq(abc: 123)
|
78
|
+
expect(subject.stack.size).to eq(1)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '#peek' do
|
83
|
+
it 'returns the top frame without removing it' do
|
84
|
+
subject.push(abc: 123)
|
85
|
+
expect(subject.peek).to eq(abc: 123)
|
86
|
+
expect(subject.stack.size).to eq(2)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe '#prepend' do
|
91
|
+
it 'returns a HashStack' do
|
92
|
+
expect(subject.prepend(Grape::Util::HashStack.new)).to be_kind_of(Grape::Util::HashStack)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "prepends a HashStack's stack onto its own stack" do
|
96
|
+
other = Grape::Util::HashStack.new.push(abc: 123)
|
97
|
+
expect(subject.prepend(other).stack).to eq([{}, { abc: 123 }, {}])
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe '#concat' do
|
102
|
+
it 'returns a HashStack' do
|
103
|
+
expect(subject.concat(Grape::Util::HashStack.new)).to be_kind_of(Grape::Util::HashStack)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "appends a HashStack's stack onto its own stack" do
|
107
|
+
other = Grape::Util::HashStack.new.push(abc: 123)
|
108
|
+
expect(subject.concat(other).stack).to eq([{}, {}, { abc: 123 }])
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe '#update' do
|
113
|
+
it 'merges! into the top frame' do
|
114
|
+
subject.update(abc: 123)
|
115
|
+
expect(subject.stack).to eq([{ abc: 123 }])
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'returns a HashStack' do
|
119
|
+
expect(subject.update(abc: 123)).to be_kind_of(Grape::Util::HashStack)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe '#clone' do
|
124
|
+
it 'performs a deep copy' do
|
125
|
+
subject[:abc] = 123
|
126
|
+
subject.push def: 234
|
127
|
+
clone = subject.clone
|
128
|
+
clone[:def] = 345
|
129
|
+
expect(subject[:def]).to eq(234)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|