我們知道Rails預設使用file來存儲session資料,放在tmp\sessions目錄下
其實我們還可以使用資料庫、drb_server、mem_cache甚至記憶體來存儲session資料
方法就是更改environment.rb:
[code]
config.action_controller.session_store = :active_record_store || :drb_store || :mem_cache_store || :memory_store
[/code]
當然使用資料庫存儲session資料時要先建立資料庫表
[code]
rake db:sessions:create
[/code]
這在以前的Rails良藥系列文章中也提到了
Rails的session管理的源代碼檔案為session_management.rb:
[code]
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
[/code]
alias_method_chain對process和process_cleanup方法加上session_management_support
我們在看action_controller/base.rb檔案時知道process方法是action調用的入口,而process_cleanup是action調用結束時調用的
而@_session變量也是在base.rb裡定義的:
[code]
@_response = response
@_response.session = request.session
@_session = @_response.session
[/code]
我們可以在controller裡調用session這個類方法來指定使用session的範圍:
[code]
# 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] }
[/code]
看看active_record_store.rb中的一段代碼:
[code]
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
[/code]
active_record_store是使用資料庫儲存session,裡面定義了create_table!方法以及update/delete/close等與資料庫@session相關的操作
而mem_cache_store的update/delete/close等方法的實作則是對@cache的操作,drb_store則是對DRbObject的操作,不同的方式有不同的實作