sqlite3_cache 0.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/CHANGELOG.md +1 -0
  2. data/LICENSE +0 -0
  3. data/README.md +3 -0
  4. data/ROADMAP.md +1 -0
  5. data/lib/sqlite3_cache.rb +193 -0
  6. metadata +66 -0
data/CHANGELOG.md ADDED
@@ -0,0 +1 @@
1
+ 0.08 - Removed dependency on Bundler and fixed spec temp files...
data/LICENSE ADDED
File without changes
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # SQLite3::Cache
2
+ ## What?
3
+ SQLite3::Cache
data/ROADMAP.md ADDED
@@ -0,0 +1 @@
1
+ Performance improvements?? Bug fixes?
@@ -0,0 +1,193 @@
1
+ require 'sqlite3'
2
+ require 'logger'
3
+ require 'thread'
4
+
5
+ module SQLite3
6
+ class Cache
7
+ # @param options [Hash] hash of options
8
+ # see method for options description...
9
+ # options are...
10
+ # :file - sqlite3 file to use as persistence
11
+ # :namespace - specify cache namespace (must be a string)
12
+ # :expiration - number of seconds until keys expire
13
+ # :timestamp_on_read - note the last time we touched a record...
14
+ def initialize(options)
15
+ @OPTIONS = options
16
+ raise "Must supply :file!" unless @OPTIONS[:file]
17
+ raise "Must supply :namespace!" unless @OPTIONS[:namespace]
18
+ raise "Must supply :expiration" unless @OPTIONS[:expiration]
19
+ @LOGGER = Logger.new(STDOUT)
20
+ if @OPTIONS[:debug]
21
+ @LOGGER.level = Logger::DEBUG
22
+ else
23
+ @LOGGER.level = Logger::WARN
24
+ end
25
+
26
+ unless @OPTIONS[:timestamp_on_read] == nil
27
+ @OPTIONS[:timestamp_on_read] = false
28
+ end
29
+
30
+ begin
31
+ @MUTEX = Mutex.new
32
+ @SQLITE3 = SQLite3::Database.new(@OPTIONS[:file])
33
+ @SQLITE3.busy_timeout = 60000
34
+ rescue => e
35
+ raise "Unable to open #{@OPTIONS[:file]}: #{e.message}"
36
+ end
37
+ end
38
+
39
+ # @param expiration [Integer] number of seconds before keys expire
40
+ # @return the value that was passed in
41
+ def expiration=(expiration)
42
+ @OPTIONS[:expiration] = expiration
43
+ end
44
+
45
+ # @param key [String] key the key to delete
46
+ # deletes a key from the cache
47
+ def delete!(key)
48
+ @LOGGER.debug {"Deleting #{key} from namespace #{@OPTIONS[:namespace]}"}
49
+ @SQLITE3.execute("delete from #{@OPTIONS[:namespace]} where key = ?", key)
50
+ end
51
+
52
+ # @param key [String] key to save
53
+ # @param value [String] value to persist
54
+ # sets a value in the cache
55
+ def []=(key, value)
56
+ @LOGGER.debug {"Setting #{key} to #{value} for namespace: #{@OPTIONS[:namespace]}"}
57
+ # make sure that table exists...
58
+ table_init(@OPTIONS[:namespace])
59
+ # see if the key exists...
60
+ existing_key = nil
61
+ @MUTEX.synchronize do
62
+ @SQLITE3.transaction do
63
+ @SQLITE3.execute("select key from #{@OPTIONS[:namespace]} where key = ?", key) do |row|
64
+ existing_key = row[0]
65
+ end
66
+ begin
67
+ unless existing_key
68
+ @SQLITE3.execute("insert into #{@OPTIONS[:namespace]} (key, created, value) values (?, ?, ?)", key, Time.now.to_s, Marshal.dump(value))
69
+ else
70
+ @SQLITE3.execute("update #{@OPTIONS[:namespace]} set created = ?, value = ? where key = ?", Time.now.to_s, Marshal.dump(value), key)
71
+ end
72
+ rescue => e
73
+ raise e
74
+ end
75
+ return value
76
+ end
77
+ end
78
+ end
79
+
80
+ # @param key [String] key to test for existence
81
+ # @return true if exist, false if not
82
+ # checks to see if we have a given key...
83
+ def exist?(key)
84
+ @LOGGER.debug {"Checking for existence of key: #{key}"}
85
+ @MUTEX.synchronize do
86
+ @SQLITE3.transaction do
87
+ unless table_exists?(@OPTIONS[:namespace])
88
+ return nil
89
+ end
90
+ end
91
+ end
92
+ @SQLITE3.execute("SELECT * from #{@OPTIONS[:namespace]} where key = ?", key) do |row|
93
+ return true
94
+ end
95
+ return false
96
+ end
97
+
98
+ # @param key [String] key to retrieve
99
+ # @return value of the key or nil
100
+ # retrieves a value from the cache
101
+ def [](key)
102
+ @LOGGER.debug {"Retreiving key: #{key} for namespace: #{@OPTIONS[:namespace]}"}
103
+ value = nil
104
+ @MUTEX.synchronize do
105
+ @SQLITE3.transaction do
106
+ unless table_exists?(@OPTIONS[:namespace])
107
+ return nil
108
+ end
109
+ @SQLITE3.results_as_hash = true
110
+ value = nil
111
+ @SQLITE3.execute("SELECT * from #{@OPTIONS[:namespace]} where key = ?", key) do |row|
112
+ value = Marshal.load(row["value"])
113
+ if value
114
+ @LOGGER.debug {"Cache hit!"}
115
+ else
116
+ @LOGGER.debug {"Cache miss!"}
117
+ end
118
+ end
119
+ end
120
+ if @OPTIONS[:timestamp_on_read]
121
+ # now update access time and count...
122
+ @SQLITE3.execute("UPDATE #{@OPTIONS[:namespace]} SET accessed = ? where key = ?", Time.now.to_s, key)
123
+ # retrieve count...
124
+ access_count = nil
125
+ @SQLITE3.execute("SELECT ACCESS_COUNT from #{@OPTIONS[:namespace]} where key = ?", key) do |row|
126
+ access_count = row[0]
127
+ end
128
+ if access_count
129
+ @SQLITE3.execute("UPDATE #{@OPTIONS[:namespace]} set access_count = ? where key = ?", access_count + 1, key)
130
+ end
131
+ end
132
+ end
133
+ return value
134
+ end
135
+
136
+ # gets rid of ALL keys in this cache...
137
+ def purge!
138
+ @SQLITE3.execute("DROP TABLE #{@OPTIONS[:namespace]}") if table_exists?(@OPTIONS[:namespace])
139
+ end
140
+
141
+ # purge any expired keys...
142
+ def purge_expired!
143
+ # okay, so see if any objects are older than
144
+ # CURRENT_TIME - @OPTIONS[:expiration]
145
+ expire_time = (Time.now - @OPTIONS[:expiration]).to_s
146
+ if table_exists?(@OPTIONS[:namespace])
147
+ @SQLITE3.execute("DELETE FROM #{@OPTIONS[:namespace]} where created < ?", expire_time)
148
+ end
149
+ end
150
+
151
+ # iterate over each key and value in the namespace
152
+ def each_pair(&block)
153
+ @SQLITE3.results_as_hash = true
154
+ @SQLITE3.execute("SELECT * from #{@OPTIONS[:namespace]}") do |row|
155
+ key = row["key"]
156
+ value = Marshal.load(row["value"])
157
+ yield key, value
158
+ end
159
+ end
160
+
161
+ # @return list of keys
162
+ def keys
163
+ @SQLITE3.results_as_hash = true
164
+ keys = []
165
+ @SQLITE3.execute("SELECT key from #{@OPTIONS[:namespace]}") do |row|
166
+ key = row["key"]
167
+ if block_given?
168
+ yield key
169
+ else
170
+ keys << key
171
+ end
172
+ end
173
+ return keys
174
+ end
175
+
176
+ private
177
+
178
+ def table_exists?(table)
179
+ @SQLITE3.execute("SELECT CASE WHEN tbl_name = ? THEN 1 ELSE 0 END FROM sqlite_master WHERE tbl_name = ? AND type = 'table'", table, table) do |row|
180
+ return row[0] == 1 ? true : false
181
+ end
182
+ return false
183
+ end
184
+
185
+ def table_init(table)
186
+ query = "create table if not exists #{table}(key text primary key, created timestamp, accessed timestamp, access_count timestamp, value blob)"
187
+ @SQLITE3.execute(query)
188
+ query = "create index if not exists key_index on #{table} (key)"
189
+ @SQLITE3.execute(query)
190
+ end
191
+
192
+ end
193
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sqlite3_cache
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.16'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Joshua Harding
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-07-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: sqlite3
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: Cache module that uses SQLite3 as backend
31
+ email:
32
+ - josh@statewidesoftware.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - lib/sqlite3_cache.rb
38
+ - LICENSE
39
+ - README.md
40
+ - ROADMAP.md
41
+ - CHANGELOG.md
42
+ homepage: http://statewidesoftware.com
43
+ licenses: []
44
+ post_install_message:
45
+ rdoc_options: []
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ! '>='
58
+ - !ruby/object:Gem::Version
59
+ version: 1.3.6
60
+ requirements: []
61
+ rubyforge_project:
62
+ rubygems_version: 1.8.24
63
+ signing_key:
64
+ specification_version: 3
65
+ summary: Cache module that uses SQLite3 as backend
66
+ test_files: []