Class: CGI::Session::ActiveRecordStore::SqlBypass

Inherits:
Object
  • Object
show all
Defined in:
lib/action_controller/session/active_record_store.rb

Overview

A barebones session store which duck-types with the default session store but bypasses Active Record and issues SQL directly.

The database connection, table name, and session id and data columns are configurable class attributes. Marshaling and unmarshaling are implemented as class methods that you may override. By default, marshaling data is Base64.encode64(Marshal.dump(data)) and unmarshaling data is Marshal.load(Base64.decode64(data)).

This marshaling behavior is intended to store the widest range of binary session data in a text column. For higher performance, store in a blob column instead and forgo the Base64 encoding.

Constant Summary collapse

@@table_name =
'sessions'
@@session_id_column =
'session_id'
@@data_column =
'data'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attributes) ⇒ SqlBypass

Look for normal and marshaled data, self.find_by_session_id’s way of telling us to postpone unmarshaling until the data is requested. We need to handle a normal data attribute in case of a new record.



177
178
179
180
# File 'lib/action_controller/session/active_record_store.rb', line 177

def initialize(attributes)
  @session_id, @data, @marshaled_data = attributes[:session_id], attributes[:data], attributes[:marshaled_data]
  @new_record = @marshaled_data.nil?
end

Instance Attribute Details

#dataObject

Lazy-unmarshal session state. Take a fingerprint so we can detect whether to save changes later.



188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/action_controller/session/active_record_store.rb', line 188

def data
  unless @data
    if @marshaled_data
      @fingerprint = self.class.fingerprint(@marshaled_data)
      @data, @marshaled_data = self.class.unmarshal(@marshaled_data), nil
    else
      @data = {}
      @fingerprint = nil
    end
  end
  @data
end

#session_idObject (readonly)

Returns the value of attribute session_id.



171
172
173
# File 'lib/action_controller/session/active_record_store.rb', line 171

def session_id
  @session_id
end

Class Method Details

.connectionObject



128
129
130
# File 'lib/action_controller/session/active_record_store.rb', line 128

def self.connection
  @@connection ||= ActiveRecord::Base.connection
end

.create_table!Object



156
157
158
159
160
161
162
163
164
# File 'lib/action_controller/session/active_record_store.rb', line 156

def create_table!
  @@connection.execute <<-end_sql
    CREATE TABLE #{table_name} (
      id INTEGER PRIMARY KEY,
      #{@@connection.quote_column_name(session_id_column)} TEXT UNIQUE,
      #{@@connection.quote_column_name(data_column)} TEXT
    )
  end_sql
end

.drop_table!Object



166
167
168
# File 'lib/action_controller/session/active_record_store.rb', line 166

def drop_table!
  @@connection.execute "DROP TABLE #{table_name}"
end

.find_by_session_id(session_id) ⇒ Object

Look up a session by id and unmarshal its data if found.



146
147
148
149
150
# File 'lib/action_controller/session/active_record_store.rb', line 146

def find_by_session_id(session_id)
  if record = @@connection.select_one("SELECT * FROM #{@@table_name} WHERE #{@@session_id_column}=#{@@connection.quote(session_id)}")
    new(:session_id => session_id, :marshaled_data => record['data'])
  end
end

.fingerprint(data) ⇒ Object



154
# File 'lib/action_controller/session/active_record_store.rb', line 154

def fingerprint(data) Digest::MD5.hexdigest(data)         end

.marshal(data) ⇒ Object



152
# File 'lib/action_controller/session/active_record_store.rb', line 152

def marshal(data)     Base64.encode64(Marshal.dump(data)) end

.unmarshal(data) ⇒ Object



153
# File 'lib/action_controller/session/active_record_store.rb', line 153

def unmarshal(data)   Marshal.load(Base64.decode64(data)) end

Instance Method Details

#destroyObject



225
226
227
228
229
230
231
232
# File 'lib/action_controller/session/active_record_store.rb', line 225

def destroy
  unless @new_record
    @@connection.delete <<-end_sql, 'Destroy session'
      DELETE FROM #{@@table_name}
      WHERE #{@@connection.quote_column_name(@@session_id_column)}=#{@@connection.quote(session_id)}
    end_sql
  end
end

#new_record?Boolean

Returns:

  • (Boolean)


182
183
184
# File 'lib/action_controller/session/active_record_store.rb', line 182

def new_record?
  @new_record
end

#saveObject



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/action_controller/session/active_record_store.rb', line 201

def save
  marshaled_data = self.class.marshal(data)
  if @new_record
    @new_record = false
    @@connection.update <<-end_sql, 'Create session'
      INSERT INTO #{@@table_name} (
        #{@@connection.quote_column_name(@@session_id_column)},
        #{@@connection.quote_column_name(@@data_column)} )
      VALUES (
        #{@@connection.quote(session_id)},
        #{@@connection.quote(marshaled_data)} )
    end_sql
  else
    old_fingerprint, @fingerprint = @fingerprint, self.class.fingerprint(marshaled_data)
    if old_fingerprint != @fingerprint
      @@connection.update <<-end_sql, 'Update session'
        UPDATE #{@@table_name}
        SET #{@@connection.quote_column_name(@@data_column)}=#{@@connection.quote(marshaled_data)}
        WHERE #{@@connection.quote_column_name(@@session_id_column)}=#{@@connection.quote(session_id)}
      end_sql
    end
  end
end