lru 0.1.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.
@@ -0,0 +1,6 @@
1
+ == 0.1 2007-09-29
2
+
3
+ * Initial release.
4
+ * Full test suite.
5
+ * LRU implemented based on the maximum number of elements to cache.
6
+
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 FIXME full name
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,25 @@
1
+ init.rb
2
+ History.txt
3
+ License.txt
4
+ Manifest.txt
5
+ README.txt
6
+ Rakefile
7
+ config/hoe.rb
8
+ config/requirements.rb
9
+ lib/test.rb
10
+ lib/lru.rb
11
+ lib/test/version.rb
12
+ script/destroy
13
+ script/generate
14
+ script/txt2html
15
+ setup.rb
16
+ tasks/deployment.rake
17
+ tasks/environment.rake
18
+ tasks/website.rake
19
+ test/test_helper.rb
20
+ test/test_lru.rb
21
+ website/index.html
22
+ website/index.txt
23
+ website/javascripts/rounded_corners_lite.inc.js
24
+ website/stylesheets/screen.css
25
+ website/template.rhtml
@@ -0,0 +1,29 @@
1
+ = Efficient LRU Cache in Ruby
2
+
3
+ This class implements an LRU Cache where all operations are
4
+ implemented in constant time. This is in contract to other classes,
5
+ e.g. ruby-cache, whose operations complete in Order(n) time. The
6
+ primary implementation details for this class include:
7
+
8
+ - Simple API. We do not provide a complete Hash API, but do provide
9
+ enumerable and common shortcuts.
10
+
11
+ - True linked list underlying the LRU. Ruby-cache uses a ruby array
12
+ and traverses ALL elements for key operations (like get).
13
+
14
+ - No aggressive cache flushing. Items are expired only once the
15
+ cache reaches the capacity you configured. There is no garbage
16
+ collection or other process that can delay operations. Every
17
+ access to the cache occurs in constant time.
18
+
19
+ == Usage
20
+ require 'lru'
21
+ cache = Cache::LRU.new(:max_elements => 5)
22
+ cache.put(:a, 1)
23
+ cache[:a] = 2
24
+ cache.get(:b) { 1 }
25
+ cache[:b]
26
+
27
+ == Authors
28
+ Michael Bryzek mbryzek<at>alum.mit.edu
29
+ Phong Nguyen phong<at>gilt.com
@@ -0,0 +1,4 @@
1
+ require 'config/requirements'
2
+ require 'config/hoe' # setup Hoe + all gem configuration
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
@@ -0,0 +1,70 @@
1
+ require 'test/version'
2
+
3
+ AUTHOR = ['Michael Bryzek', 'Phong Nguyen'] # can also be an array of Authors
4
+ EMAIL = "mbryzek@alum.mit.edu"
5
+ DESCRIPTION = "Efficient LRU in pure ruby where all operations are constant time"
6
+ GEM_NAME = 'lru' # what ppl will type to install your gem
7
+ RUBYFORGE_PROJECT = 'lru' # The unix name for your project
8
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
9
+ DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
10
+
11
+ @config_file = "~/.rubyforge/user-config.yml"
12
+ @config = nil
13
+ RUBYFORGE_USERNAME = "unknown"
14
+ def rubyforge_username
15
+ unless @config
16
+ begin
17
+ @config = YAML.load(File.read(File.expand_path(@config_file)))
18
+ rescue
19
+ puts <<-EOS
20
+ ERROR: No rubyforge config file found: #{@config_file}
21
+ Run 'rubyforge setup' to prepare your env for access to Rubyforge
22
+ - See http://newgem.rubyforge.org/rubyforge.html for more details
23
+ EOS
24
+ exit
25
+ end
26
+ end
27
+ RUBYFORGE_USERNAME.replace @config["username"]
28
+ end
29
+
30
+
31
+ REV = nil
32
+ # UNCOMMENT IF REQUIRED:
33
+ # REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil
34
+ VERS = Test::VERSION::STRING + (REV ? ".#{REV}" : "")
35
+ RDOC_OPTS = ['--quiet', '--title', 'test documentation',
36
+ "--opname", "index.html",
37
+ "--line-numbers",
38
+ "--main", "README",
39
+ "--inline-source"]
40
+
41
+ class Hoe
42
+ def extra_deps
43
+ @extra_deps.reject! { |x| Array(x).first == 'hoe' }
44
+ @extra_deps
45
+ end
46
+ end
47
+
48
+ # Generate all the Rake tasks
49
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
50
+ hoe = Hoe.new(GEM_NAME, VERS) do |p|
51
+ p.author = AUTHOR
52
+ p.description = DESCRIPTION
53
+ p.email = EMAIL
54
+ p.summary = DESCRIPTION
55
+ p.url = HOMEPATH
56
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
57
+ p.test_globs = ["test/**/test_*.rb"]
58
+ p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
59
+
60
+ # == Optional
61
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\\n\\n")
62
+ #p.extra_deps = [] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
63
+
64
+ #p.spec_extras = {} # A hash of extra values to set in the gemspec.
65
+
66
+ end
67
+
68
+ CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
69
+ PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
70
+ hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
@@ -0,0 +1,17 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ require 'rubygems'
5
+ %w[rake hoe newgem rubigen].each do |req_gem|
6
+ begin
7
+ require req_gem
8
+ rescue LoadError
9
+ puts "This Rakefile requires the '#{req_gem}' RubyGem."
10
+ puts "Installation: gem install #{req_gem} -y"
11
+ exit
12
+ end
13
+ end
14
+
15
+ $:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
16
+
17
+ require 'test'
data/init.rb ADDED
@@ -0,0 +1,21 @@
1
+ # Copyright (c) 2007 Michael Bryzek
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all
11
+ # copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ # SOFTWARE.
20
+
21
+ require 'lru'
@@ -0,0 +1,193 @@
1
+ # Copyright (c) 2007 Michael Bryzek
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all
11
+ # copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ # SOFTWARE.
20
+ #
21
+ module Cache
22
+
23
+ # Defines a local LRU cache. All operations are maintained in
24
+ # constant size regardless of the size of the Cache. When creating
25
+ # the cache, you specify the max size of the cache.
26
+ #
27
+ # Example:
28
+ # cache = Cache::LRU.new(:max_elements => 5)
29
+ # cache.put(:a, 1)
30
+ # cache[:a] = 2
31
+ # cache.get(:b) { 1 }
32
+ # cache[:b]
33
+ class LRU
34
+
35
+ include Enumerable
36
+
37
+ attr_reader :size
38
+ attr_reader :keys
39
+ # opts:
40
+ # - max_elements - maximum number of elements to keep in the
41
+ # cache at any time. The default is 100 elements
42
+ def initialize(opts = {})
43
+ opts = { :max_elements => 100 }.merge(opts)
44
+ @max_elements = opts.delete(:max_elements)
45
+ raise "Invalid options: #{opts.keys.join(' ')}" if opts.keys.size > 0
46
+ @keys = LinkedList.new
47
+ @map = {}
48
+ @size = 0
49
+ end
50
+
51
+ def clear!
52
+ initialize( :max_elements => @max_elements )
53
+ end
54
+
55
+ # Iterates through all of the key/value pairs added to the cache,
56
+ # in random order. Accepts a block that is yielded to with the key
57
+ # and value for each entry in the cache.
58
+ def each
59
+ @map.each do |k, el|
60
+ yield k, el.value
61
+ end
62
+ end
63
+
64
+ def [](key)
65
+ get(key)
66
+ end
67
+
68
+ # Fetches the value of the element with the given key. If this key
69
+ # does not exist in the cache, you can provide an optional code
70
+ # block that we'll yield to to repopulate the value
71
+ def get(key)
72
+ if el = @map[key]
73
+ @keys.move_to_head(el)
74
+ return el.value
75
+ elsif block_given?
76
+ return put(key, yield)
77
+ end
78
+ return nil
79
+ end
80
+
81
+ def []=(key, value)
82
+ put(key, value)
83
+ end
84
+
85
+ def put(key, value)
86
+ el = @map[key]
87
+ if el
88
+ el.value = value
89
+ @keys.move_to_head(el)
90
+ else
91
+ el = @keys.add(key, value)
92
+ @size += 1
93
+ end
94
+ @map[key] = el
95
+
96
+ if @size > @max_elements
97
+ delete_element(@keys.last)
98
+ @size -= 1
99
+ end
100
+ value
101
+ end
102
+
103
+ def delete(key)
104
+ if el = @map[key]
105
+ delete_element(el)
106
+ @size -= 1
107
+ else
108
+ nil
109
+ end
110
+ end
111
+
112
+ private
113
+ def delete_element(el)
114
+ @keys.remove_element(el)
115
+ @map.delete(el.key)
116
+ el.value
117
+ end
118
+
119
+ end
120
+
121
+ class LinkedList
122
+
123
+ attr_reader :last
124
+ def initialize
125
+ @head = @last = nil
126
+ end
127
+
128
+ def add(key, value)
129
+ add_element(Element.new(key, value, @head))
130
+ end
131
+
132
+ def add_element(el)
133
+ @head.previous_element = el if @head
134
+
135
+ el.next_element = @head
136
+ el.previous_element = nil
137
+ @head = el
138
+ @last = el unless @last
139
+ el
140
+ end
141
+
142
+ def remove_element(el)
143
+ el.previous_element.next_element = el.next_element if el.previous_element
144
+ el.next_element.previous_element = el.previous_element if el.next_element
145
+
146
+ @last = el.previous_element if el == @last
147
+ @head = el.next_element if el == @head
148
+ end
149
+
150
+ def move_to_head(el)
151
+ remove_element(el)
152
+ add_element(el)
153
+ end
154
+
155
+ # Returns a nicely formatted stirng of all elements in the linked
156
+ # list. First element is most recently used, last element is least
157
+ # recently used.
158
+ def pp
159
+ s = ''
160
+ el = @head
161
+ while el
162
+ s << ', ' if s.size > 0
163
+ s << el.to_s
164
+ el = el.next_element
165
+ end
166
+ s
167
+ end
168
+
169
+ class Element
170
+
171
+ attr_accessor :key, :value, :previous_element, :next_element
172
+ def initialize(key, value, next_element)
173
+ @key = key
174
+ @value = value
175
+ @next_element = next_element
176
+ @previous_element = nil
177
+ end
178
+
179
+ def inspect
180
+ to_s
181
+ end
182
+
183
+ def to_s
184
+ p = @previous_element ? @previous_element.key : 'nil'
185
+ n = @next_element ? @next_element.key : 'nil'
186
+ "[#{@key}: #{@value.inspect}, previous: #{p}, next: #{n}]"
187
+ end
188
+
189
+ end
190
+
191
+ end
192
+
193
+ end
@@ -0,0 +1,5 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ module Test
4
+
5
+ end
@@ -0,0 +1,9 @@
1
+ module Test #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.join(File.dirname(__FILE__), '..')
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.join(File.dirname(__FILE__), '..')
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ begin
5
+ require 'newgem'
6
+ rescue LoadError
7
+ puts "\n\nGenerating the website requires the newgem RubyGem"
8
+ puts "Install: gem install newgem\n\n"
9
+ exit(1)
10
+ end
11
+ require 'redcloth'
12
+ require 'syntax/convertors/html'
13
+ require 'erb'
14
+ require File.dirname(__FILE__) + '/../lib/test/version.rb'
15
+
16
+ version = Test::VERSION::STRING
17
+ download = 'http://rubyforge.org/projects/test'
18
+
19
+ class Fixnum
20
+ def ordinal
21
+ # teens
22
+ return 'th' if (10..19).include?(self % 100)
23
+ # others
24
+ case self % 10
25
+ when 1: return 'st'
26
+ when 2: return 'nd'
27
+ when 3: return 'rd'
28
+ else return 'th'
29
+ end
30
+ end
31
+ end
32
+
33
+ class Time
34
+ def pretty
35
+ return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
36
+ end
37
+ end
38
+
39
+ def convert_syntax(syntax, source)
40
+ return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^<pre>|</pre>$!,'')
41
+ end
42
+
43
+ if ARGV.length >= 1
44
+ src, template = ARGV
45
+ template ||= File.join(File.dirname(__FILE__), '/../website/template.rhtml')
46
+
47
+ else
48
+ puts("Usage: #{File.split($0).last} source.txt [template.rhtml] > output.html")
49
+ exit!
50
+ end
51
+
52
+ template = ERB.new(File.open(template).read)
53
+
54
+ title = nil
55
+ body = nil
56
+ File.open(src) do |fsrc|
57
+ title_text = fsrc.readline
58
+ body_text = fsrc.read
59
+ syntax_items = []
60
+ body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)</\1>!m){
61
+ ident = syntax_items.length
62
+ element, syntax, source = $1, $2, $3
63
+ syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}</#{element}>"
64
+ "syntax-temp-#{ident}"
65
+ }
66
+ title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
67
+ body = RedCloth.new(body_text).to_html
68
+ body.gsub!(%r!(?:<pre><code>)?syntax-temp-(\d+)(?:</code></pre>)?!){ syntax_items[$1.to_i] }
69
+ end
70
+ stat = File.stat(src)
71
+ created = stat.ctime
72
+ modified = stat.mtime
73
+
74
+ $stdout << template.result(binding)