ruby-cache 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/Manual.rd +155 -0
  2. data/README.rd +53 -0
  3. data/Rakefile +21 -0
  4. data/lib/cache.rb +296 -0
  5. data/sample/filecache.rb +51 -0
  6. metadata +59 -0
@@ -0,0 +1,155 @@
1
+ =begin
2
+
3
+ = Ruby/Cache Reference Manual
4
+
5
+ This is the reference manual for
6
+ ((<"Ruby/Cache"|URL:http://www.nongnu.org/pupa/ruby-cache.html>)) version 0.3.
7
+
8
+ Ruby/Cache provides a class for caching arbitrary objects based on LRU
9
+ algorithm. The class (({Cache})) looks like a variant of (({Hash})), and,
10
+ in fact, (({Cache})) supports most of the methods of (({Hash})).
11
+
12
+ To control how to invalidate excessive objects, Ruby/Cache allows you to
13
+ adjust the limit by four factors: the size of an object, the total size
14
+ of objects, the number of objects, and a last access time.
15
+
16
+ == Class:
17
+
18
+ Cache
19
+
20
+ == Superclass:
21
+
22
+ Object
23
+
24
+ == Included module:
25
+
26
+ Enumerable
27
+
28
+ == Class methods:
29
+
30
+ --- Cache.version
31
+ Return the version number.
32
+
33
+ --- Cache.new([max_obj_size, max_size, max_num, expiration, &hook])
34
+ --- Cache.new(hash, &hook)
35
+ Create a new Cache object.
36
+
37
+ ((|max_obj_size|)) is the maximum size per object allowed to be cached.
38
+ The size is currently determined by (({obj.to_s.size})).
39
+
40
+ ((|max_size|)) is the maximum size of all objects allowed to be cached.
41
+ The size is currently determined by the sum of the sizes of all objects.
42
+
43
+ ((|max_num|)) is the maximum number of cached objects.
44
+
45
+ ((|expiration|)) is the maximum life time of each object after the last
46
+ access time, and is specified by seconds.
47
+
48
+ ((|hook|)) is called whenever an object is invalidated, in the form
49
+ (({hook(key, value)})). So you can use ((|hook|)) for cleanups.
50
+
51
+ If the latter form is used, ((|hash|)) must be a Hash object, and it
52
+ represents keyword arguments. ((|hash|)) can have any combination of
53
+ these keys: (({:max_obj_size})), (({:max_size})), (({:max_num})) and
54
+ (({:expiration})). The meanings are the same as above.
55
+
56
+ == Methods:
57
+
58
+ --- max_obj_size
59
+ Return the maximum size per object.
60
+
61
+ --- max_size
62
+ Return the maximum size of all objects.
63
+
64
+ --- max_num
65
+ Return the maximum number of objects.
66
+
67
+ --- expiration
68
+ Return the maximum life time.
69
+
70
+ --- cached?(key)
71
+ --- include?(key)
72
+ --- member?(key)
73
+ --- key?(key)
74
+ --- has_key?(key)
75
+ Return (({true})), if the key ((|key|)) is cached.
76
+
77
+ --- cached_value?(val)
78
+ --- has_value?(val)
79
+ --- value?(val)
80
+ Return (({true})), if the value ((|val|)) is cached.
81
+
82
+ --- index(val)
83
+ Return the key corresponding to the value ((|val|)), if any. Otherwise
84
+ return (({nil})).
85
+
86
+ --- keys
87
+ Return an array of keys.
88
+
89
+ --- values
90
+ Return an array of cached objects.
91
+
92
+ --- length
93
+ --- size
94
+ Return the number of cached objects.
95
+
96
+ --- to_hash
97
+ Return keys and cached objects as a Hash object.
98
+
99
+ --- invalidate(key)
100
+ --- invalidate(key) {|key| ... }
101
+ --- delete(key)
102
+ --- delete(key) {|key| ... }
103
+ Invalidate a cached object indexed by the key ((|key|)), and return
104
+ the object. If the key isn't present, return (({nil})). If a block
105
+ is given and the key is invalid, evaluate the block and return the
106
+ result.
107
+
108
+ --- invalidate_all
109
+ --- clear
110
+ Invalidate all cached objects.
111
+
112
+ --- expire
113
+ Invalidate expired objects. This method is often called from other methods
114
+ automatically, so you wouldn't have to call it explicitly.
115
+
116
+ --- self[key]
117
+ Return a cached object corresponding to the key ((|key|)). If the key
118
+ isn't found, return (({nil})).
119
+
120
+ --- self[key]=value
121
+ --- store(key, value)
122
+ Cache the object ((|value|)) with the key ((|key|)), if possible.
123
+ Return ((|value|)).
124
+
125
+ --- each_pair {|key, obj| ... }
126
+ --- each {|key, obj| ... }
127
+ Evaluate the block with each key and a cached object corresponding to
128
+ the key.
129
+
130
+ --- each_key {|key| ... }
131
+ Evaluate the block with each key.
132
+
133
+ --- each_value {|obj| ... }
134
+ Evaluate the block with each cached object.
135
+
136
+ --- empty?
137
+ Return (({true})), if no object is cached.
138
+
139
+ --- fetch(key[, default])
140
+ --- fetch(key) {|key| ... }
141
+ Return a cached object corresponding to the key ((|key|)).
142
+ If the key isn't found and ((|default|)) is given, cache ((|default|))
143
+ as a object corresponding to ((|key|)) and return ((|default|)).
144
+ Or, if the key isn't found and a block is given, evaluate the block
145
+ with ((|key|)), cache the result, and return it.
146
+
147
+ Normally, you should use this method rather than (({self[key]})) plus
148
+ (({self[key]=value})), because this is safer.
149
+
150
+ --- statistics
151
+ Return an array of the total size of cached objects, the number of
152
+ cached objects, the number of cache hits, and the number of cache
153
+ misses.
154
+
155
+ =end
@@ -0,0 +1,53 @@
1
+ =begin
2
+ = Ruby/Cache 0.3
3
+ == What is Ruby/Cache
4
+
5
+ Ruby/Cache is a library for caching objects based on the LRU algorithm
6
+ for Ruby. The official page is
7
+ ((<URL:http://www.nongnu.org/pupa/ruby-cache.html>)).
8
+
9
+ == How to install
10
+
11
+ (1) $ ruby install.rb config
12
+ (2) $ ruby install.rb setup
13
+ (3) $ ruby install.rb install
14
+
15
+ == How to use
16
+
17
+ Here is a simple usage:
18
+
19
+ require 'cache'
20
+
21
+ cache = Cache.new
22
+ cache['foo'] = 'bar'
23
+ p cache['foo']
24
+ cache.invalidate('foo')
25
+ p cache['foo']
26
+
27
+ =>
28
+
29
+ "bar"
30
+ nil
31
+
32
+ This is a more complicated example:
33
+
34
+ # Set the maximum number of cached objects and the expiration time to
35
+ # 100 and 60 (secs), respectively.
36
+ cache = Cache.new(nil, nil, 100, 60)
37
+ puts 'I will generate a prime number greater than any given number!'
38
+ while true
39
+ puts 'Input an integer: '
40
+ i = gets.to_i
41
+ puts cache.fetch(i) { generate_prime_greater_than(i) }
42
+ end
43
+
44
+ == License
45
+
46
+ You may redistribute it and/or modify it under the same term as Ruby's.
47
+
48
+ == Author
49
+
50
+ ((<Yoshinori K. Okuji|URL:http://www.enbug.org/>))
51
+ ((<<okuji@enbug.org>|URL:mailto:okuji@enbug.org>))
52
+
53
+ =end
@@ -0,0 +1,21 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+
4
+ spec = Gem::Specification.new do |s|
5
+ s.name = "ruby-cache"
6
+ s.version = "0.3.0"
7
+ s.summary = "Ruby/Cache is a library for caching objects based on the LRU algorithm for Ruby"
8
+ s.author = "Yoshinori K. Okuji"
9
+ s.email = "okuji@enbug.org"
10
+ s.homepage = "http://www.enbug.org/"
11
+ s.description = s.summary
12
+ s.rubyforge_project = "N/A"
13
+ s.has_rdoc = true
14
+ files = Dir.glob("{lib,sample}/*")
15
+ files << 'README.rd' << 'Manual.rd' << 'Rakefile'
16
+ s.files = files
17
+ end
18
+
19
+ Rake::GemPackageTask.new(spec) do |pkg|
20
+ pkg.need_zip = true
21
+ end
@@ -0,0 +1,296 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # Copyright (C) 2002 Yoshinori K. Okuji <okuji@enbug.org>
4
+ #
5
+ # You may redistribute it and/or modify it under the same term as Ruby.
6
+
7
+ # Cache manager based on the LRU algorithm.
8
+ class Cache
9
+
10
+ CACHE_OBJECT = Struct.new('CacheObject', :content, :size, :atime)
11
+ CACHE_VERSION = '0.3'
12
+
13
+ include Enumerable
14
+
15
+ def self.version
16
+ CACHE_VERSION
17
+ end
18
+
19
+ # initialize(max_obj_size = nil, max_size = nil, max_num = nil,
20
+ # expiration = nil, &hook)
21
+ # initialize(hash, &hook)
22
+ def initialize(*args, &hook)
23
+ if args.size == 1 and args[0].kind_of?(Hash)
24
+ @max_obj_size = @max_size = @max_num = @expiration = nil
25
+ args[0].each do |k, v|
26
+ k = k.intern if k.respond_to?(:intern)
27
+ case k
28
+ when :max_obj_size
29
+ @max_obj_size = v
30
+ when :max_size
31
+ @max_size = v
32
+ when :max_num
33
+ @max_num = v
34
+ when :expiration
35
+ @expiration = v
36
+ end
37
+ end
38
+ else
39
+ @max_obj_size, @max_size, @max_num, @expiration = args
40
+ end
41
+
42
+ # Sanity checks.
43
+ if @max_obj_size and @max_size and @max_obj_size > @max_size
44
+ raise ArgumentError, "max_obj_size exceeds max_size (#{@max_obj_size} > #{@max_size})"
45
+ end
46
+ if @max_obj_size and @max_obj_size <= 0
47
+ raise ArgumentError, "invalid max_obj_size `#{@max_obj_size}'"
48
+ end
49
+ if @max_size and @max_size <= 0
50
+ raise ArgumentError, "invalid max_size `#{@max_size}'"
51
+ end
52
+ if @max_num and @max_num <= 0
53
+ raise ArgumentError, "invalid max_num `#{@max_num}'"
54
+ end
55
+ if @expiration and @expiration <= 0
56
+ raise ArgumentError, "invalid expiration `#{@expiration}'"
57
+ end
58
+
59
+ @hook = hook
60
+
61
+ @objs = {}
62
+ @size = 0
63
+ @list = []
64
+
65
+ @hits = 0
66
+ @misses = 0
67
+ end
68
+
69
+ attr_reader :max_obj_size, :max_size, :max_num, :expiration
70
+
71
+ def cached?(key)
72
+ @objs.include?(key)
73
+ end
74
+ alias :include? :cached?
75
+ alias :member? :cached?
76
+ alias :key? :cached?
77
+ alias :has_key? :cached?
78
+
79
+ def cached_value?(val)
80
+ self.each_value do |v|
81
+ return true if v == val
82
+ end
83
+ false
84
+ end
85
+ alias :has_value? :cached_value?
86
+ alias :value? :cached_value?
87
+
88
+ def index(val)
89
+ self.each_pair do |k,v|
90
+ return k if v == val
91
+ end
92
+ nil
93
+ end
94
+
95
+ def keys
96
+ @objs.keys
97
+ end
98
+
99
+ def length
100
+ @objs.length
101
+ end
102
+ alias :size :length
103
+
104
+ def to_hash
105
+ @objs.dup
106
+ end
107
+
108
+ def values
109
+ @objs.collect {|key, obj| obj.content}
110
+ end
111
+
112
+ def invalidate(key)
113
+ obj = @objs[key]
114
+ if obj
115
+ if @hook
116
+ @hook.call(key, obj.content)
117
+ end
118
+ @size -= obj.size
119
+ @objs.delete(key)
120
+ @list.each_index do |i|
121
+ if @list[i] == key
122
+ @list.delete_at(i)
123
+ break
124
+ end
125
+ end
126
+ elsif block_given?
127
+ return yield(key)
128
+ end
129
+ obj.content
130
+ end
131
+ alias :delete :invalidate
132
+
133
+ def invalidate_all()
134
+ if @hook
135
+ @objs.each do |key, obj|
136
+ @hook.call(key, obj)
137
+ end
138
+ end
139
+
140
+ @objs.clear
141
+ @list.clear
142
+ @size = 0
143
+ end
144
+ alias :clear :invalidate_all
145
+
146
+ def expire()
147
+ if @expiration
148
+ now = Time.now.to_i
149
+ @list.each_index do |i|
150
+ key = @list[i]
151
+
152
+ break unless @objs[key].atime + @expiration <= now
153
+ self.invalidate(key)
154
+ end
155
+ end
156
+ # GC.start
157
+ end
158
+
159
+ def [](key)
160
+ self.expire()
161
+
162
+ unless @objs.include?(key)
163
+ @misses += 1
164
+ return nil
165
+ end
166
+
167
+ obj = @objs[key]
168
+ obj.atime = Time.now.to_i
169
+
170
+ @list.each_index do |i|
171
+ if @list[i] == key
172
+ @list.delete_at(i)
173
+ break
174
+ end
175
+ end
176
+ @list.push(key)
177
+
178
+ @hits += 1
179
+ obj.content
180
+ end
181
+
182
+ def []=(key, obj)
183
+ self.expire()
184
+
185
+ if self.cached?(key)
186
+ self.invalidate(key)
187
+ end
188
+
189
+ size = obj.to_s.size
190
+ if @max_obj_size and @max_obj_size < size
191
+ if $DEBUG
192
+ $stderr.puts("warning: `#{obj.inspect}' isn't cached because its size exceeds #{@max_obj_size}")
193
+ end
194
+ return obj
195
+ end
196
+ if @max_obj_size.nil? and @max_size and @max_size < size
197
+ if $DEBUG
198
+ $stderr.puts("warning: `#{obj.inspect}' isn't cached because its size exceeds #{@max_size}")
199
+ end
200
+ return obj
201
+ end
202
+
203
+ if @max_num and @max_num == @list.size
204
+ self.invalidate(@list.first)
205
+ end
206
+
207
+ @size += size
208
+ if @max_size
209
+ while @max_size < @size
210
+ self.invalidate(@list.first)
211
+ end
212
+ end
213
+
214
+ @objs[key] = CACHE_OBJECT.new(obj, size, Time.now.to_i)
215
+ @list.push(key)
216
+
217
+ obj
218
+ end
219
+
220
+ def store(key, value)
221
+ self[key] = value
222
+ end
223
+
224
+ def each_pair
225
+ @objs.each do |key, obj|
226
+ yield key, obj.content
227
+ end
228
+ self
229
+ end
230
+ alias :each :each_pair
231
+
232
+ def each_key
233
+ @objs.each_key do |key|
234
+ yield key
235
+ end
236
+ self
237
+ end
238
+
239
+ def each_value
240
+ @objs.each_value do |obj|
241
+ yield obj.content
242
+ end
243
+ self
244
+ end
245
+
246
+ def empty?
247
+ @objs.empty?
248
+ end
249
+
250
+ def fetch(key, default = nil)
251
+ val = self[key]
252
+ if val.nil?
253
+ if default
254
+ val = self[key] = default
255
+ elsif block_given?
256
+ val = self[key] = yield(key)
257
+ else
258
+ raise IndexError, "invalid key `#{key}'"
259
+ end
260
+ end
261
+ val
262
+ end
263
+
264
+ # The total size of cached objects, the number of cached objects,
265
+ # the number of cache hits, and the number of cache misses.
266
+ def statistics()
267
+ [@size, @list.size, @hits, @misses]
268
+ end
269
+ end
270
+
271
+ # Run a test, if executed.
272
+ if __FILE__ == $0
273
+ cache = Cache.new(100 * 1024, 100 * 1024 * 1024, 256, 1)
274
+ 1000.times do
275
+ key = rand(1000)
276
+ cache[key] = key.to_s
277
+ end
278
+ 1000.times do
279
+ key = rand(1000)
280
+ puts cache[key]
281
+ end
282
+ sleep 1
283
+ 1000.times do
284
+ key = rand(1000)
285
+ puts cache[key]
286
+ end
287
+
288
+ stat = cache.statistics()
289
+ hits = stat[2]
290
+ misses = stat[3]
291
+ ratio = hits.to_f / (hits + misses)
292
+
293
+ puts "Total size:\t#{stat[0]}"
294
+ puts "Number:\t\t#{stat[1]}"
295
+ puts "Hit ratio:\t#{ratio * 100}% (#{hits} / #{hits + misses})"
296
+ end
@@ -0,0 +1,51 @@
1
+ # Copyright (C) 2002 Koji Arai
2
+ # Copyright (C) 2002 Yoshinori K. Okuji
3
+ #
4
+ # You may redistribute it and/or modify it under the same term as Ruby.
5
+
6
+ require 'cache'
7
+
8
+ class FileCache
9
+ def initialize(maxopen = 10)
10
+ @cache = Cache.new(:max_num => maxopen) {|key, obj| obj.close}
11
+ @saw = {}
12
+ end
13
+
14
+ def open(file)
15
+ @cache.fetch(file) do
16
+ mode = if @saw.key?(file) then 'a' else 'w' end
17
+ @saw[file] = true
18
+ File.open(file, mode)
19
+ end
20
+ end
21
+
22
+ def close
23
+ @cache.invalidate_all
24
+ end
25
+ end
26
+
27
+ if $0 == __FILE__
28
+ File.open("/tmp/foo", "w") {|f|
29
+ 1000.times {|i|
30
+ f.printf("file%03d %d\n", rand(100), i)
31
+ }
32
+ }
33
+
34
+ # /tmp/foo
35
+ # foo001 0
36
+ # foo099 1
37
+ # foo050 2
38
+ # foo001 3
39
+ # :
40
+ # :
41
+
42
+ cacheout = FileCache.new(10)
43
+
44
+ File.open("/tmp/foo") {|f|
45
+ while line = f.gets
46
+ file, number = line.split
47
+
48
+ cacheout.open(file).puts number
49
+ end
50
+ }
51
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-cache
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Yoshinori K. Okuji
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-21 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Ruby/Cache is a library for caching objects based on the LRU algorithm for Ruby
17
+ email: okuji@enbug.org
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - lib/cache.rb
26
+ - sample/filecache.rb
27
+ - README.rd
28
+ - Manual.rd
29
+ - Rakefile
30
+ has_rdoc: true
31
+ homepage: http://www.enbug.org/
32
+ licenses: []
33
+
34
+ post_install_message:
35
+ rdoc_options: []
36
+
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ requirements: []
52
+
53
+ rubyforge_project: N/A
54
+ rubygems_version: 1.3.5
55
+ signing_key:
56
+ specification_version: 3
57
+ summary: Ruby/Cache is a library for caching objects based on the LRU algorithm for Ruby
58
+ test_files: []
59
+