sqlite3_cache 0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []