Rails源码研究之ActionController:四,session

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

你可能感兴趣的:(cache,SQL Server,cgi,Rails,ActiveRecord)