Class: Ramaze::Cache::Sequel

Inherits:
Object
  • Object
show all
Includes:
Cache::API, Innate::Traited
Defined in:
lib/ramaze/cache/sequel.rb

Overview

The Sequel cache is a cache system that uses the Sequel database toolkit to store the data in a DBMS supported by Sequel. Examples of these databases are MySQL, SQLite3 and so on. In order to use this cache you'd have to do the following:

Ramaze::Cache.options.view = Ramaze::Cache::Sequel.using(
  :connection => Sequel.mysql(
    :host     => 'localhost',
    :user     => 'user',
    :password => 'password',
    :database => 'blog'
  ),
  :table => :blog_sessions
)

If you already have an existing connection you can just pass the object to the :connection option instead of creating a new connection manually.

Massive thanks to Lars Olsson for patching the original Sequel cache so that it supports multiple connections and other useful features.

Examples:

Setting a custom database connection

Ramaze::Cache.options.names.push(:sequel)
Ramaze::Cache.options.sequel = Ramaze::Cache::Sequel.using(
  :connection => Sequel.connect(
    :adapter  => 'mysql2',
    :host     => 'localhost',
    :username => 'cache',
    :password => 'cache123',
    :database => 'ramaze_cache'
  )
)

Author:

Since:

Class Attribute Summary (collapse)

Instance Attribute Summary (collapse)

Class Method Summary (collapse)

Instance Method Summary (collapse)

Methods included from Innate::Traited

#ancestral_trait, #ancestral_trait_values, #class_trait, #each_ancestral_trait, included, #trait

Constructor Details

- (Sequel) initialize(options = {})

Creates a new instance of the cache class.

Parameters:

  • options (Hash) (defaults to: {})

    A hash with custom options, see Ramaze::Cache::Sequel.using for all available options.

Author:

  • Michael Fellinger

Since:

  • 04-05-2011



135
136
137
138
139
140
141
# File 'lib/ramaze/cache/sequel.rb', line 135

def initialize(options = {})
  self.class.options ||= Ramaze::Cache::Sequel.trait[:default].merge(
    options
  )

  @options = options.merge(self.class.options)
end

Class Attribute Details

+ (Object) options

Since:

  • 18-04-2011



66
67
68
# File 'lib/ramaze/cache/sequel.rb', line 66

def options
  @options
end

Instance Attribute Details

- (Object) options

Hash containing all the default options merged with the user specified ones

Since:

  • 18-04-2011



63
64
65
# File 'lib/ramaze/cache/sequel.rb', line 63

def options
  @options
end

Class Method Details

+ (Object) using(options = {})

This method returns a subclass of Ramaze::Cache::Sequel with the provided options set. This is necessary because Ramaze expects a class and not an instance of a class for its cache option.

You can provide any parameters you want, but those not used by the cache will not get stored. No parameters are mandatory. Any missing parameters will be replaced by default values.

Examples:

##
# This will create a mysql session cache in the blog
# database in the table blog_sessions
# Please note that the permissions on the database must
# be set up correctly before you can just create a new table
#
Ramaze.options.cache.session = Ramaze::Cache::Sequel.using(
  :connection => Sequel.mysql(
    :host     =>'localhost',
    :user     =>'user',
    :password =>'password',
    :database =>'blog'
  ),
  :table => :blog_sessions
)

Parameters:

  • options (Object) (defaults to: {})

    A hash containing the options to use

Options Hash (options):

  • :connection (Object)

    a Sequel database object (Sequel::Database) You can use any parameters that Sequel supports for this object. If this option is left unset, a Sqlite memory database will be used.

  • :table (String)

    The table name you want to use for the cache. Can be either a String or a Symbol. If this option is left unset, a table called ramaze_cache will be used.

  • :ttl (Fixnum)

    Setting this value will override Ramaze's default setting for when a particular cache item will be invalidated. By default this setting is not used and the cache uses the values provided by Ramaze, but if you want to use this setting it should be set to an integer representing the number of seconds before a cache item becomes invalidated.

  • :display_warnings (TrueClass)

    When this option is set to true, failure to serialize or de-serialize cache items will produce a warning in the Ramaze log. This option is set to false by default. Please note that certain objects (for instance Procs) cannot be serialized by ruby and therefore cannot be cached by this cache class. Setting this option to true is a good way to find out if the stuff you are trying to cache is affected by this. Failure to serialize/de-serialize a cache item will never raise an exception, the item will just remain uncached.

Returns:

  • (Object)

Author:

  • Lars Olsson

Since:

  • 18-04-2011



121
122
123
124
# File 'lib/ramaze/cache/sequel.rb', line 121

def using(options = {})
  merged = Ramaze::Cache::Sequel.trait[:default].merge(options)
  Class.new(self) { @options = merged }
end

Instance Method Details

- (Object) cache_clear

Remove all key/value pairs from the cache. Should behave as if #delete had been called with all +keys+ as argument.

Author:

  • Lars Olsson

Since:

  • 18-04-2011



180
181
182
# File 'lib/ramaze/cache/sequel.rb', line 180

def cache_clear
  @dataset.delete
end

- (Object/Array/nil) cache_delete(key, *keys)

Remove the corresponding key/value pair for each key passed. If removing is not an option it should set the corresponding value to nil.

If only one key was deleted, answer with the corresponding value. If multiple keys were deleted, answer with an Array containing the values.

Parameters:

  • key (Object)

    The key for the value to delete

  • keys (Object)

    Any other keys to delete as well

Returns:

  • (Object/Array/nil)

Author:

  • Lars Olsson

Since:

  • 18-04-2011



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/ramaze/cache/sequel.rb', line 197

