grape-security 0.8.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.
- 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
|