Class: Arrow::Session::Store

Inherits:
Arrow::Object show all
Extends:
Forwardable
Includes:
PluginFactory
Defined in:
lib/arrow/session/store.rb

Overview

The Arrow::Session::Store class, a derivative of Arrow::Object. Instances of concrete deriviatives of this class provide serialization and semi-permanent storage of session data for Arrow::Session objects.

Derivative Interface ===

In order to create your own session store classes, you need to provide four methods: #insert, #update, #retrieve, #remove. All but one of the methods provides serialization and marking records as dirty in the base class, so unless you want to manage these tasks yourself, you should super() to the parent’s implementation with a block. Examples are provided for each method.

#insert

Insert a new session into the backing store. Example:

  def insert
    super {|data| @io.print(data) }
  end
#update

Update an existing session’s data in the backing store. Example:

  def update
    super {|data| @io.rewind; @io.truncate(0); @io.print(data) }
  end
#retrieve

Retrieve the serialized session data from the backing store. Example:

  def retrieve
    super { @io.rewind; @io.read }
  end
#delete

Delete the session from the backing store. Example:

  def delete
    super {|data| @io.close; File.delete(@session_file) }
  end

Optional Derivative Interface ===

Serialization ====

If you want to use something other than Marshal for object serialization, you can override the protected methods #serialized_data and #serialized_data= to provide your own serialization.

#serialized_data

Serialize the data in the instance variable @data and return it.

#serialized_data=( serialized )

Deserialize the given serialized data and assign it to @data.

Example (serializing to YAML instead of binary):

    require 'yaml'

    def serialized_data
      @data.to_yaml
    end

    def serialized_data=( data )
      @data = YAML.load( data )
    end

Lock Recommendation ====

If arrow is configured to use the ‘recommended’ session lock, your session store can recommend one it knows will work (e.g., if your session store is a database, you can recommend a lock that uses database locking). The simple way to do that is to define a RecommendedLocker constant in your class which contains the URI of the locker you wish to use. If you need more control than the URI can provide, you can also override the #create_recommended_lock method, which should return an instance of the locker that should be used.

The method will be given the instantiated Arrow::Session::Id object that identifies the session so that you can derive a filename, primary key, etc.

Example:

  def create_recommended_lock( idobj )
    return DBITransactionLock.new( idobj.to_s )
  end

Authors

  • Michael Granger

Please see the file LICENSE in the top-level directory for licensing details.

Direct Known Subclasses

DbStore, FileStore

Constant Summary

RecommendedLocker =

The URI of the lock class recommended for use with this Store.

URI.parse( 'file:.' )
DelegatedMethods =

The methods which are delegate directly to the data hash.

[
  :[], :default, :default=, :each, :each_key, :each_pair, :each_value,
  :empty?, :fetch, :has_key?, :has_value?, :include?, :index, :invert,
  :keys, :length, :member?, :merge, :rehash, :reject, :select, :size,
  :sort, :to_a, :value?, :values
]

Instance Attribute Summary

Class Method Summary

Instance Method Summary

Methods inherited from Arrow::Object

deprecate_class_method, deprecate_method, inherited

Methods included from Arrow::Loggable

#log

Constructor Details

- (Store) initialize(uri, idobj)

Create a new Arrow::Session::Store object.



135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/arrow/session/store.rb', line 135

def initialize( uri, idobj )
  @data   = {}
  @id     = idobj
  @new    = true
  @modified = false

  unless idobj.new?
    self.retrieve
  end

  super()
end

Instance Attribute Details

- (Object) data (readonly)

The raw session data hash



158
159
160
# File 'lib/arrow/session/store.rb', line 158

def data
  @data
end

Class Method Details

+ (Object) create(uri, idobj)

Overridden factory method: handle a URI object or a name



123
124
125
126
# File 'lib/arrow/session/store.rb', line 123

def self::create( uri, idobj )
  uri = Arrow::Session.parse_uri( uri ) if uri.is_a?( String )
  super( uri.scheme.dup, uri, idobj )
end

+ (Object) derivativeDirs

Returns the Array of directories to search for derivatives; part of the PluginFactory interface.



117
118
119
# File 'lib/arrow/session/store.rb', line 117

def self::derivativeDirs
  [ 'arrow/session', 'arrow/session/store' ]
end

Instance Method Details

- (Object) []=(key, value) Also known as: store

Set the value for the specified key.



162
163
164
165
# File 'lib/arrow/session/store.rb', line 162

def []=( key, value )
  @data[ key ] = value
  @modified = true
end

- (Object) clear

Clear all key/value pairs from the store for this session.



183
184
185
186
# File 'lib/arrow/session/store.rb', line 183

