rack_session_redis_store 0.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 1.9.2
5
+ - jruby-19mode # JRuby in 1.9 mode
6
+ - rbx-19mode
7
+ before_install:
8
+ - gem instal redis-namespace
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rack_session_redis_store.gemspec
4
+ gemspec
5
+
6
+ gem 'redis'
7
+ gem 'redis-namespace'
8
+
9
+ group :test do
10
+ gem 'rspec'
11
+ gem 'rspec-core'
12
+ gem 'rake'
13
+ end
14
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Spring_MT
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # RackSessionRedisStore [![Build Status](https://travis-ci.org/SpringMT/rack_session_redis_store.png)](https://travis-ci.org/SpringMT/rack_session_redis_store)
2
+
3
+ ## Installation
4
+
5
+ Add this line to your application's Gemfile:
6
+
7
+ gem 'rack_session_redis_store'
8
+
9
+ And then execute:
10
+
11
+ $ bundle
12
+
13
+ Or install it yourself as:
14
+
15
+ $ gem install rack_session_redis_store
16
+
17
+ ## Usage
18
+
19
+ ## Contributing
20
+
21
+ 1. Fork it
22
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
23
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
24
+ 4. Push to the branch (`git push origin my-new-feature`)
25
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task :default => :test
4
+
5
+ task :test do
6
+ require 'rspec/core'
7
+ require 'rspec/core/rake_task'
8
+ RSpec::Core::RakeTask.new(:test) do |spec|
9
+ spec.pattern = FileList['spec/**/*_spec.rb']
10
+ end
11
+ end
12
+
@@ -0,0 +1,3 @@
1
+ module RackSessionRedisStore
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,100 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'thread'
4
+ require 'base64'
5
+ require 'rack/session/abstract/id'
6
+ require 'redis_json_serializer'
7
+
8
+ #
9
+ # == Synopsis
10
+ #
11
+ # session store in redis
12
+ #
13
+ # == Description
14
+ #
15
+ # === Example
16
+ #
17
+ # === Features
18
+ #
19
+ # == HOWTOs
20
+ #
21
+
22
+
23
+ module RackSessionRedisStore
24
+ class Session < ::Rack::Session::Abstract::ID
25
+ attr_reader :mutex, :pool
26
+ DEFAULT_OPTIONS = ::Rack::Session::Abstract::ID::DEFAULT_OPTIONS
27
+
28
+ def initialize(app, options = {})
29
+ super
30
+ @mutex = Mutex.new
31
+ host = options[:host] || '127.0.0.1'
32
+ port = options[:port] || 6379
33
+ namespace = options[:namespace] || :session
34
+ @pool = RedisJsonSerializer::Serializer.new(host: host, port: port, namespace: namespace)
35
+ end
36
+
37
+ def generate_sid
38
+ loop do
39
+ sid = super
40
+ break sid unless @pool.get sid
41
+ end
42
+ end
43
+
44
+ # === Synopsis
45
+ # call at call -> context -> prepare_session -> SessionHash.new in rack/session/abstract/id.rb
46
+ #
47
+ def get_session(env, sid)
48
+ with_lock(env, [nil, {}]) do
49
+ unless sid and session = @pool.get(sid)
50
+ sid, session = generate_sid, {}
51
+ unless /^OK/ =~ @pool.set(sid, session)
52
+ raise "Session collision on '#{sid.inspect}'"
53
+ end
54
+ end
55
+ if session.has_key? :flash
56
+ session[:flash] = Marshal.load(::Base64.decode64(session[:flash]))
57
+ end
58
+ [sid, session]
59
+ end
60
+ end
61
+
62
+ # === Synopsis
63
+ # call at call -> context -> commit_session in rack/session/abstract/id.rb
64
+ #
65
+ def set_session(env, session_id, new_session, options)
66
+ with_lock(env, false) do
67
+ if new_session.has_key? 'flash'
68
+ new_session['flash'] = ::Base64.encode64(Marshal.dump(new_session['flash']))
69
+ end
70
+ if ttl = options[:expire_after]
71
+ @pool.setex session_id, ttl, new_session
72
+ else
73
+ @pool.set session_id, new_session
74
+ end
75
+ session_id
76
+ end
77
+ end
78
+
79
+ def destroy_session(env, session_id, options)
80
+ with_lock(env) do
81
+ @pool.del session_id
82
+ generate_sid unless options[:drop]
83
+ end
84
+ end
85
+
86
+ def with_lock(env, default=nil)
87
+ @mutex.lock if env['rack.multithread']
88
+ yield
89
+ rescue Errno::ECONNREFUSED
90
+ if $VERBOSE
91
+ warn "#{self} is unable to find Redis server."
92
+ warn $!.inspect
93
+ end
94
+ default
95
+ ensure
96
+ @mutex.unlock if @mutex.locked?
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rack_session_redis_store/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "rack_session_redis_store"
8
+ gem.version = RackSessionRedisStore::VERSION
9
+ gem.authors = ["Spring_MT"]
10
+ gem.email = ["today.is.sky.blue.sky@gmail.com"]
11
+ gem.summary = %q{Rack session using redis. Corresponding to Rails :flash}
12
+ gem.homepage = "https://github.com/SpringMT/rack_session_redis_store"
13
+
14
+ gem.rubyforge_project = 'rack_session_redis_store'
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib"]
20
+
21
+ gem.add_dependency 'rack', '~> 1.4'
22
+ gem.add_dependency 'redis_json_serializer', '>= 0'
23
+
24
+ gem.description = <<description
25
+ description
26
+
27
+ end
@@ -0,0 +1,295 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require File.dirname(__FILE__) + '/spec_helper'
5
+
6
+ require 'rack/mock'
7
+ require 'thread'
8
+ require 'json'
9
+
10
+ describe RackSessionRedisStore::Session do
11
+ session_key = RackSessionRedisStore::Session::DEFAULT_OPTIONS[:key]
12
+ session_match = /#{session_key}=([0-9a-fA-F]+);/
13
+ incrementor = lambda do |env|
14
+ env["rack.session"]["counter"] ||= 0
15
+ env["rack.session"]["counter"] += 1
16
+ Rack::Response.new(env["rack.session"].inspect).to_a
17
+ end
18
+ drop_session = proc do |env|
19
+ env['rack.session.options'][:drop] = true
20
+ incrementor.call(env)
21
+ end
22
+ renew_session = proc do |env|
23
+ env['rack.session.options'][:renew] = true
24
+ incrementor.call(env)
25
+ end
26
+ defer_session = proc do |env|
27
+ env['rack.session.options'][:defer] = true
28
+ incrementor.call(env)
29
+ end
30
+
31
+ it "creates a new cookie" do
32
+ pool = RackSessionRedisStore::Session.new(incrementor)
33
+ res = Rack::MockRequest.new(pool).get("/")
34
+ res["Set-Cookie"].should be_include("#{session_key}=")
35
+ res.body.should be_eql('{"counter"=>1}')
36
+ end
37
+
38
+ it "determines session from a cookie" do
39
+ pool = RackSessionRedisStore::Session.new(incrementor)
40
+ req = Rack::MockRequest.new(pool)
41
+ res = req.get("/")
42
+ cookie = res["Set-Cookie"]
43
+ req.get("/", "HTTP_COOKIE" => cookie).
44
+ body.should be_eql('{"counter"=>2}')
45
+ req.get("/", "HTTP_COOKIE" => cookie).
46
+ body.should be_eql('{"counter"=>3}')
47
+ end
48
+
49
+ it "determines session only from a cookie by default" do
50
+ pool = RackSessionRedisStore::Session.new(incrementor)
51
+ req = Rack::MockRequest.new(pool)
52
+ res = req.get("/")
53
+ sid = res["Set-Cookie"][session_match, 1]
54
+ req.get("/?rack.session=#{sid}").
55
+ body.should be_eql('{"counter"=>1}')
56
+ req.get("/?rack.session=#{sid}").
57
+ body.should be_eql('{"counter"=>1}')
58
+ end
59
+
60
+ it "determines session from params" do
61
+ pool = RackSessionRedisStore::Session.new(incrementor, :cookie_only => false)
62
+ req = Rack::MockRequest.new(pool)
63
+ res = req.get("/")
64
+ sid = res["Set-Cookie"][session_match, 1]
65
+ req.get("/?rack.session=#{sid}").
66
+ body.should be_eql('{"counter"=>2}')
67
+ req.get("/?rack.session=#{sid}").
68
+ body.should be_eql('{"counter"=>3}')
69
+ end
70
+
71
+ it "survives nonexistant cookies" do
72
+ bad_cookie = "rack.session=blarghfasel"
73
+ pool = RackSessionRedisStore::Session.new(incrementor)
74
+ res = Rack::MockRequest.new(pool).
75
+ get("/", "HTTP_COOKIE" => bad_cookie)
76
+ res.body.should be_eql('{"counter"=>1}')
77
+ cookie = res["Set-Cookie"][session_match]
78
+ cookie.should_not match(/#{bad_cookie}/)
79
+ end
80
+
81
+ it "maintains freshness" do
82
+ pool = RackSessionRedisStore::Session.new(incrementor, :expire_after => 3)
83
+ res = Rack::MockRequest.new(pool).get('/')
84
+ res.body.should be_include('"counter"=>1')
85
+ cookie = res["Set-Cookie"]
86
+ sid = cookie[session_match, 1]
87
+ res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie)
88
+ res["Set-Cookie"][session_match, 1].should be_eql(sid)
89
+ res.body.should be_include('"counter"=>2')
90
+ puts 'Sleeping to expire session' if $DEBUG
91
+ sleep 4
92
+ res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie)
93
+ res["Set-Cookie"][session_match, 1].should_not be_eql(sid)
94
+ res.body.should be_include('"counter"=>1')
95
+ end
96
+
97
+ it "does not send the same session id if it did not change" do
98
+ pool = RackSessionRedisStore::Session.new(incrementor)
99
+ req = Rack::MockRequest.new(pool)
100
+
101
+ res0 = req.get("/")
102
+ cookie = res0["Set-Cookie"]
103
+ res0.body.should be_eql('{"counter"=>1}')
104
+
105
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
106
+ res1["Set-Cookie"].should be_nil
107
+ res1.body.should be_eql('{"counter"=>2}')
108
+
109
+ res2 = req.get("/", "HTTP_COOKIE" => cookie)
110
+ res2["Set-Cookie"].should be_nil
111
+ res2.body.should be_eql('{"counter"=>3}')
112
+ end
113
+
114
+ it "deletes cookies with :drop option" do
115
+ pool = RackSessionRedisStore::Session.new(incrementor)
116
+ req = Rack::MockRequest.new(pool)
117
+ drop = Rack::Utils::Context.new(pool, drop_session)
118
+ dreq = Rack::MockRequest.new(drop)
119
+
120
+ res1 = req.get("/")
121
+ session = (cookie = res1["Set-Cookie"])[session_match]
122
+ res1.body.should be_eql('{"counter"=>1}')
123
+
124
+ res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
125
+ res2["Set-Cookie"].should be_nil
126
+ res2.body.should be_eql('{"counter"=>2}')
127
+
128
+ res3 = req.get("/", "HTTP_COOKIE" => cookie)
129
+ res3["Set-Cookie"][session_match].should_not be_eql(session)
130
+ res3.body.should be_eql('{"counter"=>1}')
131
+ end
132
+
133
+ it "provides new session id with :renew option" do
134
+ pool = RackSessionRedisStore::Session.new(incrementor)
135
+ req = Rack::MockRequest.new(pool)
136
+ renew = Rack::Utils::Context.new(pool, renew_session)
137
+ rreq = Rack::MockRequest.new(renew)
138
+
139
+ res1 = req.get("/")
140
+ session = (cookie = res1["Set-Cookie"])[session_match]
141
+ res1.body.should be_eql('{"counter"=>1}')
142
+
143
+ res2 = rreq.get("/", "HTTP_COOKIE" => cookie)
144
+ new_cookie = res2["Set-Cookie"]
145
+ new_session = new_cookie[session_match]
146
+ new_session.should_not be_eql(session)
147
+ res2.body.should be_eql('{"counter"=>2}')
148
+
149
+ res3 = req.get("/", "HTTP_COOKIE" => new_cookie)
150
+ res3.body.should be_eql('{"counter"=>3}')
151
+
152
+ # Old cookie was deleted
153
+ res4 = req.get("/", "HTTP_COOKIE" => cookie)
154
+ res4.body.should be_eql('{"counter"=>1}')
155
+ end
156
+
157
+ it "omits cookie with :defer option" do
158
+ pool = RackSessionRedisStore::Session.new(incrementor)
159
+ defer = Rack::Utils::Context.new(pool, defer_session)
160
+ dreq = Rack::MockRequest.new(defer)
161
+
162
+ res0 = dreq.get("/")
163
+ res0["Set-Cookie"].should be_nil
164
+ res0.body.should be_eql('{"counter"=>1}')
165
+ end
166
+
167
+ it "updates deep hashes correctly" do
168
+ hash_check = proc do |env|
169
+ session = env['rack.session']
170
+ unless session.include? 'test'
171
+ session.update :a => :b, :c => { :d => :e },
172
+ :f => { :g => { :h => :i} }, 'test' => true
173
+ else
174
+ session[:f][:g][:h] = :j
175
+ end
176
+ [200, {}, [session.inspect]]
177
+ end
178
+ pool = RackSessionRedisStore::Session.new(hash_check)
179
+ req = Rack::MockRequest.new(pool)
180
+
181
+ res0 = req.get("/")
182
+ session_id = (cookie = res0["Set-Cookie"])[session_match, 1]
183
+ ses0 = pool.pool.get(session_id)
184
+
185
+ req.get("/", "HTTP_COOKIE" => cookie)
186
+ ses1 = pool.pool.get(session_id)
187
+
188
+ ses1.should_not be_eql(ses0)
189
+ end
190
+
191
+ # anyone know how to do this better?
192
+ it "cleanly merges sessions when multithreaded" do
193
+ unless $DEBUG
194
+ 1.should be_eql(1) # fake assertion to appease the mighty bacon
195
+ next
196
+ end
197
+ warn 'Running multithread test for RackSessionRedisStore::Session'
198
+ pool = RackSessionRedisStore::Session.new(incrementor)
199
+ req = Rack::MockRequest.new(pool)
200
+
201
+ res = req.get('/')
202
+ res.body.should be_eql('{"counter"=>1}')
203
+ cookie = res["Set-Cookie"]
204
+ session_id = cookie[session_match, 1]
205
+
206
+ delta_incrementor = lambda do |env|
207
+ # emulate disconjoinment of threading
208
+ env['rack.session'] = env['rack.session'].dup
209
+ Thread.stop
210
+ env['rack.session'][(Time.now.usec*rand).to_i] = true
211
+ incrementor.call(env)
212
+ end
213
+ tses = Rack::Utils::Context.new pool, delta_incrementor
214
+ treq = Rack::MockRequest.new(tses)
215
+ tnum = rand(7).to_i+5
216
+ r = Array.new(tnum) do
217
+ Thread.new(treq) do |run|
218
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
219
+ end
220
+ end.reverse.map{|t| t.run.join.value }
221
+ r.each do |request|
222
+ request['Set-Cookie'].should be_eql(cookie)
223
+ request.body.should be_include('"counter"=>2')
224
+ end
225
+
226
+ session = pool.pool.get(session_id)
227
+ session.size.should be_eql(tnum+1) # counter
228
+ session['counter'].should be_eql(2) # meeeh
229
+
230
+ tnum = rand(7).to_i+5
231
+ r = Array.new(tnum) do |i|
232
+ app = Rack::Utils::Context.new pool, time_delta
233
+ req = Rack::MockRequest.new app
234
+ Thread.new(req) do |run|
235
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
236
+ end
237
+ end.reverse.map{|t| t.run.join.value }
238
+ r.each do |request|
239
+ request['Set-Cookie'].should be_eql(cookie)
240
+ request.body.should be_include('"counter"=>3')
241
+ end
242
+
243
+ session = pool.pool.get(session_id)
244
+ session.size.should be_eql(tnum+1)
245
+ session['counter'].should be_eql(3)
246
+
247
+ drop_counter = proc do |env|
248
+ env['rack.session'].delete 'counter'
249
+ env['rack.session']['foo'] = 'bar'
250
+ [200, {'Content-Type'=>'text/plain'}, env['rack.session'].inspect]
251
+ end
252
+ tses = Rack::Utils::Context.new pool, drop_counter
253
+ treq = Rack::MockRequest.new(tses)
254
+ tnum = rand(7).to_i+5
255
+ r = Array.new(tnum) do
256
+ Thread.new(treq) do |run|
257
+ run.get('/', "HTTP_COOKIE" => cookie, 'rack.multithread' => true)
258
+ end
259
+ end.reverse.map{|t| t.run.join.value }
260
+ r.each do |request|
261
+ request['Set-Cookie'].should be_eql(cookie)
262
+ request.body.should be_include('"foo"=>"bar"')
263
+ end
264
+
265
+ session = pool.pool.get(session_id)
266
+ session.size.should be_eql(r.size+1)
267
+ session['counter'].should be_nil
268
+ session['foo'].should be_eql('bar')
269
+ end
270
+
271
+ it "handles a flash object" do
272
+ flash_set = proc do |env|
273
+ session = env['rack.session']
274
+ session[:flash] = {notice: 'notice foo', alert: 'alert bar'}
275
+ # get_session() is called when session is refferd first
276
+ [200, {}, [session[:flash]]] ## set_session() is called at last
277
+ end
278
+ flash_get = proc do |env|
279
+ session = env['rack.session']
280
+ [200, {}, [session['flash']]]
281
+ end
282
+ pool_set = RackSessionRedisStore::Session.new flash_set
283
+ req0 = Rack::MockRequest.new pool_set
284
+ res = req0.get "/"
285
+ cookie = res["Set-Cookie"]
286
+ cookie.should be_include "#{session_key}="
287
+ pool_get = RackSessionRedisStore::Session.new flash_get
288
+ req1 = Rack::MockRequest.new pool_get
289
+ body1 = req1.get("/", "HTTP_COOKIE" => cookie).body
290
+ # Rack::MockRequest returns a to_s body. See lib/rack/mock.rb
291
+ body1.should eql({notice: 'notice foo', alert: 'alert bar'}.to_s)
292
+ end
293
+
294
+ end
295
+
@@ -0,0 +1,14 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ Bundler.setup(:default, :test)
6
+ Bundler.require(:default, :test)
7
+
8
+ require 'rspec'
9
+
10
+ $TESTING=true
11
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
12
+ require 'rack_session_redis_store'
13
+
14
+
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack_session_redis_store
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Spring_MT
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-24 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rack
16
+ requirement: &70122680270940 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.4'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70122680270940
25
+ - !ruby/object:Gem::Dependency
26
+ name: redis_json_serializer
27
+ requirement: &70122680270400 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70122680270400
36
+ description: ''
37
+ email:
38
+ - today.is.sky.blue.sky@gmail.com
39
+ executables: []
40
+ extensions: []
41
+ extra_rdoc_files: []
42
+ files:
43
+ - .gitignore
44
+ - .travis.yml
45
+ - Gemfile
46
+ - LICENSE.txt
47
+ - README.md
48
+ - Rakefile
49
+ - lib/rack_session_redis_store.rb
50
+ - lib/rack_session_redis_store/version.rb
51
+ - rack_session_redis_store.gemspec
52
+ - spec/rack_session_redis_store_spec.rb
53
+ - spec/spec_helper.rb
54
+ homepage: https://github.com/SpringMT/rack_session_redis_store
55
+ licenses: []
56
+ post_install_message:
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ! '>='
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubyforge_project: rack_session_redis_store
74
+ rubygems_version: 1.8.11
75
+ signing_key:
76
+ specification_version: 3
77
+ summary: Rack session using redis. Corresponding to Rails :flash
78
+ test_files:
79
+ - spec/rack_session_redis_store_spec.rb
80
+ - spec/spec_helper.rb
81
+ has_rdoc: