Class: SqliteCache

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

Overview

Lightweight, persistent cache, to store keys and their values in an SQLite database.

Constant Summary

TABLE_NAME =

Name of the table in the database to store things in.

'sqlitecache'

Instance Attribute Summary (collapse)

Instance Method Summary (collapse)

Constructor Details

- (SqliteCache) initialize(path)

Create a new SQLiteCache. Where path is the full path to the SQLite database file to use.



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/sqlite_cache.rb', line 20

def initialize( path )
  @db = SQLite3::Database.new( path )
  @count_hits = false
  @busy_retries = 100

  # Wait up to 10 seconds to access locked database
  @db.busy_handler do |resource,retries|
    sleep 0.1
    retries<@busy_retries
  end
  
  # Create the table, if it doesn't exist
  if @db.table_info(TABLE_NAME).empty?
    @db.execute( %Q{
      CREATE TABLE #{TABLE_NAME} (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        key TEXT,
        value TEXT,
        hits INTEGER DEFAULT 0,
        created_at INTEGER,
        updated_at INTEGER
      );
    } )
    @db.execute( %Q{ CREATE UNIQUE INDEX key_index ON #{TABLE_NAME} (key) } )
  end
end

Instance Attribute Details

- (Object) busy_retries

Number of times to retry, if database is locked [integer, default 100]



14
15
16
# File 'lib/sqlite_cache.rb', line 14

def busy_retries
  @busy_retries
end

- (Object) count_hits

Enable/disable cache hit counting [boolean]



11
12
13
# File 'lib/sqlite_cache.rb', line 11

def count_hits
  @count_hits
end

Instance Method Details

- (Object) do_cached(key, &block)

Perform a block if key isn't already cached.



103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/sqlite_cache.rb', line 103

def do_cached( key, &block )

  # have a look in the cache
  value = fetch( key )

  # Cache HIT?
  return value unless value.nil?

  # Cache MISS : execute the block
  value = block.call( key )

  # Store value in the cache
  return store( key, value )
end

- (Object) fetch(key)

Fetch something from the cache, based on a key string.



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/sqlite_cache.rb', line 58

def fetch( key )
  key = key.to_s.strip unless key.nil?
  raise "Invalid key" if key.nil? or key == ''

  id,value,hits = @db.get_first_row(
    "SELECT id,value,hits "+
    "FROM #{TABLE_NAME} "+
    "WHERE key=?",
    key.to_s.strip
  )

  # Return nil if there is cache MISS.
  return nil if value.nil?
  
  # Increment the number of hits
  if @count_hits
    @db.execute(
      "UPDATE #{TABLE_NAME} SET hits=?, updated_at=? WHERE id=?",
      hits.to_i+1, Time.now.to_i, id
    )
  end

  # Otherwise if there is a HIT, parse the YAML into an object
  return YAML::load(value)
end

- (Object) purge

Delete everything in the cache.



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

def purge
  @db.execute( "DELETE FROM #{TABLE_NAME};" )
end

- (Object) size

Return the number of items in the cache.



53
54
55
# File 'lib/sqlite_cache.rb', line 53

def size
  @db.get_first_value( "SELECT COUNT(*) FROM #{TABLE_NAME};" ).to_i
end

- (Object) store(key, value)

Store a key and value in the cache.



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/sqlite_cache.rb', line 85

def store( key, value )
  key = key.to_s.strip unless key.nil?
  raise "Invalid key" if key.nil? or key == ''

  @db.execute( %Q{
      INSERT INTO #{TABLE_NAME}
      (key,value,created_at)
      VALUES (?,?,?)
    },
    key,
    value.to_yaml,
    Time.now.to_i
  )
  
  return value
end