lrucache 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +28 -3
- data/lib/lrucache.rb +85 -30
- data/lib/lrucache/version.rb +1 -1
- data/spec/lrucache_spec.rb +52 -11
- metadata +69 -127
data/README.md
CHANGED
@@ -14,8 +14,8 @@ Example
|
|
14
14
|
puts cache[2] # b
|
15
15
|
puts cache[1] # a
|
16
16
|
puts cache[3] # c
|
17
|
-
puts cache[:does_not_exist] # 42, has no effect on LRU
|
18
|
-
cache[4] = 'd' # Out of space! Throws out the least-recently used (2 => 'b')
|
17
|
+
puts cache[:does_not_exist] # 42, has no effect on LRU
|
18
|
+
cache[4] = 'd' # Out of space! Throws out the least-recently used (2 => 'b')
|
19
19
|
puts cache.keys # [1,3,4]
|
20
20
|
|
21
21
|
|
@@ -37,4 +37,29 @@ TTL (time-to-live)
|
|
37
37
|
|
38
38
|
# Three days later ...
|
39
39
|
cache.fetch("banana") # nil
|
40
|
-
cache.fetch("monkey") # nil
|
40
|
+
cache.fetch("monkey") # nil
|
41
|
+
|
42
|
+
SOFT_TTL
|
43
|
+
========
|
44
|
+
Allows you to have two TTLs when calling fetch with a block.
|
45
|
+
After the soft-ttl expires, the block is called to refresh the value.
|
46
|
+
If the block completes normally, the value is replaced and expirations reset.
|
47
|
+
If the block raises a fatal (non-RuntimeError) exception, it bubbles up. But,
|
48
|
+
if the block raises a RuntimeError, the cached value is kept and used a little
|
49
|
+
longer, and the soft-ttl is postponed retry_delay into the future. If the block
|
50
|
+
is not called successfully before the normal TTL expires, then the cached value
|
51
|
+
expires and the block is called for a new value, but exceptions are not handled.
|
52
|
+
|
53
|
+
cache = LRUCache.new(:ttl => 1.hour,
|
54
|
+
:soft_ttl => 30.minutes,
|
55
|
+
:retry_delay => 1.minute)
|
56
|
+
cache.fetch("banana") { "yellow" } # "yellow"
|
57
|
+
cache.fetch("banana") { "george" } # "yellow"
|
58
|
+
|
59
|
+
# 30 minutes later ...
|
60
|
+
cache.fetch("banana") { raise "ruckus" } # "yellow"
|
61
|
+
cache.fetch("banana") { "george" } # "yellow"
|
62
|
+
|
63
|
+
# 1 more minute later ...
|
64
|
+
cache.fetch("banana") { "george" } # "george"
|
65
|
+
cache.fetch("banana") { "barney" } # "george"
|
data/lib/lrucache.rb
CHANGED
@@ -4,14 +4,19 @@ require "priority_queue"
|
|
4
4
|
# Not thread-safe!
|
5
5
|
class LRUCache
|
6
6
|
|
7
|
-
attr_reader :default, :max_size, :ttl
|
7
|
+
attr_reader :default, :max_size, :ttl, :soft_ttl, :retry_delay
|
8
8
|
|
9
9
|
def initialize(opts={})
|
10
10
|
@max_size = Integer(opts[:max_size] || 100)
|
11
11
|
@default = opts[:default]
|
12
12
|
@ttl = Float(opts[:ttl] || 0)
|
13
|
+
@soft_ttl = Float(opts[:soft_ttl] || 0)
|
14
|
+
@retry_delay = Float(opts[:retry_delay] || 0)
|
13
15
|
raise "max_size must not be negative" if @max_size < 0
|
14
|
-
raise "ttl must be
|
16
|
+
raise "ttl must not be negative" if @ttl < 0
|
17
|
+
raise "soft_ttl must not be negative" if @soft_ttl < 0
|
18
|
+
raise "retry_delay must not be negative" if @retry_delay < 0
|
19
|
+
|
15
20
|
@pqueue = PriorityQueue.new
|
16
21
|
@data = {}
|
17
22
|
@counter = 0
|
@@ -26,49 +31,59 @@ class LRUCache
|
|
26
31
|
def include?(key)
|
27
32
|
datum = @data[key]
|
28
33
|
return false if datum.nil?
|
29
|
-
|
30
|
-
if expires.nil? || expires > Time.now # no expiration, or not expired
|
31
|
-
access(key)
|
32
|
-
true
|
33
|
-
else # expired
|
34
|
+
if datum.expired?
|
34
35
|
delete(key)
|
35
36
|
false
|
37
|
+
else
|
38
|
+
access(key)
|
39
|
+
true
|
36
40
|
end
|
37
41
|
end
|
38
42
|
|
39
|
-
def store(key, value,
|
43
|
+
def store(key, value, args={})
|
40
44
|
evict_lru! unless @data.include?(key) || @data.size < max_size
|
41
|
-
ttl
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
else
|
46
|
-
ttl = Float(ttl)
|
47
|
-
(ttl > 0) ? (Time.now + ttl) : nil
|
48
|
-
end
|
49
|
-
@data[key] = [value, expires]
|
45
|
+
ttl, soft_ttl, retry_delay = extract_arguments(args)
|
46
|
+
expiration = expiration_date(ttl)
|
47
|
+
soft_expiration = expiration_date(soft_ttl)
|
48
|
+
@data[key] = Datum.new(value, expiration, soft_expiration)
|
50
49
|
access(key)
|
50
|
+
value
|
51
51
|
end
|
52
52
|
|
53
53
|
alias :[]= :store
|
54
54
|
|
55
|
-
def fetch(key,
|
55
|
+
def fetch(key, args={})
|
56
56
|
datum = @data[key]
|
57
|
-
|
58
|
-
|
59
|
-
|
57
|
+
if datum.nil?
|
58
|
+
if block_given?
|
59
|
+
store(key, value = yield, args)
|
60
|
+
else
|
61
|
+
@default
|
62
|
+
end
|
63
|
+
elsif datum.expired?
|
64
|
+
delete(key)
|
65
|
+
if block_given?
|
66
|
+
store(key, value = yield, args)
|
67
|
+
else
|
68
|
+
@default
|
69
|
+
end
|
70
|
+
elsif datum.soft_expired?
|
71
|
+
if block_given?
|
72
|
+
begin
|
73
|
+
store(key, value = yield, args)
|
74
|
+
rescue RuntimeError => e
|
75
|
+
access(key)
|
76
|
+
ttl, soft_ttl, retry_delay = extract_arguments(args)
|
77
|
+
datum.soft_expiration = (Time.now + retry_delay) if retry_delay > 0
|
78
|
+
datum.value
|
79
|
+
end
|
80
|
+
else
|
60
81
|
access(key)
|
61
|
-
|
62
|
-
else # expired
|
63
|
-
delete(key)
|
82
|
+
datum.value
|
64
83
|
end
|
65
|
-
end
|
66
|
-
if block_given?
|
67
|
-
value = yield
|
68
|
-
store(key, value, ttl)
|
69
|
-
value
|
70
84
|
else
|
71
|
-
|
85
|
+
access(key)
|
86
|
+
datum.value
|
72
87
|
end
|
73
88
|
end
|
74
89
|
|
@@ -93,6 +108,46 @@ class LRUCache
|
|
93
108
|
|
94
109
|
private
|
95
110
|
|
111
|
+
class Datum
|
112
|
+
attr_reader :value, :expiration, :soft_expiration
|
113
|
+
attr_writer :soft_expiration
|
114
|
+
def initialize(value, expiration, soft_expiration)
|
115
|
+
@value = value
|
116
|
+
@expiration = expiration
|
117
|
+
@soft_expiration = soft_expiration
|
118
|
+
end
|
119
|
+
|
120
|
+
def expired?
|
121
|
+
!@expiration.nil? && @expiration <= Time.now
|
122
|
+
end
|
123
|
+
|
124
|
+
def soft_expired?
|
125
|
+
!@soft_expiration.nil? && @soft_expiration <= Time.now
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def expiration_date(ttl)
|
130
|
+
if ttl.is_a?(Time)
|
131
|
+
ttl
|
132
|
+
else
|
133
|
+
ttl = Float(ttl)
|
134
|
+
(ttl > 0) ? (Time.now + ttl) : nil
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def extract_arguments(args)
|
139
|
+
if args.is_a?(Hash)
|
140
|
+
ttl = args[:ttl] || @ttl
|
141
|
+
soft_ttl = args[:soft_ttl] || @soft_ttl
|
142
|
+
retry_delay = args[:retry_delay] || @retry_delay
|
143
|
+
[ttl, soft_ttl, retry_delay]
|
144
|
+
else
|
145
|
+
# legacy arg
|
146
|
+
ttl = args || @ttl
|
147
|
+
[ttl, @soft_ttl, @retry_delay]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
96
151
|
def evict_lru!
|
97
152
|
key, priority = @pqueue.delete_min
|
98
153
|
@data.delete(key) unless priority.nil?
|
data/lib/lrucache/version.rb
CHANGED
data/spec/lrucache_spec.rb
CHANGED
@@ -197,7 +197,8 @@ describe LRUCache do
|
|
197
197
|
c = LRUCache.new(:ttl => 0)
|
198
198
|
c.store(:a,'a')
|
199
199
|
stored = c.instance_variable_get(:@data)[:a]
|
200
|
-
stored.should ==
|
200
|
+
stored.value.should == 'a'
|
201
|
+
stored.expiration.should == nil
|
201
202
|
end
|
202
203
|
end
|
203
204
|
context "when ttl is not given and the cache's default ttl is greater than zero" do
|
@@ -206,7 +207,8 @@ describe LRUCache do
|
|
206
207
|
now = Time.now
|
207
208
|
Timecop.freeze(now) { c.store(:a,'a') }
|
208
209
|
stored = c.instance_variable_get(:@data)[:a]
|
209
|
-
stored.
|
210
|
+
stored.value.should == 'a'
|
211
|
+
stored.expiration.should == now + 1
|
210
212
|
end
|
211
213
|
end
|
212
214
|
context "when ttl is a Time" do
|
@@ -215,7 +217,8 @@ describe LRUCache do
|
|
215
217
|
ttl = Time.now + 246
|
216
218
|
c.store(:a, 'a', ttl)
|
217
219
|
stored = c.instance_variable_get(:@data)[:a]
|
218
|
-
stored.
|
220
|
+
stored.value.should == 'a'
|
221
|
+
stored.expiration.should == ttl
|
219
222
|
end
|
220
223
|
end
|
221
224
|
context "when ttl can be parsed as a float" do
|
@@ -224,7 +227,8 @@ describe LRUCache do
|
|
224
227
|
now = Time.now
|
225
228
|
Timecop.freeze(now) { c.store(:a, 'a', "98.6") }
|
226
229
|
stored = c.instance_variable_get(:@data)[:a]
|
227
|
-
stored.
|
230
|
+
stored.value.should == 'a'
|
231
|
+
stored.expiration.should == now + 98.6
|
228
232
|
end
|
229
233
|
end
|
230
234
|
context "when ttl cannot be parsed as a float" do
|
@@ -302,27 +306,27 @@ describe LRUCache do
|
|
302
306
|
end
|
303
307
|
end
|
304
308
|
context "when a block is given" do
|
305
|
-
context "
|
309
|
+
context "and the key does not exist" do
|
306
310
|
it "should call the block and store and return the result" do
|
307
311
|
c = LRUCache.new
|
308
312
|
ttl = double(:ttl)
|
309
313
|
result = double(:result)
|
310
|
-
c.should_receive(:store).with(:a, result, ttl)
|
314
|
+
c.should_receive(:store).with(:a, result, ttl).and_return(result)
|
311
315
|
c.fetch(:a, ttl){ result }.should == result
|
312
316
|
end
|
313
317
|
end
|
314
|
-
context "
|
318
|
+
context "and the key has been evicted" do
|
315
319
|
it "should call the block and store and return the result" do
|
316
320
|
c = LRUCache.new
|
317
321
|
c[:a] = 'a'
|
318
322
|
c.send(:evict_lru!)
|
319
323
|
ttl = double(:ttl)
|
320
324
|
result = double(:result)
|
321
|
-
c.should_receive(:store).with(:a, result, ttl)
|
325
|
+
c.should_receive(:store).with(:a, result, ttl).and_return(result)
|
322
326
|
c.fetch(:a, ttl){ result }.should == result
|
323
327
|
end
|
324
328
|
end
|
325
|
-
context "
|
329
|
+
context "and the key has expired" do
|
326
330
|
it "should call the block and store and return the result" do
|
327
331
|
c = LRUCache.new
|
328
332
|
now = Time.now
|
@@ -330,12 +334,49 @@ describe LRUCache do
|
|
330
334
|
Timecop.freeze(now + 20) do
|
331
335
|
ttl = double(:ttl)
|
332
336
|
result = double(:result)
|
333
|
-
c.should_receive(:store).with(:a, result, ttl)
|
337
|
+
c.should_receive(:store).with(:a, result, ttl).and_return(result)
|
334
338
|
c.fetch(:a, ttl){ result }.should == result
|
335
339
|
end
|
336
340
|
end
|
337
341
|
end
|
338
|
-
context
|
342
|
+
context 'and the key has "soft"-expired' do
|
343
|
+
before(:each) do
|
344
|
+
@c = LRUCache.new
|
345
|
+
@c.store(:a, 'a', :ttl => 10_000, :soft_ttl => Time.now - 60, :retry_delay => 10)
|
346
|
+
@args = {:ttl => 10_000, :soft_ttl => 60, :retry_delay => 10}
|
347
|
+
end
|
348
|
+
context "and the block raises a runtime exception" do
|
349
|
+
it "should continue to return the old value" do
|
350
|
+
@c.should_not_receive(:store)
|
351
|
+
@c.fetch(:a, @args) { raise "no!" }.should == 'a'
|
352
|
+
end
|
353
|
+
it "should extend the soft-expiration by retry_delay" do
|
354
|
+
Timecop.freeze(Time.now) do
|
355
|
+
data = @c.instance_variable_get(:@data)
|
356
|
+
original_soft_expiration = data[:a].soft_expiration
|
357
|
+
@c.should_not_receive(:store)
|
358
|
+
@c.fetch(:a, @args) { raise "no!" }
|
359
|
+
data = @c.instance_variable_get(:@data)
|
360
|
+
data[:a].soft_expiration.should == Time.now + @args[:retry_delay]
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
context "and the block raises a fatal exception" do
|
365
|
+
it "should allow the exception through" do
|
366
|
+
expect {
|
367
|
+
@c.fetch(:a, @args) { raise(NoMemoryError,"panic!") }
|
368
|
+
}.to raise_exception(NoMemoryError)
|
369
|
+
end
|
370
|
+
end
|
371
|
+
context "and the block does not raise an exception" do
|
372
|
+
it "should call the block and store and return the result" do
|
373
|
+
result = double(:result)
|
374
|
+
@c.should_receive(:store).with(:a, result, @args).and_return(result)
|
375
|
+
@c.fetch(:a, @args) { result }.should == result
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
379
|
+
context "and the key is present and un-expired" do
|
339
380
|
it "should return the cached value without calling the block" do
|
340
381
|
c = LRUCache.new(:ttl => nil)
|
341
382
|
c[:a] = 'a'
|
metadata
CHANGED
@@ -1,160 +1,111 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: lrucache
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 1
|
9
|
-
- 1
|
10
|
-
version: 0.1.1
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Chris Johnson
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
12
|
+
date: 2012-03-31 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: PriorityQueue
|
16
|
+
requirement: &70184418403860 !ruby/object:Gem::Requirement
|
22
17
|
none: false
|
23
|
-
requirements:
|
18
|
+
requirements:
|
24
19
|
- - ~>
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
hash: 31
|
27
|
-
segments:
|
28
|
-
- 0
|
29
|
-
- 1
|
30
|
-
- 2
|
20
|
+
- !ruby/object:Gem::Version
|
31
21
|
version: 0.1.2
|
32
|
-
requirement: *id001
|
33
22
|
type: :runtime
|
34
23
|
prerelease: false
|
35
|
-
|
36
|
-
- !ruby/object:Gem::Dependency
|
37
|
-
|
24
|
+
version_requirements: *70184418403860
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &70184418403360 !ruby/object:Gem::Requirement
|
38
28
|
none: false
|
39
|
-
requirements:
|
29
|
+
requirements:
|
40
30
|
- - ~>
|
41
|
-
- !ruby/object:Gem::Version
|
42
|
-
hash: 23
|
43
|
-
segments:
|
44
|
-
- 2
|
45
|
-
- 6
|
46
|
-
- 0
|
31
|
+
- !ruby/object:Gem::Version
|
47
32
|
version: 2.6.0
|
48
|
-
requirement: *id002
|
49
33
|
type: :development
|
50
34
|
prerelease: false
|
51
|
-
|
52
|
-
- !ruby/object:Gem::Dependency
|
53
|
-
|
35
|
+
version_requirements: *70184418403360
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: simplecov
|
38
|
+
requirement: &70184418402900 !ruby/object:Gem::Requirement
|
54
39
|
none: false
|
55
|
-
requirements:
|
40
|
+
requirements:
|
56
41
|
- - ~>
|
57
|
-
- !ruby/object:Gem::Version
|
58
|
-
hash: 11
|
59
|
-
segments:
|
60
|
-
- 0
|
61
|
-
- 4
|
62
|
-
- 2
|
42
|
+
- !ruby/object:Gem::Version
|
63
43
|
version: 0.4.2
|
64
|
-
requirement: *id003
|
65
44
|
type: :development
|
66
45
|
prerelease: false
|
67
|
-
|
68
|
-
- !ruby/object:Gem::Dependency
|
69
|
-
|
46
|
+
version_requirements: *70184418402900
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rb-fsevent
|
49
|
+
requirement: &70184418402400 !ruby/object:Gem::Requirement
|
70
50
|
none: false
|
71
|
-
requirements:
|
51
|
+
requirements:
|
72
52
|
- - ~>
|
73
|
-
- !ruby/object:Gem::Version
|
74
|
-
hash: 9
|
75
|
-
segments:
|
76
|
-
- 0
|
77
|
-
- 4
|
78
|
-
- 3
|
53
|
+
- !ruby/object:Gem::Version
|
79
54
|
version: 0.4.3
|
80
|
-
requirement: *id004
|
81
55
|
type: :development
|
82
56
|
prerelease: false
|
83
|
-
|
84
|
-
- !ruby/object:Gem::Dependency
|
85
|
-
|
57
|
+
version_requirements: *70184418402400
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: guard
|
60
|
+
requirement: &70184418401940 !ruby/object:Gem::Requirement
|
86
61
|
none: false
|
87
|
-
requirements:
|
62
|
+
requirements:
|
88
63
|
- - ~>
|
89
|
-
- !ruby/object:Gem::Version
|
90
|
-
hash: 3
|
91
|
-
segments:
|
92
|
-
- 0
|
93
|
-
- 6
|
94
|
-
- 2
|
64
|
+
- !ruby/object:Gem::Version
|
95
65
|
version: 0.6.2
|
96
|
-
requirement: *id005
|
97
66
|
type: :development
|
98
67
|
prerelease: false
|
99
|
-
|
100
|
-
- !ruby/object:Gem::Dependency
|
101
|
-
|
68
|
+
version_requirements: *70184418401940
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: guard-bundler
|
71
|
+
requirement: &70184418401480 !ruby/object:Gem::Requirement
|
102
72
|
none: false
|
103
|
-
requirements:
|
73
|
+
requirements:
|
104
74
|
- - ~>
|
105
|
-
- !ruby/object:Gem::Version
|
106
|
-
hash: 29
|
107
|
-
segments:
|
108
|
-
- 0
|
109
|
-
- 1
|
110
|
-
- 3
|
75
|
+
- !ruby/object:Gem::Version
|
111
76
|
version: 0.1.3
|
112
|
-
requirement: *id006
|
113
77
|
type: :development
|
114
78
|
prerelease: false
|
115
|
-
|
116
|
-
- !ruby/object:Gem::Dependency
|
117
|
-
|
79
|
+
version_requirements: *70184418401480
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: guard-rspec
|
82
|
+
requirement: &70184418401020 !ruby/object:Gem::Requirement
|
118
83
|
none: false
|
119
|
-
requirements:
|
84
|
+
requirements:
|
120
85
|
- - ~>
|
121
|
-
- !ruby/object:Gem::Version
|
122
|
-
hash: 11
|
123
|
-
segments:
|
124
|
-
- 0
|
125
|
-
- 4
|
126
|
-
- 2
|
86
|
+
- !ruby/object:Gem::Version
|
127
87
|
version: 0.4.2
|
128
|
-
requirement: *id007
|
129
88
|
type: :development
|
130
89
|
prerelease: false
|
131
|
-
|
132
|
-
- !ruby/object:Gem::Dependency
|
133
|
-
|
90
|
+
version_requirements: *70184418401020
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: timecop
|
93
|
+
requirement: &70184418400560 !ruby/object:Gem::Requirement
|
134
94
|
none: false
|
135
|
-
requirements:
|
95
|
+
requirements:
|
136
96
|
- - ~>
|
137
|
-
- !ruby/object:Gem::Version
|
138
|
-
hash: 25
|
139
|
-
segments:
|
140
|
-
- 0
|
141
|
-
- 3
|
142
|
-
- 5
|
97
|
+
- !ruby/object:Gem::Version
|
143
98
|
version: 0.3.5
|
144
|
-
requirement: *id008
|
145
99
|
type: :development
|
146
100
|
prerelease: false
|
147
|
-
|
101
|
+
version_requirements: *70184418400560
|
148
102
|
description: A simple LRU-cache based on a hash and priority queue
|
149
|
-
email:
|
103
|
+
email:
|
150
104
|
- chris@kindkid.com
|
151
105
|
executables: []
|
152
|
-
|
153
106
|
extensions: []
|
154
|
-
|
155
107
|
extra_rdoc_files: []
|
156
|
-
|
157
|
-
files:
|
108
|
+
files:
|
158
109
|
- .gitignore
|
159
110
|
- .rvmrc
|
160
111
|
- Gemfile
|
@@ -166,39 +117,30 @@ files:
|
|
166
117
|
- lrucache.gemspec
|
167
118
|
- spec/lrucache_spec.rb
|
168
119
|
- spec/spec_helper.rb
|
169
|
-
homepage:
|
120
|
+
homepage: ''
|
170
121
|
licenses: []
|
171
|
-
|
172
122
|
post_install_message:
|
173
123
|
rdoc_options: []
|
174
|
-
|
175
|
-
require_paths:
|
124
|
+
require_paths:
|
176
125
|
- lib
|
177
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
126
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
178
127
|
none: false
|
179
|
-
requirements:
|
180
|
-
- -
|
181
|
-
- !ruby/object:Gem::Version
|
182
|
-
|
183
|
-
|
184
|
-
- 0
|
185
|
-
version: "0"
|
186
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ! '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
187
133
|
none: false
|
188
|
-
requirements:
|
189
|
-
- -
|
190
|
-
- !ruby/object:Gem::Version
|
191
|
-
|
192
|
-
segments:
|
193
|
-
- 0
|
194
|
-
version: "0"
|
134
|
+
requirements:
|
135
|
+
- - ! '>='
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
195
138
|
requirements: []
|
196
|
-
|
197
139
|
rubyforge_project: lrucache
|
198
140
|
rubygems_version: 1.8.10
|
199
141
|
signing_key:
|
200
142
|
specification_version: 3
|
201
143
|
summary: A simple LRU-cache based on a hash and priority queue
|
202
|
-
test_files:
|
144
|
+
test_files:
|
203
145
|
- spec/lrucache_spec.rb
|
204
146
|
- spec/spec_helper.rb
|