ruby-cache 0.3.0

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/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
+