天天看點

Rails源碼研究之ActionController:四,session

我們知道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的操作,不同的方式有不同的實作

繼續閱讀