pax_global_header 0000666 0000000 0000000 00000000064 12716351075 0014521 g ustar 00root root 0000000 0000000 52 comment=39e9fb5f8355e6e062ff80850a4939312c8ac28a
activerecord-session_store-1.0.0/ 0000775 0000000 0000000 00000000000 12716351075 0017066 5 ustar 00root root 0000000 0000000 activerecord-session_store-1.0.0/.gitignore 0000664 0000000 0000000 00000000224 12716351075 0021054 0 ustar 00root root 0000000 0000000 *.gem
*.rbc
.bundle
.config
.yardoc
*.lock
InstalledFiles
_yardoc
coverage
doc/
lib/bundler/man
pkg
rdoc
spec/reports
test/tmp
test/version_tmp
tmp
activerecord-session_store-1.0.0/.travis.yml 0000664 0000000 0000000 00000001423 12716351075 0021177 0 ustar 00root root 0000000 0000000 language: ruby
sudo: false
cache: bundler
rvm:
- 1.9.3
- 2.0.0
- 2.1.10
- 2.2.5
- 2.3.1
- ruby-head
gemfile:
- gemfiles/rails_4.0.gemfile
- gemfiles/rails_4.1.gemfile
- gemfiles/rails_4.2.gemfile
- gemfiles/rails_5.0.gemfile
- gemfiles/rails_edge.gemfile
matrix:
fast_finish: true
exclude:
- rvm: 1.9.3
gemfile: gemfiles/rails_5.0.gemfile
- rvm: 2.0.0
gemfile: gemfiles/rails_5.0.gemfile
- rvm: 2.1.10
gemfile: gemfiles/rails_5.0.gemfile
- rvm: 1.9.3
gemfile: gemfiles/rails_edge.gemfile
- rvm: 2.0.0
gemfile: gemfiles/rails_edge.gemfile
- rvm: 2.1.10
gemfile: gemfiles/rails_edge.gemfile
allow_failures:
- rvm: ruby-head
- gemfile: gemfiles/rails_edge.gemfile
notifications:
email: false
activerecord-session_store-1.0.0/Appraisals 0000664 0000000 0000000 00000001377 12716351075 0021120 0 ustar 00root root 0000000 0000000 [ '4.0', '4.1', '4.2' ].each do |ver|
appraise "rails-#{ver}" do
gem 'actionpack', "~> #{ver}.0"
gem 'activerecord', "~> #{ver}.0"
gem 'railties', "~> #{ver}.0"
gem 'rack', '~> 1.5'
end
end
appraise 'rails-5.0' do
gem 'actionpack', '>= 5.0.0.alpha', '< 5.1'
gem 'activerecord', '>= 5.0.0.alpha', '< 5.1'
gem 'railties', '>= 5.0.0.alpha', '< 5.1'
gem 'rack', '>= 2.0.0.alpha', '< 3'
end
appraise "rails-edge" do
git 'https://github.com/rails/rails.git', :branch => 'master' do
gem 'actionpack'
gem 'activerecord'
gem 'railties'
end
gem 'rack', :git => 'https://github.com/rack/rack.git', :branch => 'master'
gem 'arel', :git => 'https://github.com/rails/arel.git', :branch => 'master'
end
activerecord-session_store-1.0.0/CONTRIBUTING.md 0000664 0000000 0000000 00000005637 12716351075 0021332 0 ustar 00root root 0000000 0000000 Contributing to Active Record Session Store
=====================
[](https://travis-ci.org/rails/activerecord-session_store)
Active Record Session Store is work of [many contributors](https://github.com/rails/activerecord-session_store/graphs/contributors). You're encouraged to submit [pull requests](https://github.com/rails/activerecord-session_store/pulls), [propose features and discuss issues](https://github.com/rails/activerecord-session_store/issues).
#### Fork the Project
Fork the [project on Github](https://github.com/rails/activerecord-session_store) and check out your copy.
```
git clone https://github.com/contributor/activerecord-session_store.git
cd activerecord-session_store
git remote add upstream https://github.com/rails/activerecord-session_store.git
```
#### Create a Topic Branch
Make sure your fork is up-to-date and create a topic branch for your feature or bug fix.
```
git checkout master
git pull upstream master
git checkout -b my-feature-branch
```
#### Bundle Install and Test
Ensure that you can build the project and run tests.
```
bundle install
bundle exec rake test
```
#### Write Tests
Try to write a test that reproduces the problem you're trying to fix or describes a feature that you want to build. Add to [test](test).
We definitely appreciate pull requests that highlight or reproduce a problem, even without a fix.
#### Write Code
Implement your feature or bug fix.
Make sure that `bundle exec rake test` completes without errors.
#### Write Documentation
Document any external behavior in the [README](README.md).
#### Commit Changes
Make sure git knows your name and email address:
```
git config --global user.name "Your Name"
git config --global user.email "contributor@example.com"
```
Writing good commit logs is important. A commit log should describe what changed and why.
```
git add ...
git commit
```
#### Push
```
git push origin my-feature-branch
```
#### Make a Pull Request
Go to your fork at
`https://github.com/[your github username]/activerecord-session_store` and
select your feature branch. Click the 'Pull Request' button and fill out the
form. Pull requests are usually reviewed within a few days.
#### Rebase
If you've been working on a change for a while, rebase with upstream/master.
```
git fetch upstream
git rebase upstream/master
git push origin my-feature-branch -f
```
#### Check on Your Pull Request
Go back to your pull request after a few minutes and see whether it passed muster with Travis-CI. Everything should look green, otherwise fix issues and amend your commit as described above.
#### Be Patient
It's likely that your change will not be merged and that the nitpicky maintainers will ask you to do more, or fix seemingly benign problems. Hang in there!
#### Thank You
Please do know that we really appreciate and value your time and work. We love you, really.
activerecord-session_store-1.0.0/Gemfile 0000664 0000000 0000000 00000000047 12716351075 0020362 0 ustar 00root root 0000000 0000000 source 'https://rubygems.org'
gemspec
activerecord-session_store-1.0.0/MIT-LICENSE 0000664 0000000 0000000 00000002076 12716351075 0020527 0 ustar 00root root 0000000 0000000 Copyright (c) 2012-2016 David Heinemeier Hansson
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
activerecord-session_store-1.0.0/README.md 0000664 0000000 0000000 00000007055 12716351075 0020354 0 ustar 00root root 0000000 0000000 Active Record Session Store
===========================
A session store backed by an Active Record class. A default class is
provided, but any object duck-typing to an Active Record Session class
with text `session_id` and `data` attributes is sufficient.
Installation
------------
Include this gem into your Gemfile:
```ruby
gem 'activerecord-session_store'
```
Run the migration generator:
rails generate active_record:session_migration
Then, set your session store in `config/initializers/session_store.rb`:
```ruby
Rails.application.config.session_store :active_record_store, :key => '_my_app_session'
```
Configuration
--------------
The default assumes a `sessions` tables with columns:
* `id` (numeric primary key),
* `session_id` (string, usually varchar; maximum length is 255), and
* `data` (text or longtext; careful if your session data exceeds 65KB).
The `session_id` column should always be indexed for speedy lookups.
Session data is marshaled to the `data` column in Base64 format.
If the data you write is larger than the column's size limit,
ActionController::SessionOverflowError will be raised.
You may configure the table name, primary key, data column, and
serializer type. For example, at the end of `config/application.rb`:
```ruby
ActiveRecord::SessionStore::Session.table_name = 'legacy_session_table'
ActiveRecord::SessionStore::Session.primary_key = 'session_id'
ActiveRecord::SessionStore::Session.data_column_name = 'legacy_session_data'
ActiveRecord::SessionStore::Session.serializer = :json
```
Note that setting the primary key to the `session_id` frees you from
having a separate `id` column if you don't want it. However, you must
set `session.model.id = session.session_id` by hand! A before filter
on ApplicationController is a good place.
The serializer may be one of `marshal`, `json`, or `hybrid`. `marshal` is
the default and uses the built-in Marshal methods coupled with Base64
encoding. `json` does what it says on the tin, using the `parse()` and
`generate()` methods of the JSON module. `hybrid` will read either type
but write as JSON.
Since the default class is a simple Active Record, you get timestamps
for free if you add `created_at` and `updated_at` datetime columns to
the `sessions` table, making periodic session expiration a snap.
You may provide your own session class implementation, whether a
feature-packed Active Record or a bare-metal high-performance SQL
store, by setting
```ruby
ActionDispatch::Session::ActiveRecordStore.session_class = MySessionClass
```
You must implement these methods:
* `self.find_by_session_id(session_id)`
* `initialize(hash_of_session_id_and_data, options_hash = {})`
* `attr_reader :session_id`
* `attr_accessor :data`
* `save`
* `destroy`
The example SqlBypass class is a generic SQL session store. You may
use it as a basis for high-performance database-specific stores.
Please note that you will need to manually include the silencer module to your
custom logger if you are using a logger other than `Logger` and `Syslog::Logger`
and their subclasses:
```ruby
MyLogger.send :include, ActiveRecord::SessionStore::Extension::LoggerSilencer
```
This silencer is being used to silence the logger and not leaking private
information into the log, and it is required for security reason.
## Contributing to Active Record Session Store
Active Record Session Store is work of many contributors. You're encouraged to submit pull requests, propose features and discuss issues.
See [CONTRIBUTING](CONTRIBUTING.md).
## License
Active Record Session Store is released under the [MIT License](MIT-LICENSE).
activerecord-session_store-1.0.0/Rakefile 0000664 0000000 0000000 00000000344 12716351075 0020534 0 ustar 00root root 0000000 0000000 #!/usr/bin/env rake
require 'bundler/setup'
require "bundler/gem_tasks"
require 'rake/testtask'
Rake::TestTask.new do |t|
t.libs = ["test"]
t.pattern = "test/**/*_test.rb"
t.ruby_opts = ['-w']
end
task :default => :test
activerecord-session_store-1.0.0/activerecord-session_store.gemspec 0000664 0000000 0000000 00000002103 12716351075 0025776 0 ustar 00root root 0000000 0000000 require "./lib/active_record/session_store/version"
Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.name = 'activerecord-session_store'
s.version = ActiveRecord::SessionStore::VERSION
s.summary = 'An Action Dispatch session store backed by an Active Record class.'
s.required_ruby_version = '>= 1.9.3'
s.license = 'MIT'
s.author = 'David Heinemeier Hansson'
s.email = 'david@loudthinking.com'
s.homepage = 'https://github.com/rails/activerecord-session_store'
s.files = Dir['CHANGELOG.md', 'MIT-LICENSE', 'README.md', 'lib/**/*']
s.require_path = 'lib'
s.extra_rdoc_files = %w( README.md )
s.rdoc_options.concat ['--main', 'README.md']
s.add_dependency('activerecord', '>= 4.0', '< 5.1')
s.add_dependency('actionpack', '>= 4.0', '< 5.1')
s.add_dependency('railties', '>= 4.0', '< 5.1')
s.add_dependency('rack', '>= 1.5.2', '< 3')
s.add_dependency('multi_json', '~> 1.11', '>= 1.11.2')
s.add_development_dependency('sqlite3')
s.add_development_dependency('appraisal', '~> 2.1.0')
end
activerecord-session_store-1.0.0/gemfiles/ 0000775 0000000 0000000 00000000000 12716351075 0020661 5 ustar 00root root 0000000 0000000 activerecord-session_store-1.0.0/gemfiles/rails_4.0.gemfile 0000664 0000000 0000000 00000000313 12716351075 0023703 0 ustar 00root root 0000000 0000000 # This file was generated by Appraisal
source "https://rubygems.org"
gem "actionpack", "~> 4.0.0"
gem "activerecord", "~> 4.0.0"
gem "railties", "~> 4.0.0"
gem "rack", "~> 1.5"
gemspec :path => "../"
activerecord-session_store-1.0.0/gemfiles/rails_4.1.gemfile 0000664 0000000 0000000 00000000313 12716351075 0023704 0 ustar 00root root 0000000 0000000 # This file was generated by Appraisal
source "https://rubygems.org"
gem "actionpack", "~> 4.1.0"
gem "activerecord", "~> 4.1.0"
gem "railties", "~> 4.1.0"
gem "rack", "~> 1.5"
gemspec :path => "../"
activerecord-session_store-1.0.0/gemfiles/rails_4.2.gemfile 0000664 0000000 0000000 00000000313 12716351075 0023705 0 ustar 00root root 0000000 0000000 # This file was generated by Appraisal
source "https://rubygems.org"
gem "actionpack", "~> 4.2.0"
gem "activerecord", "~> 4.2.0"
gem "railties", "~> 4.2.0"
gem "rack", "~> 1.5"
gemspec :path => "../"
activerecord-session_store-1.0.0/gemfiles/rails_5.0.gemfile 0000664 0000000 0000000 00000000407 12716351075 0023710 0 ustar 00root root 0000000 0000000 # This file was generated by Appraisal
source "https://rubygems.org"
gem "actionpack", ">= 5.0.0.alpha", "< 5.1"
gem "activerecord", ">= 5.0.0.alpha", "< 5.1"
gem "railties", ">= 5.0.0.alpha", "< 5.1"
gem "rack", ">= 2.0.0.alpha", "< 3"
gemspec :path => "../"
activerecord-session_store-1.0.0/gemfiles/rails_edge.gemfile 0000664 0000000 0000000 00000000567 12716351075 0024321 0 ustar 00root root 0000000 0000000 # This file was generated by Appraisal
source "https://rubygems.org"
git "https://github.com/rails/rails.git", :branch => "master" do
gem "actionpack"
gem "activerecord"
gem "railties"
end
gem "rack", :git => "https://github.com/rack/rack.git", :branch => "master"
gem "arel", :git => "https://github.com/rails/arel.git", :branch => "master"
gemspec :path => "../"
activerecord-session_store-1.0.0/lib/ 0000775 0000000 0000000 00000000000 12716351075 0017634 5 ustar 00root root 0000000 0000000 activerecord-session_store-1.0.0/lib/action_dispatch/ 0000775 0000000 0000000 00000000000 12716351075 0022770 5 ustar 00root root 0000000 0000000 activerecord-session_store-1.0.0/lib/action_dispatch/session/ 0000775 0000000 0000000 00000000000 12716351075 0024453 5 ustar 00root root 0000000 0000000 activerecord-session_store-1.0.0/lib/action_dispatch/session/active_record_store.rb 0000664 0000000 0000000 00000013153 12716351075 0031030 0 ustar 00root root 0000000 0000000 require "active_support/core_ext/module/attribute_accessors"
require 'action_dispatch/middleware/session/abstract_store'
module ActionDispatch
module Session
# = Active Record Session Store
#
# A session store backed by an Active Record class. A default class is
# provided, but any object duck-typing to an Active Record Session class
# with text +session_id+ and +data+ attributes is sufficient.
#
# The default assumes a +sessions+ tables with columns:
# +id+ (numeric primary key),
# +session_id+ (string, usually varchar; maximum length is 255), and
# +data+ (text or longtext; careful if your session data exceeds 65KB).
#
# The +session_id+ column should always be indexed for speedy lookups.
# Session data is marshaled to the +data+ column in Base64 format.
# If the data you write is larger than the column's size limit,
# ActionController::SessionOverflowError will be raised.
#
# You may configure the table name, primary key, and data column.
# For example, at the end of config/application.rb:
#
# ActiveRecord::SessionStore::Session.table_name = 'legacy_session_table'
# ActiveRecord::SessionStore::Session.primary_key = 'session_id'
# ActiveRecord::SessionStore::Session.data_column_name = 'legacy_session_data'
#
# Note that setting the primary key to the +session_id+ frees you from
# having a separate +id+ column if you don't want it. However, you must
# set session.model.id = session.session_id by hand! A before filter
# on ApplicationController is a good place.
#
# Since the default class is a simple Active Record, you get timestamps
# for free if you add +created_at+ and +updated_at+ datetime columns to
# the +sessions+ table, making periodic session expiration a snap.
#
# You may provide your own session class implementation, whether a
# feature-packed Active Record or a bare-metal high-performance SQL
# store, by setting
#
# ActionDispatch::Session::ActiveRecordStore.session_class = MySessionClass
#
# You must implement these methods:
#
# self.find_by_session_id(session_id)
# initialize(hash_of_session_id_and_data, options_hash = {})
# attr_reader :session_id
# attr_accessor :data
# save
# destroy
#
# The example SqlBypass class is a generic SQL session store. You may
# use it as a basis for high-performance database-specific stores.
class ActiveRecordStore < ActionDispatch::Session::AbstractStore
# The class used for session storage. Defaults to
# ActiveRecord::SessionStore::Session
cattr_accessor :session_class
SESSION_RECORD_KEY = 'rack.session.record'
if Rack.const_defined?(:RACK_SESSION_OPTIONS)
ENV_SESSION_OPTIONS_KEY = Rack::RACK_SESSION_OPTIONS
else
ENV_SESSION_OPTIONS_KEY = Rack::Session::Abstract::ENV_SESSION_OPTIONS_KEY
end
private
def get_session(request, sid)
logger.silence_logger do
unless sid and session = @@session_class.find_by_session_id(sid)
# If the sid was nil or if there is no pre-existing session under the sid,
# force the generation of a new sid and associate a new session associated with the new sid
sid = generate_sid
session = @@session_class.new(:session_id => sid, :data => {})
end
request.env[SESSION_RECORD_KEY] = session
[sid, session.data]
end
end
def write_session(request, sid, session_data, options)
logger.silence_logger do
record = get_session_model(request, sid)
record.data = session_data
return false unless record.save
session_data = record.data
if session_data && session_data.respond_to?(:each_value)
session_data.each_value do |obj|
obj.clear_association_cache if obj.respond_to?(:clear_association_cache)
end
end
sid
end
end
def delete_session(request, session_id, options)
logger.silence_logger do
if sid = current_session_id(request)
if model = get_session_model(request, sid)
data = model.data
model.destroy
end
end
request.env[SESSION_RECORD_KEY] = nil
unless options[:drop]
new_sid = generate_sid
if options[:renew]
new_model = @@session_class.new(:session_id => new_sid, :data => data)
new_model.save
request.env[SESSION_RECORD_KEY] = new_model
end
new_sid
end
end
end
def get_session_model(request, id)
logger.silence_logger do
model = @@session_class.find_by_session_id(id)
if !model
id = generate_sid
model = @@session_class.new(:session_id => id, :data => {})
model.save
end
if request.env[ENV_SESSION_OPTIONS_KEY][:id].nil?
request.env[SESSION_RECORD_KEY] = model
else
request.env[SESSION_RECORD_KEY] ||= model
end
model
end
end
def find_session(request, id)
model = get_session_model(request, id)
[model.session_id, model.data]
end
def logger
ActiveRecord::Base.logger || ActiveRecord::SessionStore::NilLogger
end
end
end
end
if ActiveRecord::VERSION::MAJOR == 4
require 'action_dispatch/session/legacy_support'
ActionDispatch::Session::ActiveRecordStore.send(:include, ActionDispatch::Session::LegacySupport)
end
activerecord-session_store-1.0.0/lib/action_dispatch/session/legacy_support.rb 0000664 0000000 0000000 00000002733 12716351075 0030045 0 ustar 00root root 0000000 0000000 module ActionDispatch
module Session
module LegacySupport
EnvWrapper = Struct.new(:env)
def self.included(klass)
[
:get_session,
:get_session_model,
:write_session,
:delete_session,
:find_session
].each do |m|
klass.send(:alias_method, "#{m}_rails5".to_sym, m)
klass.send(:remove_method, m)
end
end
def get_session(env, sid)
request = EnvWrapper.new(env)
get_session_rails5(request, sid)
end
def set_session(env, sid, session_data, options)
request = EnvWrapper.new(env)
write_session_rails5(request, sid, session_data, options)
end
def destroy_session(env, session_id, options)
request = EnvWrapper.new(env)
if sid = current_session_id(request.env)
get_session_model(request, sid).destroy
request.env[self.class::SESSION_RECORD_KEY] = nil
end
generate_sid unless options[:drop]
end
def get_session_model(request, sid)
if request.env[self.class::ENV_SESSION_OPTIONS_KEY][:id].nil?
request.env[self.class::SESSION_RECORD_KEY] = find_session(sid)
else
request.env[self.class::SESSION_RECORD_KEY] ||= find_session(sid)
end
end
def find_session(id)
self.class.session_class.find_by_session_id(id) || self.class.session_class.new(:session_id => id, :data => {})
end
end
end
end
activerecord-session_store-1.0.0/lib/active_record/ 0000775 0000000 0000000 00000000000 12716351075 0022445 5 ustar 00root root 0000000 0000000 activerecord-session_store-1.0.0/lib/active_record/session_store.rb 0000664 0000000 0000000 00000006106 12716351075 0025674 0 ustar 00root root 0000000 0000000 require 'active_record/session_store/version'
require 'action_dispatch/session/active_record_store'
require "active_record/session_store/extension/logger_silencer"
require 'active_support/core_ext/hash/keys'
require 'multi_json'
module ActiveRecord
module SessionStore
module ClassMethods # :nodoc:
mattr_accessor :serializer
def serialize(data)
serializer_class.dump(data) if data
end
def deserialize(data)
serializer_class.load(data) if data
end
def drop_table!
if connection.schema_cache.respond_to?(:clear_data_source_cache!)
connection.schema_cache.clear_data_source_cache!(table_name)
else
connection.schema_cache.clear_table_cache!(table_name)
end
connection.drop_table table_name
end
def create_table!
if connection.schema_cache.respond_to?(:clear_data_source_cache!)
connection.schema_cache.clear_data_source_cache!(table_name)
else
connection.schema_cache.clear_table_cache!(table_name)
end
connection.create_table(table_name) do |t|
t.string session_id_column, :limit => 255
t.text data_column_name
end
connection.add_index table_name, session_id_column, :unique => true
end
def serializer_class
case self.serializer
when :marshal, nil then
MarshalSerializer
when :json then
JsonSerializer
when :hybrid then
HybridSerializer
else
self.serializer
end
end
# Use Marshal with Base64 encoding
class MarshalSerializer
def self.load(value)
Marshal.load(::Base64.decode64(value))
end
def self.dump(value)
::Base64.encode64(Marshal.dump(value))
end
end
# Uses built-in JSON library to encode/decode session
class JsonSerializer
def self.load(value)
hash = MultiJson.load(value)
hash.is_a?(Hash) ? hash.with_indifferent_access[:value] : hash
end
def self.dump(value)
MultiJson.dump(value: value)
end
end
# Transparently migrates existing session values from Marshal to JSON
class HybridSerializer < JsonSerializer
MARSHAL_SIGNATURE = 'BAh'.freeze
def self.load(value)
if needs_migration?(value)
Marshal.load(::Base64.decode64(value))
else
super
end
end
def self.needs_migration?(value)
value.start_with?(MARSHAL_SIGNATURE)
end
end
end
end
end
require 'active_record/session_store/session'
require 'active_record/session_store/sql_bypass'
require 'active_record/session_store/railtie' if defined?(Rails)
ActionDispatch::Session::ActiveRecordStore.session_class = ActiveRecord::SessionStore::Session
Logger.send :include, ActiveRecord::SessionStore::Extension::LoggerSilencer
begin
require "syslog/logger"
Syslog::Logger.send :include, ActiveRecord::SessionStore::Extension::LoggerSilencer
rescue LoadError; end
activerecord-session_store-1.0.0/lib/active_record/session_store/ 0000775 0000000 0000000 00000000000 12716351075 0025344 5 ustar 00root root 0000000 0000000 activerecord-session_store-1.0.0/lib/active_record/session_store/extension/ 0000775 0000000 0000000 00000000000 12716351075 0027360 5 ustar 00root root 0000000 0000000 activerecord-session_store-1.0.0/lib/active_record/session_store/extension/logger_silencer.rb 0000664 0000000 0000000 00000004244 12716351075 0033054 0 ustar 00root root 0000000 0000000 require "thread"
require "active_support/core_ext/class/attribute_accessors"
require "active_support/core_ext/module/aliasing"
require "active_support/core_ext/module/attribute_accessors"
require "active_support/concern"
module ActiveRecord
module SessionStore
module Extension
module LoggerSilencer
extend ActiveSupport::Concern
included do
cattr_accessor :silencer
self.silencer = true
alias_method :level_without_threadsafety, :level
alias_method :level, :level_with_threadsafety
alias_method :add_without_threadsafety, :add
alias_method :add, :add_with_threadsafety
end
def thread_level
Thread.current[thread_hash_level_key]
end
def thread_level=(level)
Thread.current[thread_hash_level_key] = level
end
def level_with_threadsafety
thread_level || level_without_threadsafety
end
def add_with_threadsafety(severity, message = nil, progname = nil, &block)
if (defined?(@logdev) && @logdev.nil?) || (severity || UNKNOWN) < level
true
else
add_without_threadsafety(severity, message, progname, &block)
end
end
# Silences the logger for the duration of the block.
def silence_logger(temporary_level = Logger::ERROR)
if silencer
begin
self.thread_level = temporary_level
yield self
ensure
self.thread_level = nil
end
else
yield self
end
end
for severity in Logger::Severity.constants
class_eval <<-EOT, __FILE__, __LINE__ + 1
def #{severity.downcase}? # def debug?
Logger::#{severity} >= level # DEBUG >= level
end # end
EOT
end
private
def thread_hash_level_key
@thread_hash_level_key ||= :"ThreadSafeLogger##{object_id}@level"
end
end
end
class NilLogger
def self.silence_logger
yield
end
end
end
end
activerecord-session_store-1.0.0/lib/active_record/session_store/railtie.rb 0000664 0000000 0000000 00000000315 12716351075 0027321 0 ustar 00root root 0000000 0000000 require 'rails/railtie'
module ActiveRecord
module SessionStore
class Railtie < Rails::Railtie
rake_tasks { load File.expand_path("../../../tasks/database.rake", __FILE__) }
end
end
end
activerecord-session_store-1.0.0/lib/active_record/session_store/session.rb 0000664 0000000 0000000 00000005634 12716351075 0027364 0 ustar 00root root 0000000 0000000 require "active_support/core_ext/module/attribute_accessors"
require "thread"
module ActiveRecord
module SessionStore
# The default Active Record class.
class Session < ActiveRecord::Base
extend ClassMethods
SEMAPHORE = Mutex.new
##
# :singleton-method:
# Customizable data column name. Defaults to 'data'.
cattr_accessor :data_column_name
self.data_column_name = 'data'
before_save :serialize_data!
before_save :raise_on_session_data_overflow!
# This method is defiend in `protected_attributes` gem. We can't check for
# `attr_accessible` as Rails also define this and raise `RuntimeError`
# telling you to use the gem.
if respond_to?(:accessible_attributes)
attr_accessible :session_id, :data
end
class << self
def data_column_size_limit
@data_column_size_limit ||= columns_hash[data_column_name].limit
end
# Hook to set up sessid compatibility.
def find_by_session_id(session_id)
SEMAPHORE.synchronize { setup_sessid_compatibility! }
find_by_session_id(session_id)
end
private
def session_id_column
'session_id'
end
# Compatibility with tables using sessid instead of session_id.
def setup_sessid_compatibility!
# Reset column info since it may be stale.
reset_column_information
if columns_hash['sessid']
def self.find_by_session_id(*args)
find_by_sessid(*args)
end
define_method(:session_id) { sessid }
define_method(:session_id=) { |session_id| self.sessid = session_id }
else
class << self; remove_possible_method :find_by_session_id; end
def self.find_by_session_id(session_id)
where(session_id: session_id).first
end
end
end
end
def initialize(*)
@data = nil
super
end
# Lazy-deserialize session state.
def data
@data ||= self.class.deserialize(read_attribute(@@data_column_name)) || {}
end
attr_writer :data
# Has the session been loaded yet?
def loaded?
@data
end
private
def serialize_data!
return false unless loaded?
write_attribute(@@data_column_name, self.class.serialize(data))
end
# Ensures that the data about to be stored in the database is not
# larger than the data storage column. Raises
# ActionController::SessionOverflowError.
def raise_on_session_data_overflow!
return false unless loaded?
limit = self.class.data_column_size_limit
if limit and read_attribute(@@data_column_name).size > limit
raise ActionController::SessionOverflowError
end
end
end
end
end
activerecord-session_store-1.0.0/lib/active_record/session_store/sql_bypass.rb 0000664 0000000 0000000 00000011074 12716351075 0030054 0 ustar 00root root 0000000 0000000 require "active_support/core_ext/module/attribute_accessors"
module ActiveRecord
module SessionStore
# A barebones session store which duck-types with the default session
# store but bypasses Active Record and issues SQL directly. This is
# an example session model class meant as a basis for your own classes.
#
# The database connection, table name, and session id and data columns
# are configurable class attributes. Serializing and deserializeing
# are implemented as class methods that you may override. By default,
# serializing data is
#
# ::Base64.encode64(Marshal.dump(data))
#
# and deserializing data is
#
# Marshal.load(::Base64.decode64(data))
#
# This serializing behavior is intended to store the widest range of
# binary session data in a +text+ column. For higher performance,
# store in a +blob+ column instead and forgo the Base64 encoding.
class SqlBypass
extend ClassMethods
##
# :singleton-method:
# The table name defaults to 'sessions'.
cattr_accessor :table_name
@@table_name = 'sessions'
##
# :singleton-method:
# The session id field defaults to 'session_id'.
cattr_accessor :session_id_column
@@session_id_column = 'session_id'
##
# :singleton-method:
# The data field defaults to 'data'.
cattr_accessor :data_column
@@data_column = 'data'
class << self
alias :data_column_name :data_column
# Use the ActiveRecord::Base.connection by default.
attr_writer :connection
# Use the ActiveRecord::Base.connection_pool by default.
attr_writer :connection_pool
def connection
@connection ||= ActiveRecord::Base.connection
end
def connection_pool
@connection_pool ||= ActiveRecord::Base.connection_pool
end
# Look up a session by id and deserialize its data if found.
def find_by_session_id(session_id)
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)}")
new(:session_id => session_id, :serialized_data => record['data'])
end
end
end
delegate :connection, :connection=, :connection_pool, :connection_pool=, :to => self
attr_reader :session_id, :new_record
alias :new_record? :new_record
attr_writer :data
# Look for normal and serialized data, self.find_by_session_id's way of
# telling us to postpone deserializing until the data is requested.
# We need to handle a normal data attribute in case of a new record.
def initialize(attributes)
@session_id = attributes[:session_id]
@data = attributes[:data]
@serialized_data = attributes[:serialized_data]
@new_record = @serialized_data.nil?
end
# Returns true if the record is persisted, i.e. it's not a new record
def persisted?
!@new_record
end
# Lazy-deserialize session state.
def data
unless @data
if @serialized_data
@data, @serialized_data = self.class.deserialize(@serialized_data) || {}, nil
else
@data = {}
end
end
@data
end
def loaded?
@data
end
def save
return false unless loaded?
serialized_data = self.class.serialize(data)
connect = connection
if @new_record
@new_record = false
connect.update <<-end_sql, 'Create session'
INSERT INTO #{table_name} (
#{connect.quote_column_name(session_id_column)},
#{connect.quote_column_name(data_column)} )
VALUES (
#{connect.quote(session_id)},
#{connect.quote(serialized_data)} )
end_sql
else
connect.update <<-end_sql, 'Update session'
UPDATE #{table_name}
SET #{connect.quote_column_name(data_column)}=#{connect.quote(serialized_data)}
WHERE #{connect.quote_column_name(session_id_column)}=#{connect.quote(session_id)}
end_sql
end
end
def destroy
return if @new_record
connect = connection
connect.delete <<-end_sql, 'Destroy session'
DELETE FROM #{table_name}
WHERE #{connect.quote_column_name(session_id_column)}=#{connect.quote(session_id.to_s)}
end_sql
end
end
end
end
activerecord-session_store-1.0.0/lib/active_record/session_store/version.rb 0000664 0000000 0000000 00000000112 12716351075 0027350 0 ustar 00root root 0000000 0000000 module ActiveRecord
module SessionStore
VERSION = '1.0.0'
end
end
activerecord-session_store-1.0.0/lib/activerecord/ 0000775 0000000 0000000 00000000000 12716351075 0022306 5 ustar 00root root 0000000 0000000 activerecord-session_store-1.0.0/lib/activerecord/session_store.rb 0000664 0000000 0000000 00000000046 12716351075 0025532 0 ustar 00root root 0000000 0000000 require 'active_record/session_store'
activerecord-session_store-1.0.0/lib/generators/ 0000775 0000000 0000000 00000000000 12716351075 0022005 5 ustar 00root root 0000000 0000000 activerecord-session_store-1.0.0/lib/generators/active_record/ 0000775 0000000 0000000 00000000000 12716351075 0024616 5 ustar 00root root 0000000 0000000 activerecord-session_store-1.0.0/lib/generators/active_record/session_migration_generator.rb 0000664 0000000 0000000 00000001362 12716351075 0032747 0 ustar 00root root 0000000 0000000 require 'rails/generators/active_record'
module ActiveRecord
module Generators
class SessionMigrationGenerator < Base
source_root File.expand_path("../templates", __FILE__)
argument :name, :type => :string, :default => "add_sessions_table"
def create_migration_file
migration_template "migration.rb", "db/migrate/#{file_name}.rb"
end
protected
def session_table_name
current_table_name = ActiveRecord::SessionStore::Session.table_name
if current_table_name == 'session' || current_table_name == 'sessions'
current_table_name = ActiveRecord::Base.pluralize_table_names ? 'sessions' : 'session'
end
current_table_name
end
end
end
end
activerecord-session_store-1.0.0/lib/generators/active_record/templates/ 0000775 0000000 0000000 00000000000 12716351075 0026614 5 ustar 00root root 0000000 0000000 activerecord-session_store-1.0.0/lib/generators/active_record/templates/migration.rb 0000664 0000000 0000000 00000000535 12716351075 0031135 0 ustar 00root root 0000000 0000000 class <%= migration_class_name %> < ActiveRecord::Migration
def change
create_table :<%= session_table_name %> do |t|
t.string :session_id, :null => false
t.text :data
t.timestamps
end
add_index :<%= session_table_name %>, :session_id, :unique => true
add_index :<%= session_table_name %>, :updated_at
end
end
activerecord-session_store-1.0.0/lib/tasks/ 0000775 0000000 0000000 00000000000 12716351075 0020761 5 ustar 00root root 0000000 0000000 activerecord-session_store-1.0.0/lib/tasks/database.rake 0000664 0000000 0000000 00000001774 12716351075 0023402 0 ustar 00root root 0000000 0000000 namespace 'db:sessions' do
desc "Creates a sessions migration for use with ActiveRecord::SessionStore"
task :create => [:environment, 'db:load_config'] do
raise 'Task unavailable to this database (no migration support)' unless ActiveRecord::Base.connection.supports_migrations?
Rails.application.load_generators
require 'rails/generators/rails/session_migration/session_migration_generator'
Rails::Generators::SessionMigrationGenerator.start [ ENV['MIGRATION'] || 'add_sessions_table' ]
end
desc "Clear the sessions table"
task :clear => [:environment, 'db:load_config'] do
ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::SessionStore::Session.table_name}"
end
desc "Trim old sessions from the table (default: > 30 days)"
task :trim => [:environment, 'db:load_config'] do
cutoff_period = (ENV['SESSION_DAYS_TRIM_THRESHOLD'] || 30).to_i.days.ago
ActiveRecord::SessionStore::Session.
where("updated_at < ?", cutoff_period).
delete_all
end
end
activerecord-session_store-1.0.0/test/ 0000775 0000000 0000000 00000000000 12716351075 0020045 5 ustar 00root root 0000000 0000000 activerecord-session_store-1.0.0/test/action_controller_test.rb 0000664 0000000 0000000 00000020242 12716351075 0025151 0 ustar 00root root 0000000 0000000 require 'helper'
class ActionControllerTest < ActionDispatch::IntegrationTest
class TestController < ActionController::Base
protect_from_forgery
def no_session_access
head :ok
end
def set_session_value
raise "missing session!" unless session
session[:foo] = params[:foo] || "bar"
head :ok
end
def get_session_value
if ActiveRecord::VERSION::MAJOR == 4
render :text => "foo: #{session[:foo].inspect}"
else
render :plain => "foo: #{session[:foo].inspect}"
end
end
def get_session_id
if ActiveRecord::VERSION::MAJOR == 4
render :text => "#{request.session.id}"
else
render :plain => "#{request.session.id}"
end
end
def call_reset_session
session[:foo]
reset_session
reset_session if params[:twice]
session[:foo] = "baz"
head :ok
end
def renew
request.env["rack.session.options"][:renew] = true
head :ok
end
end
def setup
ActionDispatch::Session::ActiveRecordStore.session_class.drop_table! rescue nil
ActionDispatch::Session::ActiveRecordStore.session_class.create_table!
end
%w{ session sql_bypass }.each do |class_name|
define_method("test_setting_and_getting_session_value_with_#{class_name}_store") do
with_store class_name do
with_test_route_set do
get '/set_session_value'
assert_response :success
assert cookies['_session_id']
get '/get_session_value'
assert_response :success
assert_equal 'foo: "bar"', response.body
if ActiveRecord::VERSION::MAJOR == 4
get '/set_session_value', :foo => "baz"
else
get '/set_session_value', :params => { :foo => "baz" }
end
assert_response :success
assert cookies['_session_id']
get '/get_session_value'
assert_response :success
assert_equal 'foo: "baz"', response.body
get '/call_reset_session'
assert_response :success
assert_not_equal [], headers['Set-Cookie']
end
end
end
define_method("test_renewing_with_#{class_name}_store") do
with_store class_name do
with_test_route_set do
get '/set_session_value'
assert_response :success
assert cookies['_session_id']
get '/renew'
assert_response :success
assert_not_equal [], headers['Set-Cookie']
end
end
end
end
def test_getting_nil_session_value
with_test_route_set do
get '/get_session_value'
assert_response :success
assert_equal 'foo: nil', response.body
end
end
def test_calling_reset_session_twice_does_not_raise_errors
with_test_route_set do
if ActiveRecord::VERSION::MAJOR == 4
get '/call_reset_session', :twice => "true"
else
get '/call_reset_session', :params => { :twice => "true" }
end
assert_response :success
get '/get_session_value'
assert_response :success
assert_equal 'foo: "baz"', response.body
end
end
def test_setting_session_value_after_session_reset
with_test_route_set do
get '/set_session_value'
assert_response :success
assert cookies['_session_id']
session_id = cookies['_session_id']
get '/call_reset_session'
assert_response :success
assert_not_equal [], headers['Set-Cookie']
get '/get_session_value'
assert_response :success
assert_equal 'foo: "baz"', response.body
get '/get_session_id'
assert_response :success
assert_not_equal session_id, response.body
end
end
def test_getting_session_value_after_session_reset
with_test_route_set do
get '/set_session_value'
assert_response :success
assert cookies['_session_id']
session_cookie = cookies.send(:hash_for)['_session_id']
get '/call_reset_session'
assert_response :success
assert_not_equal [], headers['Set-Cookie']
cookies << session_cookie # replace our new session_id with our old, pre-reset session_id
get '/get_session_value'
assert_response :success
assert_equal 'foo: nil', response.body, "data for this session should have been obliterated from the database"
end
end
def test_getting_from_nonexistent_session
with_test_route_set do
get '/get_session_value'
assert_response :success
assert_equal 'foo: nil', response.body
assert_nil cookies['_session_id'], "should only create session on write, not read"
end
end
def test_getting_session_id
with_test_route_set do
get '/set_session_value'
assert_response :success
assert cookies['_session_id']
session_id = cookies['_session_id']
get '/get_session_id'
assert_response :success
assert_equal session_id, response.body, "should be able to read session id without accessing the session hash"
end
end
def test_doesnt_write_session_cookie_if_session_id_is_already_exists
with_test_route_set do
get '/set_session_value'
assert_response :success
assert cookies['_session_id']
get '/get_session_value'
assert_response :success
assert_equal nil, headers['Set-Cookie'], "should not resend the cookie again if session_id cookie is already exists"
end
end
def test_prevents_session_fixation
with_test_route_set do
get '/set_session_value'
assert_response :success
assert cookies['_session_id']
get '/get_session_value'
assert_response :success
assert_equal 'foo: "bar"', response.body
session_id = cookies['_session_id']
assert session_id
reset!
if ActiveRecord::VERSION::MAJOR == 4
get '/get_session_value', :_session_id => session_id
else
get '/get_session_value', :params => { :_session_id => session_id }
end
assert_response :success
assert_equal 'foo: nil', response.body
assert_not_equal session_id, cookies['_session_id']
end
end
def test_allows_session_fixation
with_test_route_set(:cookie_only => false) do
get '/set_session_value'
assert_response :success
assert cookies['_session_id']
get '/get_session_value'
assert_response :success
assert_equal 'foo: "bar"', response.body
session_id = cookies['_session_id']
assert session_id
reset!
if ActiveRecord::VERSION::MAJOR == 4
get '/set_session_value', :_session_id => session_id, :foo => "baz"
else
get '/set_session_value', :params => { :_session_id => session_id, :foo => "baz" }
end
assert_response :success
assert_equal session_id, cookies['_session_id']
get '/get_session_value', :params => { :_session_id => session_id }
assert_response :success
assert_equal 'foo: "baz"', response.body
assert_equal session_id, cookies['_session_id']
end
end
def test_incoming_invalid_session_id_via_cookie_should_be_ignored
with_test_route_set do
open_session do |sess|
sess.cookies['_session_id'] = 'INVALID'
sess.get '/set_session_value'
new_session_id = sess.cookies['_session_id']
assert_not_equal 'INVALID', new_session_id
sess.get '/get_session_value'
new_session_id_2 = sess.cookies['_session_id']
assert_equal new_session_id, new_session_id_2
end
end
end
def test_incoming_invalid_session_id_via_parameter_should_be_ignored
with_test_route_set(:cookie_only => false) do
open_session do |sess|
if ActiveRecord::VERSION::MAJOR == 4
sess.get '/set_session_value', :_session_id => 'INVALID'
else
sess.get '/set_session_value', :params => { :_session_id => 'INVALID' }
end
new_session_id = sess.cookies['_session_id']
assert_not_equal 'INVALID', new_session_id
sess.get '/get_session_value'
new_session_id_2 = sess.cookies['_session_id']
assert_equal new_session_id, new_session_id_2
end
end
end
def test_session_store_with_all_domains
with_test_route_set(:domain => :all) do
get '/set_session_value'
assert_response :success
end
end
end
activerecord-session_store-1.0.0/test/destroy_session_test.rb 0000664 0000000 0000000 00000003034 12716351075 0024665 0 ustar 00root root 0000000 0000000 if ActiveRecord::VERSION::MAJOR > 4
require 'action_dispatch/middleware/session/abstract_store'
module ActionDispatch
class Request
class DestroySessionTest < ActiveSupport::TestCase
attr_reader :req
def setup
@req = ActionDispatch::Request.empty
ActionDispatch::Session::ActiveRecordStore.session_class.drop_table! rescue nil
ActionDispatch::Session::ActiveRecordStore.session_class.create_table!
end
def record_key
ActionDispatch::Session::ActiveRecordStore::SESSION_RECORD_KEY
end
def test_destroy_without_renew
s = Session.create(store, req, { :renew => false })
s['set_something_so_it_loads'] = true
session_model = req.env[record_key]
session_model.update_attributes(:data => {'rails' => 'ftw'})
s.destroy
renewed_session_model = req.env[record_key]
assert_equal nil, renewed_session_model.data['rails']
end
def test_destroy_with_renew
s = Session.create(store, req, { :renew => true })
s['set_something_so_it_loads'] = true
session_model = req.env[record_key]
session_model.update_attributes(:data => {'rails' => 'ftw'})
s.destroy
renewed_session_model = req.env[record_key]
assert_equal 'ftw', renewed_session_model.data['rails']
end
private
def store
ActionDispatch::Session::ActiveRecordStore.new(req.env, {})
end
end
end
end
end activerecord-session_store-1.0.0/test/generators/ 0000775 0000000 0000000 00000000000 12716351075 0022216 5 ustar 00root root 0000000 0000000 activerecord-session_store-1.0.0/test/generators/session_migration_generator_test.rb 0000664 0000000 0000000 00000002223 12716351075 0031403 0 ustar 00root root 0000000 0000000 require 'helper'
require 'rails/generators/test_case'
require 'active_record/session_store'
require 'generators/active_record/session_migration_generator'
class SessionMigrationGeneratorTest < Rails::Generators::TestCase
tests ActiveRecord::Generators::SessionMigrationGenerator
destination 'tmp'
setup :prepare_destination
def test_session_migration_with_default_name
run_generator
assert_migration "db/migrate/add_sessions_table.rb", /class AddSessionsTable < ActiveRecord::Migration/
end
def test_session_migration_with_given_name
run_generator ["create_session_table"]
assert_migration "db/migrate/create_session_table.rb", /class CreateSessionTable < ActiveRecord::Migration/
end
def test_session_migration_with_custom_table_name
ActiveRecord::SessionStore::Session.table_name = "custom_table_name"
run_generator
assert_migration "db/migrate/add_sessions_table.rb" do |migration|
assert_match(/class AddSessionsTable < ActiveRecord::Migration/, migration)
assert_match(/create_table :custom_table_name/, migration)
end
ensure
ActiveRecord::SessionStore::Session.table_name = "sessions"
end
end
activerecord-session_store-1.0.0/test/helper.rb 0000664 0000000 0000000 00000004134 12716351075 0021653 0 ustar 00root root 0000000 0000000 require 'bundler/setup'
require 'active_record'
require 'action_controller'
require 'action_dispatch'
require 'minitest/autorun'
require 'active_record/session_store'
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
SharedTestRoutes = ActionDispatch::Routing::RouteSet.new
module ActionDispatch
module SharedRoutes
def before_setup
@routes = SharedTestRoutes
super
end
end
end
class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
include ActionDispatch::SharedRoutes
def self.build_app(routes = nil)
RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware|
middleware.use ActionDispatch::DebugExceptions
middleware.use ActionDispatch::Callbacks
middleware.use ActionDispatch::Cookies
middleware.use ActionDispatch::Flash
middleware.use Rack::Head
yield(middleware) if block_given?
end
end
private
def with_test_route_set(options = {})
controller_namespace = self.class.to_s.underscore
with_routing do |set|
set.draw do
get ':action', :controller => "#{controller_namespace}/test"
end
@app = self.class.build_app(set) do |middleware|
middleware.use ActionDispatch::Session::ActiveRecordStore, options.reverse_merge(:key => '_session_id')
middleware.delete ActionDispatch::ShowExceptions
end
yield
end
end
def with_store(class_name)
session_class, ActionDispatch::Session::ActiveRecordStore.session_class =
ActionDispatch::Session::ActiveRecordStore.session_class, "ActiveRecord::SessionStore::#{class_name.camelize}".constantize
yield
ensure
ActionDispatch::Session::ActiveRecordStore.session_class = session_class
end
end
class RoutedRackApp
attr_reader :routes
def initialize(routes, &blk)
@routes = routes
@stack = ActionDispatch::MiddlewareStack.new(&blk).build(@routes)
end
def call(env)
@stack.call(env)
end
end
if ActiveSupport::TestCase.respond_to?(:test_order=)
ActiveSupport::TestCase.test_order = :random
end
activerecord-session_store-1.0.0/test/logger_silencer_test.rb 0000664 0000000 0000000 00000003765 12716351075 0024607 0 ustar 00root root 0000000 0000000 require 'helper'
require "stringio"
class LoggerSilencerTest < ActionDispatch::IntegrationTest
class TestController < ActionController::Base
def set_session_value
raise "missing session!" unless session
session[:foo] = params[:foo] || "bar"
head :ok
end
def get_session_value
if ActiveRecord::VERSION::MAJOR == 4
render :text => "foo: #{session[:foo].inspect}"
else
render :plain => "foo: #{session[:foo].inspect}"
end
end
end
def setup
session_class = ActiveRecord::SessionStore::Session
session_class.drop_table! rescue nil
session_class.create_table!
ActionDispatch::Session::ActiveRecordStore.session_class = session_class
end
%w{ session sql_bypass }.each do |class_name|
define_method("test_#{class_name}_store_does_not_log_sql") do
with_store class_name do
with_fake_logger do
with_test_route_set do
get "/set_session_value"
get "/get_session_value"
assert_no_match(/INSERT/, fake_logger.string)
assert_no_match(/SELECT/, fake_logger.string)
end
end
end
end
end
def test_log_silencer_with_logger_not_raise_exception
with_logger Logger.new(Tempfile.new("tempfile")) do
with_test_route_set do
get "/set_session_value"
end
end
end
begin
require "syslogger/logger"
def test_log_silencer_with_syslog_logger_not_raise_exception
with_logger Syslog::Logger.new("ar_session_store_test") do
with_test_route_set do
get "/set_session_value"
end
end
end
rescue LoadError; end
private
def with_logger(logger)
original_logger = ActiveRecord::Base.logger
ActiveRecord::Base.logger = logger
yield
ensure
ActiveRecord::Base.logger = original_logger
end
def with_fake_logger(&block)
with_logger(Logger.new(fake_logger), &block)
end
def fake_logger
@fake_logger ||= StringIO.new
end
end
activerecord-session_store-1.0.0/test/session_test.rb 0000664 0000000 0000000 00000010636 12716351075 0023122 0 ustar 00root root 0000000 0000000 require 'helper'
require 'active_record/session_store'
require 'active_support/core_ext/hash/keys'
module ActiveRecord
module SessionStore
class SessionTest < ActiveSupport::TestCase
attr_reader :session_klass
def setup
super
ActiveRecord::Base.connection.schema_cache.clear!
Session.drop_table! if Session.table_exists?
@session_klass = Class.new(Session)
ActiveRecord::SessionStore::Session.serializer = :json
end
def test_data_column_name
# default column name is 'data'
assert_equal 'data', Session.data_column_name
end
def test_table_name
assert_equal 'sessions', Session.table_name
end
def test_create_table!
assert !Session.table_exists?
Session.create_table!
assert Session.table_exists?
Session.drop_table!
assert !Session.table_exists?
end
def test_json_serialization
Session.create_table!
ActiveRecord::SessionStore::Session.serializer = :json
s = session_klass.create!(:data => 'world', :session_id => '7')
sessions = ActiveRecord::Base.connection.execute("SELECT * FROM #{Session.table_name}")
data = Session.deserialize(sessions[0][Session.data_column_name])
assert_equal s.data, data
end
def test_hybrid_serialization
Session.create_table!
# Star with marshal, which will serialize with Marshal
ActiveRecord::SessionStore::Session.serializer = :marshal
s1 = session_klass.create!(:data => 'world', :session_id => '1')
# Switch to hybrid, which will serialize as JSON
ActiveRecord::SessionStore::Session.serializer = :hybrid
s2 = session_klass.create!(:data => 'world', :session_id => '2')
# Check that first was serialized with Marshal and second as JSON
sessions = ActiveRecord::Base.connection.execute("SELECT * FROM #{Session.table_name}")
assert_equal ::Base64.encode64(Marshal.dump(s1.data)), sessions[0][Session.data_column_name]
assert_equal s2.data, Session.deserialize(sessions[1][Session.data_column_name])
end
def test_hybrid_deserialization
Session.create_table!
# Star with marshal, which will serialize with Marshal
ActiveRecord::SessionStore::Session.serializer = :marshal
s = session_klass.create!(:data => 'world', :session_id => '1')
# Switch to hybrid, which will deserialize with Marshal if needed
ActiveRecord::SessionStore::Session.serializer = :hybrid
# Check that it was serialized with Marshal,
sessions = ActiveRecord::Base.connection.execute("SELECT * FROM #{Session.table_name}")
assert_equal sessions[0][Session.data_column_name], ::Base64.encode64(Marshal.dump(s.data))
# deserializes properly,
session = Session.find_by_session_id(s.id)
assert_equal s.data, session.data
# and reserializes as JSON
session.save
sessions = ActiveRecord::Base.connection.execute("SELECT * FROM #{Session.table_name}")
assert_equal s.data,Session.deserialize(sessions[0][Session.data_column_name])
end
def test_find_by_sess_id_compat
# Force class reload, as we need to redo the meta-programming
ActiveRecord::SessionStore.send(:remove_const, :Session)
load 'active_record/session_store/session.rb'
Session.reset_column_information
klass = Class.new(Session) do
def self.session_id_column
'sessid'
end
end
klass.create_table!
assert klass.columns_hash['sessid'], 'sessid column exists'
session = klass.new(:data => 'hello')
session.sessid = "100"
session.save!
found = klass.find_by_session_id("100")
assert_equal session, found
assert_equal session.sessid, found.session_id
ensure
klass.drop_table!
Session.reset_column_information
end
def test_find_by_session_id
Session.create_table!
session_id = "10"
s = session_klass.create!(:data => 'world', :session_id => session_id)
t = session_klass.find_by_session_id(session_id)
assert_equal s, t
assert_equal s.data, t.data
Session.drop_table!
end
def test_loaded?
Session.create_table!
s = Session.new
assert !s.loaded?, 'session is not loaded'
end
end
end
end
activerecord-session_store-1.0.0/test/sql_bypass_test.rb 0000664 0000000 0000000 00000004142 12716351075 0023612 0 ustar 00root root 0000000 0000000 require 'helper'
require 'action_dispatch'
require 'active_record/session_store'
module ActiveRecord
module SessionStore
class SqlBypassTest < ActiveSupport::TestCase
def setup
super
Session.drop_table! if Session.table_exists?
end
def test_create_table
assert !Session.table_exists?
SqlBypass.create_table!
assert Session.table_exists?
SqlBypass.drop_table!
assert !Session.table_exists?
end
def test_new_record?
s = SqlBypass.new :data => 'foo', :session_id => 10
assert s.new_record?, 'this is a new record!'
end
def test_persisted?
s = SqlBypass.new :data => 'foo', :session_id => 10
assert !s.persisted?, 'this is a new record!'
end
def test_not_loaded?
s = SqlBypass.new({})
assert !s.loaded?, 'it is not loaded'
end
def test_loaded?
s = SqlBypass.new :data => 'hello'
assert s.loaded?, 'it is loaded'
end
def test_save
SqlBypass.create_table! unless Session.table_exists?
session_id = 20
s = SqlBypass.new :data => 'hello', :session_id => session_id
s.save
t = SqlBypass.find_by_session_id session_id
assert_equal s.session_id, t.session_id
assert_equal s.data, t.data
end
def test_destroy
SqlBypass.create_table! unless Session.table_exists?
session_id = 20
s = SqlBypass.new :data => 'hello', :session_id => session_id
s.save
s.destroy
assert_nil SqlBypass.find_by_session_id session_id
end
def test_data_column
SqlBypass.drop_table! if exists = Session.table_exists?
old, SqlBypass.data_column = SqlBypass.data_column, 'foo'
SqlBypass.create_table!
session_id = 20
SqlBypass.new(:data => 'hello', :session_id => session_id).save
assert_equal 'hello', SqlBypass.find_by_session_id(session_id).data
ensure
SqlBypass.drop_table!
SqlBypass.data_column = old
SqlBypass.create_table! if exists
end
end
end
end
activerecord-session_store-1.0.0/test/tasks/ 0000775 0000000 0000000 00000000000 12716351075 0021172 5 ustar 00root root 0000000 0000000 activerecord-session_store-1.0.0/test/tasks/database_rake_test.rb 0000664 0000000 0000000 00000003166 12716351075 0025332 0 ustar 00root root 0000000 0000000 require 'helper'
require 'rake'
module ActiveRecord
module SessionStore
class DatabaseRakeTest < ActiveSupport::TestCase
class AddTimestampsToSession < ActiveRecord::Migration
self.verbose = false
def change
add_column Session.table_name, :created_at, :datetime
add_column Session.table_name, :updated_at, :datetime
end
end
def setup
Session.drop_table! if Session.table_exists?
Session.create_table!
AddTimestampsToSession.new.exec_migration(ActiveRecord::Base.connection, :up)
Session.connection.schema_cache.clear!
Session.reset_column_information
Rake.application.rake_require "tasks/database"
Rake::Task.define_task(:environment)
Rake::Task.define_task("db:load_config")
end
def teardown
Session.drop_table! if Session.table_exists?
Session.connection.schema_cache.clear!
Session.reset_column_information
end
def test_trim_task
cutoff_period = 30.days.ago
Session.create!(data: "obsolete") do |session|
session.updated_at = 5.minutes.until(cutoff_period)
end
recent_session = Session.create!(data: "recent") do |session|
session.updated_at = 5.minutes.since(cutoff_period)
end
Rake.application.invoke_task 'db:sessions:trim'
old_session_count = Session.where("updated_at < ?", cutoff_period).count
retained_session = Session.find(recent_session.id)
assert_equal 0, old_session_count
assert_equal retained_session, recent_session
end
end
end
end