Skip to content

Commit

Permalink
Add Rails 5 support
Browse files Browse the repository at this point in the history
  • Loading branch information
ElMassimo committed Mar 30, 2017
1 parent f88fa15 commit 42198b1
Show file tree
Hide file tree
Showing 79 changed files with 285 additions and 239 deletions.
14 changes: 1 addition & 13 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,7 @@
language: ruby
script: "rake"
rvm:
- 1.9.3
- 2.0.0
- 2.1.5
- "2.2"
- ruby-head
- jruby-19mode
- jruby-head
- rbx-2.2.9
matrix:
allow_failures:
- rvm: jruby-head
- rvm: ruby-head
- rvm: rbx-2.2.9
- 2.3.0
before_script:
- wget http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.0.1.tgz -O /tmp/mongodb.tgz
- tar -xvf /tmp/mongodb.tgz
Expand Down
25 changes: 2 additions & 23 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,7 @@ group :development, :test do

gem 'test-unit'
gem 'rspec-rails'
gem 'rails', '5.0.2'

if RUBY_PLATFORM == 'java'
gem 'jdbc-sqlite3'
gem 'activerecord-jdbc-adapter'
gem 'activerecord-jdbcsqlite3-adapter'
gem 'jruby-openssl'
gem 'jruby-rack'
else
gem 'sqlite3' # for devise User storage
end

case ENV['RAILS_VERS']
when '4.2'
gem 'rails', '~>4.2.0'
else
gem 'rails'
end

case ENV['MONGOID_SESSION_STORE_ORM']
when 'mongoid'
gem 'mongoid', '>= 5.0.0'
else
gem 'mongoid'
end
gem 'mongoid', '6.1.0'
end
55 changes: 24 additions & 31 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,24 @@ def run_with_output(command)
system(command)
end

def set_versions(rails_vers, orm)
def set_versions(rails_version)
success = true
unless File.exists?("Gemfile_Rails_#{rails_vers}_#{orm}_#{RUBY_VERSION}.lock")
success &&= run_with_output("export RAILS_VERS=#{rails_vers}; export MONGOID_SESSION_STORE_ORM=#{orm}; bundle update")
success &&= run_with_output("cp Gemfile.lock Gemfile_Rails_#{rails_vers}_#{orm}_#{RUBY_VERSION}.lock")
unless File.exists?("Gemfile_Rails_#{rails_version}_mongoid_#{RUBY_VERSION}.lock")
success &&= run_with_output("export RAILS_VERS=#{rails_version}; bundle update")
success &&= run_with_output("cp Gemfile.lock Gemfile_Rails_#{rails_version}_mongoid_#{RUBY_VERSION}.lock")
else
success &&= run_with_output("rm Gemfile.lock")
success &&= run_with_output("cp Gemfile_Rails_#{rails_vers}_#{orm}_#{RUBY_VERSION}.lock Gemfile.lock")
success &&= run_with_output("cp Gemfile_Rails_#{rails_version}_mongoid_#{RUBY_VERSION}.lock Gemfile.lock")
end
success
end

@rails_versions = ['4.2']
@orms = ['mongoid']
@rails_versions = ['5.0']

task :default => :test_all

desc 'Test each session store against Rails 4.2'
desc 'Test each session store against Rails'
task :test_all do
# inspired by http://pivotallabs.com/users/jdean/blog/articles/1728-testing-your-gem-against-multiple-rubies-and-rails-versions-with-rvm

# Wait for mongod to start on Travis.
# From the Mongo Ruby Driver gem.
if ENV['TRAVIS']
Expand All @@ -49,12 +46,10 @@ task :test_all do
@failed_suites = []

@rails_versions.each do |rails_version|
@orms.each do |orm|
success = set_versions(rails_version, orm)
success = set_versions(rails_version)

unless success && run_with_output("export RAILS_VERS=#{rails_version}; export MONGOID_SESSION_STORE_ORM=#{orm}; bundle exec rspec spec")
@failed_suites << "Rails #{rails_version} / #{orm}"
end
unless success && run_with_output("export RAILS_VERS=#{rails_version}; bundle exec rspec spec")
@failed_suites << "Rails #{rails_version} / mongoid"
end
end

Expand All @@ -71,23 +66,21 @@ task :test_all do
end

@rails_versions.each do |rails_version|
@orms.each do |orm|
desc "Set Gemfile.lock to #{rails_version} with #{orm}"
task :"use_#{rails_version.gsub('.', '')}_#{orm}" do
set_versions(rails_version, orm)
end
desc "Set Gemfile.lock to #{rails_version} with mongoid"
task :"use_#{rails_version.gsub('.', '')}_mongoid" do
set_versions(rails_version)
end

