lrucache 0.1.1 → 0.1.2
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.
- 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
|