我们知道Rails默认使用file来存储session数据,放在tmp\sessions目录下
其实我们还可以使用数据库、drb_server、mem_cache甚至内存来存储session数据
方法就是更改environment.rb:
config.action_controller.session_store = :active_record_store || :drb_store || :mem_cache_store || :memory_store
当然使用数据库存储session数据时要先创建数据库表
rake db:sessions:create
这在以前的Rails良药系列文章中也提到了
Rails的session管理的源代码文件为session_management.rb:
require 'action_controller/session/drb_store'
require 'action_controller/session/mem_cache_store'
if Object.const_defined?(:ActiveRecord)
require 'action_controller/session/active_record_store'
end
module ActionController
module SessionManagement
def self.included(base)
base.extend(ClassMethods)
base.send :alias_method_chain, :process, :session_management_support
base.send :alias_method_chain, :process_cleanup, :session_management_support
end
module ClassMethods
def session_store=(store)
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:database_manager] =
store.is_a?(Symbol) ? CGI::Session.const_get(store == :drb_store ? "DRbStore" : store.to_s.camelize) : store
end
def session_store
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:database_manager]
end
def session_options
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS
end
def session(*args)
options = Hash === args.last ? args.pop : {}
options[:disabled] = true if !args.empty?
options[: only] = [*options[: only]].map { |o| o.to_s } if options[: only]
options[:except] = [*options[:except]].map { |o| o.to_s } if options[:except]
if options[: only] && options[:except]
raise ArgumentError, "only one of either : only or :except are allowed"
end
write_inheritable_array("session_options", [options])
end
def session_options_for(request, action) #:nodoc:
if (session_options = cached_session_options).empty?
{}
else
options = {}
action = action.to_s
session_options.each do |opts|
next if opts[:if] && !opts[:if].call(request)
if opts[: only] && opts[: only].include?(action)
options.merge!(opts)
elsif opts[:except] && !opts[:except].include?(action)
options.merge!(opts)
elsif !opts[: only] && !opts[:except]
options.merge!(opts)
end
end
if options.empty? then options
else
options.delete : only
options.delete :except
options.delete :if
options[:disabled] ? false : options
end
end
end
end
def process_with_session_management_support(request, response, method = :perform_action, *arguments) #:nodoc:
set_session_options(request)
process_without_session_management_support(request, response, method, *arguments)
end
private
def set_session_options(request)
request.session_options = self.class.session_options_for(request, request.parameters["action"] || "index")
end
def process_cleanup_with_session_management_support
clear_persistent_model_associations
process_cleanup_without_session_management_support
end
def clear_persistent_model_associations #:doc:
if defined?(@_session) && @_session.respond_to?(:data)
session_data = @_session.data
if session_data && session_data.respond_to?(:each_value)
session_data.each_value do |obj|
obj.clear_association_cache if obj.respond_to?(:clear_association_cache)
end
end
end
end
end
end
alias_method_chain对process和process_cleanup方法加上session_management_support
我们在看action_controller/base.rb文件时知道process方法是action调用的入口,而process_cleanup是action调用结束时调用的
而@_session变量也是在base.rb里定义的:
@_response = response
@_response.session = request.session
@_session = @_response.session
我们可以在controller里调用session这个类方法来指定使用session的范围:
# turn off session management for all actions.
session : off
# turn off session management for all actions _except_ foo and bar.
session : off, :except => %w(foo bar)
# turn off session management for only the foo and bar actions.
session : off, : only => %w(foo bar)
# the session will only work over HTTPS, but only for the foo action
session : only => :foo, :session_secure => true
# the session will only be disabled for 'foo', and only if it is requested as a web service
session : off, : only => :foo,
:if => Proc.new { |req| req.parameters[:ws] }
看看active_record_store.rb中的一段代码:
class CGI
class Session
class ActiveRecordStore
class Session < ActiveRecord::Base
cattr_accessor :data_column_name
self.data_column_name = 'data'
class << self
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
end
def drop_table!
connection.execute "DROP TABLE #{table_name}"
end
end
end
cattr_accessor :session_class
self.session_class = Session
def initialize(session, option = nil)
session_id = session.session_id
unless @session = ActiveRecord::Base.silence { @@session_class.find_by_session_id(session_id) }
unless session.new_session
raise CGI::Session::NoSession, 'uninitialized session'
end
@session = @@session_class.new(:session_id => session_id, :data => {})
end
end
def update
if @session
ActiveRecord::Base.silence { @session.save }
end
end
def close
if @session
update
@session = nil
end
end
def delete
if @session
ActiveRecord::Base.silence { @session.destroy }
@session = nil
end
end
end
end
end
active_record_store是使用数据库保存session,里面定义了create_table!方法以及update/delete/close等与数据库@session相关的操作
而mem_cache_store的update/delete/close等方法的实现则是对@cache的操作,drb_store则是对DRbObject的操作,不同的方式有不同的实现