def cache_delete(key, *keys)
  # Remove a single key
  if keys.empty?
    nkey   = namespaced(key)
    result = @dataset.select(:value).filter(:key => nkey).limit(1)

    # Ramaze expects nil values
    if result.empty?
      result = nil
    else
      result = deserialize(result.first[:value])
    end

    @dataset.filter(:key => nkey).delete
  # Remove multiple keys
  else
    nkeys  = [key, keys].flatten.map! { |n| namespaced(n) }
    result = dataset.select(:value).filter(:key => nkeys)

    result.map! do |row|
      deserialize(row[:value])
    end

    @dataset.filter(:key => nkeys).delete
  end

  return result
end

- (Object) cache_fetch(key, default = nil)

Answer with the value associated with the +key+, +nil+ if not found or expired.

Parameters:

  • key (Object)

    The key for which to fetch the value

  • default (Object) (defaults to: nil)

    Will return this if no value was found

Returns:

  • (Object)

Author:

  • Lars Olsson

Since:

  • 18-04-2011



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/ramaze/cache/sequel.rb', line 236

def cache_fetch(key, default = nil)
  nkey = namespaced(key)

  # Delete expired rows
  @dataset.select.filter(:key => nkey) do
    expires < Time.now
  end.delete

  # Get remaining row (if any)
  result = @dataset.select(:value).filter(:key => nkey).limit(1)

  if result.empty?
    return default
  else
    return deserialize(result.first[:value])
  end
end

- (Object) cache_setup(hostname, username, appname, cachename)

Executed after #initialize and before any other method.

Some parameters identifying the current process will be passed so caches that act in one global name-space can use them as a prefix.

Parameters:

  • hostname (String)

    the hostname of the machine

  • username (String)

    user executing the process

  • appname (String)

    identifier for the application

  • cachename (String)

    namespace, like 'session' or 'action'

Author:

  • Lars Olsson

Since:

  • 18-04-2011



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/ramaze/cache/sequel.rb', line 156

def cache_setup(hostname, username, appname, cachename)
  @namespace = [hostname, username, appname, cachename].compact.join(':')

  # Create the table if it's not there yet
  if !options[:connection].table_exists?(options[:table])
    options[:connection].create_table(
      options[:table]) do
      primary_key :id
      String :key  , :null => false, :unique => true
      String :value, :text => true
      Time :expires
    end
  end

  @dataset = options[:connection][options[:table].to_sym]
end

- (Object) cache_store(key, value, options = {})

Sets the given key to the given value.

Examples:

Cache.value.store(:num, 3, :ttl => 20)
Cache.value.fetch(:num)

Parameters:

  • key (Object)

    The value is stored with this key

  • value (Object)

    The key points to this value

  • options (Hash) (defaults to: {})

    for now, only :ttl => Fixnum is used.

Options Hash (options):

  • :ttl (Fixnum)

    The time in seconds after which the cache item should be expired.

Author:

  • Lars Olsson

Since:

  • 18-04-2011



269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/ramaze/cache/sequel.rb', line 269

def cache_store(key, value, options = {})
  nkey = namespaced(key)

  # Get the time after which the cache should be expired
  if options[:ttl]
    ttl = options[:ttl]
  else
    ttl = Ramaze::Cache::Sequel.options[:ttl]
  end

  expires = Time.now + ttl if ttl

  # The row already exists, update it.
  if @dataset.filter(:key => nkey).count == 1
    serialized_value = serialize(value)

    if serialized_value
      @dataset.filter(:key => nkey) \
        .update(:value => serialize(value), :expires => expires)
    end
  # The row doesn't exist yet.
  else
    serialized_value = serialize(value)

    if serialized_value
      @dataset.insert(
        :key => nkey, :value => serialize(value), :expires => expires
      )
    end
  end

  # Try to deserialize the value. If this fails we'll return a different
  # value
  deserialized = deserialize(@dataset.select(:value) \
    .filter(:key => nkey) \
    .limit(1).first[:value])

  if deserialized
    return deserialized
  else
    return value
  end
end

- (Object nil) deserialize(value)

Deserialize method, adapted from Sequels serialize plugin This method will try to deserialize a value using Marshal.load

Parameters:

  • value (Object)

    Value to be deserialized

Returns:

  • (Object nil)

Author:

  • Lars Olsson

Since:

  • 18-04-2011



334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/ramaze/cache/sequel.rb', line 334

def deserialize(value)
  begin
    ::Marshal.load(value.unpack('m')[0])
  rescue
    begin
      ::Marshal.load(value)
    rescue
      # Log the error?
      if options[:display_warnings] === true
        Ramaze::Log::warn("Failed to deserialize #{value.inspect}")
      end

      return nil
    end
  end
end

- (Object) namespaced(key)

Prefixes the given key with current namespace.

Parameters:

  • key (Object)

    Key without namespace.

Returns:

  • (Object)

Author:

  • Lars Olsson

Since:

  • 18-04-2011



321
322
323
# File 'lib/ramaze/cache/sequel.rb', line 321

def namespaced(key)
  return [@namespace, key].join(':')
end

- (Object nil) serialize(value)

Serialize method, adapted from Sequels serialize plugin This method will try to serialize a value using Marshal.dump

Parameters:

  • value (Object)

    Value to be serialized.

Returns:

  • (Object nil)

Author:

  • Lars Olsson

Since:

  • 18-04-2011



360
361
362
363
364
365
366
367
368
369
370
# File 'lib/ramaze/cache/sequel.rb', line 360

def serialize(value)
  begin
    [::Marshal.dump(value)].pack('m')
  rescue
    if options[:display_warnings] === true
      Ramaze::Log::warn("Failed to serialize #{value.inspect}")
    end

    return nil
  end
end