facets 2.7.0 → 2.8.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.
Files changed (130) hide show
  1. data/HISTORY.rdoc +135 -294
  2. data/MANIFEST +40 -91
  3. data/NOTES +1 -1
  4. data/README.rdoc +10 -8
  5. data/Rakefile +11 -34
  6. data/demo/{hook.rd → hook.rdoc} +2 -0
  7. data/demo/{scenario_require.rd → scenario_require.rdoc} +3 -0
  8. data/lib/core/facets-live.rb +7 -5
  9. data/lib/core/facets.rb +379 -359
  10. data/lib/core/facets/array/conjoin.rb +2 -2
  11. data/lib/core/facets/array/pad.rb +1 -1
  12. data/lib/core/facets/array/recursively.rb +2 -2
  13. data/lib/core/facets/array/splice.rb +1 -1
  14. data/lib/core/facets/binding/caller.rb +2 -4
  15. data/lib/core/facets/comparable/comparable.rb +2 -2
  16. data/lib/core/facets/dir/ascend.rb +3 -0
  17. data/lib/core/facets/dir/recurse.rb +4 -0
  18. data/lib/core/facets/duplicable.rb +6 -8
  19. data/lib/core/facets/enumerable/count.rb +22 -13
  20. data/lib/core/facets/enumerable/map_detect.rb +28 -0
  21. data/lib/core/facets/enumerable/mash.rb +13 -5
  22. data/lib/core/facets/enumerable/per.rb +3 -1
  23. data/lib/core/facets/hash/count.rb +14 -0
  24. data/lib/core/facets/hash/data.rb +14 -0
  25. data/lib/core/facets/kernel/__method__.rb +1 -1
  26. data/lib/core/facets/kernel/d.rb +9 -8
  27. data/lib/core/facets/kernel/eigenclass.rb +20 -0
  28. data/lib/core/facets/kernel/extend.rb +10 -0
  29. data/lib/core/facets/kernel/instance_class.rb +1 -0
  30. data/lib/core/facets/kernel/instance_variables.rb +6 -6
  31. data/lib/core/facets/kernel/meta_alias.rb +18 -0
  32. data/lib/core/facets/kernel/meta_class.rb +17 -0
  33. data/lib/core/facets/kernel/meta_def.rb +18 -0
  34. data/lib/core/facets/kernel/meta_eval.rb +18 -0
  35. data/lib/core/facets/kernel/object_hexid.rb +21 -6
  36. data/lib/core/facets/kernel/object_state.rb +4 -2
  37. data/lib/core/facets/kernel/populate.rb +3 -1
  38. data/lib/core/facets/kernel/with.rb +1 -1
  39. data/lib/core/facets/metaid.rb +6 -93
  40. data/lib/core/facets/module/class_def.rb +2 -0
  41. data/lib/core/facets/module/extend.rb +10 -11
  42. data/lib/core/facets/module/is.rb +5 -5
  43. data/lib/core/facets/module/module_def.rb +31 -0
  44. data/lib/core/facets/string/camelcase.rb +14 -12
  45. data/lib/core/facets/string/cleanlines.rb +35 -0
  46. data/lib/core/facets/string/edit_distance.rb +62 -0
  47. data/lib/core/facets/string/indent.rb +86 -4
  48. data/lib/core/facets/string/index_all.rb +24 -0
  49. data/lib/core/facets/string/lines.rb +3 -6
  50. data/lib/core/facets/string/margin.rb +2 -1
  51. data/lib/core/facets/string/newlines.rb +35 -0
  52. data/lib/core/facets/string/op_div.rb +14 -0
  53. data/lib/core/facets/string/range.rb +2 -22
  54. data/lib/core/facets/string/range_all.rb +1 -0
  55. data/lib/core/facets/string/range_of_line.rb +1 -0
  56. data/lib/core/facets/string/similarity.rb +92 -0
  57. data/lib/core/facets/string/start_with.rb +6 -6
  58. data/lib/core/facets/string/titlecase.rb +1 -1
  59. data/lib/more/facets/basicobject.rb +16 -15
  60. data/lib/more/facets/blankslate.rb +8 -0
  61. data/lib/more/facets/class_extend.rb +126 -1
  62. data/lib/more/facets/continuation.rb +53 -54
  63. data/lib/more/facets/dictionary.rb +9 -63
  64. data/lib/more/facets/erb.rb +63 -0
  65. data/lib/more/facets/filelist.rb +5 -5
  66. data/lib/more/facets/hashbuilder.rb +101 -0
  67. data/lib/more/facets/inheritor.rb +36 -45
  68. data/lib/more/facets/ini.rb +267 -0
  69. data/lib/more/facets/instance_eval.rb +4 -4
  70. data/lib/more/facets/ioredirect.rb +7 -60
  71. data/lib/more/facets/linkedlist.rb +195 -0
  72. data/lib/more/facets/matcher.rb +140 -0
  73. data/lib/more/facets/memoizer.rb +64 -0
  74. data/lib/more/facets/methodspace.rb +9 -4
  75. data/lib/more/facets/module/class_extend.rb +2 -121
  76. data/lib/more/facets/ostruct.rb +9 -9
  77. data/lib/more/facets/pathlist.rb +1 -9
  78. data/lib/more/facets/pathname.rb +11 -4
  79. data/lib/more/facets/plugin_manager.rb +50 -0
  80. data/lib/more/facets/random.rb +25 -3
  81. data/lib/more/facets/roman.rb +174 -0
  82. data/lib/more/facets/semaphore.rb +92 -0
  83. data/lib/more/facets/shellwords.rb +21 -48
  84. data/lib/more/facets/succ.rb +1 -1
  85. data/meta/{modified → released} +0 -0
  86. data/meta/repository +1 -0
  87. data/meta/suite +1 -0
  88. data/meta/version +1 -1
  89. data/script/conflicts +63 -0
  90. data/script/methods +49 -0
  91. data/test/core/binding/test_caller.rb +11 -4
  92. data/test/core/enumerable/test_count.rb +19 -10
  93. data/test/core/enumerable/test_map_detect.rb +75 -0
  94. data/test/core/enumerable/test_take.rb +1 -1
  95. data/test/core/kernel/test_object_hexid.rb +2 -1
  96. data/test/core/proc/test_to_method.rb +1 -1
  97. data/test/core/string/test_cleanlines.rb +11 -0
  98. data/test/core/string/test_indent.rb +66 -4
  99. data/test/core/string/test_lines.rb +2 -1
  100. data/test/core/string/test_newlines.rb +13 -0
  101. data/test/core/time/test_change.rb +1 -1
  102. data/test/core/time/test_stamp.rb +4 -7
  103. data/test/core/unboundmethod/test_name.rb +1 -1
  104. data/test/more/test_basicobject.rb +1 -20
  105. data/test/more/test_class_extend.rb +7 -0
  106. data/test/more/test_continuation.rb +8 -6
  107. data/test/more/test_inheritor.rb +12 -6
  108. data/test/more/test_random.rb +19 -10
  109. data/test/more/test_shellwords.rb +33 -0
  110. metadata +60 -31
  111. data/TODO +0 -5
  112. data/doc/README.core +0 -102
  113. data/doc/README.more +0 -61
  114. data/doc/manual/about.rb +0 -47
  115. data/doc/manual/annotations.rdoc +0 -60
  116. data/doc/manual/associations.rdoc +0 -55
  117. data/doc/manual/blockups.rdoc +0 -101
  118. data/doc/manual/capsule.rdoc +0 -34
  119. data/doc/manual/command.rdoc +0 -177
  120. data/doc/manual/core.rdoc +0 -37
  121. data/doc/manual/faq.rdoc +0 -32
  122. data/doc/manual/typecast.html +0 -112
  123. data/lib/more/facets/capsule.rb +0 -258
  124. data/lib/more/facets/coroutine.rb +0 -159
  125. data/lib/more/facets/enumerablepass.rb +0 -3
  126. data/lib/more/facets/fileable.rb +0 -162
  127. data/lib/more/facets/progressbar.rb +0 -253
  128. data/lib/more/facets/recorder.rb +0 -108
  129. data/meta/releases +0 -14
  130. data/test/more/test_coroutine.rb +0 -46
