# 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
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
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
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]
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]
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
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
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
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]
class SwfController < ApplicationController
- session :off
before_filter :check_api_readable
# to log:
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]
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
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'
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
require File.dirname(__FILE__) + '/../test_helper'
-class ChangesetTagTest < Test::Unit::TestCase
+class ChangesetTagTest < ActiveSupport::TestCase
api_fixtures
def test_changeset_tag_count
tag.id = 1
tag.k = key*i
tag.v = "v"
- assert_valid tag
+ assert tag.valid?
end
end
tag.id = 1
tag.k = "k"
tag.v = val*i
- assert_valid tag
+ assert tag.valid?
end
end
require File.dirname(__FILE__) + '/../test_helper'
-class ChangesetTest < Test::Unit::TestCase
+class ChangesetTest < ActiveSupport::TestCase
api_fixtures
def test_changeset_count
require File.dirname(__FILE__) + '/../test_helper'
-class DiaryCommentTest < Test::Unit::TestCase
+class DiaryCommentTest < ActiveSupport::TestCase
api_fixtures
fixtures :diary_comments
require File.dirname(__FILE__) + '/../test_helper'
-class DiaryEntryTest < Test::Unit::TestCase
+class DiaryEntryTest < ActiveSupport::TestCase
api_fixtures
fixtures :diary_entries, :languages
require File.dirname(__FILE__) + '/../test_helper'
-class FriendTest < Test::Unit::TestCase
+class FriendTest < ActiveSupport::TestCase
api_fixtures
fixtures :friends
require File.dirname(__FILE__) + '/../test_helper'
-class MessageTest < Test::Unit::TestCase
+class MessageTest < ActiveSupport::TestCase
api_fixtures
fixtures :messages
require File.dirname(__FILE__) + '/../test_helper'
-class NodeTagTest < Test::Unit::TestCase
+class NodeTagTest < ActiveSupport::TestCase
api_fixtures
def test_tag_count
tag.id = current_node_tags(:t1).id
tag.k = key*i
tag.v = "v"
- assert_valid tag
+ assert tag.valid?
end
end
tag.id = current_node_tags(:t1).id
tag.k = "k"
tag.v = val*i
- assert_valid tag
+ assert tag.valid?
end
end
require File.dirname(__FILE__) + '/../test_helper'
-class NodeTest < Test::Unit::TestCase
+class NodeTest < ActiveSupport::TestCase
api_fixtures
def test_node_count
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,
require File.dirname(__FILE__) + '/../test_helper'
-class OldNodeTest < Test::Unit::TestCase
+class OldNodeTest < ActiveSupport::TestCase
api_fixtures
def test_old_node_tag_count
tag.version = node_tags(:t1).version
tag.k = key*i
tag.v = "v"
- assert_valid tag
+ assert tag.valid?
end
end
tag.version = node_tags(:t1).version
tag.k = "k"
tag.v = val*i
- assert_valid tag
+ assert tag.valid?
end
end
require File.dirname(__FILE__) + '/../test_helper'
-class OldNodeTest < Test::Unit::TestCase
+class OldNodeTest < ActiveSupport::TestCase
api_fixtures
def test_old_node_count
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,
require File.dirname(__FILE__) + '/../test_helper'
-class OldRelationTagTest < Test::Unit::TestCase
+class OldRelationTagTest < ActiveSupport::TestCase
api_fixtures
def test_tag_count
tag.version = 1
tag.k = key*i
tag.v = "v"
- assert_valid tag
+ assert tag.valid?
end
end
tag.version = 1
tag.k = "k"
tag.v = val*i
- assert_valid tag
+ assert tag.valid?
end
end
require File.dirname(__FILE__) + '/../test_helper'
-class WayTagTest < Test::Unit::TestCase
+class WayTagTest < ActiveSupport::TestCase
api_fixtures
def test_tag_count
tag.version = 1
tag.k = key*i
tag.v = "v"
- assert_valid tag
+ assert tag.valid?
end
end
tag.version = 1
tag.k = "k"
tag.v = val*i
- assert_valid tag
+ assert tag.valid?
end
end
require File.dirname(__FILE__) + '/../test_helper'
-class RelationMemberTest < Test::Unit::TestCase
+class RelationMemberTest < ActiveSupport::TestCase
api_fixtures
def test_relation_member_count
require File.dirname(__FILE__) + '/../test_helper'
-class RelationTagTest < Test::Unit::TestCase
+class RelationTagTest < ActiveSupport::TestCase
api_fixtures
def test_relation_tag_count
tag.id = 1
tag.k = key*i
tag.v = "v"
- assert_valid tag
+ assert tag.valid?
end
end
tag.id = 1
tag.k = "k"
tag.v = val*i
- assert_valid tag
+ assert tag.valid?
end
end
require File.dirname(__FILE__) + '/../test_helper'
-class RelationTest < Test::Unit::TestCase
+class RelationTest < ActiveSupport::TestCase
api_fixtures
def test_relation_count
require File.dirname(__FILE__) + '/../test_helper'
-class TraceTest < Test::Unit::TestCase
+class TraceTest < ActiveSupport::TestCase
api_fixtures
def test_trace_count
require File.dirname(__FILE__) + '/../test_helper'
-class TracepointTest < Test::Unit::TestCase
+class TracepointTest < ActiveSupport::TestCase
api_fixtures
def test_tracepoint_count
require File.dirname(__FILE__) + '/../test_helper'
-class TracetagTest < Test::Unit::TestCase
+class TracetagTest < ActiveSupport::TestCase
api_fixtures
def test_tracetag_count
require File.dirname(__FILE__) + '/../test_helper'
-class UserTest < Test::Unit::TestCase
+class UserTest < ActiveSupport::TestCase
api_fixtures
fixtures :friends
require File.dirname(__FILE__) + '/../test_helper'
-class UserTokenTest < Test::Unit::TestCase
+class UserTokenTest < ActiveSupport::TestCase
api_fixtures
fixtures :user_tokens
require File.dirname(__FILE__) + '/../test_helper'
-class WayNodeTest < Test::Unit::TestCase
+class WayNodeTest < ActiveSupport::TestCase
api_fixtures
def test_way_nodes_count
require File.dirname(__FILE__) + '/../test_helper'
-class WayTagTest < Test::Unit::TestCase
+class WayTagTest < ActiveSupport::TestCase
api_fixtures
def test_way_tag_count
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
tag.id = current_way_tags(:t1).id
tag.k = "k"
tag.v = val*i
- assert_valid tag
+ assert tag.valid?
end
end
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
-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
== 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
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
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
-require 'sql_session_store'\r
--- /dev/null
+#
+# 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.
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.
# '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
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
# 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
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
# 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
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.
#
# 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+.
# 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
# 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
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.
#
# 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
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
# 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
# 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
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
# destroy the current session
def destroy
- self.class.delete_all(session_id)
+ self.class.delete_all("session_id='#{session_id}'")
end
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
-# An ActiveRecord class which corresponds to the database table\r
-# +sessions+. Functions +find_session+, +create_session+,\r
-# +update_session+ and +destroy+ constitute the interface to class\r
-# +SqlSessionStore+.\r
-\r
-class SqlSession < ActiveRecord::Base\r
- # this class should not be reloaded\r
- def self.reloadable?\r
- false\r
- end\r
-\r
- # retrieve session data for a given +session_id+ from the database,\r
- # return nil if no such session exists\r
- def self.find_session(session_id)\r
- find :first, :conditions => "session_id='#{session_id}'"\r
- end\r
-\r
- # create a new session with given +session_id+ and +data+\r
- def self.create_session(session_id, data)\r
- new(:session_id => session_id, :data => data)\r
- end\r
-\r
- # update session data and store it in the database\r
- def update_session(data)\r
- update_attribute('data', data)\r
- end\r
-end\r
+# 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
-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
+
+ # Rack-ism for Rails 2.3.0
+ SESSION_RECORD_KEY = 'rack.session.record'.freeze
+
+ # 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
- # 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.
+ # 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
- def initialize(session, option=nil)
- if @session = @@session_class.find_session(session.session_id)
- @data = unmarshalize(@session.data)
+ # 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
# 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
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.
# '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
# 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
# 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