brevio-session-store 0.1.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 +17 -0
- data/.rspec +3 -0
- data/.rubocop.yml +38 -0
- data/.simplecov +5 -0
- data/.travis.yml +24 -0
- data/AUTHORS.md +25 -0
- data/CHANGELOG.md +269 -0
- data/CODE_OF_CONDUCT.md +75 -0
- data/CONTRIBUTING.md +6 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +119 -0
- data/Rakefile +15 -0
- data/lib/redis-session-store.rb +219 -0
- data/redis-session-store.gemspec +22 -0
- data/spec/redis_session_store_spec.rb +564 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/support.rb +51 -0
- metadata +159 -0
@@ -0,0 +1,564 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
describe RedisSessionStore do
|
4
|
+
let :random_string do
|
5
|
+
"#{rand}#{rand}#{rand}"
|
6
|
+
end
|
7
|
+
|
8
|
+
let :options do
|
9
|
+
{}
|
10
|
+
end
|
11
|
+
|
12
|
+
subject(:store) { RedisSessionStore.new(nil, options) }
|
13
|
+
|
14
|
+
let :default_options do
|
15
|
+
store.instance_variable_get(:@default_options)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'assigns a :namespace to @default_options' do
|
19
|
+
expect(default_options[:namespace]).to eq('rack:session')
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'when initializing with the redis sub-hash options' do
|
23
|
+
let :options do
|
24
|
+
{
|
25
|
+
key: random_string,
|
26
|
+
secret: random_string,
|
27
|
+
redis: {
|
28
|
+
host: 'hosty.local',
|
29
|
+
port: 16_379,
|
30
|
+
db: 2,
|
31
|
+
key_prefix: 'myapp:session:',
|
32
|
+
expire_after: 60 * 120
|
33
|
+
}
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'creates a redis instance' do
|
38
|
+
expect(store.instance_variable_get(:@redis)).to_not be_nil
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'assigns the :host option to @default_options' do
|
42
|
+
expect(default_options[:host]).to eq('hosty.local')
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'assigns the :port option to @default_options' do
|
46
|
+
expect(default_options[:port]).to eq(16_379)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'assigns the :db option to @default_options' do
|
50
|
+
expect(default_options[:db]).to eq(2)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'assigns the :key_prefix option to @default_options' do
|
54
|
+
expect(default_options[:key_prefix]).to eq('myapp:session:')
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'assigns the :expire_after option to @default_options' do
|
58
|
+
expect(default_options[:expire_after]).to eq(60 * 120)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'when configured with both :ttl and :expire_after' do
|
63
|
+
let(:ttl_seconds) { 60 * 120 }
|
64
|
+
let :options do
|
65
|
+
{
|
66
|
+
key: random_string,
|
67
|
+
secret: random_string,
|
68
|
+
redis: {
|
69
|
+
host: 'hosty.local',
|
70
|
+
port: 16_379,
|
71
|
+
db: 2,
|
72
|
+
key_prefix: 'myapp:session:',
|
73
|
+
ttl: ttl_seconds,
|
74
|
+
expire_after: nil
|
75
|
+
}
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'assigns the :ttl option to @default_options' do
|
80
|
+
expect(default_options[:ttl]).to eq(ttl_seconds)
|
81
|
+
expect(default_options[:expire_after]).to be_nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe 'when initializing with top-level redis options' do
|
86
|
+
let :options do
|
87
|
+
{
|
88
|
+
key: random_string,
|
89
|
+
secret: random_string,
|
90
|
+
host: 'hostersons.local',
|
91
|
+
port: 26_379,
|
92
|
+
db: 4,
|
93
|
+
key_prefix: 'appydoo:session:',
|
94
|
+
expire_after: 60 * 60
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'creates a redis instance' do
|
99
|
+
expect(store.instance_variable_get(:@redis)).to_not be_nil
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'assigns the :host option to @default_options' do
|
103
|
+
expect(default_options[:host]).to eq('hostersons.local')
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'assigns the :port option to @default_options' do
|
107
|
+
expect(default_options[:port]).to eq(26_379)
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'assigns the :db option to @default_options' do
|
111
|
+
expect(default_options[:db]).to eq(4)
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'assigns the :key_prefix option to @default_options' do
|
115
|
+
expect(default_options[:key_prefix]).to eq('appydoo:session:')
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'assigns the :expire_after option to @default_options' do
|
119
|
+
expect(default_options[:expire_after]).to eq(60 * 60)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe 'when initializing with existing redis object' do
|
124
|
+
let :options do
|
125
|
+
{
|
126
|
+
key: random_string,
|
127
|
+
secret: random_string,
|
128
|
+
redis: {
|
129
|
+
client: redis_client,
|
130
|
+
key_prefix: 'myapp:session:',
|
131
|
+
expire_after: 60 * 30
|
132
|
+
}
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
let(:redis_client) { double('redis_client') }
|
137
|
+
|
138
|
+
it 'assigns given redis object to @redis' do
|
139
|
+
expect(store.instance_variable_get(:@redis)).to be(redis_client)
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'assigns the :client option to @default_options' do
|
143
|
+
expect(default_options[:client]).to be(redis_client)
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'assigns the :key_prefix option to @default_options' do
|
147
|
+
expect(default_options[:key_prefix]).to eq('myapp:session:')
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'assigns the :expire_after option to @default_options' do
|
151
|
+
expect(default_options[:expire_after]).to eq(60 * 30)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe 'rack 1.45 compatibility' do
|
156
|
+
# Rack 1.45 (which Rails 3.2.x depends on) uses the return value of
|
157
|
+
# set_session to set the cookie value. See:
|
158
|
+
# https://github.com/rack/rack/blob/1.4.5/lib/rack/session/abstract/id.rb
|
159
|
+
|
160
|
+
let(:env) { double('env') }
|
161
|
+
let(:session_id) { 12_345 }
|
162
|
+
let(:session_data) { double('session_data') }
|
163
|
+
let(:options) { { expire_after: 123 } }
|
164
|
+
|
165
|
+
context 'when successfully persisting the session' do
|
166
|
+
it 'returns the session id' do
|
167
|
+
expect(store.send(:set_session, env, session_id, session_data, options))
|
168
|
+
.to eq(session_id)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context 'when unsuccessfully persisting the session' do
|
173
|
+
before do
|
174
|
+
allow(store).to receive(:redis).and_raise(Redis::CannotConnectError)
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'returns false' do
|
178
|
+
expect(store.send(:set_session, env, session_id, session_data, options))
|
179
|
+
.to eq(false)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
context 'when no expire_after option is given' do
|
184
|
+
let(:options) { {} }
|
185
|
+
|
186
|
+
it 'sets the session value without expiry' do
|
187
|
+
expect(store.send(:set_session, env, session_id, session_data, options))
|
188
|
+
.to eq(session_id)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
context 'when redis is down' do
|
193
|
+
before do
|
194
|
+
allow(store).to receive(:redis).and_raise(Redis::CannotConnectError)
|
195
|
+
store.on_redis_down = ->(*_a) { @redis_down_handled = true }
|
196
|
+
end
|
197
|
+
|
198
|
+
it 'returns false' do
|
199
|
+
expect(store.send(:set_session, env, session_id, session_data, options))
|
200
|
+
.to eq(false)
|
201
|
+
end
|
202
|
+
|
203
|
+
it 'calls the on_redis_down handler' do
|
204
|
+
store.send(:set_session, env, session_id, session_data, options)
|
205
|
+
expect(@redis_down_handled).to eq(true)
|
206
|
+
end
|
207
|
+
|
208
|
+
context 'when :on_redis_down re-raises' do
|
209
|
+
before { store.on_redis_down = ->(e, *) { raise e } }
|
210
|
+
|
211
|
+
it 'explodes' do
|
212
|
+
expect do
|
213
|
+
store.send(:set_session, env, session_id, session_data, options)
|
214
|
+
end.to raise_error(Redis::CannotConnectError)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
describe 'checking for session existence' do
|
221
|
+
let(:session_id) { 'foo' }
|
222
|
+
|
223
|
+
before do
|
224
|
+
allow(store).to receive(:current_session_id)
|
225
|
+
.with(:env).and_return(session_id)
|
226
|
+
end
|
227
|
+
|
228
|
+
context 'when session id is not provided' do
|
229
|
+
context 'when session id is nil' do
|
230
|
+
let(:session_id) { nil }
|
231
|
+
it 'returns false' do
|
232
|
+
expect(store.send(:session_exists?, :env)).to eq(false)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
context 'when session id is empty string' do
|
237
|
+
let(:session_id) { '' }
|
238
|
+
it 'returns false' do
|
239
|
+
allow(store).to receive(:current_session_id).with(:env).and_return('')
|
240
|
+
expect(store.send(:session_exists?, :env)).to eq(false)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
context 'when session id is provided' do
|
246
|
+
let(:redis) do
|
247
|
+
double('redis').tap do |o|
|
248
|
+
allow(store).to receive(:redis).and_return(o)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
context 'when session id does not exist in redis' do
|
253
|
+
it 'returns false' do
|
254
|
+
expect(redis).to receive(:exists).with('foo').and_return(false)
|
255
|
+
expect(store.send(:session_exists?, :env)).to eq(false)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
context 'when session id exists in redis' do
|
260
|
+
it 'returns true' do
|
261
|
+
expect(redis).to receive(:exists).with('foo').and_return(true)
|
262
|
+
expect(store.send(:session_exists?, :env)).to eq(true)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
context 'when redis is down' do
|
267
|
+
it 'returns true (fallback to old behavior)' do
|
268
|
+
allow(store).to receive(:redis).and_raise(Redis::CannotConnectError)
|
269
|
+
expect(store.send(:session_exists?, :env)).to eq(true)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
describe 'fetching a session' do
|
276
|
+
let :options do
|
277
|
+
{
|
278
|
+
key_prefix: 'customprefix::'
|
279
|
+
}
|
280
|
+
end
|
281
|
+
|
282
|
+
let(:fake_key) { 'thisisarediskey' }
|
283
|
+
|
284
|
+
it 'retrieves the prefixed key from redis' do
|
285
|
+
redis = double('redis')
|
286
|
+
allow(store).to receive(:redis).and_return(redis)
|
287
|
+
allow(store).to receive(:generate_sid).and_return(fake_key)
|
288
|
+
expect(redis).to receive(:get).with("#{options[:key_prefix]}#{fake_key}")
|
289
|
+
|
290
|
+
store.send(:get_session, double('env'), fake_key)
|
291
|
+
end
|
292
|
+
|
293
|
+
context 'when redis is down' do
|
294
|
+
before do
|
295
|
+
allow(store).to receive(:redis).and_raise(Redis::CannotConnectError)
|
296
|
+
allow(store).to receive(:generate_sid).and_return('foop')
|
297
|
+
end
|
298
|
+
|
299
|
+
it 'returns an empty session hash' do
|
300
|
+
expect(store.send(:get_session, double('env'), fake_key).last)
|
301
|
+
.to eq({})
|
302
|
+
end
|
303
|
+
|
304
|
+
it 'returns a newly generated sid' do
|
305
|
+
expect(store.send(:get_session, double('env'), fake_key).first)
|
306
|
+
.to eq('foop')
|
307
|
+
end
|
308
|
+
|
309
|
+
context 'when :on_redis_down re-raises' do
|
310
|
+
before { store.on_redis_down = ->(e, *) { raise e } }
|
311
|
+
|
312
|
+
it 'explodes' do
|
313
|
+
expect do
|
314
|
+
store.send(:get_session, double('env'), fake_key)
|
315
|
+
end.to raise_error(Redis::CannotConnectError)
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
describe 'destroying a session' do
|
322
|
+
context 'when the key is in the cookie hash' do
|
323
|
+
let(:env) { { 'rack.request.cookie_hash' => cookie_hash } }
|
324
|
+
let(:cookie_hash) { double('cookie hash') }
|
325
|
+
let(:fake_key) { 'thisisarediskey' }
|
326
|
+
|
327
|
+
before do
|
328
|
+
allow(cookie_hash).to receive(:[]).and_return(fake_key)
|
329
|
+
end
|
330
|
+
|
331
|
+
it 'deletes the prefixed key from redis' do
|
332
|
+
redis = double('redis')
|
333
|
+
allow(store).to receive(:redis).and_return(redis)
|
334
|
+
expect(redis).to receive(:del)
|
335
|
+
.with("#{options[:key_prefix]}#{fake_key}")
|
336
|
+
|
337
|
+
store.send(:destroy, env)
|
338
|
+
end
|
339
|
+
|
340
|
+
context 'when redis is down' do
|
341
|
+
before do
|
342
|
+
allow(store).to receive(:redis).and_raise(Redis::CannotConnectError)
|
343
|
+
end
|
344
|
+
|
345
|
+
it 'returns false' do
|
346
|
+
expect(store.send(:destroy, env)).to eq(false)
|
347
|
+
end
|
348
|
+
|
349
|
+
context 'when :on_redis_down re-raises' do
|
350
|
+
before { store.on_redis_down = ->(e, *) { raise e } }
|
351
|
+
|
352
|
+
it 'explodes' do
|
353
|
+
expect do
|
354
|
+
store.send(:destroy, env)
|
355
|
+
end.to raise_error(Redis::CannotConnectError)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
context 'when destroyed via #destroy_session' do
|
362
|
+
it 'deletes the prefixed key from redis' do
|
363
|
+
redis = double('redis', setnx: true)
|
364
|
+
allow(store).to receive(:redis).and_return(redis)
|
365
|
+
sid = store.send(:generate_sid)
|
366
|
+
expect(redis).to receive(:del).with("#{options[:key_prefix]}#{sid}")
|
367
|
+
|
368
|
+
store.send(:destroy_session, {}, sid, nil)
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
describe 'session encoding' do
|
374
|
+
let(:env) { double('env') }
|
375
|
+
let(:session_id) { 12_345 }
|
376
|
+
let(:session_data) { { 'some' => 'data' } }
|
377
|
+
let(:options) { {} }
|
378
|
+
let(:encoded_data) { Marshal.dump(session_data) }
|
379
|
+
let(:redis) { double('redis', set: nil, get: encoded_data) }
|
380
|
+
let(:expected_encoding) { encoded_data }
|
381
|
+
|
382
|
+
before do
|
383
|
+
allow(store).to receive(:redis).and_return(redis)
|
384
|
+
end
|
385
|
+
|
386
|
+
shared_examples_for 'serializer' do
|
387
|
+
it 'encodes correctly' do
|
388
|
+
expect(redis).to receive(:set).with('12345', expected_encoding)
|
389
|
+
store.send(:set_session, env, session_id, session_data, options)
|
390
|
+
end
|
391
|
+
|
392
|
+
it 'decodes correctly' do
|
393
|
+
expect(store.send(:get_session, env, session_id))
|
394
|
+
.to eq([session_id, session_data])
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
context 'marshal' do
|
399
|
+
let(:options) { { serializer: :marshal } }
|
400
|
+
it_should_behave_like 'serializer'
|
401
|
+
end
|
402
|
+
|
403
|
+
context 'json' do
|
404
|
+
let(:options) { { serializer: :json } }
|
405
|
+
let(:encoded_data) { '{"some":"data"}' }
|
406
|
+
|
407
|
+
it_should_behave_like 'serializer'
|
408
|
+
end
|
409
|
+
|
410
|
+
context 'hybrid' do
|
411
|
+
let(:options) { { serializer: :hybrid } }
|
412
|
+
let(:expected_encoding) { '{"some":"data"}' }
|
413
|
+
|
414
|
+
context 'marshal encoded data' do
|
415
|
+
it_should_behave_like 'serializer'
|
416
|
+
end
|
417
|
+
|
418
|
+
context 'json encoded data' do
|
419
|
+
let(:encoded_data) { '{"some":"data"}' }
|
420
|
+
|
421
|
+
it_should_behave_like 'serializer'
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
context 'custom' do
|
426
|
+
let :custom_serializer do
|
427
|
+
Class.new do
|
428
|
+
def self.load(_value)
|
429
|
+
{ 'some' => 'data' }
|
430
|
+
end
|
431
|
+
|
432
|
+
def self.dump(_value)
|
433
|
+
'somedata'
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
let(:options) { { serializer: custom_serializer } }
|
439
|
+
let(:expected_encoding) { 'somedata' }
|
440
|
+
|
441
|
+
it_should_behave_like 'serializer'
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
describe 'handling decode errors' do
|
446
|
+
context 'when a class is serialized that does not exist' do
|
447
|
+
before do
|
448
|
+
allow(store).to receive(:redis)
|
449
|
+
.and_return(double('redis',
|
450
|
+
get: "\x04\bo:\nNonExistentClass\x00",
|
451
|
+
del: true))
|
452
|
+
end
|
453
|
+
|
454
|
+
it 'returns an empty session' do
|
455
|
+
expect(store.send(:load_session_from_redis, 'whatever')).to be_nil
|
456
|
+
end
|
457
|
+
|
458
|
+
it 'destroys and drops the session' do
|
459
|
+
expect(store).to receive(:destroy_session_from_sid)
|
460
|
+
.with('wut', drop: true)
|
461
|
+
store.send(:load_session_from_redis, 'wut')
|
462
|
+
end
|
463
|
+
|
464
|
+
context 'when a custom on_session_load_error handler is provided' do
|
465
|
+
before do
|
466
|
+
store.on_session_load_error = lambda do |e, sid|
|
467
|
+
@e = e
|
468
|
+
@sid = sid
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
it 'passes the error and the sid to the handler' do
|
473
|
+
store.send(:load_session_from_redis, 'foo')
|
474
|
+
expect(@e).to be_kind_of(StandardError)
|
475
|
+
expect(@sid).to eq('foo')
|
476
|
+
end
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
context 'when the encoded data is invalid' do
|
481
|
+
before do
|
482
|
+
allow(store).to receive(:redis)
|
483
|
+
.and_return(double('redis', get: "\x00\x00\x00\x00", del: true))
|
484
|
+
end
|
485
|
+
|
486
|
+
it 'returns an empty session' do
|
487
|
+
expect(store.send(:load_session_from_redis, 'bar')).to be_nil
|
488
|
+
end
|
489
|
+
|
490
|
+
it 'destroys and drops the session' do
|
491
|
+
expect(store).to receive(:destroy_session_from_sid)
|
492
|
+
.with('wut', drop: true)
|
493
|
+
store.send(:load_session_from_redis, 'wut')
|
494
|
+
end
|
495
|
+
|
496
|
+
context 'when a custom on_session_load_error handler is provided' do
|
497
|
+
before do
|
498
|
+
store.on_session_load_error = lambda do |e, sid|
|
499
|
+
@e = e
|
500
|
+
@sid = sid
|
501
|
+
end
|
502
|
+
end
|
503
|
+
|
504
|
+
it 'passes the error and the sid to the handler' do
|
505
|
+
store.send(:load_session_from_redis, 'foo')
|
506
|
+
expect(@e).to be_kind_of(StandardError)
|
507
|
+
expect(@sid).to eq('foo')
|
508
|
+
end
|
509
|
+
end
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
describe 'validating custom handlers' do
|
514
|
+
%w(on_redis_down on_session_load_error).each do |h|
|
515
|
+
context 'when nil' do
|
516
|
+
it 'does not explode at init' do
|
517
|
+
expect { store }.to_not raise_error
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
context 'when callable' do
|
522
|
+
let(:options) { { "#{h}": ->(*) { true } } }
|
523
|
+
|
524
|
+
it 'does not explode at init' do
|
525
|
+
expect { store }.to_not raise_error
|
526
|
+
end
|
527
|
+
end
|
528
|
+
|
529
|
+
context 'when not callable' do
|
530
|
+
let(:options) { { "#{h}": 'herpderp' } }
|
531
|
+
|
532
|
+
it 'explodes at init' do
|
533
|
+
expect { store }.to raise_error(ArgumentError)
|
534
|
+
end
|
535
|
+
end
|
536
|
+
end
|
537
|
+
end
|
538
|
+
|
539
|
+
describe 'setting the session' do
|
540
|
+
it 'allows changing the session' do
|
541
|
+
env = { 'rack.session.options' => {} }
|
542
|
+
sid = 1234
|
543
|
+
allow(store).to receive(:redis).and_return(Redis.new)
|
544
|
+
data1 = { 'foo' => 'bar' }
|
545
|
+
store.send(:set_session, env, sid, data1)
|
546
|
+
data2 = { 'baz' => 'wat' }
|
547
|
+
store.send(:set_session, env, sid, data2)
|
548
|
+
_, session = store.send(:get_session, env, sid)
|
549
|
+
expect(session).to eq(data2)
|
550
|
+
end
|
551
|
+
|
552
|
+
it 'allows changing the session when the session has an expiry' do
|
553
|
+
env = { 'rack.session.options' => { expire_after: 60 } }
|
554
|
+
sid = 1234
|
555
|
+
allow(store).to receive(:redis).and_return(Redis.new)
|
556
|
+
data1 = { 'foo' => 'bar' }
|
557
|
+
store.send(:set_session, env, sid, data1)
|
558
|
+
data2 = { 'baz' => 'wat' }
|
559
|
+
store.send(:set_session, env, sid, data2)
|
560
|
+
_, session = store.send(:get_session, env, sid)
|
561
|
+
expect(session).to eq(data2)
|
562
|
+
end
|
563
|
+
end
|
564
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/spec/support.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
unless defined?(Rack::Session::Abstract::ENV_SESSION_OPTIONS_KEY)
|
2
|
+
module Rack
|
3
|
+
module Session
|
4
|
+
module Abstract
|
5
|
+
ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
unless defined?(ActionDispatch::Session::AbstractStore)
|
12
|
+
module ActionDispatch
|
13
|
+
module Session
|
14
|
+
class AbstractStore
|
15
|
+
ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze
|
16
|
+
DEFAULT_OPTIONS = {
|
17
|
+
key: '_session_id',
|
18
|
+
path: '/',
|
19
|
+
domain: nil,
|
20
|
+
expire_after: nil,
|
21
|
+
secure: false,
|
22
|
+
httponly: true,
|
23
|
+
cookie_only: true
|
24
|
+
}.freeze
|
25
|
+
|
26
|
+
def initialize(app, options = {})
|
27
|
+
@app = app
|
28
|
+
@default_options = DEFAULT_OPTIONS.dup.merge(options)
|
29
|
+
@key = @default_options[:key]
|
30
|
+
@cookie_only = @default_options[:cookie_only]
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def generate_sid
|
36
|
+
rand(999..9999).to_s(16)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
unless defined?(Rails)
|
44
|
+
require 'logger'
|
45
|
+
|
46
|
+
module Rails
|
47
|
+
def self.logger
|
48
|
+
@logger ||= Logger.new('/dev/null')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|