lrucache 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ coverage/*
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use ruby-1.9.2@lrucache
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in lrucache.gemspec
4
+ gemspec
@@ -0,0 +1,14 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'bundler' do
5
+ watch('Gemfile')
6
+ watch(/^.+\.gemspec/)
7
+ end
8
+
9
+ guard 'rspec', :cli => '-c --format documentation -r ./spec/spec_helper.rb',
10
+ :version => 2 do
11
+ watch(%r{^spec/.+_spec\.rb})
12
+ watch(%r{^lib/(.+)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" }
13
+ watch('spec/spec_helper.rb') { "spec" }
14
+ end
@@ -0,0 +1,40 @@
1
+ lrucache - A simple LRU-cache based on a hash and a priority queue.
2
+
3
+ Setup
4
+ =====
5
+ gem install lrucache
6
+
7
+ Example
8
+ =======
9
+ require 'lrucache'
10
+ cache = LRUCache.new(:max_size => 3, :default => 42)
11
+ cache[1] = 'a'
12
+ cache[2] = 'b'
13
+ cache[3] = 'c'
14
+ puts cache[2] # b
15
+ puts cache[1] # a
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').
19
+ puts cache.keys # [1,3,4]
20
+
21
+
22
+ TTL (time-to-live)
23
+ ==================
24
+ cache = LRUCache.new(:expires => 1.hour)
25
+ cache.store("banana", "yellow")
26
+ cache.store("monkey", "banana", Time.now + 3.days)
27
+ # or ...
28
+ cache.store("monkey", "banana", 3.days)
29
+
30
+ # Three minutes later ...
31
+ cache.fetch("banana") # "yellow"
32
+ cache.fetch("monkey") # "banana"
33
+
34
+ # Three hours later ...
35
+ cache.fetch("banana") # nil
36
+ cache.fetch("monkey") # "banana"
37
+
38
+ # Three days later ...
39
+ cache.fetch("banana") # nil
40
+ cache.fetch("monkey") # nil
@@ -0,0 +1,24 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ desc "Run all specs"
5
+ RSpec::Core::RakeTask.new(:spec) do |t|
6
+ t.rspec_opts = [
7
+ '-c',
8
+ '--format documentation',
9
+ '-r ./spec/spec_helper.rb'
10
+ ]
11
+ t.pattern = 'spec/**/*_spec.rb'
12
+ end
13
+
14
+ desc "open coverage report"
15
+ task :coverage do
16
+ system 'rake spec'
17
+ system 'open coverage/index.html'
18
+ end
19
+
20
+ desc "Open development console"
21
+ task :console do
22
+ puts "Loading development console..."
23
+ system "irb -I #{File.join('.', 'lib')} -r #{File.join('.', 'lib', 'lrucache')}"
24
+ end
@@ -0,0 +1,93 @@
1
+ require "lrucache/version"
2
+ require "priority_queue"
3
+
4
+ # Not thread-safe!
5
+ class LRUCache
6
+
7
+ attr_reader :default, :max_size
8
+
9
+ def initialize(opts={})
10
+ @max_size = (opts[:max_size] || 100).to_i
11
+ @default = opts[:default]
12
+ @expires = (opts[:expires] || 0).to_f
13
+ raise "max_size must be greather than zero" unless @max_size > 0
14
+ @pqueue = PriorityQueue.new
15
+ @data = {}
16
+ @counter = 0
17
+ end
18
+
19
+ def clear
20
+ @data.clear
21
+ @pqueue.delete_min until @pqueue.empty?
22
+ end
23
+
24
+ def include?(key)
25
+ datum = @data[key]
26
+ return false if datum.nil?
27
+ value, expires = datum
28
+ if expires.nil? || expires > Time.now # no expiration, or not expired
29
+ access(key)
30
+ true
31
+ else # expired
32
+ delete(key)
33
+ false
34
+ end
35
+ end
36
+
37
+ def store(key, value, expires=nil)
38
+ expire_lru! unless @data.include?(key) || @data.size < @max_size
39
+ expiration =
40
+ if expires.nil?
41
+ (@expires > 0) ? (Time.now + @expires) : nil
42
+ elsif expires.is_a?(Time)
43
+ expires
44
+ else
45
+ expires = expires.to_f
46
+ (expires > 0) ? (Time.now + expires) : nil
47
+ end
48
+ @data[key] = [value, expiration]
49
+ access(key)
50
+ end
51
+
52
+ alias :[]= :store
53
+
54
+ def fetch(key)
55
+ datum = @data[key]
56
+ return @default if datum.nil?
57
+ value, expires = datum
58
+ if expires.nil? || expires > Time.now # no expiration, or not expired
59
+ access(key)
60
+ value
61
+ else # expired
62
+ delete(key)
63
+ @default
64
+ end
65
+ end
66
+
67
+ alias :[] :fetch
68
+
69
+ def size
70
+ @data.size
71
+ end
72
+
73
+ def keys
74
+ @data.keys
75
+ end
76
+
77
+ def delete(key)
78
+ @data.delete(key)
79
+ @pqueue.delete(key)
80
+ end
81
+
82
+ private
83
+
84
+ def expire_lru!
85
+ key, priority = @pqueue.delete_min
86
+ @data.delete(key) unless priority.nil?
87
+ end
88
+
89
+ def access(key)
90
+ @pqueue.change_priority(key, @counter += 1)
91
+ end
92
+
93
+ end
@@ -0,0 +1,3 @@
1
+ class LRUCache
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "lrucache/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "lrucache"
7
+ s.version = LRUCache::VERSION
8
+ s.authors = ["Chris Johnson"]
9
+ s.email = ["chris@kindkid.com"]
10
+ s.homepage = ""
11
+ s.summary = "A simple LRU-cache based on a hash and priority queue"
12
+ s.description = s.summary
13
+
14
+ s.rubyforge_project = "lrucache"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+ s.add_dependency "PriorityQueue", '~> 0.1.2'
21
+
22
+ s.add_development_dependency "rspec", "~> 2.6.0"
23
+ s.add_development_dependency "simplecov", "~> 0.4.2"
24
+ s.add_development_dependency("rb-fsevent", "~> 0.4.3") if RUBY_PLATFORM =~ /darwin/i
25
+ s.add_development_dependency "guard", "~> 0.6.2"
26
+ s.add_development_dependency "guard-bundler", "~> 0.1.3"
27
+ s.add_development_dependency "guard-rspec", "~> 0.4.2"
28
+ s.add_development_dependency "timecop", "~> 0.3.5"
29
+ end
@@ -0,0 +1,102 @@
1
+ describe LRUCache do
2
+ it "should never exceed its max_size" do
3
+ c = LRUCache.new(:max_size => 7)
4
+ (1..100).each do |i|
5
+ c[i] = rand(2**16)
6
+ c.size.should <= 7
7
+ end
8
+ c.size.should == 7
9
+ end
10
+
11
+ it "should expire the eldest entries first" do
12
+ c = LRUCache.new(:max_size => 3)
13
+ c[1] = 'a'
14
+ c[2] = 'b'
15
+ c[3] = 'c'
16
+ c[2].should == 'b'
17
+ c[1].should == 'a'
18
+ c[3].should == 'c'
19
+ c[4] = 'd' # Out of space! Throws out the least-recently used (2 => 'b').
20
+ c.keys.sort.should == [1,3,4]
21
+ c[5] = 'e'
22
+ c.keys.sort.should == [3,4,5]
23
+ c[1] = 'a'
24
+ c.keys.sort.should == [1,4,5]
25
+ end
26
+
27
+ it "should return the default value for expired and non-existent entries" do
28
+ default = double(:default)
29
+ c = LRUCache.new(:max_size => 3, :default => default)
30
+ c[:a].should == default
31
+ c[:a] = 'a'
32
+ c[:a].should == 'a'
33
+ c[:b] = 'b'
34
+ c[:c] = 'c'
35
+ c[:d] = 'd'
36
+ c[:a].should == default
37
+ end
38
+
39
+ it "should honor TTL" do
40
+ c = LRUCache.new(:expires => 20)
41
+ now = Time.now
42
+ Timecop.freeze(now) do
43
+ c.store(:a, 'a')
44
+ c.store(:b, 'b', now + 50)
45
+ c.store(:c, 'c', 50)
46
+ c[:a].should == 'a'
47
+ c[:b].should == 'b'
48
+ c[:c].should == 'c'
49
+ c.size.should == 3
50
+ end
51
+ Timecop.freeze(now + 19) do
52
+ c[:a].should == 'a'
53
+ c[:b].should == 'b'
54
+ c[:c].should == 'c'
55
+ c.size.should == 3
56
+ end
57
+ Timecop.freeze(now + 49) do
58
+ c[:a].should be_nil
59
+ c[:b].should == 'b'
60
+ c[:c].should == 'c'
61
+ c.size.should == 2
62
+ end
63
+ Timecop.freeze(now + 50) do
64
+ c[:a].should be_nil
65
+ c[:b].should be_nil
66
+ c[:c].should be_nil
67
+ c.size.should == 0
68
+ end
69
+ c[:a].should be_nil
70
+ c[:b].should be_nil
71
+ c[:c].should be_nil
72
+ c.size.should == 0
73
+ end
74
+
75
+ it "should have a default max_size of 100" do
76
+ LRUCache.new.max_size.should == 100
77
+ LRUCache.new(:max_size => 82).max_size.should == 82
78
+ end
79
+
80
+ it "can be cleared" do
81
+ c = LRUCache.new
82
+ (1..100).each {|i| c[i] = rand(2**16)}
83
+ c.size.should == 100
84
+ c.clear
85
+ c.size.should == 0
86
+ c.keys.should == []
87
+ end
88
+
89
+ describe ".include?(key)" do
90
+ it "affects the access time of the key" do
91
+ c = LRUCache.new(:max_size => 3)
92
+ c[1] = 'a'
93
+ c[2] = 'b'
94
+ c[3] = 'c'
95
+ c.include?(1).should be_true
96
+ c[4] = 'd'
97
+ c[5] = 'e'
98
+ c.include?(1).should be_true
99
+ c.include?(2).should be_false
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,10 @@
1
+ require 'simplecov'
2
+ SimpleCov.start do
3
+ add_filter "/spec/"
4
+ add_group "Models", "lib"
5
+ end
6
+
7
+ require 'rubygems'
8
+ require 'bundler'
9
+
10
+ Bundler.require(:default, :test, :development)
metadata ADDED
@@ -0,0 +1,206 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lrucache
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Chris Johnson
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-09-07 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: PriorityQueue
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 31
30
+ segments:
31
+ - 0
32
+ - 1
33
+ - 2
34
+ version: 0.1.2
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: rspec
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ hash: 23
46
+ segments:
47
+ - 2
48
+ - 6
49
+ - 0
50
+ version: 2.6.0
51
+ type: :development
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: simplecov
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ hash: 11
62
+ segments:
63
+ - 0
64
+ - 4
65
+ - 2
66
+ version: 0.4.2
67
+ type: :development
68
+ version_requirements: *id003
69
+ - !ruby/object:Gem::Dependency
70
+ name: rb-fsevent
71
+ prerelease: false
72
+ requirement: &id004 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ hash: 9
78
+ segments:
79
+ - 0
80
+ - 4
81
+ - 3
82
+ version: 0.4.3
83
+ type: :development
84
+ version_requirements: *id004
85
+ - !ruby/object:Gem::Dependency
86
+ name: guard
87
+ prerelease: false
88
+ requirement: &id005 !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ hash: 3
94
+ segments:
95
+ - 0
96
+ - 6
97
+ - 2
98
+ version: 0.6.2
99
+ type: :development
100
+ version_requirements: *id005
101
+ - !ruby/object:Gem::Dependency
102
+ name: guard-bundler
103
+ prerelease: false
104
+ requirement: &id006 !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ hash: 29
110
+ segments:
111
+ - 0
112
+ - 1
113
+ - 3
114
+ version: 0.1.3
115
+ type: :development
116
+ version_requirements: *id006
117
+ - !ruby/object:Gem::Dependency
118
+ name: guard-rspec
119
+ prerelease: false
120
+ requirement: &id007 !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ~>
124
+ - !ruby/object:Gem::Version
125
+ hash: 11
126
+ segments:
127
+ - 0
128
+ - 4
129
+ - 2
130
+ version: 0.4.2
131
+ type: :development
132
+ version_requirements: *id007
133
+ - !ruby/object:Gem::Dependency
134
+ name: timecop
135
+ prerelease: false
136
+ requirement: &id008 !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ~>
140
+ - !ruby/object:Gem::Version
141
+ hash: 25
142
+ segments:
143
+ - 0
144
+ - 3
145
+ - 5
146
+ version: 0.3.5
147
+ type: :development
148
+ version_requirements: *id008
149
+ description: A simple LRU-cache based on a hash and priority queue
150
+ email:
151
+ - chris@kindkid.com
152
+ executables: []
153
+
154
+ extensions: []
155
+
156
+ extra_rdoc_files: []
157
+
158
+ files:
159
+ - .gitignore
160
+ - .rvmrc
161
+ - Gemfile
162
+ - Guardfile
163
+ - README.md
164
+ - Rakefile
165
+ - lib/lrucache.rb
166
+ - lib/lrucache/version.rb
167
+ - lrucache.gemspec
168
+ - spec/lrucache_spec.rb
169
+ - spec/spec_helper.rb
170
+ has_rdoc: true
171
+ homepage: ""
172
+ licenses: []
173
+
174
+ post_install_message:
175
+ rdoc_options: []
176
+
177
+ require_paths:
178
+ - lib
179
+ required_ruby_version: !ruby/object:Gem::Requirement
180
+ none: false
181
+ requirements:
182
+ - - ">="
183
+ - !ruby/object:Gem::Version
184
+ hash: 3
185
+ segments:
186
+ - 0
187
+ version: "0"
188
+ required_rubygems_version: !ruby/object:Gem::Requirement
189
+ none: false
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ hash: 3
194
+ segments:
195
+ - 0
196
+ version: "0"
197
+ requirements: []
198
+
199
+ rubyforge_project: lrucache
200
+ rubygems_version: 1.6.2
201
+ signing_key:
202
+ specification_version: 3
203
+ summary: A simple LRU-cache based on a hash and priority queue
204
+ test_files:
205
+ - spec/lrucache_spec.rb
206
+ - spec/spec_helper.rb