Move to version 2.3.2 of rails.

This commit is contained in:
Tom Hughes 2009-06-04 08:08:24 +00:00
parent 9126d989c8
commit 2f3e23f6eb
47 changed files with 390 additions and 304 deletions

View file

@ -43,7 +43,6 @@ class AmfController < ApplicationController
# Help methods for checking boundary sanity and area size
include MapBoundary
session :off
before_filter :check_api_writable
# Main AMF handlers: process the raw AMF string (using AMF library) and

View file

@ -1,6 +1,5 @@
class ApiController < ApplicationController
session :off
before_filter :check_api_readable, :except => [:capabilities]
after_filter :compress_output
around_filter :api_call_handle_error, :api_call_timeout

View file

@ -113,7 +113,7 @@ class ApplicationController < ActionController::Base
I18n.locale = request.compatible_language_from(I18n.available_locales)
response.headers['Content-Language'] = I18n.locale
response.headers['Content-Language'] = I18n.locale.to_s
end
def api_call_handle_error

View file

@ -4,7 +4,6 @@ class ChangesetController < ApplicationController
layout 'site'
require 'xml/libxml'
session :off, :except => [:list, :list_user, :list_bbox]
before_filter :authorize_web, :only => [:list, :list_user, :list_bbox]
before_filter :set_locale, :only => [:list, :list_user, :list_bbox]
before_filter :authorize, :only => [:create, :update, :delete, :upload, :include, :close]

View file

@ -3,7 +3,6 @@
class NodeController < ApplicationController
require 'xml/libxml'
session :off
before_filter :authorize, :only => [:create, :update, :delete]
before_filter :require_public_data, :only => [:create, :update, :delete]
before_filter :check_api_writable, :only => [:create, :update, :delete]

View file

@ -1,7 +1,6 @@
class OldNodeController < ApplicationController
require 'xml/libxml'
session :off
before_filter :check_api_readable
after_filter :compress_output
around_filter :api_call_handle_error, :api_call_timeout

View file

@ -1,7 +1,6 @@
class OldRelationController < ApplicationController
require 'xml/libxml'
session :off
before_filter :check_api_readable
after_filter :compress_output
around_filter :api_call_handle_error, :api_call_timeout

View file

@ -1,7 +1,6 @@
class OldWayController < ApplicationController
require 'xml/libxml'
session :off
before_filter :check_api_readable
after_filter :compress_output
around_filter :api_call_handle_error, :api_call_timeout

View file

@ -1,7 +1,6 @@
class RelationController < ApplicationController
require 'xml/libxml'
session :off
before_filter :authorize, :only => [:create, :update, :delete]
before_filter :require_public_data, :only => [:create, :update, :delete]
before_filter :check_api_writable, :only => [:create, :update, :delete]

View file

@ -1,5 +1,4 @@
class SwfController < ApplicationController
session :off
before_filter :check_api_readable
# to log:

View file

@ -1,7 +1,6 @@
class WayController < ApplicationController
require 'xml/libxml'
session :off
before_filter :authorize, :only => [:create, :update, :delete]
before_filter :require_public_data, :only => [:create, :update, :delete]
before_filter :check_api_writable, :only => [:create, :update, :delete]

View file

@ -44,6 +44,7 @@ module Rails
def load_initializer
require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
Rails::Initializer.run(:install_gem_spec_stubs)
Rails::GemDependency.add_frozen_gem_path
end
end

View file

@ -5,7 +5,7 @@
ENV['RAILS_ENV'] ||= 'production'
# Specifies gem version of Rails to use when vendor/rails is not present
RAILS_GEM_VERSION = '2.2.2' unless defined? RAILS_GEM_VERSION
RAILS_GEM_VERSION = '2.3.2' unless defined? RAILS_GEM_VERSION
# Set the server URL
SERVER_URL = ENV['OSM_SERVER_URL'] || 'www.openstreetmap.org'

View file