@@ -0,0 +1,267 @@
1
+ # ini.rb - read and write ini files
2
+ #
3
+ # Copyright (C) 2007 Jeena Paradies
4
+ # License: GPL
5
+ # Author: Jeena Paradies (info@jeenaparadies.net)
6
+ #
7
+ # This file provides a read-wite handling for ini files.
8
+ # The data of a ini file is represented by a object which
9
+ # is populated with strings.
10
+
11
+ # Class with methods to read from and write into ini files.
12
+ #
13
+ # A ini file is a text file in a specific format,
14
+ # it may include several fields which are sparated by
15
+ # field headlines which are enclosured by "[]".
16
+ # Each field may include several key-value pairs.
17
+ #
18
+ # Each key-value pair is represented by one line and
19
+ # the value is sparated from the key by a "=".
20
+ #
21
+ # == Examples
22
+ #
23
+ # === Example ini file
24
+ #
25
+ # # this is the first comment which will be saved in the comment attribute
26
+ # mail=info@example.com
27
+ # domain=example.com # this is a comment which will not be saved
28
+ # [database]
29
+ # db=example
30
+ # user=john
31
+ # passwd=very-secure
32
+ # host=localhost
33
+ # # this is another comment
34
+ # [filepaths]
35
+ # tmp=/tmp/example
36
+ # lib=/home/john/projects/example/lib
37
+ # htdocs=/home/john/projects/example/htdocs
38
+ # [ texts ]
39
+ # wellcome=Wellcome on my new website!
40
+ # Website description = This is only a example. # and another comment
41
+ #
42
+ # === Example object
43
+ #
44
+ # A Ini#comment stores:
45
+ # "this is the first comment which will be saved in the comment attribute"
46
+ #
47
+ # A Ini object stores:
48
+ #
49
+ # {
50
+ # "mail" => "info@example.com",
51
+ # "domain" => "example.com",
52
+ # "database" => {
53
+ # "db" => "example",
54
+ # "user" => "john",
55
+ # "passwd" => "very-secure",
56
+ # "host" => "localhost"
57
+ # },
58
+ # "filepaths" => {
59
+ # "tmp" => "/tmp/example",
60
+ # "lib" => "/home/john/projects/example/lib",
61
+ # "htdocs" => "/home/john/projects/example/htdocs"
62
+ # }
63
+ # "texts" => {
64
+ # "wellcome" => "Wellcome on my new website!",
65
+ # "Website description" => "This is only a example."
66
+ # }
67
+ # }
68
+ #
69
+ # As you can see this module gets rid of all comments, linebreaks
70
+ # and unnecessary spaces at the beginning and the end of each
71
+ # field headline, key or value.
72
+ #
73
+ # === Using the object
74
+ #
75
+ # Using the object is stright forward:
76
+ #
77
+ # ini = Ini.new("path/settings.ini")
78
+ # ini["mail"] = "info@example.com"
79
+ # ini["filepaths"] = { "tmp" => "/tmp/example" }
80
+ # ini.comment = "This is\na comment"
81
+ # puts ini["filepaths"]["tmp"]
82
+ # # => /tmp/example
83
+ # ini.write()
84
+ #
85
+
86
+ class Ini
87
+
88
+
89
+ #
90
+ # :inihash is a hash which holds all ini data
91
+ # :comment is a string which holds the comments on the top of the file
92
+ #
93
+ attr_accessor :inihash, :comment
94
+
95
+ #
96
+ # Creating a new Ini object
97
+ #
98
+ # +path+ is a path to the ini file
99
+ # +load+ if nil restores the data if possible
100
+ # if true restores the data, if not possible raises an error
101
+ # if false does not resotre the data
102
+ #
103
+ def initialize(path, load=nil)
104
+ @path = path
105
+ @inihash = {}
106
+
107
+ if load or ( load.nil? and FileTest.readable_real? @path )
108
+ restore()
109
+ end
110
+ end
111
+
112
+ #
113
+ # Retrive the ini data for the key +key+
114
+ #
115
+ def [](key)
116
+ @inihash[key]
117
+ end
118
+
119
+ #
120
+ # Set the ini data for the key +key+
121
+ #
122
+ def []=(key, value)
123
+ raise TypeError, "String expected" unless key.is_a? String
124
+ raise TypeError, "String or Hash expected" unless value.is_a? String or value.is_a? Hash
125
+
126
+ @inihash[key] = value
127
+ end
128
+
129
+ #
130
+ # Restores the data from file into the object
131
+ #
132
+ def restore()
133
+ @inihash = Ini.read_from_file(@path)
134
+ @comment = Ini.read_comment_from_file(@path)
135
+ end
136
+
137
+ #
138
+ # Store data from the object in the file
139
+ #
140
+ def update()
141
+ Ini.write_to_file(@path, @inihash, @comment)
142
+ end
143
+
144
+ #
145
+ def to_h
146
+ @inihash.dup
147
+ end
148
+
149
+ #
150
+ # Reading data from file
151
+ #
152
+ # +path+ is a path to the ini file
153
+ #
154
+ # returns a hash which represents the data from the file
155
+ #
156
+ def Ini.read_from_file(path)
157
+
158
+ inihash = {}
159
+ headline = nil
160
+
161
+ IO.foreach(path) do |line|
162
+
163
+ line = line.strip.split(/#/)[0]
164
+
165
+ # read it only if the line doesn't begin with a "=" and is long enough
166
+ unless line.length < 2 and line[0,1] == "="
167
+
168
+ # it's a headline if the line begins with a "[" and ends with a "]"
169
+ if line[0,1] == "[" and line[line.length - 1, line.length] == "]"
170
+
171
+ # get rid of the [] and unnecessary spaces
172
+ headline = line[1, line.length - 2 ].strip
173
+ inihash[headline] = {}
174
+ else
175
+
176
+ key, value = line.split(/=/, 2)
177
+
178
+ key = key.strip unless key.nil?
179
+ value = value.strip unless value.nil?
180
+
181
+ unless headline.nil?
182
+ inihash[headline][key] = value
183
+ else
184
+ inihash[key] = value unless key.nil?
185
+ end
186
+ end
187
+ end
188
+ end
189
+
190
+ inihash
191
+ end
192
+
193
+ #
194
+ # Reading comments from file
195
+ #
196
+ # +path+ is a path to the ini file
197
+ #
198
+ # Returns a string with comments from the beginning of the
199
+ # ini file.
200
+ #
201
+ def Ini.read_comment_from_file(path)
202
+ comment = ""
203
+
204
+ IO.foreach(path) do |line|
205
+ line.strip!
206
+ break unless line[0,1] == "#" or line == ""
207
+
208
+ comment << "#{line[1, line.length ].strip}\n"
209
+ end
210
+
211
+ comment
212
+ end
213
+
214
+ #
215
+ # Writing a ini hash into a file
216
+ #
217
+ # +path+ is a path to the ini file
218
+ # +inihash+ is a hash representing the ini File. Default is a empty hash.
219
+ # +comment+ is a string with comments which appear on the
220
+ # top of the file. Each line will get a "#" before.
221
+ # Default is no comment.
222
+ #
223
+ def Ini.write_to_file(path, inihash={}, comment=nil)
224
+ raise TypeError, "String expected" unless comment.is_a? String or comment.nil?
225
+
226
+ raise TypeError, "Hash expected" unless inihash.is_a? Hash
227
+ File.open(path, "w") { |file|
228
+
229
+ unless comment.nil?
230
+ comment.each do |line|
231
+ file << "# #{line}"
232
+ end
233
+ end
234
+
235
+ file << Ini.to_s(inihash)
236
+ }
237
+ end
238
+
239
+ #
240
+ # Turn a hash (up to 2 levels deepness) into a ini string
241
+ #
242
+ # +inihash+ is a hash representing the ini File. Default is a empty hash.
243
+ #
244
+ # Returns a string in the ini file format.
245
+ #
246
+ def Ini.to_s(inihash={})
247
+ str = ""
248
+
249
+ inihash.each do |key, value|
250
+
251
+ if value.is_a? Hash
252
+ str << "[#{key.to_s}]\n"
253
+
254
+ value.each do |under_key, under_value|
255
+ str << "#{under_key.to_s}=#{under_value.to_s unless under_value.nil?}\n"
256
+ end
257
+
258
+ else
259
+ str << "#{key.to_s}=#{value.to_s unless value2.nil?}\n"
260
+ end
261
+ end
262
+
263
+ str
264
+ end
265
+
266
+ end
267
+
@@ -31,16 +31,16 @@ class Object
31
31
  #
32
32
  # TODO: Will only support calls with blocks as of Ruby 1.9+.
33
33
  #
34
- def instance_eval(&block)
35
- return super if block
34
+ def instance_eval(*args, &block)
35
+ return super if block or !args.empty?
36
36
  @_instance_eval ||= Functor.new do |op, *a|
37
37
  instance_eval{ send(op, *a) }
38
38
  end
39
39
  end
40
40
 
41
41
  # TODO for Ruby 1.9
42
- #def instance_eval(&block)
43
- # return super if block
42
+ #def instance_eval(*args, &block)
43
+ # return super if block or !args.empty?
44
44
  # @_instance_functor ||= Functor.new do |op, *a, &b|
45
45
  # fcall(op, *a, &b)
46
46
  # end
@@ -1,22 +1,8 @@
1
+ warn "IORedirect is being deprecated. It is not a robust solution. If you use this library, please consider contributing to Facets by rewritting the library so we can keep it in Facets."
2
+
1
3
  # = IORedirect
2
4
  #
3
- # A class to redirect $stdout, or other IO object, to a StringIO object,
4
- # or any other object with a write() method.
5
- #
6
- # s = StringIO.new
7
- # r = Redirector.redirect($stdout, s) do
8
- # $stdout.puts "this is a test"
9
- # end
10
- #
11
- # == History
12
- #
13
- # * IORedirect was ported from Paul Brannan's Ruby Treasures.
14
- #
15
- # == Authors
16
- #
17
- # * Paul Brannan <paul@atdesk.com>
18
- #
19
- # = Copying
5
+ # IORedirect was ported from Paul Brannan's Ruby Treasures.
20
6
  #
21
7
  # Copyright (C) 2002 Paul Brannan <paul@atdesk.com>
22
8
  #
@@ -29,6 +15,7 @@
29
15
  # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
30
16
  # FOR A PARTICULAR PURPOSE.
31
17
 
18
+ require 'thread'
32
19
 
33
20
  # = IORedirect
34
21
  #
@@ -36,7 +23,7 @@
36
23
  # or any other object with a write() method.
37
24
  #
38
25
  # s = StringIO.new
39
- # r = Redirector.redirect($stdout, s) do
26
+ # r = IORedirect.redirect($stdout, s) do
40
27
  # $stdout.puts "this is a test"
41
28
  # end
42
29
  #
@@ -55,7 +42,7 @@ class IORedirect
55
42
  def start
56
43
  raise "Redirection already in progress" if @t
57
44
  tmp = @from.dup
58
- r, w = IO.pipe
45
+ r, w = *IO.pipe
59
46
  @from.reopen(w)
60
47
  @t = Thread.new do
61
48
  begin
@@ -78,53 +65,13 @@ class IORedirect
78
65
 
79
66
  # An exception-safe class method for redirection
80
67
  def self.redirect(from, to)
81
- s = self.new(from, to)
68
+ s = new(from, to)
82
69
  begin
83
70
  yield
84
71
  ensure
85
72
  s.stop
86
73
  end
87
74
  end
88
- end
89
-
90
-
91
- # --- test ---
92
-
93
- if __FILE__ == $0 then
94
-
95
- class SimpleStringIO
96
- attr_reader :str
97
- def initialize; @str = ''; end
98
- def write(str); @str << str; end
99
- end
100
-
101
- Thread.abort_on_exception = true
102
- s = SimpleStringIO.new
103
- r = Redirector.redirect($stdout, s) do
104
- $stdout.puts "this is a test"
105
- $stdout.puts "of the StringIO redirection system"
106
- end
107
- puts "Done redirecting."
108
- puts "Result:\n#{s.str}"
109
75
 
110
76
  end
111
77
 
112
-
113
-
114
- # _____ _
115
- # |_ _|__ ___| |_
116
- # | |/ _ \/ __| __|
117
- # | | __/\__ \ |_
118
- # |_|\___||___/\__|
119
- #
120
-
121
- # TODO
122
-
123
- =begin #testing
124
-
125
- require 'test/unit'
126
-
127
- =end
128
-
129
-
130
-
@@ -0,0 +1,195 @@
1
+ # LinkedList
2
+ #
3
+ # Copyright (C) 2006 Kirk Haines <khaines@enigo.com>.
4
+ #
5
+ # General Public License (GPL)
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining
8
+ # a copy of this software and associated documentation files (the
9
+ # "Software"), to deal in the Software without restriction, including
10
+ # without limitation the rights to use, copy, modify, merge, publish,
11
+ # distribute, sublicense, and/or sell copies of the Software, and to
12
+ # permit persons to whom the Software is furnished to do so, subject to
13
+ # the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+
26
+ require 'enumerator'
27
+
28
+ # LinkedList implements a simple doubly linked list with efficient
29
+ # hash-like element access.
30
+ #
31
+ # This is a simple linked list implementation with efficient random
32
+ # access of data elements. It was inspired by George Moscovitis'
33
+ # LRUCache implementation found in Facets 1.7.30, but unlike the
34
+ # linked list in that cache, this one does not require the use of a
35
+ # mixin on any class to be stored. The linked list provides the
36
+ # push, pop, shift, unshift, first, last, delete and length methods
37
+ # which work just like their namesakes in the Array class, but it
38
+ # also supports setting and retrieving values by key, just like a
39
+ # hash.
40
+ #
41
+ # LinkedList was ported from the original in Kirk Hanes IOWA web framework.
42
+ #
43
+ class LinkedList
44
+
45
+ include Enumerable
46
+
47
+ # Represents a single node of the linked list.
48
+
49
+ class Node
50
+ attr_accessor :key, :value, :prev_node, :next_node
51
+
52
+ def initialize(key=nil,value=nil,prev_node=nil,next_node=nil)
53
+ @key = key
54
+ @value = value
55
+ @prev_node = prev_node
56
+ @next_node = next_node
57
+ end
58
+ end
59
+
60
+ def initialize
61
+ @head = Node.new
62
+ @tail = Node.new
63
+ @lookup = Hash.new
64
+ node_join(@head,@tail)
65
+ end
66
+
67
+ def [](v)
68
+ @lookup[v].value
69
+ end
70
+
71
+ def []=(k,v)
72
+ if @lookup.has_key?(k)
73
+ @lookup[k].value = v
74
+ else
75
+ n = Node.new(k,v,@head,@head.next_node)
76
+ node_join(n,@head.next_node)
77
+ node_join(@head,n)
78
+ @lookup[k] = n
79
+ end
80
+ v
81
+ end
82
+
83
+ def empty?
84
+ @lookup.empty?
85
+ end
86
+
87
+ def delete(k)
88
+ n = @lookup.delete(k)
89
+ v = n ? node_purge(n) : nil
90
+ v
91
+ end
92
+
93
+ def first
94
+ @head.next_node.value
95
+ end
96
+
97
+ def last
98
+ @tail.prev_node.value
99
+ end
100
+
101
+ def shift
102
+ k = @head.next_node.key
103
+ n = @lookup.delete(k)
104
+ node_delete(n) if n
105
+ end
106
+
107
+ def unshift(v)
108
+ if @lookup.has_key?(v)
109
+ n = @lookup[v]
110
+ node_delete(n)
111
+ node_join(n,@head.next_node)
112
+ node_join(@head,n)
113
+ else
114
+ n = Node.new(v,v,@head,@head.next_node)
115
+ node_join(n,@head.next_node)
116
+ node_join(@head,n)
117
+ @lookup[v] = n
118
+ end
119
+ v
120
+ end
121
+
122
+ def pop
123
+ k = @tail.prev_node.key
124
+ n = @lookup.delete(k)
125
+ node_delete(n) if n
126
+ end
127
+
128
+ def push(v)
129
+ if @lookup.has_key?(v)
130
+ n = @lookup[v]
131
+ node_delete(n)
132
+ node_join(@tail.prev_node,n)
133
+ node_join(n,@tail)
134
+ else
135
+ n = Node.new(v,v,@tail.prev_node,@tail)
136
+ node_join(@tail.prev_node,n)
137
+ node_join(n,@tail)
138
+ @lookup[v] = n
139
+ end
140
+ v
141
+ end
142
+
143
+ def queue
144
+ r = []
145
+ n = @head
146
+ while (n = n.next_node) and n != @tail
147
+ r << n.key
148
+ end
149
+ r
150
+ end
151
+
152
+ def to_a
153
+ r = []
154
+ n = @head
155
+ while (n = n.next_node) and n != @tail
156
+ r << n.value
157
+ end
158
+ r
159
+ end
160
+
161
+ def length
162
+ @lookup.length
163
+ end
164
+
165
+ def each
166
+ n = @head
167
+ while (n = n.next_node) and n != @tail
168
+ yield(n.key,n.value)
169
+ end
170
+ end
171
+
172
+ private
173
+
174
+ def node_delete(n)
175
+ node_join(n.prev_node,n.next_node)
176
+ v = n.value
177
+ end
178
+
179
+ def node_purge(n)
180
+ node_join(n.prev_node,n.next_node)
181
+ v = n.value
182
+ n.value = nil
183
+ n.key = nil
184
+ n.next_node = nil
185
+ n.prev_node = nil
186
+ v
187
+ end
188
+
189
+ def node_join(a,b)
190
+ a.next_node = b
191
+ b.prev_node = a
192
+ end
193
+
194
+ end
195
+