Skip to content

Commit

Permalink
Merged in new-sessions branch
Browse files Browse the repository at this point in the history
  • Loading branch information
fabien committed Sep 7, 2008
1 parent dde7edf commit 9e68fba
Show file tree
Hide file tree
Showing 13 changed files with 233 additions and 289 deletions.
9 changes: 0 additions & 9 deletions merb_activerecord/lib/active_record/merbtasks.rb
Expand Up @@ -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'
Expand All @@ -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"]
Expand Down
4 changes: 2 additions & 2 deletions 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
Expand Up @@ -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
Expand Down
7 changes: 0 additions & 7 deletions merb_activerecord/lib/merb/orms/active_record/connection.rb
Expand Up @@ -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
Expand Down
177 changes: 50 additions & 127 deletions 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 <tt>session_id</tt> 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<String>:: 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<String>:: ID of the session to set.
# data<ContainerSession>:: 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<String>:: 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
7 changes: 6 additions & 1 deletion 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"))

Expand All @@ -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
Expand Down
57 changes: 57 additions & 0 deletions 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
11 changes: 2 additions & 9 deletions merb_activerecord/specs/merb_active_record_spec.rb
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions 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'

0 comments on commit 9e68fba

Please sign in to comment.