diff --git a/merb_activerecord/lib/active_record/merbtasks.rb b/merb_activerecord/lib/active_record/merbtasks.rb
index 520f5eb..a7b6241 100644
--- a/merb_activerecord/lib/active_record/merbtasks.rb
+++ b/merb_activerecord/lib/active_record/merbtasks.rb
@@ -316,11 +316,6 @@ def local_database?(config, &block)
end
namespace :sessions do
- desc "Create sessions table"
- task :create => :merb_start do
- Merb::ActiveRecordSession.create_table!
- end
-
desc "Clear the sessions table"
task :clear => :merb_start do
session_table = 'session'
@@ -330,10 +325,6 @@ def local_database?(config, &block)
end
end
-def session_table_name
- ActiveRecord::Base.pluralize_table_names ? :sessions : :session
-end
-
def set_firebird_env(config)
ENV["ISC_USER"] = config["username"].to_s if config["username"]
ENV["ISC_PASSWORD"] = config["password"].to_s if config["password"]
diff --git a/merb_activerecord/lib/generators/session_migration.rb b/merb_activerecord/lib/generators/session_migration.rb
index 4289241..7987ccb 100644
--- a/merb_activerecord/lib/generators/session_migration.rb
+++ b/merb_activerecord/lib/generators/session_migration.rb
@@ -1,4 +1,4 @@
Merb::Generators::SessionMigrationGenerator.template :session_migration_activerecord, :orm => :activerecord do
- source(File.dirname(__FILE__), 'templates/session_migration/schema/migrations/%version%_sessions.rb')
- destination("schema/migrations/#{version}_sessions.rb")
+ source(File.dirname(__FILE__), 'templates/session_migration/schema/migrations/%version%_database_sessions.rb')
+ destination("schema/migrations/#{version}_database_sessions.rb")
end
\ No newline at end of file
diff --git a/merb_activerecord/lib/generators/templates/session_migration/schema/migrations/%version%_sessions.rb b/merb_activerecord/lib/generators/templates/session_migration/schema/migrations/%version%_database_sessions.rb
similarity index 88%
rename from merb_activerecord/lib/generators/templates/session_migration/schema/migrations/%version%_sessions.rb
rename to merb_activerecord/lib/generators/templates/session_migration/schema/migrations/%version%_database_sessions.rb
index 082796b..c382223 100644
--- a/merb_activerecord/lib/generators/templates/session_migration/schema/migrations/%version%_sessions.rb
+++ b/merb_activerecord/lib/generators/templates/session_migration/schema/migrations/%version%_database_sessions.rb
@@ -3,6 +3,7 @@ def self.up
create_table :sessions do |t|
t.column :session_id, :string
t.column :data, :text
+ t.column :created_at, :datetime
end
add_index :sessions, :session_id
end
diff --git a/merb_activerecord/lib/merb/orms/active_record/connection.rb b/merb_activerecord/lib/merb/orms/active_record/connection.rb
index 8e1e589..31cca2d 100644
--- a/merb_activerecord/lib/merb/orms/active_record/connection.rb
+++ b/merb_activerecord/lib/merb/orms/active_record/connection.rb
@@ -73,13 +73,6 @@ def connect
end
end
- # Registering this ORM lets the user choose active_record as a session
- # in merb.yml's session_store: option.
- def register_session_type
- Merb.register_session_type("activerecord",
- "merb/session/active_record_session",
- "Using ActiveRecord database sessions")
- end
end
end
end
diff --git a/merb_activerecord/lib/merb/session/active_record_session.rb b/merb_activerecord/lib/merb/session/active_record_session.rb
index 36e965c..150cbc3 100644
--- a/merb_activerecord/lib/merb/session/active_record_session.rb
+++ b/merb_activerecord/lib/merb/session/active_record_session.rb
@@ -1,142 +1,65 @@
-require "active_record"
+require 'active_record'
+require 'merb-core/dispatch/session'
+require 'base64'
module Merb
- module SessionMixin
- def setup_session
- before = cookies[_session_id_key]
- request.session, cookies[_session_id_key] = Merb::ActiveRecordSession.persist(cookies[_session_id_key])
- @_fingerprint = Marshal.dump(request.session.data).hash
- @_new_cookie = cookies[_session_id_key] != before
- end
- def finalize_session
- request.session.save if @_fingerprint != Marshal.dump(request.session.data).hash
- set_cookie(_session_id_key, request.session.session_id, Time.now + _session_expiry) if (@_new_cookie || request.session.needs_new_cookie)
- end
+ # Sessions stored in ActiveRecord model.
+ #
+ # To use ActiveRecord based sessions add the following to config/init.rb:
+ #
+ # Merb::Config[:session_store] = 'activerecord'
+
+ class ActiveRecordSessionStore < ::ActiveRecord::Base
+
+ table_name = (Merb::Plugins.config[:merb_active_record][:session_table_name] || "sessions")
+
+ set_table_name table_name
- def session_store_type
- "activerecord"
- end
- end # ActiveRecordMixin
-
- class ActiveRecordSession < ::ActiveRecord::Base
- set_table_name 'sessions'
- # Customizable data column name. Defaults to 'data'.
- cattr_accessor :data_column_name
- self.data_column_name = 'data'
- before_save :marshal_data!
- before_save :raise_on_session_data_overflow!
- attr_accessor :needs_new_cookie
-
+ serialize :data
+
class << self
- # Generates a new session ID and creates a row for the new session in the database.
- def generate
- create(:session_id => Merb::SessionMixin::rand_uuid, :data => {})
- end
-
- # Gets the existing session based on the session_id available in cookies.
- # If none is found, generates a new session.
- def persist(session_id)
- if session_id
- session = find_by_session_id(session_id)
- end
- unless session
- session = generate
+
+ # ==== Parameters
+ # session_id:: ID of the session to retrieve.
+ #
+ # ==== Returns
+ # ContainerSession:: The session corresponding to the ID.
+ def retrieve_session(session_id)
+ if item = find_by_session_id(session_id)
+ item.data
end
- [session, session.session_id]
- end
-
- # Don't try to reload ARStore::Session in dev mode.
- def reloadable?
- false
- end
-
- def data_column_size_limit
- @data_column_size_limit ||= columns_hash[@@data_column_name].limit
end
- def marshal(data) Base64.encode64(Marshal.dump(data)) if data end
- def unmarshal(data) Marshal.load(Base64.decode64(data)) if data end
-
- def create_table!
- connection.execute <<-end_sql
- CREATE TABLE #{table_name} (
- id INTEGER PRIMARY KEY,
- #{connection.quote_column_name('session_id')} TEXT UNIQUE,
- #{connection.quote_column_name(@@data_column_name)} TEXT(255)
- )
- end_sql
+ # ==== Parameters
+ # session_id:: ID of the session to set.
+ # data:: The session to set.
+ def store_session(session_id, data)
+ if item = find_by_session_id(session_id)
+ item.update_attributes!(:data => data)
+ else
+ create(:session_id => session_id, :data => data)
+ end
end
- def drop_table!
- connection.execute "DROP TABLE #{table_name}"
+ # ==== Parameters
+ # session_id:: ID of the session to delete.
+ def delete_session(session_id)
+ delete_all(["#{connection.quote_column_name('session_id')} IN (?)", session_id])
end
- end
-
- # Regenerate the Session ID
- def regenerate
- update_attributes(:session_id => Merb::SessionMixin::rand_uuid)
- self.needs_new_cookie = true
- end
-
- # Recreates the cookie with the default expiration time
- # Useful during log in for pushing back the expiration date
- def refresh_expiration
- self.needs_new_cookie = true
- end
-
- # Lazy-delete of session data
- def delete(key = nil)
- key ? self.data.delete(key) : self.data.clear
- end
-
- def [](key)
- data[key]
- end
-
- def empty?
- data.empty?
- end
-
- def each(&b)
- data.each(&b)
+
end
- def each_with_index(&b)
- data.each_with_index(&b)
- end
+ end
- def []=(key, val)
- data[key] = val
- end
+ class ActiveRecordSession < SessionStoreContainer
- # Lazy-unmarshal session state.
- def data
- @data ||= self.class.unmarshal(read_attribute(@@data_column_name)) || {}
- end
-
- # Has the session been loaded yet?
- def loaded?
- !! @data
- end
-
- private
- attr_writer :data
-
- def marshal_data!
- return false if !loaded?
- write_attribute(@@data_column_name, self.class.marshal(self.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 if !loaded?
- limit = self.class.data_column_size_limit
- if loaded? and limit and read_attribute(@@data_column_name).size > limit
- raise MerbController::SessionOverflowError
- end
- end
- end # ActiveRecordSessionMixin
-end # Merb
+ # The session store type
+ self.session_store_type = :activerecord
+
+ # The store object is the model class itself
+ self.store = ActiveRecordSessionStore
+
+ end
+
+end
\ No newline at end of file
diff --git a/merb_activerecord/lib/merb_activerecord.rb b/merb_activerecord/lib/merb_activerecord.rb
index 149d1fc..823a6bb 100644
--- a/merb_activerecord/lib/merb_activerecord.rb
+++ b/merb_activerecord/lib/merb_activerecord.rb
@@ -1,5 +1,7 @@
if defined?(Merb::Plugins)
+
dependency "activerecord"
+
require File.join(File.dirname(__FILE__) / "merb" / "orms" / "active_record" / "connection")
Merb::Plugins.add_rakefiles(File.join(File.dirname(__FILE__) / "active_record" / "merbtasks"))
@@ -9,7 +11,10 @@ class Merb::Orms::ActiveRecord::Connect < Merb::BootLoader
def self.run
Merb::Orms::ActiveRecord.connect
- Merb::Orms::ActiveRecord.register_session_type
+ if Merb::Config.session_stores.include?(:activerecord)
+ Merb.logger.debug "Using ActiveRecord sessions"
+ require File.join(File.dirname(__FILE__) / "merb" / "session" / "active_record_session")
+ end
end
end
diff --git a/merb_activerecord/specs/merb_active_record_session_spec.rb b/merb_activerecord/specs/merb_active_record_session_spec.rb
new file mode 100644
index 0000000..3865423
--- /dev/null
+++ b/merb_activerecord/specs/merb_active_record_session_spec.rb
@@ -0,0 +1,57 @@
+$:.push File.join(File.dirname(__FILE__), '..', 'lib')
+require 'merb-core'
+require 'merb-core/test'
+require 'merb-core/test/helpers'
+
+Merb::BootLoader.before_app_loads do
+ require "merb/session/active_record_session"
+end
+
+Merb.start_environment( :environment => 'test', :adapter => 'runner',
+ :session_store => 'activerecord')
+
+Spec::Runner.configure do |config|
+ config.include Merb::Test::RequestHelper
+end
+
+require 'merb_activerecord'
+ActiveRecord::Base.establish_connection(:adapter => "sqlite3",
+ :dbfile => ":memory:")
+
+ActiveRecord::Schema.define do
+ create_table :sessions do |t|
+ t.column :session_id, :string
+ t.column :data, :text
+ t.column :created_at, :datetime
+ end
+end
+
+# Load up the shared specs from merb-core
+if (gem_spec = Gem.source_index.search('merb-core').last) &&
+ gem_spec.files.include?('spec/public/session/controllers/sessions.rb')
+ require gem_spec.full_gem_path / 'spec/public/session/controllers/sessions.rb'
+ require gem_spec.full_gem_path / 'spec/public/session/session_spec.rb'
+end
+
+describe Merb::ActiveRecordSession do
+
+ before do
+ @session_class = Merb::ActiveRecordSession
+ @session = @session_class.generate
+ end
+
+ it_should_behave_like "All session-store backends"
+
+ it "should have a session_store_type class attribute" do
+ @session.class.session_store_type.should == :activerecord
+ end
+
+end
+
+describe Merb::ActiveRecordSession, "mixed into Merb::Controller" do
+
+ before(:all) { @session_class = Merb::ActiveRecordSession }
+
+ it_should_behave_like "All session-stores mixed into Merb::Controller"
+
+end
\ No newline at end of file
diff --git a/merb_activerecord/specs/merb_active_record_spec.rb b/merb_activerecord/specs/merb_active_record_spec.rb
index 07c0b41..2a9a8fc 100644
--- a/merb_activerecord/specs/merb_active_record_spec.rb
+++ b/merb_activerecord/specs/merb_active_record_spec.rb
@@ -10,14 +10,12 @@
end
end
-
-
describe "Merb ActiveRecord extension" do
before :all do
@wd = Dir.pwd
Merb.stub!(:dir_for).with(:config).and_return(@wd)
- @config_file_path = @wd / "database.yml"
- @sample_file_path = @wd / "database.yml.sample"
+ @config_file_path = @wd / "config" / "database.yml"
+ @sample_file_path = @wd / "config" / "database.yml.sample"
@sample_source = Merb::Orms::ActiveRecord.sample_source
@config_sample = Erubis.load_yaml_file(@sample_source)
@@ -52,11 +50,6 @@
@config_sample[:development][:encoding].should == "utf8"
end
- it "stores configurations from config file" do
- Erubis.should_receive(:load_yaml_file).with(@config_file_path).and_return(@config_sample)
- Merb::Orms::ActiveRecord.configurations[:development][:database].should == "sample_development"
- end
-
it "provides Rack with a way to start a transcantion" do
Merb::Orms::ActiveRecord.should respond_to(:open_sandbox!)
end
diff --git a/merb_activerecord/specs/spec_helper.rb b/merb_activerecord/specs/spec_helper.rb
index b6eaa87..496172e 100644
--- a/merb_activerecord/specs/spec_helper.rb
+++ b/merb_activerecord/specs/spec_helper.rb
@@ -1,5 +1,4 @@
$TESTING = true
$:.push File.join(File.dirname(__FILE__), '..', 'lib')
require 'merb-core'
-require 'merb_activerecord'
-require 'merb/test/model_helper/active_record'
+require 'merb_activerecord'
\ No newline at end of file
diff --git a/merb_sequel/lib/merb/orms/sequel/connection.rb b/merb_sequel/lib/merb/orms/sequel/connection.rb
index 15b2b2b..e0efc84 100644
--- a/merb_sequel/lib/merb/orms/sequel/connection.rb
+++ b/merb_sequel/lib/merb/orms/sequel/connection.rb
@@ -1,4 +1,5 @@
require "fileutils"
+require "sequel"
module Merb
module Orms
@@ -15,9 +16,7 @@ def copy_sample_config
end
def config
-
- @config ||=
- begin
+ @config ||= begin
# Convert string keys to symbols
full_config = Erubis.load_yaml_file(config_file)
config = (Merb::Plugins.config[:merb_sequel] = {})
@@ -26,14 +25,10 @@ def config
end
config
end
-
end
# Database connects as soon as the gem is loaded
def connect
-
- require "sequel"
-
if File.exists?(config_file)
Merb.logger.info!("Connecting to the '#{config[:database]}' database on '#{config[:host]}' using '#{config[:adapter]}' ...")
connection = ::Sequel.connect(config_options(config))
@@ -46,11 +41,9 @@ def connect
Merb.logger.error! "A sample file was created called config/database.yml.sample for you to copy and edit."
exit(1)
end
-
end
def config_options(config = {})
-
options = {}
options[:adapter] = (config[:adapter] || "sqlite")
options[:host] = (config[:host] || "localhost")
@@ -63,16 +56,6 @@ def config_options(config = {})
options[:logger] = Merb.logger
options
end
-
- # Registering this ORM lets the user choose sequel as a session store
- # in merb.yml's session_store: option.
- def register_session_type
- Merb.register_session_type(
- "sequel",
- "merb/session/sequel_session",
- "Using Sequel database sessions"
- )
- end
end
diff --git a/merb_sequel/lib/merb/session/sequel_session.rb b/merb_sequel/lib/merb/session/sequel_session.rb
index a761c11..604212d 100644
--- a/merb_sequel/lib/merb/session/sequel_session.rb
+++ b/merb_sequel/lib/merb/session/sequel_session.rb
@@ -1,156 +1,106 @@
-require "base64"
+require 'sequel'
+require 'merb-core/dispatch/session'
+require 'base64'
module Merb
- module SessionMixin
- def setup_session
- before = cookies[_session_id_key]
- request.session, cookies[_session_id_key] = Merb::SequelSession.persist(cookies[_session_id_key])
- @_fingerprint = Marshal.dump(request.session.data).hash
- @_new_cookie = cookies[_session_id_key] != before
- end
-
- def finalize_session
- request.session.save if @_fingerprint != Marshal.dump(request.session.data).hash
- set_cookie(_session_id_key, request.session.values[:session_id], Time.now + _session_expiry) if (@_new_cookie || request.session.needs_new_cookie)
- end
-
- def session_store_type
- "sequel"
- end
-
- end
-
table_name = (Merb::Plugins.config[:merb_sequel][:session_table_name] || "sessions")
- class SequelSession < Sequel::Model(table_name.to_sym)
+ # Sessions stored in Sequel model.
+ #
+ # To use Sequel based sessions add the following to config/init.rb:
+ #
+ # Merb::Config[:session_store] = 'sequel'
+
+ class SequelSessionStore < Sequel::Model(table_name.to_sym)
set_schema do
primary_key :id
varchar :session_id
- varchar :data
+ text :data
timestamp :created_at
end
- attr_accessor :needs_new_cookie
-
class << self
- # Generates a new session ID and creates a row for the new session in the database.
- def generate
- create(:session_id => Merb::SessionMixin::rand_uuid,
- :data => marshal({}), :created_at => Time.now)
+
+ # ==== Parameters
+ # session_id:: ID of the session to retrieve.
+ #
+ # ==== Returns
+ # ContainerSession:: The session corresponding to the ID.
+ def retrieve_session(session_id)
+ if item = find(:session_id => session_id)
+ item.data
+ end
end
- # Gets the existing session based on the session_id available in cookies.
- # If none is found, generates a new session.
- def persist(session_id)
- if session_id
- session = find(:session_id => session_id)
+ # ==== Parameters
+ # session_id:: ID of the session to set.
+ # data:: The session to set.
+ def store_session(session_id, data)
+ if item = find(:session_id => session_id)
+ item.update(:data => data)
+ else
+ create(:session_id => session_id, :data => data, :created_at => Time.now)
end
- unless session
- session = generate
- end
- [session, session.values[:session_id]]
end
- # Don't try to reload ARStore::Session in dev mode.
- def reloadable?
- false
+ # ==== Parameters
+ # session_id:: ID of the session to delete.
+ def delete_session(session_id)
+ if item = find(:session_id => session_id)
+ item.delete
+ end
end
-
+
+ # ==== Returns
+ # Integer:: The maximum length of the 'data' column.
def data_column_size_limit
- 255
- end
-
- def marshal(data)
- Base64.encode64(Marshal.dump(data)) if data
- end
-
- def unmarshal(data)
- Marshal.load(Base64.decode64(data)) if data
+ 512 # TODO - figure out how much space we actually have
end
alias :create_table! :create_table
alias :drop_table! :drop_table
end
- # Regenerate the Session ID
- def regenerate
- update_attributes(:session_id => Merb::SessionMixin::rand_uuid)
- self.needs_new_cookie = true
- end
-
- # Recreates the cookie with the default expiration time
- # Useful during log in for pushing back the expiration date
- def refresh_expiration
- self.needs_new_cookie = true
- end
-
- # Lazy-delete of session data
- def delete(key = nil)
- key ? self.data.delete(key) : self.data.clear
- end
-
- def [](key)
- data[key]
- end
-
- def []=(key, val)
- data[key] = val
- end
-
- def empty?
- data.empty?
- end
-
- def each(&b)
- data.each(&b)
+ # Lazy-unserialize session state.
+ def data
+ @data ||= (@values[:data] ? Marshal.load(@values[:data]) : {})
end
- def each_with_index(&b)
- data.each_with_index(&b)
- end
-
- # Lazy-unmarshal session state.
- def data
- @data ||= self.class.unmarshal(@values[:data]) || {}
+ # Virtual attribute writer - override.
+ def data=(hsh)
+ @data = hsh if hsh.is_a?(Hash)
end
# Has the session been loaded yet?
def loaded?
- !! @data
+ !!@data
end
- private
-
- attr_writer :data
-
- before_save do # marshal_data!
- # return false if !loaded?
- @values[:data] = self.class.marshal(self.data)
+ before_save do
+ @values[:data] = Marshal.dump(self.data)
+ if @values[:data].size > self.class.data_column_size_limit
+ raise Merb::SessionMixin::SessionOverflow
+ end
end
-
- # Ensures that the data about to be stored in the database is not
- # larger than the data storage column. Raises
- # ActionController::SessionOverflowError.
- # before_save do # raise_on_session_data_overflow!
- # return false if !loaded?
- # limit = self.class.data_column_size_limit
- # if loaded? and limit and read_attribute(@@data_column_name).size > limit
- # raise MerbController::SessionOverflowError
- # end
- # end
end
unless Sequel::Model.db.table_exists?(table_name.to_sym)
puts "Warning: The database did not contain a '#{table_name}' table for sessions."
-
- SequelSession.class_eval do
- create_table unless table_exists?
- end
-
+ SequelSessionStore.class_eval { create_table unless table_exists? }
puts "Created sessions table."
end
+
+ class SequelSession < SessionStoreContainer
+
+ # The session store type
+ self.session_store_type = :sequel
+
+ # The store object is the model class itself
+ self.store = SequelSessionStore
+
+ end
end
diff --git a/merb_sequel/lib/merb_sequel.rb b/merb_sequel/lib/merb_sequel.rb
index 197fcc6..e332ae1 100644
--- a/merb_sequel/lib/merb_sequel.rb
+++ b/merb_sequel/lib/merb_sequel.rb
@@ -9,7 +9,10 @@ class Merb::Orms::Sequel::Connect < Merb::BootLoader
def self.run
Merb::Orms::Sequel.connect
- Merb::Orms::Sequel.register_session_type
+ if Merb::Config.session_stores.include?(:sequel)
+ Merb.logger.debug "Using Sequel sessions"
+ require File.join(File.dirname(__FILE__) / "merb" / "session" / "sequel_session")
+ end
end
end
diff --git a/merb_sequel/specs/merb_sequel_session_spec.rb b/merb_sequel/specs/merb_sequel_session_spec.rb
new file mode 100644
index 0000000..49c31d4
--- /dev/null
+++ b/merb_sequel/specs/merb_sequel_session_spec.rb
@@ -0,0 +1,46 @@
+$:.push File.join(File.dirname(__FILE__), '..', 'lib')
+require 'merb-core'
+require 'merb-core/test'
+require 'merb-core/test/helpers'
+
+Merb::BootLoader.before_app_loads do
+ require 'sequel'
+ DB = Sequel.sqlite
+ require "merb/session/sequel_session"
+end
+
+Merb.start :environment => 'test', :adapter => 'runner', :session_store => 'sequel'
+
+Spec::Runner.configure do |config|
+ config.include Merb::Test::RequestHelper
+end
+
+# Load up the shared specs from merb-core
+if (gem_spec = Gem.source_index.search('merb-core').last) &&
+ gem_spec.files.include?('spec/public/session/controllers/sessions.rb')
+ require gem_spec.full_gem_path / 'spec/public/session/controllers/sessions.rb'
+ require gem_spec.full_gem_path / 'spec/public/session/session_spec.rb'
+end
+
+describe Merb::SequelSession do
+
+ before do
+ @session_class = Merb::SequelSession
+ @session = @session_class.generate
+ end
+
+ it_should_behave_like "All session-store backends"
+
+ it "should have a session_store_type class attribute" do
+ @session.class.session_store_type.should == :sequel
+ end
+
+end
+
+describe Merb::SequelSession, "mixed into Merb::Controller" do
+
+ before(:all) { @session_class = Merb::SequelSession }
+
+ it_should_behave_like "All session-stores mixed into Merb::Controller"
+
+end
\ No newline at end of file