activerecord-session_store 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord-session_store might be problematic. Click here for more details.
- data/README.md +69 -0
- data/lib/action_dispatch/session/active_record_store.rb +118 -0
- data/lib/active_record/session_store.rb +36 -0
- data/lib/active_record/session_store/railtie.rb +9 -0
- data/lib/active_record/session_store/session.rb +88 -0
- data/lib/active_record/session_store/sql_bypass.rb +140 -0
- data/lib/activerecord/session_store.rb +1 -0
- data/lib/generators/active_record/session_migration_generator.rb +24 -0
- data/lib/generators/active_record/templates/migration.rb +12 -0
- data/lib/tasks/database.rake +14 -0
- metadata +140 -0
data/README.md
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
Active Record Session Store
|
2
|
+
===========================
|
3
|
+
|
4
|
+
A session store backed by an Active Record class. A default class is
|
5
|
+
provided, but any object duck-typing to an Active Record Session class
|
6
|
+
with text `session_id` and `data` attributes is sufficient.
|
7
|
+
|
8
|
+
Installation
|
9
|
+
------------
|
10
|
+
|
11
|
+
Include this gem into your Gemfile:
|
12
|
+
|
13
|
+
gem 'activerecord-session_store', github: 'rails/activerecord-session_store'
|
14
|
+
|
15
|
+
Run the migration generator:
|
16
|
+
|
17
|
+
rails generate active_record:session_migration
|
18
|
+
|
19
|
+
Then, set your session store in `config/initializers/session_store.rb`:
|
20
|
+
|
21
|
+
Foo::Application.config.session_store :active_record_store
|
22
|
+
|
23
|
+
Configuration
|
24
|
+
--------------
|
25
|
+
|
26
|
+
The default assumes a `sessions` tables with columns:
|
27
|
+
|
28
|
+
* `id` (numeric primary key),
|
29
|
+
* `session_id` (string, usually varchar; maximum length is 255), and
|
30
|
+
* `data` (text or longtext; careful if your session data exceeds 65KB).
|
31
|
+
|
32
|
+
The `session_id` column should always be indexed for speedy lookups.
|
33
|
+
Session data is marshaled to the `data` column in Base64 format.
|
34
|
+
If the data you write is larger than the column's size limit,
|
35
|
+
ActionController::SessionOverflowError will be raised.
|
36
|
+
|
37
|
+
You may configure the table name, primary key, and data column.
|
38
|
+
For example, at the end of `config/application.rb`:
|
39
|
+
|
40
|
+
ActiveRecord::SessionStore::Session.table_name = 'legacy_session_table'
|
41
|
+
ActiveRecord::SessionStore::Session.primary_key = 'session_id'
|
42
|
+
ActiveRecord::SessionStore::Session.data_column_name = 'legacy_session_data'
|
43
|
+
|
44
|
+
Note that setting the primary key to the `session_id` frees you from
|
45
|
+
having a separate `id` column if you don't want it. However, you must
|
46
|
+
set `session.model.id = session.session_id` by hand! A before filter
|
47
|
+
on ApplicationController is a good place.
|
48
|
+
|
49
|
+
Since the default class is a simple Active Record, you get timestamps
|
50
|
+
for free if you add `created_at` and `updated_at` datetime columns to
|
51
|
+
the `sessions` table, making periodic session expiration a snap.
|
52
|
+
|
53
|
+
You may provide your own session class implementation, whether a
|
54
|
+
feature-packed Active Record or a bare-metal high-performance SQL
|
55
|
+
store, by setting
|
56
|
+
|
57
|
+
ActiveRecord::SessionStore.session_class = MySessionClass
|
58
|
+
|
59
|
+
You must implement these methods:
|
60
|
+
|
61
|
+
* `self.find_by_session_id(session_id)`
|
62
|
+
* `initialize(hash_of_session_id_and_data, options_hash = {})`
|
63
|
+
* `attr_reader :session_id`
|
64
|
+
* `attr_accessor :data`
|
65
|
+
* `save`
|
66
|
+
* `destroy`
|
67
|
+
|
68
|
+
The example SqlBypass class is a generic SQL session store. You may
|
69
|
+
use it as a basis for high-performance database-specific stores.
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'action_dispatch/middleware/session/abstract_store'
|
2
|
+
|
3
|
+
module ActionDispatch
|
4
|
+
module Session
|
5
|
+
# = Active Record Session Store
|
6
|
+
#
|
7
|
+
# A session store backed by an Active Record class. A default class is
|
8
|
+
# provided, but any object duck-typing to an Active Record Session class
|
9
|
+
# with text +session_id+ and +data+ attributes is sufficient.
|
10
|
+
#
|
11
|
+
# The default assumes a +sessions+ tables with columns:
|
12
|
+
# +id+ (numeric primary key),
|
13
|
+
# +session_id+ (string, usually varchar; maximum length is 255), and
|
14
|
+
# +data+ (text or longtext; careful if your session data exceeds 65KB).
|
15
|
+
#
|
16
|
+
# The +session_id+ column should always be indexed for speedy lookups.
|
17
|
+
# Session data is marshaled to the +data+ column in Base64 format.
|
18
|
+
# If the data you write is larger than the column's size limit,
|
19
|
+
# ActionController::SessionOverflowError will be raised.
|
20
|
+
#
|
21
|
+
# You may configure the table name, primary key, and data column.
|
22
|
+
# For example, at the end of <tt>config/application.rb</tt>:
|
23
|
+
#
|
24
|
+
# ActiveRecord::SessionStore::Session.table_name = 'legacy_session_table'
|
25
|
+
# ActiveRecord::SessionStore::Session.primary_key = 'session_id'
|
26
|
+
# ActiveRecord::SessionStore::Session.data_column_name = 'legacy_session_data'
|
27
|
+
#
|
28
|
+
# Note that setting the primary key to the +session_id+ frees you from
|
29
|
+
# having a separate +id+ column if you don't want it. However, you must
|
30
|
+
# set <tt>session.model.id = session.session_id</tt> by hand! A before filter
|
31
|
+
# on ApplicationController is a good place.
|
32
|
+
#
|
33
|
+
# Since the default class is a simple Active Record, you get timestamps
|
34
|
+
# for free if you add +created_at+ and +updated_at+ datetime columns to
|
35
|
+
# the +sessions+ table, making periodic session expiration a snap.
|
36
|
+
#
|
37
|
+
# You may provide your own session class implementation, whether a
|
38
|
+
# feature-packed Active Record or a bare-metal high-performance SQL
|
39
|
+
# store, by setting
|
40
|
+
#
|
41
|
+
# ActionDispatch::Session::ActiveRecordStore.session_class = MySessionClass
|
42
|
+
#
|
43
|
+
# You must implement these methods:
|
44
|
+
#
|
45
|
+
# self.find_by_session_id(session_id)
|
46
|
+
# initialize(hash_of_session_id_and_data, options_hash = {})
|
47
|
+
# attr_reader :session_id
|
48
|
+
# attr_accessor :data
|
49
|
+
# save
|
50
|
+
# destroy
|
51
|
+
#
|
52
|
+
# The example SqlBypass class is a generic SQL session store. You may
|
53
|
+
# use it as a basis for high-performance database-specific stores.
|
54
|
+
class ActiveRecordStore < ActionDispatch::Session::AbstractStore
|
55
|
+
# The class used for session storage. Defaults to
|
56
|
+
# ActiveRecord::SessionStore::Session
|
57
|
+
cattr_accessor :session_class
|
58
|
+
|
59
|
+
SESSION_RECORD_KEY = 'rack.session.record'
|
60
|
+
ENV_SESSION_OPTIONS_KEY = Rack::Session::Abstract::ENV_SESSION_OPTIONS_KEY
|
61
|
+
|
62
|
+
private
|
63
|
+
def get_session(env, sid)
|
64
|
+
ActiveRecord::Base.logger.quietly do
|
65
|
+
unless sid and session = @@session_class.find_by_session_id(sid)
|
66
|
+
# If the sid was nil or if there is no pre-existing session under the sid,
|
67
|
+
# force the generation of a new sid and associate a new session associated with the new sid
|
68
|
+
sid = generate_sid
|
69
|
+
session = @@session_class.new(:session_id => sid, :data => {})
|
70
|
+
end
|
71
|
+
env[SESSION_RECORD_KEY] = session
|
72
|
+
[sid, session.data]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def set_session(env, sid, session_data, options)
|
77
|
+
ActiveRecord::Base.logger.quietly do
|
78
|
+
record = get_session_model(env, sid)
|
79
|
+
record.data = session_data
|
80
|
+
return false unless record.save
|
81
|
+
|
82
|
+
session_data = record.data
|
83
|
+
if session_data && session_data.respond_to?(:each_value)
|
84
|
+
session_data.each_value do |obj|
|
85
|
+
obj.clear_association_cache if obj.respond_to?(:clear_association_cache)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
sid
|
91
|
+
end
|
92
|
+
|
93
|
+
def destroy_session(env, session_id, options)
|
94
|
+
if sid = current_session_id(env)
|
95
|
+
ActiveRecord::Base.logger.quietly do
|
96
|
+
get_session_model(env, sid).destroy
|
97
|
+
env[SESSION_RECORD_KEY] = nil
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
generate_sid unless options[:drop]
|
102
|
+
end
|
103
|
+
|
104
|
+
def get_session_model(env, sid)
|
105
|
+
if env[ENV_SESSION_OPTIONS_KEY][:id].nil?
|
106
|
+
env[SESSION_RECORD_KEY] = find_session(sid)
|
107
|
+
else
|
108
|
+
env[SESSION_RECORD_KEY] ||= find_session(sid)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def find_session(id)
|
113
|
+
@@session_class.find_by_session_id(id) ||
|
114
|
+
@@session_class.new(:session_id => id, :data => {})
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'action_dispatch/session/active_record_store'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module SessionStore
|
5
|
+
module ClassMethods # :nodoc:
|
6
|
+
def marshal(data)
|
7
|
+
::Base64.encode64(Marshal.dump(data)) if data
|
8
|
+
end
|
9
|
+
|
10
|
+
def unmarshal(data)
|
11
|
+
Marshal.load(::Base64.decode64(data)) if data
|
12
|
+
end
|
13
|
+
|
14
|
+
def drop_table!
|
15
|
+
connection.schema_cache.clear_table_cache!(table_name)
|
16
|
+
connection.drop_table table_name
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_table!
|
20
|
+
connection.schema_cache.clear_table_cache!(table_name)
|
21
|
+
connection.create_table(table_name) do |t|
|
22
|
+
t.string session_id_column, :limit => 255
|
23
|
+
t.text data_column_name
|
24
|
+
end
|
25
|
+
connection.add_index table_name, session_id_column, :unique => true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
require 'active_record/session_store/session'
|
32
|
+
require 'active_record/session_store/sql_bypass'
|
33
|
+
require 'active_record/session_store/railtie' if defined?(Rails)
|
34
|
+
|
35
|
+
|
36
|
+
ActionDispatch::Session::ActiveRecordStore.session_class = ActiveRecord::SessionStore::Session
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module SessionStore
|
3
|
+
# The default Active Record class.
|
4
|
+
class Session < ActiveRecord::Base
|
5
|
+
extend ClassMethods
|
6
|
+
|
7
|
+
##
|
8
|
+
# :singleton-method:
|
9
|
+
# Customizable data column name. Defaults to 'data'.
|
10
|
+
cattr_accessor :data_column_name
|
11
|
+
self.data_column_name = 'data'
|
12
|
+
|
13
|
+
before_save :marshal_data!
|
14
|
+
before_save :raise_on_session_data_overflow!
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def data_column_size_limit
|
18
|
+
@data_column_size_limit ||= columns_hash[data_column_name].limit
|
19
|
+
end
|
20
|
+
|
21
|
+
# Hook to set up sessid compatibility.
|
22
|
+
def find_by_session_id(session_id)
|
23
|
+
setup_sessid_compatibility!
|
24
|
+
find_by_session_id(session_id)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def session_id_column
|
29
|
+
'session_id'
|
30
|
+
end
|
31
|
+
|
32
|
+
# Compatibility with tables using sessid instead of session_id.
|
33
|
+
def setup_sessid_compatibility!
|
34
|
+
# Reset column info since it may be stale.
|
35
|
+
reset_column_information
|
36
|
+
if columns_hash['sessid']
|
37
|
+
def self.find_by_session_id(*args)
|
38
|
+
find_by_sessid(*args)
|
39
|
+
end
|
40
|
+
|
41
|
+
define_method(:session_id) { sessid }
|
42
|
+
define_method(:session_id=) { |session_id| self.sessid = session_id }
|
43
|
+
else
|
44
|
+
class << self; remove_possible_method :find_by_session_id; end
|
45
|
+
|
46
|
+
def self.find_by_session_id(session_id)
|
47
|
+
where(session_id: session_id).first
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize(attributes = nil)
|
54
|
+
@data = nil
|
55
|
+
super
|
56
|
+
end
|
57
|
+
|
58
|
+
# Lazy-unmarshal session state.
|
59
|
+
def data
|
60
|
+
@data ||= self.class.unmarshal(read_attribute(@@data_column_name)) || {}
|
61
|
+
end
|
62
|
+
|
63
|
+
attr_writer :data
|
64
|
+
|
65
|
+
# Has the session been loaded yet?
|
66
|
+
def loaded?
|
67
|
+
@data
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
def marshal_data!
|
72
|
+
return false unless loaded?
|
73
|
+
write_attribute(@@data_column_name, self.class.marshal(data))
|
74
|
+
end
|
75
|
+
|
76
|
+
# Ensures that the data about to be stored in the database is not
|
77
|
+
# larger than the data storage column. Raises
|
78
|
+
# ActionController::SessionOverflowError.
|
79
|
+
def raise_on_session_data_overflow!
|
80
|
+
return false unless loaded?
|
81
|
+
limit = self.class.data_column_size_limit
|
82
|
+
if limit and read_attribute(@@data_column_name).size > limit
|
83
|
+
raise ActionController::SessionOverflowError
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module SessionStore
|
3
|
+
# A barebones session store which duck-types with the default session
|
4
|
+
# store but bypasses Active Record and issues SQL directly. This is
|
5
|
+
# an example session model class meant as a basis for your own classes.
|
6
|
+
#
|
7
|
+
# The database connection, table name, and session id and data columns
|
8
|
+
# are configurable class attributes. Marshaling and unmarshaling
|
9
|
+
# are implemented as class methods that you may override. By default,
|
10
|
+
# marshaling data is
|
11
|
+
#
|
12
|
+
# ::Base64.encode64(Marshal.dump(data))
|
13
|
+
#
|
14
|
+
# and unmarshaling data is
|
15
|
+
#
|
16
|
+
# Marshal.load(::Base64.decode64(data))
|
17
|
+
#
|
18
|
+
# This marshaling behavior is intended to store the widest range of
|
19
|
+
# binary session data in a +text+ column. For higher performance,
|
20
|
+
# store in a +blob+ column instead and forgo the Base64 encoding.
|
21
|
+
class SqlBypass
|
22
|
+
extend ClassMethods
|
23
|
+
|
24
|
+
##
|
25
|
+
# :singleton-method:
|
26
|
+
# The table name defaults to 'sessions'.
|
27
|
+
cattr_accessor :table_name
|
28
|
+
@@table_name = 'sessions'
|
29
|
+
|
30
|
+
##
|
31
|
+
# :singleton-method:
|
32
|
+
# The session id field defaults to 'session_id'.
|
33
|
+
cattr_accessor :session_id_column
|
34
|
+
@@session_id_column = 'session_id'
|
35
|
+
|
36
|
+
##
|
37
|
+
# :singleton-method:
|
38
|
+
# The data field defaults to 'data'.
|
39
|
+
cattr_accessor :data_column
|
40
|
+
@@data_column = 'data'
|
41
|
+
|
42
|
+
class << self
|
43
|
+
alias :data_column_name :data_column
|
44
|
+
|
45
|
+
# Use the ActiveRecord::Base.connection by default.
|
46
|
+
attr_writer :connection
|
47
|
+
|
48
|
+
# Use the ActiveRecord::Base.connection_pool by default.
|
49
|
+
attr_writer :connection_pool
|
50
|
+
|
51
|
+
def connection
|
52
|
+
@connection ||= ActiveRecord::Base.connection
|
53
|
+
end
|
54
|
+
|
55
|
+
def connection_pool
|
56
|
+
@connection_pool ||= ActiveRecord::Base.connection_pool
|
57
|
+
end
|
58
|
+
|
59
|
+
# Look up a session by id and unmarshal its data if found.
|
60
|
+
def find_by_session_id(session_id)
|
61
|
+
if record = connection.select_one("SELECT #{connection.quote_column_name(data_column)} AS data FROM #{@@table_name} WHERE #{connection.quote_column_name(@@session_id_column)}=#{connection.quote(session_id.to_s)}")
|
62
|
+
new(:session_id => session_id, :marshaled_data => record['data'])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
delegate :connection, :connection=, :connection_pool, :connection_pool=, :to => self
|
68
|
+
|
69
|
+
attr_reader :session_id, :new_record
|
70
|
+
alias :new_record? :new_record
|
71
|
+
|
72
|
+
attr_writer :data
|
73
|
+
|
74
|
+
# Look for normal and marshaled data, self.find_by_session_id's way of
|
75
|
+
# telling us to postpone unmarshaling until the data is requested.
|
76
|
+
# We need to handle a normal data attribute in case of a new record.
|
77
|
+
def initialize(attributes)
|
78
|
+
@session_id = attributes[:session_id]
|
79
|
+
@data = attributes[:data]
|
80
|
+
@marshaled_data = attributes[:marshaled_data]
|
81
|
+
@new_record = @marshaled_data.nil?
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns true if the record is persisted, i.e. it's not a new record
|
85
|
+
def persisted?
|
86
|
+
!@new_record
|
87
|
+
end
|
88
|
+
|
89
|
+
# Lazy-unmarshal session state.
|
90
|
+
def data
|
91
|
+
unless @data
|
92
|
+
if @marshaled_data
|
93
|
+
@data, @marshaled_data = self.class.unmarshal(@marshaled_data) || {}, nil
|
94
|
+
else
|
95
|
+
@data = {}
|
96
|
+
end
|
97
|
+
end
|
98
|
+
@data
|
99
|
+
end
|
100
|
+
|
101
|
+
def loaded?
|
102
|
+
@data
|
103
|
+
end
|
104
|
+
|
105
|
+
def save
|
106
|
+
return false unless loaded?
|
107
|
+
marshaled_data = self.class.marshal(data)
|
108
|
+
connect = connection
|
109
|
+
|
110
|
+
if @new_record
|
111
|
+
@new_record = false
|
112
|
+
connect.update <<-end_sql, 'Create session'
|
113
|
+
INSERT INTO #{table_name} (
|
114
|
+
#{connect.quote_column_name(session_id_column)},
|
115
|
+
#{connect.quote_column_name(data_column)} )
|
116
|
+
VALUES (
|
117
|
+
#{connect.quote(session_id)},
|
118
|
+
#{connect.quote(marshaled_data)} )
|
119
|
+
end_sql
|
120
|
+
else
|
121
|
+
connect.update <<-end_sql, 'Update session'
|
122
|
+
UPDATE #{table_name}
|
123
|
+
SET #{connect.quote_column_name(data_column)}=#{connect.quote(marshaled_data)}
|
124
|
+
WHERE #{connect.quote_column_name(session_id_column)}=#{connect.quote(session_id)}
|
125
|
+
end_sql
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def destroy
|
130
|
+
return if @new_record
|
131
|
+
|
132
|
+
connect = connection
|
133
|
+
connect.delete <<-end_sql, 'Destroy session'
|
134
|
+
DELETE FROM #{table_name}
|
135
|
+
WHERE #{connect.quote_column_name(session_id_column)}=#{connect.quote(session_id.to_s)}
|
136
|
+
end_sql
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'active_record/session_store'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rails/generators/active_record'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Generators
|
5
|
+
class SessionMigrationGenerator < Base
|
6
|
+
source_root File.expand_path("../templates", __FILE__)
|
7
|
+
argument :name, :type => :string, :default => "add_sessions_table"
|
8
|
+
|
9
|
+
def create_migration_file
|
10
|
+
migration_template "migration.rb", "db/migrate/#{file_name}.rb"
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def session_table_name
|
16
|
+
current_table_name = ActiveRecord::SessionStore::Session.table_name
|
17
|
+
if current_table_name == 'session' || current_table_name == 'sessions'
|
18
|
+
current_table_name = ActiveRecord::Base.pluralize_table_names ? 'sessions' : 'session'
|
19
|
+
end
|
20
|
+
current_table_name
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :<%= session_table_name %> do |t|
|
4
|
+
t.string :session_id, :null => false
|
5
|
+
t.text :data
|
6
|
+
t.timestamps
|
7
|
+
end
|
8
|
+
|
9
|
+
add_index :<%= session_table_name %>, :session_id
|
10
|
+
add_index :<%= session_table_name %>, :updated_at
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
namespace 'db:sessions' do
|
2
|
+
desc "Creates a sessions migration for use with ActiveRecord::SessionStore"
|
3
|
+
task :create => [:environment, 'db:load_config'] do
|
4
|
+
raise 'Task unavailable to this database (no migration support)' unless ActiveRecord::Base.connection.supports_migrations?
|
5
|
+
Rails.application.load_generators
|
6
|
+
require 'rails/generators/rails/session_migration/session_migration_generator'
|
7
|
+
Rails::Generators::SessionMigrationGenerator.start [ ENV['MIGRATION'] || 'add_sessions_table' ]
|
8
|
+
end
|
9
|
+
|
10
|
+
desc "Clear the sessions table"
|
11
|
+
task :clear => [:environment, 'db:load_config'] do
|
12
|
+
ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::SessionStore::Session.table_name}"
|
13
|
+
end
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activerecord-session_store
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- David Heinemeier Hansson
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-22 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activerecord
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 4.0.0.beta
|
22
|
+
- - <
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '5'
|
25
|
+
type: :runtime
|
26
|
+
prerelease: false
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 4.0.0.beta
|
33
|
+
- - <
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '5'
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: actionpack
|
38
|
+
requirement: !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 4.0.0.beta
|
44
|
+
- - <
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '5'
|
47
|
+
type: :runtime
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 4.0.0.beta
|
55
|
+
- - <
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '5'
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: railties
|
60
|
+
requirement: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: 4.0.0.beta
|
66
|
+
- - <
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '5'
|
69
|
+
type: :runtime
|
70
|
+
prerelease: false
|
71
|
+
version_requirements: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 4.0.0.beta
|
77
|
+
- - <
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '5'
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: sqlite3
|
82
|
+
requirement: !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ! '>='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
description:
|
97
|
+
email: david@loudthinking.com
|
98
|
+
executables: []
|
99
|
+
extensions: []
|
100
|
+
extra_rdoc_files:
|
101
|
+
- README.md
|
102
|
+
files:
|
103
|
+
- README.md
|
104
|
+
- lib/action_dispatch/session/active_record_store.rb
|
105
|
+
- lib/active_record/session_store/railtie.rb
|
106
|
+
- lib/active_record/session_store/session.rb
|
107
|
+
- lib/active_record/session_store/sql_bypass.rb
|
108
|
+
- lib/active_record/session_store.rb
|
109
|
+
- lib/activerecord/session_store.rb
|
110
|
+
- lib/generators/active_record/session_migration_generator.rb
|
111
|
+
- lib/generators/active_record/templates/migration.rb
|
112
|
+
- lib/tasks/database.rake
|
113
|
+
homepage: http://www.rubyonrails.org
|
114
|
+
licenses:
|
115
|
+
- MIT
|
116
|
+
post_install_message:
|
117
|
+
rdoc_options:
|
118
|
+
- --main
|
119
|
+
- README.md
|
120
|
+
require_paths:
|
121
|
+
- lib
|
122
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
123
|
+
none: false
|
124
|
+
requirements:
|
125
|
+
- - ! '>='
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: 1.9.3
|
128
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
requirements: []
|
135
|
+
rubyforge_project:
|
136
|
+
rubygems_version: 1.8.23
|
137
|
+
signing_key:
|
138
|
+
specification_version: 3
|
139
|
+
summary: An Action Dispatch session store backed by an Active Record class.
|
140
|
+
test_files: []
|