timedcache 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/MIT_LICENSE ADDED
@@ -0,0 +1,7 @@
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 ADDED
@@ -0,0 +1 @@
1
+ Thu Dec 07 18:24:58 GMT 2006
@@ -0,0 +1,27 @@
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>
@@ -0,0 +1,29 @@
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>
@@ -0,0 +1,31 @@
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 ADDED
@@ -0,0 +1,24 @@
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>
@@ -0,0 +1,208 @@
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; }
@@ -0,0 +1,2 @@
1
+ # This is just a pointer to the actual timedcache.rb file.
2
+ require File.join(File.dirname(__FILE__), "timedcache")
data/lib/timedcache.rb ADDED
@@ -0,0 +1,175 @@
1
+ require "pstore"
2
+
3
+ # == TimedCache
4
+ #
5
+ # TimedCache implements a cache in which you can place objects
6
+ # and specify a timeout value.
7
+ #
8
+ # If you attempt to retrieve the object within the specified timeout
9
+ # period, the object will be returned. If the timeout period has elapsed,
10
+ # the TimedCache will return nil.
11
+ #
12
+ # e.g.:
13
+ # cache = TimedCache.new
14
+ # cache.put :my_object_key, "Expensive data", 10 # => "Expensive data"
15
+ #
16
+ # cache.get :my_object_key # => "Expensive data"
17
+ # cache[:my_object_key] # => "Expensive data"
18
+ #
19
+ # ... 10 seconds later:
20
+ # cache.get :my_object_key # => nil
21
+ #
22
+ # === Default timeout
23
+ #
24
+ # When creating a new TimedCache, a default timeout value can be set. This value
25
+ # will be used for each object added to the cache, unless a different timeout value
26
+ # is specifically set for that object.
27
+ #
28
+ # e.g.:
29
+ #
30
+ # cache = TimedCache.new(:default_timeout => 120)
31
+ # cache.default_timeout # => 120
32
+ #
33
+ # === File-based cache
34
+ #
35
+ # By default, TimedCache will use an in-memory store. A file-based store (using the
36
+ # PStore library) can also be used.
37
+ #
38
+ # e.g.:
39
+ #
40
+ # TimedCache.new(:type => :file, :filename => "my_cache.db")
41
+ #
42
+ # Note that objects that cannot be marshalled (e.g. a Proc) can't be stored using the file-based cache.
43
+ class TimedCache
44
+ Version = "0.1"
45
+
46
+ attr_reader :default_timeout
47
+
48
+ # Create a new TimedCache. Available options are:
49
+ # <tt>type</tt>:: <tt>:memory</tt> or <tt>:file</tt> (defaults to <tt>:memory</tt>).
50
+ # <tt>default_timeout</tt>:: Timeout to use if none is specified when adding an object to the cache.
51
+ # <tt>filename</tt>:: Must be specified when using the <tt>:file</tt> type store.
52
+ #
53
+ # e.g.:
54
+ # TimedCache.new(:type => :file, :filename => "cache.db")
55
+ def initialize(opts = {})
56
+ opts[:type] ||= :memory
57
+ @default_timeout = opts[:default_timeout] || 60
58
+ @store = new_store(opts)
59
+ end
60
+
61
+ # Add an object to the cache. e.g.:
62
+ # cache.put(:session_id, 12345)
63
+ #
64
+ # The third parameter is an optional timeout value. If not specified, the
65
+ # <tt>:default_timeout</tt> for this TimedCache will be used instead.
66
+ def put(key, value, timeout = @default_timeout)
67
+ @store.put(key, value, timeout)
68
+ end
69
+
70
+ # Retrieve the object which the given +key+. If the object has expired or
71
+ # is not present, +nil+ is returned.
72
+ def get(key)
73
+ @store.get(key)
74
+ end
75
+
76
+ # Add to the cache using a hash-like syntax. e.g.:
77
+ # cache[:name] = "Nick"
78
+ #
79
+ # Note that adding to the cache this way does not allow you to specify timeout values
80
+ # on a per-object basis.
81
+ def []=(key, value)
82
+ put(key, value)
83
+ end
84
+
85
+ # Fetch objects using the hash syntax. e.g.:
86
+ # cache[:name] # => "Nick"
87
+ def [](key)
88
+ get(key)
89
+ end
90
+
91
+ protected
92
+
93
+ def new_store(options) #:nodoc:
94
+ self.class.const_get(options[:type].to_s.capitalize + "Store").new(options)
95
+ end
96
+
97
+ class Store #:nodoc:
98
+ def initialize(options)
99
+ @options = options
100
+ end
101
+ end
102
+
103
+ class MemoryStore < Store #:nodoc:
104
+ def initialize(options)
105
+ super
106
+ @cache = Hash.new
107
+ end
108
+
109
+ def put(key, value, timeout)
110
+ @cache[key.to_s.intern] = ObjectContainer.new(value, timeout)
111
+ # Return just the given value, so that references to the
112
+ # ObjectStore instance can't be held outside this TimedCache:
113
+ value
114
+ end
115
+
116
+ def get(key)
117
+ if object_store = @cache[key.to_s.intern]
118
+ if object_store.expired?
119
+ # Free up memory:
120
+ @cache[key.to_s.intern] = nil
121
+ else
122
+ object_store.object
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ class FileStore < Store #:nodoc:
129
+ def initialize(*args)
130
+ super(*args)
131
+ filename = @options[:filename]
132
+ unless filename
133
+ raise ArgumentError, ":filename option must be specified for :file type store."
134
+ end
135
+ @cache = PStore.new(filename)
136
+ end
137
+
138
+ def put(key, value, timeout = nil)
139
+ @cache.transaction do
140
+ @cache[key.to_s.intern] = ObjectContainer.new(value, timeout)
141
+ end
142
+
143
+ # Return just the given value, so that references to the
144
+ # ObjectStore instance can't be held outside this TimedCache:
145
+ value
146
+ end
147
+
148
+ def get(key)
149
+ @cache.transaction do
150
+ if object_store = @cache[key.to_s.intern]
151
+ if object_store.expired?
152
+ # Free up memory:
153
+ @cache[key.to_s.intern] = nil
154
+ else
155
+ object_store.object
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ class ObjectContainer #:nodoc:
163
+ attr_reader :object
164
+
165
+ def initialize(object, timeout)
166
+ @created_at = Time.now.utc
167
+ @timeout = timeout
168
+ @object = object
169
+ end
170
+
171
+ def expired?
172
+ (Time.now.utc - @timeout) > @created_at
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,67 @@
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 "After the specified timeout value has elapsed, nil should be returned" do
32
+ @caches.each do |cache|
33
+ cache.put(:myobject, "This needs caching", 0).should_equal "This needs caching"
34
+ cache.get(:myobject).should_equal nil
35
+ end
36
+ end
37
+
38
+ specify "If no object matching the given key is found, nil should be returned" do
39
+ @caches.each do |cache|
40
+ cache.get(:my_nonexistant_object).should_equal nil
41
+ end
42
+ end
43
+
44
+ specify "Should be able to use an array as a cache key" do
45
+ @caches.each do |cache|
46
+ cache.put([123,234], "Array").should_equal "Array"
47
+ cache.get([123,234]).should_equal "Array"
48
+ end
49
+ end
50
+ end
51
+
52
+ context "Specifying a default timeout" do
53
+ specify "Should be able to specify a default timeout when creating a TimedCache" do
54
+ cache = TimedCache.new(:default_timeout => 20)
55
+ cache.should_be_kind_of TimedCache
56
+ cache.default_timeout.should_equal 20
57
+ end
58
+
59
+ specify "If no default timeout is set, 60 seconds should be used" do
60
+ cache = TimedCache.new
61
+ cache.should_be_kind_of TimedCache
62
+ cache.default_timeout.should_equal 60
63
+ end
64
+
65
+ specify "Timeout specified when putting a new object into the cache should override default timeout" do
66
+ end
67
+ end
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.0
3
+ specification_version: 1
4
+ name: timedcache
5
+ version: !ruby/object:Gem::Version
6
+ version: "0.1"
7
+ date: 2006-12-07 00:00:00 +00: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:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Nicholas Dainty
31
+ files:
32
+ - doc/classes
33
+ - doc/created.rid
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: []
45
+
46
+ rdoc_options:
47
+ - --main
48
+ - TimedCache
49
+ - --line-numbers
50
+ - --inline-source
51
+ extra_rdoc_files:
52
+ - MIT_LICENSE
53
+ executables: []
54
+
55
+ extensions: []
56
+
57
+ requirements: []
58
+
59
+ dependencies: []
60
+