Permalink
Browse files

initial checkin

  • Loading branch information...
1 parent 6ceeb04 commit 8695a006c0a0cf39ed66bcd359657416e4e4845b @skaes committed Sep 14, 2006
View
27 README
@@ -0,0 +1,27 @@
+SqlSessionStore
+===============
+
+See http://railsexpress.de/blog/articles/2005/12/19/roll-your-own-sql-session-store
+
+Only Mysql and Postgres are currently supported.
+
+Run
+ script/generate sql_session_store mysql
+or
+ script/generate sql_session_store postgresql
+
+Then run
+ rake migrate
+or
+ rake db:migrate
+for edge rails.
+
+Add this to environment.rb:
+ config.action_controller.session_store = :sql_session_store
+
+Finally, depending on your database type, add
+ SqlSessionStore.session_class = MysqlSession
+or
+ SqlSessionStore.session_class = PostgresqlSession
+after the initializer section in environment.rb
+
View
@@ -0,0 +1,22 @@
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test the sql_session_store plugin.'
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = true
+end
+
+desc 'Generate documentation for the sql_session_store plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'SqlSessionStore'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
@@ -0,0 +1,17 @@
+Description:
+ The sql_session_store generator creates a migration for use with
+ the sql session store. It takes one argument: the database
+ type. Only mysql and postgreql are currently supported.
+
+Example:
+ ./script/generate sql_session_store mysql
+
+ This will create the following migration:
+
+ db/migrate/XXX_add_sql_session.rb
+
+ Use
+
+ ./script/generate sql_session_store postgreql
+
+ to get a migration for postgres.
@@ -0,0 +1,23 @@
+class SqlSessionStoreGenerator < Rails::Generator::NamedBase
+ def initialize(runtime_args, runtime_options = {})
+ runtime_args.insert(0, 'add_sql_session')
+ if runtime_args.include?('postgresql')
+ @_database = 'postgresql'
+ elsif runtime_args.include?('mysql')
+ @_database = 'mysql'
+ else
+ puts "error: database type not given.\nvalid arguments are: mysql or postgresql"
+ exit
+ end
+ super
+ end
+
+ def manifest
+ record do |m|
+ m.migration_template("migration.rb", 'db/migrate',
+ :assigns => { :migration_name => "SqlSessionStoreSetup", :database => @_database },
+ :migration_file_name => "sql_session_store_setup"
+ )
+ end
+ end
+end
@@ -0,0 +1,38 @@
+class <%= migration_name %> < ActiveRecord::Migration
+
+ class Session < ActiveRecord::Base; end
+
+ def self.up
+ c = ActiveRecord::Base.connection
+ if c.tables.include?('sessions')
+ if (columns = Session.column_names).include?('sessid')
+ rename_column :sessions, :sessid, :session_id
+ else
+ add_column :sessions, :session_id, :string unless columns.include?('session_id')
+ add_column :sessions, :data, :text unless columns.include?('text')
+ if columns.include?('created_on')
+ rename_column :sessions, :created_on, :created_at
+ else
+ add_column :sessions, :created_at, :timestamp unless columns.include?('created_at')
+ end
+ if columns.include?('updated_on')
+ rename_column :sessions, :updated_on, :updated_at
+ else
+ add_column :sessions, :updated_at, :timestamp unless columns.include?('updated_at')
+ end
+ end
+ else
+ create_table :sessions, :options => '<%= database == "mysql" ? "ENGINE=MyISAM" : "" %>' do |t|
+ t.column :session_id, :string
+ t.column :data, :text
+ t.column :created_at, :timestamp
+ t.column :updated_at, :timestamp
+ end
+ add_index :sessions, :session_id, :name => 'session_id_idx'
+ end
+ end
+
+ def self.down
+ raise IrreversibleMigration
+ end
+end
View
@@ -0,0 +1 @@
+SqlSessionStore.session_class = MysqlSession
View
@@ -0,0 +1,2 @@
+# Install hook code here
+puts IO.read(File.join(File.dirname(__FILE__), 'README'))
View
@@ -0,0 +1,130 @@
+require 'mysql'
+
+# allow access to the real Mysql connection
+class ActiveRecord::ConnectionAdapters::MysqlAdapter
+ attr_reader :connection
+end
+
+# MysqlSession is a down to the bare metal session store
+# implementation to be used with +SQLSessionStore+. It is much faster
+# than the default ActiveRecord implementation.
+#
+# The implementation assumes that the table column names are 'id',
+# 'data', 'created_at' and 'updated_at'. If you want use other names,
+# you will need to change the SQL statments in the code.
+
+class MysqlSession
+
+ # if you need Rails components, and you have a pages which create
+ # new sessions, and embed components insides this pages that need
+ # session access, then you *must* set +eager_session_creation+ to
+ # true (as of Rails 1.0).
+ cattr_accessor :eager_session_creation
+ @@eager_session_creation = false
+
+ attr_accessor :id, :session_id, :data
+
+ def initialize(session_id, data)
+ @session_id = session_id
+ @data = data
+ @id = nil
+ end
+
+ class << self
+
+ # retrieve the ActiveRecordStore::Session connection and get the 'raw' Mysql connection from it
+ def session_connection
+ CGI::Session::ActiveRecordStore::Session.connection.connection
+ end
+
+ # try to find a session with a given +session_id+. returns nil if
+ # no such session exists. note that we don't retrieve
+ # +created_at+ and +updated_at+ as they are not accessed anywhyere
+ # outside this class
+ def find_session(session_id)
+ connection = session_connection
+ connection.query_with_result = true
+ result = connection.query("SELECT id, data FROM sessions WHERE `session_id`='#{session_id}' LIMIT 1")
+ my_session = nil
+ # each is used below, as other methods barf on my 64bit linux machine
+ # I suspect this to be a bug in mysql-ruby
+ result.each do |row|
+ my_session = new(session_id, row[1])
+ my_session.id = row[0]
+ end
+ result.free
+ my_session
+ end
+
+ # create a new session with given +session_id+ and +data+
+ # and save it immediately to the database
+ def create_session(session_id, data)
+ new_session = new(session_id, data)
+ if @@eager_session_creation
+ connection = session_connection
+ connection.query("INSERT INTO sessions (`created_at`, `updated_at`, `session_id`, `data`) VALUES (NOW(), NOW(), '#{session_id}', '#{Mysql::quote(data)}')")
+ new_session.id = connection.insert_id
+ end
+ new_session
+ end
+
+ # delete all sessions meeting a given +condition+. it is the
+ # caller's responsibility to pass a valid sql condition
+ def delete_all(condition=nil)
+ if condition
+ session_connection.query("DELETE FROM sessions WHERE #{condition}")
+ else
+ session_connection.query("DELETE FROM sessions")
+ end
+ end
+
+ end # class methods
+
+ # update session with given +data+.
+ # unlike the default implementation using ActiveRecord, updating of
+ # column `updated_at` will be done by the datbase itself
+ def update_session(data)
+ connection = self.class.session_connection
+ if @id
+ # if @id is not nil, this is a session already stored in the database
+ # update the relevant field using @id as key
+ connection.query("UPDATE sessions SET `updated_at`=NOW(), `data`='#{Mysql::quote(data)}' WHERE id=#{@id}")
+ else
+ # if @id is nil, we need to create a new session in the database
+ # and set @id to the primary key of the inserted record
+ connection.query("INSERT INTO sessions (`created_at`, `updated_at`, `session_id`, `data`) VALUES (NOW(), NOW(), '#{@session_id}', '#{Mysql::quote(data)}')")
+ @id = connection.insert_id
+ end
+ end
+
+ # destroy the current session
+ def destroy
+ self.class.delete_all("session_id='#{session_id}'")
+ end
+
+end
+
+__END__
+
+# This software is released under the MIT license
+#
+# Copyright (c) 2005,2006 Stefan Kaes
+
+# 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.
Oops, something went wrong.

0 comments on commit 8695a00

Please sign in to comment.