@ -3,7 +3,7 @@ require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
require 'test_help'
load 'composite_primary_keys/fixtures.rb'
class Test::Unit::TestCase
class ActiveSupport::TestCase
# Transactional fixtures accelerate your tests by wrapping each test method
# in a transaction that's rolled back on completion. This ensures that the
# test database remains unchanged so your fixtures don't have to be reloaded

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class ChangesetTagTest < Test::Unit::TestCase
class ChangesetTagTest < ActiveSupport::TestCase
api_fixtures
def test_changeset_tag_count
@ -14,7 +14,7 @@ class ChangesetTagTest < Test::Unit::TestCase
tag.id = 1
tag.k = key*i
tag.v = "v"
assert_valid tag
assert tag.valid?
end
end
@ -25,7 +25,7 @@ class ChangesetTagTest < Test::Unit::TestCase
tag.id = 1
tag.k = "k"
tag.v = val*i
assert_valid tag
assert tag.valid?
end
end

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class ChangesetTest < Test::Unit::TestCase
class ChangesetTest < ActiveSupport::TestCase
api_fixtures
def test_changeset_count

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class DiaryCommentTest < Test::Unit::TestCase
class DiaryCommentTest < ActiveSupport::TestCase
api_fixtures
fixtures :diary_comments

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class DiaryEntryTest < Test::Unit::TestCase
class DiaryEntryTest < ActiveSupport::TestCase
api_fixtures
fixtures :diary_entries, :languages

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class FriendTest < Test::Unit::TestCase
class FriendTest < ActiveSupport::TestCase
api_fixtures
fixtures :friends

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class MessageTest < Test::Unit::TestCase
class MessageTest < ActiveSupport::TestCase
api_fixtures
fixtures :messages

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class NodeTagTest < Test::Unit::TestCase
class NodeTagTest < ActiveSupport::TestCase
api_fixtures
def test_tag_count
@ -24,7 +24,7 @@ class NodeTagTest < Test::Unit::TestCase
tag.id = current_node_tags(:t1).id
tag.k = key*i
tag.v = "v"
assert_valid tag
assert tag.valid?
end
end
@ -35,7 +35,7 @@ class NodeTagTest < Test::Unit::TestCase
tag.id = current_node_tags(:t1).id
tag.k = "k"
tag.v = val*i
assert_valid tag
assert tag.valid?
end
end

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class NodeTest < Test::Unit::TestCase
class NodeTest < ActiveSupport::TestCase
api_fixtures
def test_node_count
@ -56,7 +56,7 @@ class NodeTest < Test::Unit::TestCase
assert_equal dbnode.version, node.version
assert_equal dbnode.visible, node.visible
#assert_equal node.tile, QuadTile.tile_for_point(node.lat, node.lon)
assert_valid node
assert node.valid?
end
# This helper method will check to make sure that a node is outwith the world,

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class OldNodeTest < Test::Unit::TestCase
class OldNodeTest < ActiveSupport::TestCase
api_fixtures
def test_old_node_tag_count
@ -15,7 +15,7 @@ class OldNodeTest < Test::Unit::TestCase
tag.version = node_tags(:t1).version
tag.k = key*i
tag.v = "v"
assert_valid tag
assert tag.valid?
end
end
@ -27,7 +27,7 @@ class OldNodeTest < Test::Unit::TestCase
tag.version = node_tags(:t1).version
tag.k = "k"
tag.v = val*i
assert_valid tag
assert tag.valid?
end
end

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class OldNodeTest < Test::Unit::TestCase
class OldNodeTest < ActiveSupport::TestCase
api_fixtures
def test_old_node_count
@ -56,7 +56,7 @@ class OldNodeTest < Test::Unit::TestCase
assert_equal dbnode.visible, node.visible
assert_equal dbnode.timestamp, node.timestamp
#assert_equal node.tile, QuadTile.tile_for_point(nodes(nod).lat, nodes(nod).lon)
assert_valid node
assert node.valid?
end
# This helpermethod will check to make sure that a node is outwith the world,

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class OldRelationTagTest < Test::Unit::TestCase
class OldRelationTagTest < ActiveSupport::TestCase
api_fixtures
def test_tag_count
@ -15,7 +15,7 @@ class OldRelationTagTest < Test::Unit::TestCase
tag.version = 1
tag.k = key*i
tag.v = "v"
assert_valid tag
assert tag.valid?
end
end
@ -27,7 +27,7 @@ class OldRelationTagTest < Test::Unit::TestCase
tag.version = 1
tag.k = "k"
tag.v = val*i
assert_valid tag
assert tag.valid?
end
end

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class WayTagTest < Test::Unit::TestCase
class WayTagTest < ActiveSupport::TestCase
api_fixtures
def test_tag_count
@ -15,7 +15,7 @@ class WayTagTest < Test::Unit::TestCase
tag.version = 1
tag.k = key*i
tag.v = "v"
assert_valid tag
assert tag.valid?
end
end
@ -27,7 +27,7 @@ class WayTagTest < Test::Unit::TestCase
tag.version = 1
tag.k = "k"
tag.v = val*i
assert_valid tag
assert tag.valid?
end
end

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class RelationMemberTest < Test::Unit::TestCase
class RelationMemberTest < ActiveSupport::TestCase
api_fixtures
def test_relation_member_count

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class RelationTagTest < Test::Unit::TestCase
class RelationTagTest < ActiveSupport::TestCase
api_fixtures
def test_relation_tag_count
@ -14,7 +14,7 @@ class RelationTagTest < Test::Unit::TestCase
tag.id = 1
tag.k = key*i
tag.v = "v"
assert_valid tag
assert tag.valid?
end
end
@ -25,7 +25,7 @@ class RelationTagTest < Test::Unit::TestCase
tag.id = 1
tag.k = "k"
tag.v = val*i
assert_valid tag
assert tag.valid?
end
end

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class RelationTest < Test::Unit::TestCase
class RelationTest < ActiveSupport::TestCase
api_fixtures
def test_relation_count

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class TraceTest < Test::Unit::TestCase
class TraceTest < ActiveSupport::TestCase
api_fixtures
def test_trace_count

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class TracepointTest < Test::Unit::TestCase
class TracepointTest < ActiveSupport::TestCase
api_fixtures
def test_tracepoint_count

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class TracetagTest < Test::Unit::TestCase
class TracetagTest < ActiveSupport::TestCase
api_fixtures
def test_tracetag_count

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class UserTest < Test::Unit::TestCase
class UserTest < ActiveSupport::TestCase
api_fixtures
fixtures :friends

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class UserTokenTest < Test::Unit::TestCase
class UserTokenTest < ActiveSupport::TestCase
api_fixtures
fixtures :user_tokens

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class WayNodeTest < Test::Unit::TestCase
class WayNodeTest < ActiveSupport::TestCase
api_fixtures
def test_way_nodes_count

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class WayTagTest < Test::Unit::TestCase
class WayTagTest < ActiveSupport::TestCase
api_fixtures
def test_way_tag_count
@ -14,7 +14,7 @@ class WayTagTest < Test::Unit::TestCase
tag.id = current_way_tags(:t1).id
tag.k = key*i
tag.v = current_way_tags(:t1).v
assert_valid tag
assert tag.valid?
end
end
@ -25,7 +25,7 @@ class WayTagTest < Test::Unit::TestCase
tag.id = current_way_tags(:t1).id
tag.k = "k"
tag.v = val*i
assert_valid tag
assert tag.valid?
end
end

View file

@ -1,6 +1,6 @@
require File.dirname(__FILE__) + '/../test_helper'
class WayTest < Test::Unit::TestCase
class WayTest < ActiveSupport::TestCase
api_fixtures
# Check that we have the correct number of currnet ways in the db

View file

@ -1,4 +1,4 @@
Copyright (c) 2006-2008 Dr.-Ing. Stefan Kaes
Copyright (c) 2006 Dr.-Ing. Stefan Kaes
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View file

@ -1,8 +1,20 @@
== SqlSessionStore
See http://railsexpress.de/blog/articles/2005/12/19/roll-your-own-sql-session-store
This version of SqlSessionStore properly supports both CGI-based sessions (Rails < 2.3)
and Rack-based sessions released in Rails 2.3. For the latest version of +SqlSessionStore+,
see:
Only Mysql, Postgres and Oracle are currently supported (others work,
http://github.com/nateware/sql_session_store/tree/master
To install, use:
script/plugin install git://github.com/nateware/sql_session_store.git
This version also includes the "native columns" feature, which enables +session[:xyz]+
to map directly to column +xyz+ in the sessions table transparently. For info,
scroll down to "Step 4".
Note: Only Mysql, PostgreSQL, and Oracle are currently supported (others work,
but you won't see much performance improvement).
== Step 1
@ -10,29 +22,33 @@ but you won't see much performance improvement).
If you have generated your sessions table using rake db:sessions:create, go to Step 2
If you're using an old version of sql_session_store, run
script/generate sql_session_store DB
script/generate sql_session_store [DB]
where DB is mysql, postgresql or oracle
Then run
rake migrate
or
rake db:migrate
for edge rails.
to create the sessions table.
== Step 2
Add the code below after the initializer config section:
Add the code below in +config/environment.rb+, inside the initializers section
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS.
update(:database_manager => SqlSessionStore)
# Use SqlSessionStore instead of the standard ActiveRecord store
config.action_controller.session_store = :sql_session_store
Finally, depending on your database type, add
Then, depending on your database type, add
SqlSessionStore.session_class = MysqlSession
or
SqlSessionStore.session_class = PostgresqlSession
or
SqlSessionStore.session_class = OracleSession
after the initializer section in environment.rb
@ -46,6 +62,32 @@ environment.rb:
SqlSession.establish_connection :sessions
== Step 4 (optional)
If you want to store certain pieces of data as actual columns in the
+sessions+ table transparently, simply update the sessions migration
to include the columns. For example, if you wanted to store +user_id+
and +language+ as columns, your migration might look something like:
create_table :sessions do |t|
t.string :session_id, :null => false, :references => nil, :unique => true
t.integer :user_id
t.string :language
t.text :data
t.timestamps
end
Then, use the "native columns" feature of the specific database driver:
OracleSession.native_columns = [:user_id, :language]
This will map these columns transparently for you. Simply access them like
normal columns:
session[:user_id] = @user.id
session[:language] = @language
And the appropriate columns in the sessions table will be updated for you.
== IMPORTANT NOTES

View file

@ -1 +0,0 @@
require 'sql_session_store'

View file

@ -0,0 +1,88 @@
#
# This is a common base class for database-specific session store implementations
#
require 'cgi/session'
require 'base64'
class AbstractSession
# 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
# Some attributes you may want to store natively in the database
# in actual columns. This allows other models and database queries
# to get to the data without having to unmarshal the data blob.
# One common example is the user_id of the session, so it can be
# related to the users table
cattr_accessor :native_columns
@@native_columns = []
# Allow the user to change the table name
cattr_accessor :table_name
@@table_name = 'sessions'
cattr_reader :timestamp_columns
@@timestamp_columns = [:created_at, :updated_at]
attr_accessor :id, :session_id, :data
def initialize(session_id, data, id=nil)
@session_id = session_id
@data = data
@id = id
end
class << self
# 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.exec("DELETE FROM sessions WHERE #{condition}")
else
session_connection.exec("DELETE FROM sessions")
end
end
# retrieve the session table connection and get the 'raw' driver connection from it
def session_connection
SqlSession.connection.raw_connection
end
def unmarshalize(data)
Marshal.load(Base64.decode64(data))
end
def marshalize(data)
Base64.encode64(Marshal.dump(data))
end
end
end
__END__
# This software is released under the MIT license
#
# Copyright (c) 2005, 2006, 2008 Stefan Kaes
# Copyright (c) 2008, 2009 Nate Wiger
#
# 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.

View file

@ -1,10 +1,5 @@
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.
@ -13,30 +8,8 @@ end
# '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 MysqlSession < AbstractSession
class << self
# retrieve the session table connection and get the 'raw' Mysql connection from it
def session_connection
SqlSession.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
@ -44,13 +17,12 @@ class MysqlSession
def find_session(session_id)
connection = session_connection
connection.query_with_result = true
session_id = Mysql::quote(session_id)
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 = new(session_id, AbstractSession.unmarshalize(row[1]))
my_session.id = row[0]
end
result.free
@ -59,12 +31,11 @@ class MysqlSession
# create a new session with given +session_id+ and +data+
# and save it immediately to the database
def create_session(session_id, data)
session_id = Mysql::quote(session_id)
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)}')")
connection.query("INSERT INTO sessions (`created_at`, `updated_at`, `session_id`, `data`) VALUES (NOW(), NOW(), '#{session_id}', '#{Mysql::quote(AbstractSession.marshalize(data))}')")
new_session.id = connection.insert_id
end
new_session
@ -90,11 +61,11 @@ class MysqlSession
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}")
connection.query("UPDATE sessions SET `updated_at`=NOW(), `data`='#{Mysql::quote(AbstractSession.marshalize(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)}')")
# 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(AbstractSession.marshalize(data))}')")
@id = connection.insert_id
end
end
@ -110,7 +81,7 @@ __END__
# This software is released under the MIT license
#
# Copyright (c) 2005-2008 Stefan Kaes
# 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

View file

@ -1,10 +1,5 @@
require 'oci8'
# allow access to the real Oracle connection
class ActiveRecord::ConnectionAdapters::OracleAdapter
attr_reader :connection
end
# OracleSession is a down to the bare metal session store
# implementation to be used with +SQLSessionStore+. It is much faster
# than the default ActiveRecord implementation.
@ -15,74 +10,81 @@ end
#
# This table layout is compatible with ActiveRecordStore.
class OracleSession
# if you need Rails components, and you have a pages which create
# new sessions, and embed components insides these pages that need
# session access, then you *must* set +eager_session_creation+ to
# true (as of Rails 1.0). Not needed for Rails 1.1 and up.
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 OracleSession < AbstractSession
class << self
# retrieve the session table connection and get the 'raw' Oracle connection from it
def session_connection
SqlSession.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)
new_session = nil
connection = session_connection
result = connection.exec("SELECT id, data FROM sessions WHERE session_id = :a and rownum=1", session_id)
# Make sure to save the @id if we find an existing session
while row = result.fetch
new_session = new(session_id,row[1].read)
new_session.id = row[0]
cursor = session_connection.exec(find_session_sql, session_id)
if row = cursor.fetch_hash
new_session = new(session_id, unmarshalize(row['DATA'].read), row['ID'])
# Pull out native columns
native_columns.each do |col|
new_session.data[col] = row[col.to_s.upcase]
new_session.data[col] = row[col.to_s.upcase]
end
end
result.close
cursor.close
new_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)
def create_session(session_id, data={})
new_session = new(session_id, data)
if @@eager_session_creation
connection = session_connection
connection.exec("INSERT INTO sessions (id, created_at, updated_at, session_id, data)"+
" VALUES (sessions_seq.nextval, SYSDATE, SYSDATE, :a, :b)",
session_id, data)
result = connection.exec("SELECT sessions_seq.currval FROM dual")
row = result.fetch
new_session.id = row[0].to_i
if eager_session_creation
new_session.id = next_id
cursor = session_connection.parse(insert_session_sql)
# Now bind all variables
cursor.bind_param(':id', new_session.id)
cursor.bind_param(':session_id', session_id)
native_columns.each do |col|
cursor.bind_param(":#{col}", data.delete(col) || '')
end
cursor.bind_param(':data', marshalize(data))
cursor.exec
cursor.close
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.exec("DELETE FROM sessions WHERE #{condition}")
else
session_connection.exec("DELETE FROM sessions")
end
# Internal methods for generating SQL
# Get the next ID from the sequence
def next_id
cursor = session_connection.exec("SELECT #{table_name}_seq.nextval FROM dual")
id = cursor.fetch.first.to_i
cursor.close
id
end
# Dynamically generate finder SQL so we can include our special columns
def find_session_sql
@find_session_sql ||=
"SELECT " + ([:id, :data] + native_columns).join(', ') +
" FROM #{table_name} WHERE session_id = :session_id AND rownum = 1"
end
def insert_session_sql
@insert_session_sql ||=
"INSERT INTO #{table_name} (" + ([:id, :data, :session_id] + native_columns + [:created_at, :updated_at]).join(', ') + ")" +
" VALUES (" + ([:id, :data, :session_id] + native_columns).collect{|col| ":#{col}" }.join(', ') +
" , SYSDATE, SYSDATE)"
end
def update_session_sql
@update_session_sql ||=
"UPDATE #{table_name} SET "+
([:data] + native_columns).collect{|col| "#{col} = :#{col}"}.join(', ') +
" , updated_at = SYSDATE WHERE ID = :id"
end
end # class methods
# update session with given +data+.
@ -90,26 +92,33 @@ class OracleSession
# column `updated_at` will be done by the database itself
def update_session(data)
connection = self.class.session_connection
cursor = nil
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.exec("UPDATE sessions SET updated_at = SYSDATE, data = :a WHERE id = :b",
data, @id)
cursor = connection.parse(self.class.update_session_sql)
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.exec("INSERT INTO sessions (id, created_at, updated_at, session_id, data)"+
" VALUES (sessions_seq.nextval, SYSDATE, SYSDATE, :a, :b)",
@session_id, data)
result = connection.exec("SELECT sessions_seq.currval FROM dual")
row = result.fetch
@id = row[0].to_i
@id = self.class.next_id
cursor = connection.parse(self.class.insert_session_sql)
cursor.bind_param(':session_id', @session_id)
end
# These are always the same, as @id is set above!
cursor.bind_param(':id', @id, Fixnum)
native_columns.each do |col|
cursor.bind_param(":#{col}", data.delete(col) || '')
end
cursor.bind_param(':data', self.class.marshalize(data))
cursor.exec
cursor.close
end
# destroy the current session
def destroy
self.class.delete_all("session_id='#{session_id}'")
self.class.delete_all(["session_id = ?", session_id])
end
end
@ -118,9 +127,9 @@ __END__
# This software is released under the MIT license
#
# Copyright (c) 2006-2008 Stefan Kaes
# Copyright (c) 2006-2008 Tiago Macedo
# Copyright (c) 2007-2008 Nate Wiger
# Copyright (c) 2006 Stefan Kaes
# Copyright (c) 2006 Tiago Macedo
# Copyright (c) 2007 Nate Wiger
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the