def clear
  @data.clear
  @modified = true
end

Returns an instance of the recommended lock object for the receiving store. If no recommended locking strategy is known, this method raises a SessionError.



294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
# File 'lib/arrow/session/store.rb', line 294

def create_recommended_lock( idobj )
  self.log.debug "Searching for recommended lock for %s" %
    self.class.name

  # Traverse the class hierarchy to find a class which defines a
  # RecommendedLocker constant
  adviceClass = self.class.ancestors.find {|klass|
    klass.const_defined?( :RecommendedLocker )
  } or raise SessionError, "No recommended locker for %p" %
    self.class.ancestors

  uri = adviceClass.const_get( :RecommendedLocker ) or
    raise SessionError, "Could not fetch RecommendedLocker constant"

  self.log.debug "Creating recommended lock %s" % uri
  uri = Arrow::Session.parse_uri( uri ) if
    uri.is_a?( String )

  lock = Arrow::Session::Lock.create( uri, idobj )
  self.log.debug "Created recommended lock object: %p" % lock

  return lock
end

- (Object) insert {|self.serialized_data| ... }

Insert the current data hash into whatever permanent storage the Store object is acting as an interface to. Concrete implementations should provide an overriding implementation of this method that calls #super with a block which will be called with the serialized data that should be stored.

Yields:

  • (self.serialized_data)


253
254
255
256
257
# File 'lib/arrow/session/store.rb', line 253

def insert
  self.log.debug "Inserting session data for key %s" % @id
  yield( self.serialized_data )
  @new = @modified = false
end

- (Object) merge!(other, &block) Also known as: update

Adds the contents of the other hash to the session data, overwriting entries in the session data with values from the other hash where there are duplicates. If a block is given, it is called for each duplicate key, and the return value is the value set in the hash.



194
195
196
197
198
# File 'lib/arrow/session/store.rb', line 194

def merge!( other, &block ) # :yields: key, sessionValue, otherValue
  @data.merge!( other, &block )
ensure
  @modified = true
end

- (Boolean) modified?

Returns true if the receiver’s data is out of sync with the data in the backing store.

Returns:

  • (Boolean)


224
225
226
# File 'lib/arrow/session/store.rb', line 224

def modified?
  @modified
end

- (Boolean) new?

Returns true if the data in the receiver has not yet been saved to the backing store, or if the entry in the backing store has been deleted since it was last saved.

Returns:

  • (Boolean)


232
233
234
# File 'lib/arrow/session/store.rb', line 232

def new?
  @new
end

- (Object) reject!(&block) Also known as: delete_if

Deletes every key-value pair from the session data for which the block evaluates to true.



213
214
215
216
217
218
# File 'lib/arrow/session/store.rb', line 213

def reject!( &block ) # :yields: key, value
  rval = @data.reject!( &block )
  return rval
ensure
  @modified = true if rval
end

- (Object) remove

Permanently remove the data hash associated with the id used in the receiver’s creation from permanent storage.



285
286
287
288
# File 'lib/arrow/session/store.rb', line 285

def remove
  self.log.debug "Removing session data for key %s" % @id
  @new = true
end

- (Object) replace(other)

Replace the contents of the session hash with those of the given other hash.



204
205
206
207
208
# File 'lib/arrow/session/store.rb', line 204

def replace( other )
  @data.replace( other )
ensure
  @modified = true
end

- (Object) retrieve

Retrieve the data hash stored in permanent storage associated with the id the object was created with. Concrete implementations should provide an overriding implementation of this method that calls #super with a block that returns the serialized data to be restored.



276
277
278
279
280
# File 'lib/arrow/session/store.rb', line 276

def retrieve
  self.log.debug "Retrieving session data for key %s" % @id
  self.serialized_data = yield
  @new = @modified = false
end

- (Object) save

Save the session data to the backing store



238
239
240
241
242
243
244
245
# File 'lib/arrow/session/store.rb', line 238

def save
  return false unless self.modified? || self.new?
  if self.new?
    self.insert
  else
    self.update
  end
end

- (Object) serialized_data (protected)

Returns the data in the session store as a serialized object.



324
325
326
327
# File 'lib/arrow/session/store.rb', line 324

def serialized_data
  data = strip_hash( @data )      
  return Marshal.dump( data )
end

- (Object) serialized_data=(string) (protected)

Sets the session’s data by deserializing the object contained in the given string.



332
333
334
335
336
337
338
# File 'lib/arrow/session/store.rb', line 332

def serialized_data=( string )
  if string.empty?
    self.log.error "No session data: retaining default hash"
  else
    @data = Marshal.restore( string )
  end
end