Class: SQLite3::Cache

Inherits:
Object
  • Object
show all
Defined in:
lib/sqlite3_cache.rb

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ Cache

see method for options description… options are…

:file       - sqlite3 file to use as persistence
:namespace  - specify cache namespace (must be a string)
:expiration - number of seconds until keys expire
:timestamp_on_read - note the last time we touched a record...

Parameters:

  • options (Hash)

    hash of options



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/sqlite3_cache.rb', line 14

def initialize(options)
  @OPTIONS = options
  raise "Must supply :file!" unless @OPTIONS[:file]
  raise "Must supply :namespace!" unless @OPTIONS[:namespace]
  raise "Must supply :expiration" unless @OPTIONS[:expiration]
  @LOGGER = Logger.new(STDOUT)
  if @OPTIONS[:debug]
    @LOGGER.level = Logger::DEBUG
  else
    @LOGGER.level = Logger::WARN
  end

  unless @OPTIONS[:timestamp_on_read] == nil
    @OPTIONS[:timestamp_on_read] = false
  end

  begin
    @MUTEX = Mutex.new
    @SQLITE3 = SQLite3::Database.new(@OPTIONS[:file])
    @SQLITE3.busy_timeout = 60000
  rescue => e
    raise "Unable to open #{@OPTIONS[:file]}: #{e.message}"
  end
end

Instance Method Details

#[](key) ⇒ Object

retrieves a value from the cache

Parameters:

  • key (String)

    key to retrieve

Returns:

  • value of the key or nil



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/sqlite3_cache.rb', line 101

def [](key)
  @LOGGER.debug {"Retreiving key: #{key} for namespace: #{@OPTIONS[:namespace]}"}
  value = nil
  @MUTEX.synchronize do
    @SQLITE3.transaction do
      unless table_exists?(@OPTIONS[:namespace])
        return nil
      end
      @SQLITE3.results_as_hash = true
      value = nil
      @SQLITE3.execute("SELECT * from #{@OPTIONS[:namespace]} where key = ?", key) do |row|
        value = Marshal.load(row["value"])
        if value
          @LOGGER.debug {"Cache hit!"}
        else
          @LOGGER.debug {"Cache miss!"}
        end
      end
    end
    if @OPTIONS[:timestamp_on_read]
      # now update access time and count...
      @SQLITE3.execute("UPDATE #{@OPTIONS[:namespace]} SET accessed = ? where key = ?", Time.now.to_s, key)
      # retrieve count...
      access_count = nil
      @SQLITE3.execute("SELECT ACCESS_COUNT from #{@OPTIONS[:namespace]} where key = ?", key) do |row|
        access_count = row[0]
      end
      if access_count
        @SQLITE3.execute("UPDATE #{@OPTIONS[:namespace]} set access_count = ? where key = ?", access_count + 1, key)
      end
    end
  end
  return value
end

#[]=(key, value) ⇒ Object

sets a value in the cache

Parameters:

  • key (String)

    key to save

  • value (String)

    value to persist



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/sqlite3_cache.rb', line 55

def []=(key, value)
  @LOGGER.debug {"Setting #{key} to #{value} for namespace: #{@OPTIONS[:namespace]}"}
  # make sure that table exists...
  table_init(@OPTIONS[:namespace])
  # see if the key exists...
  existing_key = nil
  @MUTEX.synchronize do
    @SQLITE3.transaction do
      @SQLITE3.execute("select key from #{@OPTIONS[:namespace]} where key = ?", key) do |row|
        existing_key = row[0]
      end
      begin
        unless existing_key
          @SQLITE3.execute("insert into #{@OPTIONS[:namespace]} (key, created, value) values (?, ?, ?)", key, Time.now.to_s, Marshal.dump(value))
        else
          @SQLITE3.execute("update #{@OPTIONS[:namespace]} set created = ?, value = ? where key = ?", Time.now.to_s, Marshal.dump(value), key)
        end
      rescue => e
        raise e
      end
      return value
    end
  end
end

#delete!(key) ⇒ Object

deletes a key from the cache

Parameters:

  • key (String)

    key the key to delete



47
48
49
50
# File 'lib/sqlite3_cache.rb', line 47

def delete!(key)
  @LOGGER.debug {"Deleting #{key} from namespace #{@OPTIONS[:namespace]}"}
  @SQLITE3.execute("delete from #{@OPTIONS[:namespace]} where key = ?", key)
end

#each_pair(&block) ⇒ Object

iterate over each key and value in the namespace



152
153
154
155
156
157
158
159
# File 'lib/sqlite3_cache.rb', line 152

def each_pair(&block)
  @SQLITE3.results_as_hash = true
  @SQLITE3.execute("SELECT * from #{@OPTIONS[:namespace]}") do |row|
    key = row["key"]
    value = Marshal.load(row["value"])
    yield key, value
  end
end

#exist?(key) ⇒ Boolean

checks to see if we have a given key…

Parameters:

  • key (String)

    key to test for existence

Returns:

  • (Boolean)

    true if exist, false if not



83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/sqlite3_cache.rb', line 83

def exist?(key)
  @LOGGER.debug {"Checking for existence of key: #{key}"}
  @MUTEX.synchronize do
    @SQLITE3.transaction do
      unless table_exists?(@OPTIONS[:namespace])
        return nil
      end
    end
  end
  @SQLITE3.execute("SELECT * from #{@OPTIONS[:namespace]} where key = ?", key) do |row|
    return true
  end
  return false
end

#expiration=(expiration) ⇒ Object

Returns the value that was passed in.

Parameters:

  • expiration (Integer)

    number of seconds before keys expire

Returns:

  • the value that was passed in



41
42
43
# File 'lib/sqlite3_cache.rb', line 41

def expiration=(expiration)
  @OPTIONS[:expiration] = expiration
end

#keysObject

Returns list of keys.

Returns:

  • list of keys



162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/sqlite3_cache.rb', line 162

def keys
  @SQLITE3.results_as_hash = true
  keys = []
  @SQLITE3.execute("SELECT key from #{@OPTIONS[:namespace]}") do |row|
    key = row["key"]
    if block_given?
    yield key
    else
    keys << key
    end
  end
  return keys
end

#purge!Object

gets rid of ALL keys in this cache…



137
138
139
# File 'lib/sqlite3_cache.rb', line 137

def purge!
  @SQLITE3.execute("DROP TABLE #{@OPTIONS[:namespace]}") if table_exists?(@OPTIONS[:namespace])
end

#purge_expired!Object

purge any expired keys…



142
143
144
145
146
147
148
149
# File 'lib/sqlite3_cache.rb', line 142

def purge_expired!
  # okay, so see if any objects are older than
  # CURRENT_TIME - @OPTIONS[:expiration]
  expire_time = (Time.now - @OPTIONS[:expiration]).to_s
  if table_exists?(@OPTIONS[:namespace])
    @SQLITE3.execute("DELETE FROM #{@OPTIONS[:namespace]} where created < ?", expire_time)
  end
end