require 'active_support/cache'
require 'action_dispatch/middleware/session/abstract_store'
require 'dalli'

# Dalli-based session store for Rails 3.0.
module ActionDispatch
  module Session
    class DalliStore < AbstractStore
      def initialize(app, options = {})
        # Support old :expires option
        options[:expire_after] ||= options[:expires]

        super

        @default_options = { :namespace => 'rack:session' }.merge(@default_options)

        @pool = options[:cache] || begin
          Dalli::Client.new(
              @default_options[:memcache_server], @default_options)
        end
        @namespace = @default_options[:namespace]

        @raise_errors = !!@default_options[:raise_errors]

        super
      end

      def reset
        @pool.reset
      end

      private

      def get_session(env, sid)
        sid = generate_sid unless sid and !sid.empty?
        begin
          session = @pool.get(sid) || {}
        rescue Dalli::DalliError => ex
          # re-raise ArgumentError so Rails' session abstract_store.rb can autoload any missing models
          raise ArgumentError, ex.message if ex.message =~ /unmarshal/
          Rails.logger.warn("Session::DalliStore#get: #{ex.message}")
          session = {}
        end
        [sid, session]
      end

      def set_session(env, sid, session_data, options = nil)
        options ||= env[ENV_SESSION_OPTIONS_KEY]
        expiry  = options[:expire_after]
        @pool.set(sid, session_data, expiry)
        sid
      rescue Dalli::DalliError
        Rails.logger.warn("Session::DalliStore#set: #{$!.message}")
        raise if @raise_errors
        false
      end

      def destroy_session(env, session_id, options)
        begin
          @pool.delete(session_id)
        rescue Dalli::DalliError
          Rails.logger.warn("Session::DalliStore#destroy_session: #{$!.message}")
          raise if @raise_errors
        end
        return nil if options[:drop]
        generate_sid
      end

      def destroy(env)
        if sid = current_session_id(env)
          @pool.delete(sid)
        end
      rescue Dalli::DalliError
        Rails.logger.warn("Session::DalliStore#destroy: #{$!.message}")
        raise if @raise_errors
        false
      end

    end
  end
end
