rack-session-file 0.3.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.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ Copyright (c) 2012 ITO Nobuaki
2
+ Copyright (c) 2011 Uchio Kondo
3
+ Copyright (c) 2010 Jonathan Rudenberg
4
+
5
+ MIT License
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining
8
+ a copy of this software and associated documentation files (the
9
+ "Software"), to deal in the Software without restriction, including
10
+ without limitation the rights to use, copy, modify, merge, publish,
11
+ distribute, sublicense, and/or sell copies of the Software, and to
12
+ permit persons to whom the Software is furnished to do so, subject to
13
+ the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be
16
+ included in all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # Rack::Session::File
2
+
3
+ Rack session store on plain file system.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'rack-session-file', :git =>
11
+ 'git://github.com/dayflower/rack-session-file.git'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle install
17
+
18
+ ## Usage
19
+
20
+ ```ruby
21
+ use Rack::Session::File, :storage => ENV['TEMP'],
22
+ :expire_after => 1800
23
+ ```
24
+
25
+ ### Options
26
+
27
+ #### File storage directory (`:storage`)
28
+
29
+ Default is `Dir.tmpdir`.
30
+
31
+ #### Backend serializer (`:driver`)
32
+
33
+ You can specify backend serializer via `:driver` option.
34
+
35
+ ```ruby
36
+ use Rack::Session::File, :driver => :YAML
37
+ ```
38
+
39
+ Bundled drivers are:
40
+
41
+ * `:PStore` (default)
42
+ * `:YAML`
43
+ * `:Marshal` (discouraged to use)
44
+
45
+ Also you can `use` backend driver class explicitly.
46
+
47
+ ```ruby
48
+ use Rack::Session::File::YAML
49
+ ```
50
+
51
+ ## Contributing
52
+
53
+ 1. Fork it
54
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
55
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
56
+ 4. Push to the branch (`git push origin my-new-feature`)
57
+ 5. Create new Pull Request
58
+
59
+ ## Acknowledgement
60
+
61
+ Most of main code and test codes are derived from
62
+ titanous's [mongo-store](https://github.com/titanous/mongo-store)
63
+ and udzura's [rack-session-dbm](https://github.com/udzura/rack-session-dbm).
64
+
65
+ ## Copyright and license
66
+
67
+ Copyright © 2012 ITO Nobuaki.
68
+ Copyright © 2011 Uchio Kondo. (as the author of
69
+ [rack-session-dbm](https://github.com/udzura/rack-session-dbm) )
70
+ Copyright © 2010 Jonathan Rudenberg. (as the author of
71
+ [mongo-store](https://github.com/titanous/mongo-store) )
72
+ See LICENSE for details (MIT License).
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/rack/session/file')
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+
3
+ module Rack
4
+ module Session
5
+ module File
6
+ VERSION = '0.3.0'
7
+
8
+ autoload :Marshal, 'rack/session/file/marshal'
9
+ autoload :PStore, 'rack/session/file/pstore'
10
+ autoload :YAML, 'rack/session/file/yaml'
11
+
12
+ def self.new(app, options = {})
13
+ mapping = {
14
+ :pstore => :PStore,
15
+ :yaml => :YAML,
16
+ :marshal => :Marshal,
17
+ }
18
+
19
+ driver = options.delete(:driver) || :pstore
20
+ driver = mapping[driver.to_s.downcase.to_sym] || driver if driver.is_a?(Symbol)
21
+ if ! driver.is_a?(Class)
22
+ require autoload?(driver) if autoload?(driver)
23
+ driver = const_get(driver)
24
+ end
25
+
26
+ return driver.new(app, options)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,168 @@
1
+ # coding: utf-8
2
+ require 'rubygems'
3
+ require 'rack/session/abstract/id'
4
+
5
+ module Rack
6
+ module Session
7
+ module File
8
+ class Abstract < Rack::Session::Abstract::ID
9
+ DEFAULT_OPTIONS = Rack::Session::Abstract::ID::DEFAULT_OPTIONS.merge({
10
+ :storage => Dir.tmpdir,
11
+ :clear_expired_after => 30 * 60,
12
+ })
13
+
14
+ def initialize(app, options = {})
15
+ super
16
+
17
+ @storage = @default_options.delete(:storage)
18
+ @recheck_expire_period = @default_options.delete(:clear_expired_after).to_i
19
+
20
+ @next_expire_period = nil
21
+
22
+ @transaction_options = {
23
+ :storage => @storage,
24
+ }
25
+ end
26
+
27
+ def get_session(env, sid)
28
+ new_transaction(env).with_lock do |transaction|
29
+ session = find_session(env, sid) if sid
30
+ unless sid and session
31
+ env['rack.errors'].puts("Session '#{sid}' not found, initializing...") if $VERBOSE and not sid.nil?
32
+ session = {}
33
+ sid = new_sid(env)
34
+ transaction.save_session(sid)
35
+ end
36
+ session.instance_variable_set('@old', {}.merge(session))
37
+ session.instance_variable_set('@sid', sid)
38
+ return [sid, session]
39
+ end
40
+ end
41
+
42
+ def set_session(env, sid, new_session, options)
43
+ new_transaction(env).with_lock do |transaction|
44
+ expires = Time.now + options[:expire_after] if ! options[:expire_after].nil?
45
+ session = find_session(env, sid) || {}
46
+
47
+ if options[:renew] or options[:drop]
48
+ transaction.delete_session(sid)
49
+ return false if options[:drop]
50
+ sid = new_sid(env)
51
+ #transaction.save_session(sid, session, expires)
52
+ end
53
+
54
+ old_session = new_session.instance_variable_defined?('@old') ? new_session.instance_variable_get('@old') : {}
55
+ session = merge_sessions(sid, old_session, new_session, session)
56
+ transaction.save_session(sid, session, expires)
57
+ return sid
58
+ end
59
+ end
60
+
61
+ def destroy_session(env, sid, options)
62
+ new_transaction(env).with_lock do |transaction|
63
+ transaction.delete_session(sid)
64
+ new_sid(env) unless options[:drop]
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ def new_transaction(env)
71
+ raise '#new_transaction not implemented'
72
+ end
73
+
74
+ def new_sid(env)
75
+ loop do
76
+ sid = generate_sid()
77
+ break sid unless find_session(env, sid)
78
+ end
79
+ end
80
+
81
+ def find_session(env, sid)
82
+ transaction = new_transaction(env)
83
+
84
+ time = Time.now
85
+ if @recheck_expire_period != -1 && (@next_expire_period.nil? || @next_expire_period < time)
86
+ @next_expire_period = time + @recheck_expire_period
87
+ transaction.expire_sessions(time)
88
+ end
89
+
90
+ transaction.find_session(sid)
91
+ end
92
+
93
+ def merge_sessions(sid, old, new, current = nil)
94
+ current ||= {}
95
+ unless Hash === old and Hash === new
96
+ warn 'Bad old or new sessions provided.'
97
+ return current
98
+ end
99
+
100
+ # delete keys that are not in common
101
+ deletes = current.keys - (new.keys & current.keys)
102
+ warn "//@#{sid}: dropping #{deletes*','}" if $DEBUG and not deletes.empty?
103
+ deletes.each { |k| current.delete(k) }
104
+
105
+ updates = new.keys.select { |k| ! current.has_key?(k) || new[k] != current[k] || new[k].kind_of?(Hash) || new[k].kind_of?(Array) }
106
+ warn "//@#{sid}: updating #{updates*','}" if $DEBUG and not updates.empty?
107
+ updates.each { |k| current[k] = new[k] }
108
+
109
+ return current
110
+ end
111
+
112
+ class Transaction
113
+ def initialize(env, options = {})
114
+ @env = env
115
+ @storage = options[:storage]
116
+ @mutex = Mutex.new if want_thread_safe?
117
+ end
118
+
119
+ def with_lock
120
+ @mutex.lock if want_thread_safe?
121
+ begin
122
+ yield(self)
123
+ ensure
124
+ @mutex.unlock if want_thread_safe?
125
+ end
126
+ end
127
+
128
+ def find_session(sid)
129
+ session = load_session(sid)
130
+
131
+ if session && session[:expires] != nil && session[:expires] < Time.now
132
+ delete_session(sid)
133
+ session = nil
134
+ end
135
+
136
+ return session ? session[:data] : nil
137
+ end
138
+
139
+ def save_session(sid, session = {}, expires = nil)
140
+ store_session(sid, { :data => session, :expires => expires })
141
+ end
142
+
143
+ def store_session(sid, data)
144
+ raise '#store_session not implemented'
145
+ end
146
+
147
+ def load_session(sid)
148
+ raise '#load_session not implemented'
149
+ end
150
+
151
+ def delete_session(sid)
152
+ raise '#delete_session not implemented'
153
+ end
154
+
155
+ def expire_sessions(time)
156
+ raise '#expire_sessions not implemented'
157
+ end
158
+
159
+ private
160
+
161
+ def want_thread_safe?
162
+ @env['rack.multithread']
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,66 @@
1
+ # coding: utf-8
2
+ require 'rubygems'
3
+ require 'fileutils'
4
+ require File.expand_path(File.dirname(__FILE__) + '/abstract')
5
+
6
+ module Rack
7
+ module Session
8
+ module File
9
+ class Marshal < Abstract
10
+ def new_transaction(env)
11
+ Rack::Session::File::Marshal::Transaction.new(env, @transaction_options)
12
+ end
13
+
14
+ class Transaction < Rack::Session::File::Abstract::Transaction
15
+ def store_session(sid, data)
16
+ open_session_file(sid, 'w') do |file|
17
+ ::Marshal.dump(data, file)
18
+ end
19
+ end
20
+
21
+ def load_session(sid)
22
+ begin
23
+ open_session_file(sid, 'r') do |file|
24
+ return ::Marshal.load(file)
25
+ end
26
+ rescue Errno::ENOENT
27
+ return nil
28
+ end
29
+ end
30
+
31
+ def delete_session(sid)
32
+ filename = session_file_name(sid)
33
+ if ::File.exists?(filename)
34
+ ::File.unlink(filename)
35
+ end
36
+ end
37
+
38
+ def expire_sessions(time)
39
+ # TODO
40
+ end
41
+
42
+ private
43
+
44
+ def open_session_file(sid, mode = 'r')
45
+ ensure_storage_accessible() if mode =~ /^w/
46
+ if block_given?
47
+ open(session_file_name(sid), mode) do |file|
48
+ yield file
49
+ end
50
+ else
51
+ return open(session_file_name(sid), mode)
52
+ end
53
+ end
54
+
55
+ def session_file_name(sid)
56
+ return ::File.join(@storage, sid)
57
+ end
58
+
59
+ def ensure_storage_accessible
60
+ FileUtils.mkdir_p(@storage)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,70 @@
1
+ # coding: utf-8
2
+ require 'rubygems'
3
+ require 'fileutils'
4
+ require 'pstore'
5
+ require File.expand_path(File.dirname(__FILE__) + '/abstract')
6
+
7
+ module Rack
8
+ module Session
9
+ module File
10
+ class PStore < Abstract
11
+ def new_transaction(env)
12
+ Rack::Session::File::PStore::Transaction.new(env, @transaction_options)
13
+ end
14
+
15
+ class Transaction < Rack::Session::File::Abstract::Transaction
16
+ def store_session(sid, data)
17
+ ensure_storage_accessible()
18
+
19
+ store_for_sid(sid).transaction do |db|
20
+ data.keys.each do |key|
21
+ db[key] = data[key]
22
+ end
23
+ end
24
+ end
25
+
26
+ def load_session(sid)
27
+ ensure_storage_accessible()
28
+
29
+ data = {}
30
+ store_for_sid(sid).transaction(true) do |db|
31
+ db.roots.each do |key|
32
+ data[key] = db[key]
33
+ end
34
+ end
35
+ return data
36
+ end
37
+
38
+ def delete_session(sid)
39
+ filename = store_for_sid(sid).path
40
+ if ::File.exists?(filename)
41
+ ::File.unlink(filename)
42
+ end
43
+ end
44
+
45
+ def expire_sessions(time)
46
+ # TODO
47
+ end
48
+
49
+ private
50
+
51
+ def store_for_sid(sid)
52
+ begin
53
+ ::PStore.new(session_file_name(sid), want_thread_safe?)
54
+ rescue ArgumentError
55
+ ::PStore.new(session_file_name(sid))
56
+ end
57
+ end
58
+
59
+ def session_file_name(sid)
60
+ return ::File.join(@storage, sid)
61
+ end
62
+
63
+ def ensure_storage_accessible
64
+ FileUtils.mkdir_p(@storage)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,76 @@
1
+ # coding: utf-8
2
+ require 'rubygems'
3
+ require 'fileutils'
4
+ require 'yaml/store'
5
+ require File.expand_path(File.dirname(__FILE__) + '/abstract')
6
+
7
+ module Rack
8
+ module Session
9
+ module File
10
+ class YAML < Abstract
11
+ def initialize(app, options = {})
12
+ super
13
+ @transaction_options['yaml'] = @default_options.delete(:yaml) || {}
14
+ end
15
+
16
+ def new_transaction(env)
17
+ Rack::Session::File::YAML::Transaction.new(env, @transaction_options)
18
+ end
19
+
20
+ class Transaction < Rack::Session::File::Abstract::Transaction
21
+ def initialize(env, options = {})
22
+ super
23
+ @yaml_options = options[:yaml] || {}
24
+ end
25
+
26
+ def store_session(sid, data)
27
+ ensure_storage_accessible()
28
+
29
+ store_for_sid(sid).transaction do |db|
30
+ data.keys.each do |key|
31
+ db[key] = data[key]
32
+ end
33
+ end
34
+ end
35
+
36
+ def load_session(sid)
37
+ ensure_storage_accessible()
38
+
39
+ data = {}
40
+ store_for_sid(sid).transaction(true) do |db|
41
+ db.roots.each do |key|
42
+ data[key] = db[key]
43
+ end
44
+ end
45
+ return data
46
+ end
47
+
48
+ def delete_session(sid)
49
+ filename = store_for_sid(sid).path
50
+ if ::File.exists?(filename)
51
+ ::File.unlink(filename)
52
+ end
53
+ end
54
+
55
+ def expire_sessions(time)
56
+ # TODO
57
+ end
58
+
59
+ private
60
+
61
+ def store_for_sid(sid)
62
+ ::YAML::Store.new(session_file_name(sid), @yaml_options)
63
+ end
64
+
65
+ def session_file_name(sid)
66
+ return ::File.join(@storage, sid)
67
+ end
68
+
69
+ def ensure_storage_accessible
70
+ FileUtils.mkdir_p(@storage)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,50 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.authors = ["ITO Nobuaki"]
5
+ gem.email = ["daydream.trippers@gmail.com"]
6
+ gem.description = %q{A rack-based session store on plain file system.}
7
+ gem.summary = %q{A rack-based session store on plain file system}
8
+ gem.homepage = ""
9
+
10
+ gem.files = [
11
+ "rack-session-file.gemspec",
12
+ "Gemfile",
13
+ "LICENSE",
14
+ "README.md",
15
+ "Rakefile",
16
+ "lib/rack-session-file.rb",
17
+ "lib/rack/session/file.rb",
18
+ "lib/rack/session/file/abstract.rb",
19
+ "lib/rack/session/file/marshal.rb",
20
+ "lib/rack/session/file/pstore.rb",
21
+ "lib/rack/session/file/yaml.rb",
22
+ "spec/common.rb",
23
+ "spec/rack-session-file_spec.rb",
24
+ "spec/rack-session-file-marshal_spec.rb",
25
+ "spec/rack-session-file-pstore_spec.rb",
26
+ "spec/rack-session-file-yaml_spec.rb",
27
+ "spec/spec_helper.rb",
28
+ ]
29
+
30
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
31
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
32
+ gem.name = "rack-session-file"
33
+ gem.require_paths = ["lib"]
34
+ gem.version = '0.3.0'
35
+
36
+ if gem.respond_to? :specification_version then
37
+ gem.specification_version = 3
38
+
39
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
40
+ gem.add_runtime_dependency 'rack', '>= 1.1.0'
41
+ gem.add_development_dependency 'rspec', '>= 1.2.9'
42
+ else
43
+ gem.add_dependency 'rack', '>= 1.1.0'
44
+ gem.add_dependency 'rspec', '>= 1.2.9'
45
+ end
46
+ else
47
+ gem.add_dependency 'rack', '>= 1.1.0'
48
+ gem.add_dependency 'rspec', '>= 1.2.9'
49
+ end
50
+ end
data/spec/common.rb ADDED
@@ -0,0 +1,164 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'fileutils'
3
+ require 'tempfile'
4
+
5
+ shared_examples_for Rack::Session::File do
6
+ before(:all) do
7
+ if described_class.constants.include?(:DEFAULT_OPTIONS)
8
+ @session_key = described_class::DEFAULT_OPTIONS[:key]
9
+ else
10
+ require 'rack/session/abstract/id'
11
+ @session_key = Rack::Session::Abstract::ID::DEFAULT_OPTIONS[:key]
12
+ end
13
+ @session_match = /#{@session_key}=[0-9A-Fa-f]+;/
14
+
15
+ tf = Tempfile.open("racksess")
16
+ @storage = tf.path
17
+ tf.close!
18
+
19
+ @increment_mockapp = lambda do |env|
20
+ env['rack.session']['counter'] ||= 0
21
+ env['rack.session']['counter'] += 1
22
+ Rack::Response.new(env['rack.session'].inspect).finish
23
+ end
24
+
25
+ @drop_session_mockapp = lambda do |env|
26
+ env['rack.session.options'][:drop] = true
27
+ @increment_mockapp.call(env)
28
+ end
29
+
30
+ @renew_session_mockapp = lambda do |env|
31
+ env['rack.session.options'][:renew] = true
32
+ @increment_mockapp.call(env)
33
+ end
34
+
35
+ @defer_session_mockapp = lambda do |env|
36
+ env['rack.session.options'][:defer] = true
37
+ @increment_mockapp.call(env)
38
+ end
39
+ end
40
+
41
+ after(:all) do
42
+ FileUtils.remove_dir(@storage)
43
+ end
44
+
45
+ let(:pool) { pool = described_class.new(@increment_mockapp, :storage => @storage) }
46
+
47
+ it 'creates a new cookie' do
48
+ res = Rack::MockRequest.new(pool).get('/')
49
+
50
+ res['Set-Cookie'].should match(/#{@session_key}=/)
51
+ res.body.should == '{"counter"=>1}'
52
+ end
53
+
54
+ it 'determines session from a cookie' do
55
+ req = Rack::MockRequest.new(pool)
56
+
57
+ res = req.get('/')
58
+ cookie = res['Set-Cookie']
59
+
60
+ res = req.get('/', 'HTTP_COOKIE' => cookie)
61
+ res.body.should == '{"counter"=>2}'
62
+
63
+ res = req.get('/', 'HTTP_COOKIE' => cookie)
64
+ res.body.should == '{"counter"=>3}'
65
+ end
66
+
67
+ it 'survives nonexistent cookies' do
68
+ bad_cookie = 'rack.session=blarghfasel'
69
+ res = Rack::MockRequest.new(pool) \
70
+ .get('/', 'HTTP_COOKIE' => bad_cookie)
71
+
72
+ res.body.should == '{"counter"=>1}'
73
+ cookie = res['Set-Cookie'][@session_match]
74
+ cookie.should_not match(/#{bad_cookie}/)
75
+ end
76
+
77
+ it 'should maintain freshness' do
78
+ pool2 = described_class.new(@increment_mockapp, :storage => @storage, :expire_after => 2)
79
+ expired_time = Time.now + 5
80
+ res = Rack::MockRequest.new(pool2).get('/')
81
+ res.body.should include('"counter"=>1')
82
+
83
+ cookie = res['Set-Cookie']
84
+
85
+ res = Rack::MockRequest.new(pool2).get('/', 'HTTP_COOKIE' => cookie)
86
+ res['Set-Cookie'].should == cookie
87
+ res.body.should include('"counter"=>2')
88
+
89
+ Time.stub!(:now).and_return(expired_time)
90
+
91
+ res = Rack::MockRequest.new(pool2).get('/', 'HTTP_COOKIE' => cookie)
92
+ res['Set-Cookie'].should_not == cookie
93
+ res.body.should include('"counter"=>1')
94
+ end
95
+
96
+ it 'deletes cookies with :drop option' do
97
+ req = Rack::MockRequest.new(pool)
98
+ drop = Rack::Utils::Context.new(pool, @drop_session_mockapp)
99
+ dreq = Rack::MockRequest.new(drop)
100
+
101
+ res0 = req.get('/')
102
+ session = (cookie = res0['Set-Cookie'])[@session_match]
103
+ res0.body.should == '{"counter"=>1}'
104
+
105
+ res1 = req.get('/', 'HTTP_COOKIE' => cookie)
106
+ # res1['Set-Cookie'][@session_match].should == session
107
+ res1.body.should == '{"counter"=>2}'
108
+
109
+ res2 = dreq.get('/', 'HTTP_COOKIE' => cookie)
110
+ res2['Set-Cookie'].should be_nil
111
+ res2.body.should == '{"counter"=>3}'
112
+
113
+ res3 = req.get('/', 'HTTP_COOKIE' => cookie)
114
+ res3['Set-Cookie'][@session_match].should_not == session
115
+ res3.body.should == '{"counter"=>1}'
116
+ end
117
+
118
+ it 'provides new session id with :renew option' do
119
+ req = Rack::MockRequest.new(pool)
120
+ renew = Rack::Utils::Context.new(pool, @renew_session_mockapp)
121
+ rreq = Rack::MockRequest.new(renew)
122
+
123
+ res0 = req.get('/')
124
+ session = (cookie = res0['Set-Cookie'])[@session_match]
125
+ res0.body.should == '{"counter"=>1}'
126
+
127
+ res1 = req.get('/', 'HTTP_COOKIE' => cookie)
128
+ # res1['Set-Cookie'][@session_match].should == session
129
+ res1.body.should == '{"counter"=>2}'
130
+
131
+ res2 = rreq.get('/', 'HTTP_COOKIE' => cookie)
132
+ new_cookie = res2['Set-Cookie']
133
+ new_session = new_cookie[@session_match]
134
+ new_session.should_not == session
135
+ res2.body.should == '{"counter"=>3}'
136
+
137
+ res3 = req.get('/', 'HTTP_COOKIE' => new_cookie)
138
+ # res3['Set-Cookie'][@session_match].should == new_session
139
+ res3.body.should == '{"counter"=>4}'
140
+ end
141
+
142
+ it 'omits cookie with :defer option' do
143
+ req = Rack::MockRequest.new(pool)
144
+ defer = Rack::Utils::Context.new(pool, @defer_session_mockapp)
145
+ dreq = Rack::MockRequest.new(defer)
146
+
147
+ res0 = req.get('/')
148
+ session = (cookie = res0['Set-Cookie'])[@session_match]
149
+ res0.body.should == '{"counter"=>1}'
150
+
151
+ res1 = req.get('/', 'HTTP_COOKIE' => cookie)
152
+ # res1['Set-Cookie'][@session_match].should == session
153
+ res1.body.should == '{"counter"=>2}'
154
+
155
+ res2 = dreq.get('/', 'HTTP_COOKIE' => cookie)
156
+ res2['Set-Cookie'].should be_nil
157
+ res2.body.should == '{"counter"=>3}'
158
+
159
+ res3 = req.get('/', 'HTTP_COOKIE' => cookie)
160
+ # res3['Set-Cookie'][@session_match].should == session
161
+ res3.body.should == '{"counter"=>4}'
162
+ end
163
+ end
164
+
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/common')
3
+
4
+ describe Rack::Session::File::Marshal do
5
+ it_behaves_like Rack::Session::File
6
+ end
7
+
@@ -0,0 +1,6 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/common')
3
+
4
+ describe Rack::Session::File::PStore do
5
+ it_behaves_like Rack::Session::File
6
+ end
@@ -0,0 +1,6 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/common')
3
+
4
+ describe Rack::Session::File::YAML do
5
+ it_behaves_like Rack::Session::File
6
+ end
@@ -0,0 +1,6 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/common')
3
+
4
+ describe Rack::Session::File do
5
+ it_behaves_like Rack::Session::File
6
+ end
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'rack-session-file'
5
+
6
+ require 'rack/mock'
7
+ require 'rack/response'
8
+ require 'thread'
9
+
10
+ RSpec.configure do |config|
11
+ #
12
+ end
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-session-file
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - ITO Nobuaki
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rack
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.1.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.1.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 1.2.9
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.2.9
46
+ description: A rack-based session store on plain file system.
47
+ email:
48
+ - daydream.trippers@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - rack-session-file.gemspec
54
+ - Gemfile
55
+ - LICENSE
56
+ - README.md
57
+ - Rakefile
58
+ - lib/rack-session-file.rb
59
+ - lib/rack/session/file.rb
60
+ - lib/rack/session/file/abstract.rb
61
+ - lib/rack/session/file/marshal.rb
62
+ - lib/rack/session/file/pstore.rb
63
+ - lib/rack/session/file/yaml.rb
64
+ - spec/common.rb
65
+ - spec/rack-session-file_spec.rb
66
+ - spec/rack-session-file-marshal_spec.rb
67
+ - spec/rack-session-file-pstore_spec.rb
68
+ - spec/rack-session-file-yaml_spec.rb
69
+ - spec/spec_helper.rb
70
+ homepage: ''
71
+ licenses: []
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ segments:
83
+ - 0
84
+ hash: 3561167824695945220
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ! '>='
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ segments:
92
+ - 0
93
+ hash: 3561167824695945220
94
+ requirements: []
95
+ rubyforge_project:
96
+ rubygems_version: 1.8.23
97
+ signing_key:
98
+ specification_version: 3
99
+ summary: A rack-based session store on plain file system
100
+ test_files:
101
+ - spec/common.rb
102
+ - spec/rack-session-file_spec.rb
103
+ - spec/rack-session-file-marshal_spec.rb
104
+ - spec/rack-session-file-pstore_spec.rb
105
+ - spec/rack-session-file-yaml_spec.rb
106
+ - spec/spec_helper.rb