require 'CGI' require 'final' class CGI class Session attr_reader :session_id def Session::callback(dbman) lambda{ dbman.close } end def create_new_id require 'md5' md5 = MD5::new md5.update(String(Time::now)) md5.update(String(rand(0))) md5.update(String($$)) md5.update('foobar') @session_id = md5.hexdigest[0,16] end private :create_new_id def initialize(request, option={}) session_key = option['session_key'] || '_session_id' id, = option['session_id'] unless id if option['new_session'] id = create_new_id end end unless id id, = request[session_key] unless id id, = request.cookies[session_key] end unless id if option.key?('new_session') and not option['new_session'] raise ArgumentError, "session_key `%s' should be supplied"%session_key end id = create_new_id end end @session_id = id dbman = option['database_manager'] || FileStore @dbman = dbman::new(self, option) request.instance_eval do @output_hidden = {session_key => id} @output_cookies = [Cookie::new(session_key,id)] end ObjectSpace::define_finalizer(self, Session::callback(@dbman)) end def [](key) unless @data @data = @dbman.restore end @data[key] end def []=(key, val) unless @write_lock @write_lock = true end unless @data @data = @dbman.restore end @data[key] = String(val) end def update @dbman.update end def delete @dbman.delete end class FileStore def initialize(session, option={}) dir = option['tmpdir'] || ENV['TMP'] || '/tmp' prefix = option['prefix'] || '' path = dir+"/"+prefix+session.session_id path.untaint unless File::exist? path @hash = {} end begin @f = open(path, "r+") rescue Errno::ENOENT @f = open(path, "w+") end end def restore unless @hash @hash = {} @f.flock File::LOCK_EX @f.rewind for line in @f line.chomp! k, v = line.split('=',2) @hash[CGI::unescape(k)] = CGI::unescape(v) end end @hash end def update @f.rewind for k,v in @hash @f.printf "%s=%s\n", CGI::escape(k), CGI::escape(v) end @f.truncate @f.tell end def close update @f.close end def delete path = @f.path @f.close File::unlink path end end class MemoryStore GLOBAL_HASH_TABLE = {} def initialize(session, option={}) @session_id = session.session_id GLOBAL_HASH_TABLE[@session_id] = {} end def restore GLOBAL_HASH_TABLE[@session_id] end def update # don't need to update; hash is shared end def close # don't need to close end def delete GLOBAL_HASH_TABLE[@session_id] = nil end end end end