timedcache 0.1.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +13 -0
- data/Manifest.txt +8 -0
- data/README.txt +57 -0
- data/Rakefile +16 -0
- data/lib/timedcache.rb +90 -34
- data/misc/thread_test.rb +24 -0
- data/spec/timed_cache_spec.rb +102 -0
- metadata +57 -46
- data/MIT_LICENSE +0 -7
- data/doc/created.rid +0 -1
- data/doc/fr_class_index.html +0 -27
- data/doc/fr_file_index.html +0 -29
- data/doc/fr_method_index.html +0 -31
- data/doc/index.html +0 -24
- data/doc/rdoc-style.css +0 -208
- data/specs/timed_cache_spec.rb +0 -80
data/History.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
=== 0.2 / 2008-02-02
|
2
|
+
|
3
|
+
* Updated specs to be compatible with the latest rSpec release.
|
4
|
+
|
5
|
+
* Made thread safe by synchronizing all access to the @store instance
|
6
|
+
variable used by a TimedCache instance.
|
7
|
+
|
8
|
+
* Altered the API so that strings and symbols are not treated as equivalent keys.
|
9
|
+
This is in order to prevent unintentional memory leaks.
|
10
|
+
|
11
|
+
=== 0.1 / 2006-12-07
|
12
|
+
|
13
|
+
* Initial release.
|
data/Manifest.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
= timedcache
|
2
|
+
|
3
|
+
* http://timedcache.rubyforge.org
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
TimedCache implements a cache in which you can place objects
|
8
|
+
and specify a timeout value.
|
9
|
+
|
10
|
+
If you attempt to retrieve the object within the specified timeout
|
11
|
+
period, the object will be returned. If the timeout period has elapsed,
|
12
|
+
the TimedCache will return nil.
|
13
|
+
|
14
|
+
== FEATURES/PROBLEMS:
|
15
|
+
|
16
|
+
* Memory or file-based data stores available.
|
17
|
+
* Thread safety.
|
18
|
+
|
19
|
+
== SYNOPSIS:
|
20
|
+
|
21
|
+
cache = TimedCache.new
|
22
|
+
cache.put :my_object_key, "Expensive data", 10 #=> "Expensive data"
|
23
|
+
|
24
|
+
cache.get :my_object_key #=> "Expensive data"
|
25
|
+
cache[:my_object_key] #=> "Expensive data"
|
26
|
+
|
27
|
+
... 10 seconds later:
|
28
|
+
cache.get :my_object_key #=> nil
|
29
|
+
|
30
|
+
== INSTALL:
|
31
|
+
|
32
|
+
sudo gem timedcache
|
33
|
+
|
34
|
+
== LICENSE:
|
35
|
+
|
36
|
+
(The MIT License)
|
37
|
+
|
38
|
+
Copyright (c) 2008 Nicholas Dainty
|
39
|
+
|
40
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
41
|
+
a copy of this software and associated documentation files (the
|
42
|
+
'Software'), to deal in the Software without restriction, including
|
43
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
44
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
45
|
+
permit persons to whom the Software is furnished to do so, subject to
|
46
|
+
the following conditions:
|
47
|
+
|
48
|
+
The above copyright notice and this permission notice shall be
|
49
|
+
included in all copies or substantial portions of the Software.
|
50
|
+
|
51
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
52
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
53
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
54
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
55
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
56
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
57
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
require './lib/timedcache.rb'
|
6
|
+
|
7
|
+
Hoe.new('timedcache', TimedCache::VERSION) do |p|
|
8
|
+
p.rubyforge_name = 'timedcache'
|
9
|
+
p.author = 'Nicholas Dainty'
|
10
|
+
p.email = 'nick@npad.co.uk'
|
11
|
+
p.summary = 'A very simple time-based object cache.'
|
12
|
+
p.description = p.paragraphs_of('README.txt', 3..4).join("\n\n")
|
13
|
+
p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
|
14
|
+
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
15
|
+
p.remote_rdoc_dir = '' # Place RDocs on project home page.
|
16
|
+
end
|
data/lib/timedcache.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "pstore"
|
2
|
+
require "monitor"
|
2
3
|
|
3
4
|
# == TimedCache
|
4
5
|
#
|
@@ -39,9 +40,13 @@ require "pstore"
|
|
39
40
|
#
|
40
41
|
# TimedCache.new(:type => :file, :filename => "my_cache.db")
|
41
42
|
#
|
43
|
+
# The file-based cache makes it possible to easily share a cache between several ruby
|
44
|
+
# processes. However when using the cache in this way, you will probably want to update the
|
45
|
+
# cache by passing a block to the TimedCache#get method (see below for details).
|
46
|
+
#
|
42
47
|
# Note that objects that cannot be marshalled (e.g. a Proc) can't be stored using the file-based cache.
|
43
48
|
class TimedCache
|
44
|
-
|
49
|
+
VERSION = "0.2"
|
45
50
|
|
46
51
|
attr_reader :default_timeout
|
47
52
|
|
@@ -56,6 +61,7 @@ class TimedCache
|
|
56
61
|
opts[:type] ||= :memory
|
57
62
|
@default_timeout = opts[:default_timeout] || 60
|
58
63
|
@store = new_store(opts)
|
64
|
+
@store.extend(MonitorMixin)
|
59
65
|
end
|
60
66
|
|
61
67
|
# Add an object to the cache. e.g.:
|
@@ -64,13 +70,31 @@ class TimedCache
|
|
64
70
|
# The third parameter is an optional timeout value. If not specified, the
|
65
71
|
# <tt>:default_timeout</tt> for this TimedCache will be used instead.
|
66
72
|
def put(key, value, timeout = @default_timeout)
|
67
|
-
@store.
|
73
|
+
@store.synchronize do
|
74
|
+
@store.put(key, value, timeout) unless value.nil?
|
75
|
+
end
|
68
76
|
end
|
69
77
|
|
70
78
|
# Retrieve the object which the given +key+. If the object has expired or
|
71
79
|
# is not present, +nil+ is returned.
|
72
|
-
|
73
|
-
|
80
|
+
#
|
81
|
+
# Optionally, a block can be given. The result of evaluating the block will
|
82
|
+
# be substituted as the cache value, if the cache has expired. This is particularly
|
83
|
+
# useful when using a file-based cache from multiple ruby processes, as it
|
84
|
+
# will prevent your application from making multiple simultaneous attempts to
|
85
|
+
# re-populate the cache.
|
86
|
+
#
|
87
|
+
# e.g.:
|
88
|
+
#
|
89
|
+
# cache.get("slow_database_query") do
|
90
|
+
# MyDatabase.query("SELECT * FROM bigtable...")
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# The block syntax can also be used with the in-memory cache.
|
94
|
+
def get(key, &block)
|
95
|
+
@store.synchronize do
|
96
|
+
@store.get(key, &block)
|
97
|
+
end
|
74
98
|
end
|
75
99
|
|
76
100
|
# Add to the cache using a hash-like syntax. e.g.:
|
@@ -91,18 +115,52 @@ class TimedCache
|
|
91
115
|
protected
|
92
116
|
|
93
117
|
def new_store(options) #:nodoc:
|
94
|
-
self.class.const_get(options[:type].to_s.capitalize + "Store").new(options)
|
118
|
+
self.class.const_get(options[:type].to_s.capitalize + "Store").new(self, options)
|
95
119
|
end
|
96
120
|
|
97
121
|
class Store #:nodoc:
|
98
|
-
def initialize(options)
|
99
|
-
@
|
122
|
+
def initialize(timed_cache, options)
|
123
|
+
@timed_cache = timed_cache
|
124
|
+
@options = options
|
125
|
+
end
|
126
|
+
|
127
|
+
protected
|
128
|
+
|
129
|
+
def generic_get(key, timeout, callback, fetch_key = lambda {|k| @cache[key]})
|
130
|
+
if object_store = fetch_key.call(key)
|
131
|
+
if object_store.expired?
|
132
|
+
if callback
|
133
|
+
run_callback_and_add_to_cache(key, object_store, callback, timeout)
|
134
|
+
else
|
135
|
+
# Free up memory:
|
136
|
+
@cache[key] = nil
|
137
|
+
end
|
138
|
+
else
|
139
|
+
object_store.object
|
140
|
+
end
|
141
|
+
elsif callback
|
142
|
+
run_callback_and_add_to_cache(key, object_store, callback, timeout)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def run_callback_and_add_to_cache(key, object_store, callback, timeout)
|
147
|
+
object_store.no_expiry! if object_store
|
148
|
+
|
149
|
+
begin
|
150
|
+
result = callback.call
|
151
|
+
rescue
|
152
|
+
object_store.reset_expiry! if object_store
|
153
|
+
raise
|
154
|
+
end
|
155
|
+
|
156
|
+
@timed_cache.put(key, result, timeout)
|
157
|
+
result
|
100
158
|
end
|
101
159
|
end
|
102
160
|
|
103
161
|
class MemoryStore < Store #:nodoc:
|
104
|
-
def initialize(
|
105
|
-
super
|
162
|
+
def initialize(*args)
|
163
|
+
super(*args)
|
106
164
|
@cache = Hash.new
|
107
165
|
end
|
108
166
|
|
@@ -113,15 +171,8 @@ class TimedCache
|
|
113
171
|
value
|
114
172
|
end
|
115
173
|
|
116
|
-
def get(key)
|
117
|
-
|
118
|
-
if object_store.expired?
|
119
|
-
# Free up memory:
|
120
|
-
@cache[key] = nil
|
121
|
-
else
|
122
|
-
object_store.object
|
123
|
-
end
|
124
|
-
end
|
174
|
+
def get(key, timeout = @timed_cache.default_timeout, &block)
|
175
|
+
generic_get(key, timeout, block)
|
125
176
|
end
|
126
177
|
end
|
127
178
|
|
@@ -135,41 +186,46 @@ class TimedCache
|
|
135
186
|
@cache = PStore.new(filename)
|
136
187
|
end
|
137
188
|
|
138
|
-
def put(key, value, timeout
|
139
|
-
@cache.transaction
|
140
|
-
@cache[key] = ObjectContainer.new(value, timeout)
|
141
|
-
end
|
189
|
+
def put(key, value, timeout)
|
190
|
+
@cache.transaction { @cache[key] = ObjectContainer.new(value, timeout) }
|
142
191
|
|
143
192
|
# Return just the given value, so that references to the
|
144
193
|
# ObjectStore instance can't be held outside this TimedCache:
|
145
194
|
value
|
146
195
|
end
|
147
196
|
|
148
|
-
def get(key)
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
@cache[key] = nil
|
154
|
-
else
|
155
|
-
object_store.object
|
156
|
-
end
|
157
|
-
end
|
197
|
+
def get(key, timeout = @timed_cache.default_timeout, &block)
|
198
|
+
if block
|
199
|
+
generic_get(key, timeout, block, lambda {|k| @cache.transaction { @cache[key] } })
|
200
|
+
else
|
201
|
+
@cache.transaction { generic_get(key, timeout, block) }
|
158
202
|
end
|
159
203
|
end
|
160
204
|
end
|
161
205
|
|
162
206
|
class ObjectContainer #:nodoc:
|
163
|
-
|
207
|
+
attr_accessor :object
|
164
208
|
|
165
209
|
def initialize(object, timeout)
|
166
210
|
@created_at = Time.now.utc
|
167
211
|
@timeout = timeout
|
168
212
|
@object = object
|
213
|
+
@frozen = false
|
169
214
|
end
|
170
215
|
|
171
216
|
def expired?
|
172
|
-
|
217
|
+
if @frozen: false
|
218
|
+
else
|
219
|
+
(Time.now.utc - @timeout) > @created_at
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def no_expiry!
|
224
|
+
@frozen = true
|
225
|
+
end
|
226
|
+
|
227
|
+
def reset_expiry!
|
228
|
+
@frozen = false
|
173
229
|
end
|
174
230
|
end
|
175
231
|
end
|
data/misc/thread_test.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "../lib/timedcache")
|
2
|
+
|
3
|
+
# This script will almost certainly raise an exception if
|
4
|
+
# thread safety is not implemented.
|
5
|
+
|
6
|
+
db = File.join(File.dirname(__FILE__), "thread_test.db")
|
7
|
+
|
8
|
+
cache = TimedCache.new(:type => :file, :filename => File.join(db))
|
9
|
+
|
10
|
+
threads = []
|
11
|
+
|
12
|
+
10.times do
|
13
|
+
threads << Thread.new do
|
14
|
+
100.times {
|
15
|
+
cache.put("thread-safe?", "let's find out")
|
16
|
+
cache.get("thread-safe?")
|
17
|
+
}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
threads.each { |t| t.join }
|
23
|
+
|
24
|
+
File.delete(db)
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "../lib/timedcache")
|
2
|
+
|
3
|
+
$filename = File.join(File.dirname(__FILE__), "specs.db")
|
4
|
+
|
5
|
+
describe "Adding and retrieving objects from the cache" do
|
6
|
+
setup do
|
7
|
+
@memory_cache = TimedCache.new
|
8
|
+
@file_cache = TimedCache.new(:type => :file, :filename => $filename)
|
9
|
+
@caches = [@memory_cache, @file_cache]
|
10
|
+
end
|
11
|
+
|
12
|
+
teardown do
|
13
|
+
File.delete($filename)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "Can add an object to the cache, specifying a timeout value" do
|
17
|
+
@caches.each do |cache|
|
18
|
+
cache.put(:myobject, "This needs caching", 10).should == "This needs caching"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it "Cache should hold seperate values for each key" do
|
23
|
+
@caches.each do |cache|
|
24
|
+
cache.put(:myobject, "This needs caching", 10).should == "This needs caching"
|
25
|
+
cache.put(:my_other_object, "...and this too", 10).should == "...and this too"
|
26
|
+
cache.get(:myobject).should == "This needs caching"
|
27
|
+
cache.get(:my_other_object).should == "...and this too"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it "String and symbol keys are not treated as equivalent" do
|
32
|
+
@caches.each do |cache|
|
33
|
+
cache[:symbolkey] = "Referenced by symbol"
|
34
|
+
cache["stringkey"] = "Referenced by string"
|
35
|
+
|
36
|
+
cache[:symbolkey].should == "Referenced by symbol"
|
37
|
+
cache["symbolkey"].should == nil
|
38
|
+
|
39
|
+
cache["stringkey"].should == "Referenced by string"
|
40
|
+
cache[:stringkey].should == nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it "After the specified timeout value has elapsed, nil should be returned" do
|
45
|
+
@caches.each do |cache|
|
46
|
+
cache.put(:myobject, "This needs caching", 0).should == "This needs caching"
|
47
|
+
cache.get(:myobject).should == nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it "If no object matching the given key is found, nil should be returned" do
|
52
|
+
@caches.each do |cache|
|
53
|
+
cache.get(:my_nonexistant_object).should == nil
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it "Should be able to use an array as a cache key" do
|
58
|
+
@caches.each do |cache|
|
59
|
+
cache.put([123,234], "Array").should == "Array"
|
60
|
+
cache.get([123,234]).should == "Array"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it "Passing a block to the TimedCache#get method should substitute the " +
|
65
|
+
"result of the block as the value for the given key" do
|
66
|
+
@caches.each do |cache|
|
67
|
+
cache.put("block_test", 1984, 0)
|
68
|
+
cache.get("block_test") { 2001 }.should == 2001
|
69
|
+
cache.get("block_test").should == 2001
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it "Passing a block to TimedCache#get should add the result of the callback " +
|
74
|
+
"when there is no existing value for the key given" do
|
75
|
+
@caches.each do |cache|
|
76
|
+
cache.get("new_key_with_block").should == nil
|
77
|
+
cache.get("new_key_with_block") { "Nicholas" }.should == "Nicholas"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "Specifying a default timeout" do
|
83
|
+
it "Should be able to it a default timeout when creating a TimedCache" do
|
84
|
+
cache = TimedCache.new(:default_timeout => 20)
|
85
|
+
cache.should be_kind_of(TimedCache)
|
86
|
+
cache.default_timeout.should == 20
|
87
|
+
end
|
88
|
+
|
89
|
+
it "If no default timeout is set, 60 seconds should be used" do
|
90
|
+
cache = TimedCache.new
|
91
|
+
cache.should be_kind_of(TimedCache)
|
92
|
+
cache.default_timeout.should == 60
|
93
|
+
end
|
94
|
+
|
95
|
+
it "Timeout specified when putting a new object into the cache should override default timeout" do
|
96
|
+
cache = TimedCache.new(:default_timeout => 20)
|
97
|
+
cache.default_timeout.should == 20
|
98
|
+
cache.put("alternative_timeout", "2 minutes", 120)
|
99
|
+
cache.instance_variable_get(:@store).instance_variable_get(
|
100
|
+
:@cache)["alternative_timeout"].instance_variable_get(:@timeout).should == 120
|
101
|
+
end
|
102
|
+
end
|
metadata
CHANGED
@@ -1,60 +1,71 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.0
|
3
|
-
specification_version: 1
|
4
2
|
name: timedcache
|
5
3
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2007-03-26 00:00:00 +01:00
|
8
|
-
summary: A very simple time-based object cache.
|
9
|
-
require_paths:
|
10
|
-
- lib
|
11
|
-
email: nick@npad.co.uk
|
12
|
-
homepage: http://timedcache.rubyforge.org
|
13
|
-
rubyforge_project: timedcache
|
14
|
-
description: TimedCache implements a cache in which you can place objects and specify a timeout value. If you attempt to retrieve the object within the specified timeout period, the object will be returned. If the timeout period has elapsed, the TimedCache will return nil.
|
15
|
-
autorequire: timedcache
|
16
|
-
default_executable:
|
17
|
-
bindir: bin
|
18
|
-
has_rdoc: true
|
19
|
-
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">"
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 0.0.0
|
24
|
-
version:
|
4
|
+
version: "0.2"
|
25
5
|
platform: ruby
|
26
|
-
signing_key:
|
27
|
-
cert_chain:
|
28
|
-
post_install_message:
|
29
6
|
authors:
|
30
7
|
- Nicholas Dainty
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
- doc/files
|
35
|
-
- doc/fr_class_index.html
|
36
|
-
- doc/fr_file_index.html
|
37
|
-
- doc/fr_method_index.html
|
38
|
-
- doc/index.html
|
39
|
-
- doc/rdoc-style.css
|
40
|
-
- lib/timed_cache.rb
|
41
|
-
- lib/timedcache.rb
|
42
|
-
- specs/timed_cache_spec.rb
|
43
|
-
- MIT_LICENSE
|
44
|
-
test_files: []
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
45
11
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
-
|
50
|
-
|
51
|
-
|
52
|
-
|
12
|
+
date: 2008-02-03 00:00:00 +00:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hoe
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.5.0
|
23
|
+
version:
|
24
|
+
description: TimedCache implements a cache in which you can place objects and specify a timeout value. If you attempt to retrieve the object within the specified timeout period, the object will be returned. If the timeout period has elapsed, the TimedCache will return nil.
|
25
|
+
email: nick@npad.co.uk
|
53
26
|
executables: []
|
54
27
|
|
55
28
|
extensions: []
|
56
29
|
|
30
|
+
extra_rdoc_files:
|
31
|
+
- History.txt
|
32
|
+
- Manifest.txt
|
33
|
+
- README.txt
|
34
|
+
files:
|
35
|
+
- History.txt
|
36
|
+
- Manifest.txt
|
37
|
+
- README.txt
|
38
|
+
- Rakefile
|
39
|
+
- lib/timed_cache.rb
|
40
|
+
- lib/timedcache.rb
|
41
|
+
- misc/thread_test.rb
|
42
|
+
- spec/timed_cache_spec.rb
|
43
|
+
has_rdoc: true
|
44
|
+
homepage:
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options:
|
47
|
+
- --main
|
48
|
+
- README.txt
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
57
63
|
requirements: []
|
58
64
|
|
59
|
-
|
65
|
+
rubyforge_project: timedcache
|
66
|
+
rubygems_version: 1.0.1
|
67
|
+
signing_key:
|
68
|
+
specification_version: 2
|
69
|
+
summary: A very simple time-based object cache.
|
70
|
+
test_files: []
|
60
71
|
|
data/MIT_LICENSE
DELETED
@@ -1,7 +0,0 @@
|
|
1
|
-
Copyright (c) 2006 Nicholas Dainty
|
2
|
-
|
3
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
-
|
5
|
-
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
-
|
7
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/doc/created.rid
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
Mon Mar 26 22:35:48 +0100 2007
|
data/doc/fr_class_index.html
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
|
2
|
-
<?xml version="1.0" encoding="iso-8859-1"?>
|
3
|
-
<!DOCTYPE html
|
4
|
-
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
5
|
-
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
6
|
-
|
7
|
-
<!--
|
8
|
-
|
9
|
-
Classes
|
10
|
-
|
11
|
-
-->
|
12
|
-
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
13
|
-
<head>
|
14
|
-
<title>Classes</title>
|
15
|
-
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
16
|
-
<link rel="stylesheet" href="rdoc-style.css" type="text/css" />
|
17
|
-
<base target="docwin" />
|
18
|
-
</head>
|
19
|
-
<body>
|
20
|
-
<div id="index">
|
21
|
-
<h1 class="section-bar">Classes</h1>
|
22
|
-
<div id="index-entries">
|
23
|
-
<a href="classes/TimedCache.html">TimedCache</a><br />
|
24
|
-
</div>
|
25
|
-
</div>
|
26
|
-
</body>
|
27
|
-
</html>
|
data/doc/fr_file_index.html
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
|
2
|
-
<?xml version="1.0" encoding="iso-8859-1"?>
|
3
|
-
<!DOCTYPE html
|
4
|
-
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
5
|
-
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
6
|
-
|
7
|
-
<!--
|
8
|
-
|
9
|
-
Files
|
10
|
-
|
11
|
-
-->
|
12
|
-
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
13
|
-
<head>
|
14
|
-
<title>Files</title>
|
15
|
-
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
16
|
-
<link rel="stylesheet" href="rdoc-style.css" type="text/css" />
|
17
|
-
<base target="docwin" />
|
18
|
-
</head>
|
19
|
-
<body>
|
20
|
-
<div id="index">
|
21
|
-
<h1 class="section-bar">Files</h1>
|
22
|
-
<div id="index-entries">
|
23
|
-
<a href="files/MIT_LICENSE.html">MIT_LICENSE</a><br />
|
24
|
-
<a href="files/lib/timed_cache_rb.html">lib/timed_cache.rb</a><br />
|
25
|
-
<a href="files/lib/timedcache_rb.html">lib/timedcache.rb</a><br />
|
26
|
-
</div>
|
27
|
-
</div>
|
28
|
-
</body>
|
29
|
-
</html>
|
data/doc/fr_method_index.html
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
|
2
|
-
<?xml version="1.0" encoding="iso-8859-1"?>
|
3
|
-
<!DOCTYPE html
|
4
|
-
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
5
|
-
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
6
|
-
|
7
|
-
<!--
|
8
|
-
|
9
|
-
Methods
|
10
|
-
|
11
|
-
-->
|
12
|
-
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
13
|
-
<head>
|
14
|
-
<title>Methods</title>
|
15
|
-
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
16
|
-
<link rel="stylesheet" href="rdoc-style.css" type="text/css" />
|
17
|
-
<base target="docwin" />
|
18
|
-
</head>
|
19
|
-
<body>
|
20
|
-
<div id="index">
|
21
|
-
<h1 class="section-bar">Methods</h1>
|
22
|
-
<div id="index-entries">
|
23
|
-
<a href="classes/TimedCache.html#M000005">[] (TimedCache)</a><br />
|
24
|
-
<a href="classes/TimedCache.html#M000004">[]= (TimedCache)</a><br />
|
25
|
-
<a href="classes/TimedCache.html#M000003">get (TimedCache)</a><br />
|
26
|
-
<a href="classes/TimedCache.html#M000001">new (TimedCache)</a><br />
|
27
|
-
<a href="classes/TimedCache.html#M000002">put (TimedCache)</a><br />
|
28
|
-
</div>
|
29
|
-
</div>
|
30
|
-
</body>
|
31
|
-
</html>
|
data/doc/index.html
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
<?xml version="1.0" encoding="iso-8859-1"?>
|
2
|
-
<!DOCTYPE html
|
3
|
-
PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
|
4
|
-
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
|
5
|
-
|
6
|
-
<!--
|
7
|
-
|
8
|
-
TimedCache Documentation
|
9
|
-
|
10
|
-
-->
|
11
|
-
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
12
|
-
<head>
|
13
|
-
<title>TimedCache Documentation</title>
|
14
|
-
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
15
|
-
</head>
|
16
|
-
<frameset rows="20%, 80%">
|
17
|
-
<frameset cols="25%,35%,45%">
|
18
|
-
<frame src="fr_file_index.html" title="Files" name="Files" />
|
19
|
-
<frame src="fr_class_index.html" name="Classes" />
|
20
|
-
<frame src="fr_method_index.html" name="Methods" />
|
21
|
-
</frameset>
|
22
|
-
<frame src="classes/TimedCache.html" name="docwin" />
|
23
|
-
</frameset>
|
24
|
-
</html>
|
data/doc/rdoc-style.css
DELETED
@@ -1,208 +0,0 @@
|
|
1
|
-
|
2
|
-
body {
|
3
|
-
font-family: Verdana,Arial,Helvetica,sans-serif;
|
4
|
-
font-size: 90%;
|
5
|
-
margin: 0;
|
6
|
-
margin-left: 40px;
|
7
|
-
padding: 0;
|
8
|
-
background: white;
|
9
|
-
}
|
10
|
-
|
11
|
-
h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; }
|
12
|
-
h1 { font-size: 150%; }
|
13
|
-
h2,h3,h4 { margin-top: 1em; }
|
14
|
-
|
15
|
-
a { background: #eef; color: #039; text-decoration: none; }
|
16
|
-
a:hover { background: #039; color: #eef; }
|
17
|
-
|
18
|
-
/* Override the base stylesheet's Anchor inside a table cell */
|
19
|
-
td > a {
|
20
|
-
background: transparent;
|
21
|
-
color: #039;
|
22
|
-
text-decoration: none;
|
23
|
-
}
|
24
|
-
|
25
|
-
/* and inside a section title */
|
26
|
-
.section-title > a {
|
27
|
-
background: transparent;
|
28
|
-
color: #eee;
|
29
|
-
text-decoration: none;
|
30
|
-
}
|
31
|
-
|
32
|
-
/* === Structural elements =================================== */
|
33
|
-
|
34
|
-
div#index {
|
35
|
-
margin: 0;
|
36
|
-
margin-left: -40px;
|
37
|
-
padding: 0;
|
38
|
-
font-size: 90%;
|
39
|
-
}
|
40
|
-
|
41
|
-
|
42
|
-
div#index a {
|
43
|
-
margin-left: 0.7em;
|
44
|
-
}
|
45
|
-
|
46
|
-
div#index .section-bar {
|
47
|
-
margin-left: 0px;
|
48
|
-
padding-left: 0.7em;
|
49
|
-
background: #ccc;
|
50
|
-
font-size: small;
|
51
|
-
}
|
52
|
-
|
53
|
-
|
54
|
-
div#classHeader, div#fileHeader {
|
55
|
-
width: auto;
|
56
|
-
color: white;
|
57
|
-
padding: 0.5em 1.5em 0.5em 1.5em;
|
58
|
-
margin: 0;
|
59
|
-
margin-left: -40px;
|
60
|
-
border-bottom: 3px solid #006;
|
61
|
-
}
|
62
|
-
|
63
|
-
div#classHeader a, div#fileHeader a {
|
64
|
-
background: inherit;
|
65
|
-
color: white;
|
66
|
-
}
|
67
|
-
|
68
|
-
div#classHeader td, div#fileHeader td {
|
69
|
-
background: inherit;
|
70
|
-
color: white;
|
71
|
-
}
|
72
|
-
|
73
|
-
|
74
|
-
div#fileHeader {
|
75
|
-
background: #057;
|
76
|
-
}
|
77
|
-
|
78
|
-
div#classHeader {
|
79
|
-
background: #048;
|
80
|
-
}
|
81
|
-
|
82
|
-
|
83
|
-
.class-name-in-header {
|
84
|
-
font-size: 180%;
|
85
|
-
font-weight: bold;
|
86
|
-
}
|
87
|
-
|
88
|
-
|
89
|
-
div#bodyContent {
|
90
|
-
padding: 0 1.5em 0 1.5em;
|
91
|
-
}
|
92
|
-
|
93
|
-
div#description {
|
94
|
-
padding: 0.5em 1.5em;
|
95
|
-
background: #efefef;
|
96
|
-
border: 1px dotted #999;
|
97
|
-
}
|
98
|
-
|
99
|
-
div#description h1,h2,h3,h4,h5,h6 {
|
100
|
-
color: #125;;
|
101
|
-
background: transparent;
|
102
|
-
}
|
103
|
-
|
104
|
-
div#validator-badges {
|
105
|
-
text-align: center;
|
106
|
-
}
|
107
|
-
div#validator-badges img { border: 0; }
|
108
|
-
|
109
|
-
div#copyright {
|
110
|
-
color: #333;
|
111
|
-
background: #efefef;
|
112
|
-
font: 0.75em sans-serif;
|
113
|
-
margin-top: 5em;
|
114
|
-
margin-bottom: 0;
|
115
|
-
padding: 0.5em 2em;
|
116
|
-
}
|
117
|
-
|
118
|
-
|
119
|
-
/* === Classes =================================== */
|
120
|
-
|
121
|
-
table.header-table {
|
122
|
-
color: white;
|
123
|
-
font-size: small;
|
124
|
-
}
|
125
|
-
|
126
|
-
.type-note {
|
127
|
-
font-size: small;
|
128
|
-
color: #DEDEDE;
|
129
|
-
}
|
130
|
-
|
131
|
-
.xxsection-bar {
|
132
|
-
background: #eee;
|
133
|
-
color: #333;
|
134
|
-
padding: 3px;
|
135
|
-
}
|
136
|
-
|
137
|
-
.section-bar {
|
138
|
-
color: #333;
|
139
|
-
border-bottom: 1px solid #999;
|
140
|
-
margin-left: -20px;
|
141
|
-
}
|
142
|
-
|
143
|
-
|
144
|
-
.section-title {
|
145
|
-
background: #79a;
|
146
|
-
color: #eee;
|
147
|
-
padding: 3px;
|
148
|
-
margin-top: 2em;
|
149
|
-
margin-left: -30px;
|
150
|
-
border: 1px solid #999;
|
151
|
-
}
|
152
|
-
|
153
|
-
.top-aligned-row { vertical-align: top }
|
154
|
-
.bottom-aligned-row { vertical-align: bottom }
|
155
|
-
|
156
|
-
/* --- Context section classes ----------------------- */
|
157
|
-
|
158
|
-
.context-row { }
|
159
|
-
.context-item-name { font-family: monospace; font-weight: bold; color: black; }
|
160
|
-
.context-item-value { font-size: small; color: #448; }
|
161
|
-
.context-item-desc { color: #333; padding-left: 2em; }
|
162
|
-
|
163
|
-
/* --- Method classes -------------------------- */
|
164
|
-
.method-detail {
|
165
|
-
background: #efefef;
|
166
|
-
padding: 0;
|
167
|
-
margin-top: 0.5em;
|
168
|
-
margin-bottom: 1em;
|
169
|
-
border: 1px dotted #ccc;
|
170
|
-
}
|
171
|
-
.method-heading {
|
172
|
-
color: black;
|
173
|
-
background: #ccc;
|
174
|
-
border-bottom: 1px solid #666;
|
175
|
-
padding: 0.2em 0.5em 0 0.5em;
|
176
|
-
}
|
177
|
-
.method-signature { color: black; background: inherit; }
|
178
|
-
.method-name { font-weight: bold; }
|
179
|
-
.method-args { font-style: italic; }
|
180
|
-
.method-description { padding: 0 0.5em 0 0.5em; }
|
181
|
-
|
182
|
-
/* --- Source code sections -------------------- */
|
183
|
-
|
184
|
-
a.source-toggle { font-size: 90%; }
|
185
|
-
div.method-source-code {
|
186
|
-
background: #262626;
|
187
|
-
color: #ffdead;
|
188
|
-
margin: 1em;
|
189
|
-
padding: 0.5em;
|
190
|
-
border: 1px dashed #999;
|
191
|
-
overflow: hidden;
|
192
|
-
}
|
193
|
-
|
194
|
-
div.method-source-code pre { color: #ffdead; overflow: hidden; }
|
195
|
-
|
196
|
-
/* --- Ruby keyword styles --------------------- */
|
197
|
-
|
198
|
-
.standalone-code { background: #221111; color: #ffdead; overflow: hidden; }
|
199
|
-
|
200
|
-
.ruby-constant { color: #7fffd4; background: transparent; }
|
201
|
-
.ruby-keyword { color: #00ffff; background: transparent; }
|
202
|
-
.ruby-ivar { color: #eedd82; background: transparent; }
|
203
|
-
.ruby-operator { color: #00ffee; background: transparent; }
|
204
|
-
.ruby-identifier { color: #ffdead; background: transparent; }
|
205
|
-
.ruby-node { color: #ffa07a; background: transparent; }
|
206
|
-
.ruby-comment { color: #b22222; font-weight: bold; background: transparent; }
|
207
|
-
.ruby-regexp { color: #ffa07a; background: transparent; }
|
208
|
-
.ruby-value { color: #7fffd4; background: transparent; }
|
data/specs/timed_cache_spec.rb
DELETED
@@ -1,80 +0,0 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), "../lib/timedcache")
|
2
|
-
|
3
|
-
$filename = File.join(File.dirname(__FILE__), "specs.db")
|
4
|
-
|
5
|
-
context "Adding and retrieving objects from the cache" do
|
6
|
-
setup do
|
7
|
-
@memory_cache = TimedCache.new
|
8
|
-
@file_cache = TimedCache.new(:type => :file, :filename => $filename)
|
9
|
-
@caches = [@memory_cache, @file_cache]
|
10
|
-
end
|
11
|
-
|
12
|
-
teardown do
|
13
|
-
File.delete($filename)
|
14
|
-
end
|
15
|
-
|
16
|
-
specify "Can add an object to the cache, specifying a timeout value" do
|
17
|
-
@caches.each do |cache|
|
18
|
-
cache.put(:myobject, "This needs caching", 10).should_equal "This needs caching"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
specify "Cache should hold seperate values for each key" do
|
23
|
-
@caches.each do |cache|
|
24
|
-
cache.put(:myobject, "This needs caching", 10).should_equal "This needs caching"
|
25
|
-
cache.put(:my_other_object, "...and this too", 10).should_equal "...and this too"
|
26
|
-
cache.get(:myobject).should_equal "This needs caching"
|
27
|
-
cache.get(:my_other_object).should_equal "...and this too"
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
specify "String and symbol keys are not treated as equivalent" do
|
32
|
-
@caches.each do |cache|
|
33
|
-
cache[:symbolkey] = "Referenced by symbol"
|
34
|
-
cache["stringkey"] = "Referenced by string"
|
35
|
-
|
36
|
-
cache[:symbolkey].should_equal "Referenced by symbol"
|
37
|
-
cache["symbolkey"].should_equal nil
|
38
|
-
|
39
|
-
cache["stringkey"].should_equal "Referenced by string"
|
40
|
-
cache[:stringkey].should_equal nil
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
specify "After the specified timeout value has elapsed, nil should be returned" do
|
45
|
-
@caches.each do |cache|
|
46
|
-
cache.put(:myobject, "This needs caching", 0).should_equal "This needs caching"
|
47
|
-
cache.get(:myobject).should_equal nil
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
specify "If no object matching the given key is found, nil should be returned" do
|
52
|
-
@caches.each do |cache|
|
53
|
-
cache.get(:my_nonexistant_object).should_equal nil
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
specify "Should be able to use an array as a cache key" do
|
58
|
-
@caches.each do |cache|
|
59
|
-
cache.put([123,234], "Array").should_equal "Array"
|
60
|
-
cache.get([123,234]).should_equal "Array"
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
context "Specifying a default timeout" do
|
66
|
-
specify "Should be able to specify a default timeout when creating a TimedCache" do
|
67
|
-
cache = TimedCache.new(:default_timeout => 20)
|
68
|
-
cache.should_be_kind_of TimedCache
|
69
|
-
cache.default_timeout.should_equal 20
|
70
|
-
end
|
71
|
-
|
72
|
-
specify "If no default timeout is set, 60 seconds should be used" do
|
73
|
-
cache = TimedCache.new
|
74
|
-
cache.should_be_kind_of TimedCache
|
75
|
-
cache.default_timeout.should_equal 60
|
76
|
-
end
|
77
|
-
|
78
|
-
specify "Timeout specified when putting a new object into the cache should override default timeout" do
|
79
|
-
end
|
80
|
-
end
|