desc "Test against #{rails_version} with #{orm}"
task :"test_#{rails_version.gsub('.', '')}_#{orm}" do
set_versions(rails_version, orm)
success = run_with_output("export RAILS_VERS=#{rails_version}; export MONGOID_SESSION_STORE_ORM=#{orm}; bundle exec rspec spec")
exit(1) unless success
end
desc "Test against #{rails_version} with mongoid"
task :"test_#{rails_version.gsub('.', '')}_mongoid" do
set_versions(rails_version)
success = run_with_output("export RAILS_VERS=#{rails_version}; bundle exec rspec spec")
exit(1) unless success
end

desc "Rebundle for #{rails_version} with #{orm}"
task :"rebundle_#{rails_version.gsub('.', '')}_#{orm}" do
run_with_output "rm Gemfile_Rails_#{rails_version}_#{orm}_#{RUBY_VERSION}.lock Gemfile.lock"
set_versions(rails_version, orm)
end
desc "Rebundle for #{rails_version} with mongoid"
task :"rebundle_#{rails_version.gsub('.', '')}_mongoid" do
run_with_output "rm Gemfile_Rails_#{rails_version}_mongoid_#{RUBY_VERSION}.lock Gemfile.lock"
set_versions(rails_version)
end
end
49 changes: 46 additions & 3 deletions lib/mongoid_session_store/session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,57 @@ class Session
include Mongoid::Document
include Mongoid::Timestamps

field :data, type: BSON::Binary, default: -> { Session.binary_data({}) }
# Fields
# Public: The session id.
field :_id, as: :session_id, type: String, overwrite: true

def self.binary_data(data)
BSON::Binary.new(Marshal.dump(data), :generic)
# Public: Session data, stored in binary format.
field :data, type: BSON::Binary

# This method is defined in the `protected_attributes` gem.
if respond_to?(:accessible_attributes)
attr_accessible :session_id, :data
end

# Callbacks
# Internal: Ensure we store data in binary format.
before_save :serialize_data

def self.stats
collection.client.command(collstats: storage_options[:collection]).first
end

def self.find_by_session_id(sid)
where(session_id: sid).first if sid
end

# Internal: Convert session data to binary format.
def self.to_binary(data)
BSON::Binary.new(Marshal.dump(data), :generic)
end

# Internal: Convert session data from binary format to a Ruby object.
def self.from_binary(packed)
Marshal.load(packed.data) if packed
end

def initialize(*)
@data = nil
super
end

# Public: Lazy-unpack the session state that is stored in binary format.
def data
@data ||= self.class.from_binary(read_attribute(:data)) || {}
end

attr_writer :data

private

# Internal: Ensure we convert session data to binary format.
def serialize_data
write_attribute(:data, self.class.to_binary(data))
end
end
end
87 changes: 49 additions & 38 deletions lib/mongoid_session_store/store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,60 +3,70 @@

module MongoidSessionStore
class Store < ActionDispatch::Session::AbstractStore
SESSION_RECORD_KEY = 'rack.session.record'.freeze
SESSION_OPTIONS_KEY = Rack::Session::Abstract::ENV_SESSION_OPTIONS_KEY
# The class used for session storage. Defaults to MongoidSessionStore::Session
cattr_accessor :session_class
self.session_class = MongoidSessionStore::Session

SESSION_RECORD_KEY = 'rack.session.record'
SESSION_OPTIONS_KEY = Rack::RACK_SESSION_OPTIONS

private

def generate_sid
SecureRandom.hex(32)
# Rack: If nil is provided as the session id, generation of a new valid id
# should occur within.
#
# Returns [session_id, session].
def find_session(request, sid)
record = get_session_record(request, sid)
record.session_id = generate_sid if record.new_record?
[record.session_id, record.data]
end

def get_session(env, sid)
record = find_or_initialize_session(sid)
env[SESSION_RECORD_KEY] = record
[record._id, unpack(record.data)]
# Rack: Returns the session id if the session was saved successfully, or
# false if the session could not be saved.
def write_session(request, sid, session_data, options)
session = get_session_record(request, sid)
session.data = session_data
session.save ? session.session_id : false
end

def set_session(env, id, session_data, options = {})
record = get_session_record(env, id)
# Rack spec dictates that set_session should return true or false
# depending on whether or not the session was saved or not.
# However, ActionPack seems to want a session id instead.
record.update_attributes(id: id, data: pack(session_data)) ? record.id : false
end
# Rack: Returns a new session id or nil if options[:drop].
def delete_session(request, _sid, options)
old_record = destroy_session(request)
return if options[:drop]

def find_or_initialize_session(id)
id && MongoidSessionStore::Session.where(_id: id).first || MongoidSessionStore::Session.new
generate_sid.tap do |new_sid|
if options[:renew]
request.env[SESSION_RECORD_KEY] =
@@session_class.create(session_id: new_sid, data: old_record&.data)
end
end
end

def get_session_record(env, id)
if env[SESSION_OPTIONS_KEY][:id] && env[SESSION_RECORD_KEY]
env[SESSION_RECORD_KEY]
else
env[SESSION_RECORD_KEY] = find_or_initialize_session(id)
# Internal: Deletes the record for the current session and removes it from
# the request env.
#
# Returns the old record, if one existed.
def destroy_session(request)
@@session_class.find_by_session_id(current_session_id(request)).tap do |record|
request.env[SESSION_RECORD_KEY] = nil
record&.destroy
end
end

def destroy_session(env, session_id, options)
destroy(env)
generate_sid unless options[:drop]
end
# Internal: Finds the record for the current session (or initializes a
# record if none exists) and sets it in the request env.
def get_session_record(request, sid)
model = @@session_class.find_by_session_id(sid) ||
@@session_class.new(session_id: sid || generate_sid)

def destroy(env)
if sid = current_session_id(env)
get_session_record(env, sid).destroy
env[SESSION_RECORD_KEY] = nil
if request.env[SESSION_OPTIONS_KEY][:id].nil?
request.env[SESSION_RECORD_KEY] = model
else
request.env[SESSION_RECORD_KEY] ||= model
end
end

def pack(data)
MongoidSessionStore::Session.binary_data(data)
end

def unpack(packed)
return nil unless packed
Marshal.load(packed.data)
model
end
end
end
Expand All @@ -66,4 +76,5 @@ module Session
MongoidStore = MongoidSessionStore::Store
end
end

MongoidStore = MongoidSessionStore::Store
2 changes: 1 addition & 1 deletion lib/mongoid_session_store/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module MongoidSessionStore
VERSION = "6.0.0"
VERSION = '5.0.0'
end
3 changes: 1 addition & 2 deletions mongoid_session_store.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ Gem::Specification.new do |s|
s.homepage = 'http://github.com/SolarCS/mongoid_session_store'
s.license = 'MIT'
s.require_paths = ['lib']
s.rubygems_version = '1.3.7'
s.summary = 'Mongoid session store for Rails'
s.add_dependency 'actionpack', '>= 3.1'
s.add_dependency 'actionpack', '>= 5.0.2'
end
25 changes: 13 additions & 12 deletions perf/benchmark.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
end

RUNS = 2000
SESSION_OPTIONS_KEY = MongoidStore::SESSION_OPTIONS_KEY
SESSION_RECORD_KEY = MongoidStore::SESSION_RECORD_KEY

def benchmark(test_name, &block)
time = Benchmark.realtime do
Expand All @@ -36,7 +38,7 @@ def benchmark_store(store)
collection = MongoidSessionStore::Session.collection
collection.delete_many

large_session = {
large_session = {
:something => "not nothing",
:left => "not right",
:welcome => "not despised",
Expand All @@ -57,29 +59,28 @@ def benchmark_store(store)
ids = []

env = {
'rack.session' => large_session,
'rack.session.options' => { :id => store.send(:generate_sid) }
SESSION_RECORD_KEY => large_session,
SESSION_OPTIONS_KEY => { :id => store.send(:generate_sid) }
}
benchmark "session save" do
id = store.send(:generate_sid)
ids << id
store.send(:set_session, env.merge({'rack.session.options' => { :id => id }}), id, env['rack.session'])
# store.send(:commit_session, env.merge({'rack.session.options' => { :id => ids.last }}), 200, {}, [])
request = ActionDispatch::Request.new(env.merge(SESSION_OPTIONS_KEY => { id: id }))
store.send(:write_session, request, id, env[SESSION_RECORD_KEY], {})
end

ids.shuffle!

env = {
'rack.request.cookie_string' => "",
'HTTP_COOKIE' => "",
'rack.request.cookie_hash' => { '_session_id' => MongoidSessionStore::Session.last._id }
Rack::RACK_REQUEST_COOKIE_STRING => '',
Rack::HTTP_COOKIE => '',
SESSION_RECORD_KEY => large_session,
SESSION_OPTIONS_KEY => { :id => store.send(:generate_sid) }
}
benchmark "session load" do
id = ids.pop
local_env = env.merge({'rack.request.cookie_hash' => { '_session_id' => id }})
# store.send(:prepare_session, local_env)
sid, data = store.send(:get_session, local_env, id)
# something = local_env['rack.session']["something"] # trigger the load
request = ActionDispatch::Request.new(env.merge({ Rack::RACK_REQUEST_COOKIE_HASH => { 'session_id' => id } }))
sid, data = store.send(:find_session, request, id)
raise data.inspect unless data[:something] == "not nothing" && data[:a_bunch_of_floats_in_embedded_docs][0] == {:float_a => 3.141, :float_b => -1.1}
end

Expand Down
Loading

0 comments on commit 42198b1

Please sign in to comment.