module Merb module SessionMixin def setup_session MERB_LOGGER.info("Setting up session") before = @_cookies[_session_id_key] @_session , @_cookies[_session_id_key] = Merb::MemorySession.persist(@_cookies[_session_id_key]) @_new_cookie = @_cookies[_session_id_key] != before end def finalize_session MERB_LOGGER.info("Finalize session") set_cookie(_session_id_key, @_session.session_id, _session_expiry) if (@_new_cookie || @_session.needs_new_cookie) end end class MemorySession attr_accessor :session_id attr_accessor :data attr_accessor :needs_new_cookie def initialize(session_id) @session_id = session_id @data = {} end class << self # Generates a new session ID and creates a row for the new session in the database. def generate sid = Merb::SessionMixin::rand_uuid MemorySessionContainer[sid] = new(sid) end # Gets the existing session based on the session_id available in cookies. # If none is found, generates a new session. def persist(session_id) if session_id session = session = MemorySessionContainer[session_id] end unless session session = generate end [session, session.session_id] end end # Regenerate the Session ID def regenerate new_sid = Merb::SessionMixin::rand_uuid old_sid = @session_id MemorySessionContainer[new_sid] = MemorySessionContainer[old_sid] @session_id = new_sid MemorySessionContainer.delete(old_sid) self.needs_new_cookie=true end # Recreates the cookie with the default expiration time # Useful during log in for pushing back the expiration date def refresh_expiration self.needs_new_cookie=true end # Lazy-delete of session data def delete @data = {} end def [](key) @data[key] end def []=(key, val) @data[key] = val end # Has the session been loaded yet? def loaded? !! @data end end class MemorySessionContainer class << self def setup(opts={}) @opts = opts @sessions = Hash.new @timestamps = Hash.new @mutex = Mutex.new @session_ttl = opts.fetch(:session_ttl, 60*60) # default 1 hour start_timer self end def create(opts={}) self[opts[:session_id]] = opts[:data] end def [](key) @mutex.synchronize { @timestamps[key] = Time.now @sessions[key] } end def []=(key, val) @mutex.synchronize { @timestamps[key] = Time.now @sessions[key] = val } end def delete(key) @mutex.synchronize { @sessions.delete(key) @timestamps.delete(key) } end def reap_old_sessions @timestamps.each do |key,stamp| if stamp + @session_ttl < Time.now delete(key) end end GC.start end def start_timer Thread.new do loop { sleep @session_ttl reap_old_sessions } end end def sessions @sessions end end # end singleton class end # end MemorySessionContainer end