View file

@ -1,10 +1,5 @@
require 'pg'
# allow access to the real Posqtgresql connection
class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
attr_reader :connection
end
# PostgresqlSession is a down to the bare metal session store
# implementation to be used with +SQLSessionStore+. It is much faster
# than the default ActiveRecord implementation.
@ -15,30 +10,8 @@ end
#
# This table layout is compatible with ActiveRecordStore.
class PostgresqlSession
# if you need Rails components, and you have a pages which create
# new sessions, and embed components insides these pages that need
# session access, then you *must* set +eager_session_creation+ to
# true (as of Rails 1.0). Not needed for Rails 1.1 and up.
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 PostgresqlSession < AbstractSession
class << self
# retrieve the session table connection and get the 'raw' Postgresql connection from it
def session_connection
SqlSession.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
@ -47,7 +20,7 @@ class PostgresqlSession
connection = session_connection
result = connection.query("SELECT id, data FROM sessions WHERE session_id = $1 LIMIT 1", [session_id])
if result.ntuples > 0
my_session = new(session_id, result.getvalue(0, 1))
my_session = new(session_id, AbstractSession.unmarshalize(result.getvalue(0, 1)))
my_session.id = result.getvalue(0, 0)
else
my_session = nil
@ -58,11 +31,11 @@ class PostgresqlSession
# create a new session with given +session_id+ and +data+
# and save it immediately to the database
def create_session(session_id, data)
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(), $1, $2)", [session_id, data])
connection.query("INSERT INTO sessions (created_at, updated_at, session_id, data) VALUES (NOW(), NOW(), $1, $2)", [session_id, AbstractSession.marshalize(data)])
new_session.id = connection.lastval
end
new_session
@ -70,9 +43,9 @@ class PostgresqlSession
# delete all sessions meeting a given +condition+. it is the
# caller's responsibility to pass a valid sql condition
def delete_all(id=nil)
if id
session_connection.query("DELETE FROM sessions WHERE session_id = $1", [id])
def delete_all(condition=nil)
if condition
session_connection.query("DELETE FROM sessions WHERE #{condition}")
else
session_connection.query("DELETE FROM sessions")
end
@ -88,11 +61,11 @@ class PostgresqlSession
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 = $1 WHERE id = $2", [data, @id])
connection.query("UPDATE sessions SET updated_at = NOW(), data = $1 WHERE id = $2", [AbstractSession.marshalize(data), @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
result = connection.query("INSERT INTO sessions (created_at, updated_at, session_id, data) VALUES (NOW(), NOW(), $1, $2) RETURNING id", [@session_id, data])
result = connection.query("INSERT INTO sessions (created_at, updated_at, session_id, data) VALUES (NOW(), NOW(), $1, $2) RETURNING id", [@session_id, AbstractSession.marshalize(data)])
@id = result.getvalue(0, 0)
result.clear
end
@ -100,7 +73,7 @@ class PostgresqlSession
# destroy the current session
def destroy
self.class.delete_all(session_id)
self.class.delete_all("session_id='#{session_id}'")
end
end
@ -109,7 +82,7 @@ __END__
# This software is released under the MIT license
#
# Copyright (c) 2006-2008 Stefan Kaes
# Copyright (c) 2006 Stefan Kaes
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the

View file

@ -1,27 +1,27 @@
# An ActiveRecord class which corresponds to the database table
# +sessions+. Functions +find_session+, +create_session+,
# +update_session+ and +destroy+ constitute the interface to class
# +SqlSessionStore+.
class SqlSession < ActiveRecord::Base
# this class should not be reloaded
def self.reloadable?
false
end
# retrieve session data for a given +session_id+ from the database,
# return nil if no such session exists
def self.find_session(session_id)
find :first, :conditions => "session_id='#{session_id}'"
end
# create a new session with given +session_id+ and +data+
def self.create_session(session_id, data)
new(:session_id => session_id, :data => data)
end
# update session data and store it in the database
def update_session(data)
update_attribute('data', data)
end
end
# An ActiveRecord class which corresponds to the database table
# +sessions+. Functions +find_session+, +create_session+,
# +update_session+ and +destroy+ constitute the interface to class
# +SqlSessionStore+.
class SqlSession < ActiveRecord::Base
# this class should not be reloaded
def self.reloadable?
false
end
# retrieve session data for a given +session_id+ from the database,
# return nil if no such session exists
def self.find_session(session_id)
find :first, :conditions => { :session_id => session_id }
end
# create a new session with given +session_id+ and +data+
def self.create_session(session_id, data)
new(:session_id => session_id, :data => data)
end
# update session data and store it in the database
def update_session(data)
update_attribute('data', data)
end
end

View file

@ -1,47 +1,98 @@
require 'active_record'
require 'cgi'
require 'cgi/session'
begin
require 'base64'
rescue LoadError
end
require 'base64'
# +SqlSessionStore+ is a stripped down, optimized for speed version of
# class +ActiveRecordStore+.
class SqlSessionStore
# Hack for older versions of Rails
unless defined?(ActionController::Session::AbstractStore)
module ActionController
module Session
class AbstractStore
end
end
end
end
class SqlSessionStore < ActionController::Session::AbstractStore
# The class to be used for creating, retrieving and updating sessions.
# Defaults to SqlSessionStore::Session, which is derived from +ActiveRecord::Base+.
# Defaults to SqlSessionStore::SqlSession, which is derived from +ActiveRecord::Base+.
#
# In order to achieve acceptable performance you should implement
# your own session class, similar to the one provided for Myqsl.
#
# Only functions +find_session+, +create_session+,
# +update_session+ and +destroy+ are required. See file +mysql_session.rb+.
# +update_session+ and +destroy+ are required. The best implementations
# are +postgresql_session.rb+ and +oracle_session.rb+.
cattr_accessor :session_class
@@session_class = SqlSession
self.session_class = SqlSession
# Create a new SqlSessionStore instance.
#
# +session+ is the session for which this instance is being created.
#
# +option+ is currently ignored as no options are recognized.
# Rack-ism for Rails 2.3.0
SESSION_RECORD_KEY = 'rack.session.record'.freeze
def initialize(session, option=nil)
if @session = @@session_class.find_session(session.session_id)
@data = unmarshalize(@session.data)
# Backwards-compat indicators (booleans for speed)
cattr_accessor :use_rack_session, :use_cgi_session
self.use_rack_session = false
self.use_cgi_session = false
# For Rack compatibility (Rails 2.3.0+)
def get_session(env, sid)
sid ||= generate_sid
#puts "get_session(#{sid})"
session = find_or_create_session(sid)
env[SESSION_RECORD_KEY] = session
[sid, session.data]
end
# For Rack compatibility (Rails 2.3.0+)
def set_session(env, sid, session_data)
#puts "set_session(#{sid})"
session = env[SESSION_RECORD_KEY]
session.update_session(session_data)
return true # indicate ok to Rack
end
# Create a new SqlSessionStore instance. This method hooks into
# the find/create methods of a given driver class.
#
# +session_id+ is the session ID for which this instance is being created.
def find_or_create_session(session_id)
if @session = session_class.find_session(session_id)
@data = @session.data
else
@session = @@session_class.create_session(session.session_id, marshalize({}))
@session = session_class.create_session(session_id)
@data = {}
end
@session
end
# Below here is for pre-Rails 2.3.0 and not used in Rack-based servers
# The CGI::Session methods are a bit odd in that half are class and half
# are instance-based methods
# Note that +option+ is currently ignored as no options are recognized.
def initialize(session, options={})
# This is just some optimization since this is called over and over and over
if self.use_rack_session
super # MUST call super for Rack sessions
return true
elsif self.use_cgi_session
find_or_create_session(session.session_id)
else
version ||= Rails.version.split('.')
if version[0].to_i == 2 && version[1].to_i < 3
find_or_create_session(session.session_id)
self.use_cgi_session = true
else
super # MUST call super for Rack sessions
self.use_rack_session = true
end
end
end
# Update the database and disassociate the session object
def close
if @session
@session.update_session(marshalize(@data))
@session.update_session(@data)
@session = nil
end
end
@ -57,43 +108,33 @@ class SqlSessionStore
# Restore session data from the session object
def restore
if @session
@data = unmarshalize(@session.data)
@data = @session.data
end
end
# Save session data in the session object
def update
if @session
@session.update_session(marshalize(@data))
@session.update_session(@data)
end
end
private
if defined?(Base64)
def unmarshalize(data)
Marshal.load(Base64.decode64(data))
end
def marshalize(data)
Base64.encode64(Marshal.dump(data))
end
else
def unmarshalize(data)
Marshal.load(data.unpack("m").first)
end
def marshalize(data)
[Marshal.dump(data)].pack("m")
end
def id
@session.id
end
end
class CGI::Session
def id
@dbman.id
end
end
__END__
# This software is released under the MIT license
#
# Copyright (c) 2005-2008 Stefan Kaes
# Copyright (c) 2008, 2009 Nate Wiger
# 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

View file

@ -1,10 +1,5 @@
require 'sqlite3'
# allow access to the real Sqlite connection
#class ActiveRecord::ConnectionAdapters::SQLiteAdapter
# attr_reader :connection
#end
# SqliteSession is a down to the bare metal session store
# implementation to be used with +SQLSessionStore+. It is much faster
# than the default ActiveRecord implementation.
@ -13,37 +8,15 @@ require 'sqlite3'
# 'data', 'created_at' and 'updated_at'. If you want use other names,
# you will need to change the SQL statments in the code.
class SqliteSession
# 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 SqliteSession < AbstractSession
class << self
# retrieve the session table connection and get the 'raw' Sqlite connection from it
def session_connection
SqlSession.connection.instance_variable_get(:@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
session_id = SQLite3::Database.quote(session_id)
result = connection.execute("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
@ -59,7 +32,6 @@ class SqliteSession
# create a new session with given +session_id+ and +data+
# and save it immediately to the database
def create_session(session_id, data)
session_id = SQLite3::Database.quote(session_id)
new_session = new(session_id, data)
if @@eager_session_creation
connection = session_connection
@ -110,8 +82,8 @@ __END__
# This software is released under the MIT license
#
# Copyright (c) 2005-2008 Stefan Kaes
# Copyright (c) 2006-2008 Ted X Toth
# Copyright (c) 2005, 2006 Stefan Kaes
# Copyright (c) 2006 Ted X Toth
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the