lrucache 0.0.1

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.
@@ -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