linked 0.4.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +71 -0
- data/.travis.yml +1 -0
- data/Gemfile +2 -0
- data/Rakefile +18 -5
- data/bin/console +4 -3
- data/lib/linked.rb +4 -2
- data/lib/linked/item.rb +20 -27
- data/lib/linked/list.rb +95 -272
- data/lib/linked/list_enumerable.rb +86 -0
- data/lib/linked/listable.rb +421 -258
- data/lib/linked/util.rb +10 -0
- data/lib/linked/version.rb +2 -1
- data/linked.gemspec +28 -16
- metadata +56 -11
- data/lib/linked/list/eol.rb +0 -84
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 44eaaab09ac6111dd607d3cd52e35d2d072315bbc4614b7805254c8a7bab311a
|
4
|
+
data.tar.gz: 4672f6d56f0dde64a6fe12ecaefdf82d39795a957d97ccb55eb0558ffe23285d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c94e42bee0a6717053ff3c33de9f36c03ab4ba24d35ddb3b387ed78a3e4a54898269eb62237117a05db4cf4c96b5555c376cc507cb2d8cc02aed1a80059f69a3
|
7
|
+
data.tar.gz: 479b9a24c6dc2a785993072e620695bebe35e9599cfb9f8842fd1a767d6783c5b0838f6ccd0abce247a155e416930fa76cc6c164cb6ae85e2e9aac23c7948c9f
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
AllCops:
|
2
|
+
Exclude:
|
3
|
+
- 'vendor/**/*'
|
4
|
+
- 'tmp/**/*'
|
5
|
+
TargetRubyVersion: 2.3
|
6
|
+
|
7
|
+
Layout/EndOfLine:
|
8
|
+
EnforcedStyle: lf
|
9
|
+
|
10
|
+
Layout/ClassStructure:
|
11
|
+
Enabled: true
|
12
|
+
Categories:
|
13
|
+
module_inclusion:
|
14
|
+
- include
|
15
|
+
- prepend
|
16
|
+
- extend
|
17
|
+
ExpectedOrder:
|
18
|
+
- module_inclusion
|
19
|
+
- constants
|
20
|
+
- public_class_methods
|
21
|
+
- initializer
|
22
|
+
- instance_methods
|
23
|
+
- protected_methods
|
24
|
+
- private_methods
|
25
|
+
|
26
|
+
Layout/IndentHeredoc:
|
27
|
+
EnforcedStyle: squiggly
|
28
|
+
|
29
|
+
Lint/AmbiguousBlockAssociation:
|
30
|
+
Exclude:
|
31
|
+
- 'test/**/*.rb'
|
32
|
+
|
33
|
+
Lint/InterpolationCheck:
|
34
|
+
Exclude:
|
35
|
+
- 'test/**/*.rb'
|
36
|
+
|
37
|
+
Metrics/BlockLength:
|
38
|
+
Exclude:
|
39
|
+
- 'Rakefile'
|
40
|
+
- '**/*.rake'
|
41
|
+
- 'test/**/*.rb'
|
42
|
+
|
43
|
+
Metrics/ModuleLength:
|
44
|
+
Exclude:
|
45
|
+
- 'test/**/*.rb'
|
46
|
+
|
47
|
+
Metrics/ParameterLists:
|
48
|
+
CountKeywordArgs: false
|
49
|
+
|
50
|
+
Naming/UncommunicativeMethodParamName:
|
51
|
+
AllowedNames:
|
52
|
+
- "_"
|
53
|
+
- x
|
54
|
+
- y
|
55
|
+
- i
|
56
|
+
- p
|
57
|
+
- n
|
58
|
+
- t
|
59
|
+
- r
|
60
|
+
- g
|
61
|
+
- b
|
62
|
+
- to
|
63
|
+
|
64
|
+
Style/FrozenStringLiteralComment:
|
65
|
+
EnforcedStyle: always
|
66
|
+
|
67
|
+
Style/FormatStringToken:
|
68
|
+
Enabled: false
|
69
|
+
|
70
|
+
Style/MultipleComparison:
|
71
|
+
Enabled: false
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -1,10 +1,23 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rubocop/rake_task'
|
6
|
+
require 'yard'
|
3
7
|
|
4
8
|
Rake::TestTask.new(:test) do |t|
|
5
|
-
t.libs <<
|
6
|
-
t.libs <<
|
9
|
+
t.libs << 'test'
|
10
|
+
t.libs << 'lib'
|
7
11
|
t.test_files = FileList['test/**/*_test.rb']
|
8
12
|
end
|
9
13
|
|
10
|
-
|
14
|
+
RuboCop::RakeTask.new(:rubocop)
|
15
|
+
|
16
|
+
YARD::Rake::YardocTask.new(:yard) do |t|
|
17
|
+
t.stats_options = %w[--list-undoc]
|
18
|
+
end
|
19
|
+
|
20
|
+
desc 'Generate Ruby documentation'
|
21
|
+
task doc: %w[yard]
|
22
|
+
|
23
|
+
task default: %w[test rubocop:auto_correct]
|
data/bin/console
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
require
|
4
|
-
require
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'linked'
|
5
6
|
|
6
7
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
8
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -10,5 +11,5 @@ require "linked"
|
|
10
11
|
# require "pry"
|
11
12
|
# Pry.start
|
12
13
|
|
13
|
-
require
|
14
|
+
require 'irb'
|
14
15
|
IRB.start
|
data/lib/linked.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'linked/version'
|
4
|
+
require 'linked/util'
|
4
5
|
require 'linked/listable'
|
5
6
|
require 'linked/item'
|
6
|
-
require 'linked/
|
7
|
+
require 'linked/list_enumerable'
|
7
8
|
require 'linked/list'
|
8
9
|
|
10
|
+
# Linked List implementation.
|
9
11
|
module Linked
|
10
|
-
|
12
|
+
private_constant :Util, :ListEnumerable
|
11
13
|
end
|
data/lib/linked/item.rb
CHANGED
@@ -1,37 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Linked
|
4
|
-
#
|
4
|
+
# This is the default implementation of a listable object
|
5
5
|
#
|
6
6
|
# This class implements a listable value object that wraps an arbitrary value
|
7
|
-
# an can be
|
8
|
-
|
7
|
+
# an can be with other listable items.
|
9
8
|
class Item
|
10
9
|
include Listable
|
11
10
|
|
12
11
|
# The Item can hold an arbitrary object as its value and it will stay with
|
13
12
|
# the item.
|
14
|
-
|
13
|
+
# @return [Object] any object that is stored in the item.
|
15
14
|
attr_accessor :value
|
16
15
|
|
17
16
|
# Creates a new item. If a list is given the item will be considered a part
|
18
17
|
# of that list and appended to the end of it.
|
19
18
|
#
|
20
|
-
# value
|
21
|
-
# list - an object responding to #head and #tail.
|
22
|
-
#
|
23
|
-
# Returns a new Item.
|
24
|
-
|
19
|
+
# @param value [Object] an arbitrary object to store with the item.
|
25
20
|
def initialize(value = nil)
|
26
21
|
@value = value
|
27
22
|
super()
|
28
23
|
end
|
29
24
|
|
30
25
|
# Calling #dup on an item returns a copy that is no longer connected to the
|
31
|
-
# original item chain
|
26
|
+
# original item chain. The value will also be copied.
|
32
27
|
#
|
33
|
-
#
|
34
|
-
|
28
|
+
# @param source [Item] the item to copy.
|
29
|
+
# @return [item] a new Item.
|
35
30
|
def initialize_dup(source)
|
36
31
|
@value = begin
|
37
32
|
source.value.dup
|
@@ -45,10 +40,9 @@ module Linked
|
|
45
40
|
# responds to #value, and its value is equal (#==) to this value, the
|
46
41
|
# objects are considered equal.
|
47
42
|
#
|
48
|
-
# other
|
49
|
-
#
|
50
|
-
#
|
51
|
-
|
43
|
+
# @param other [#value, Object] any object.
|
44
|
+
# @return [true, false] true if the value of the given object is equal to
|
45
|
+
# the item value.
|
52
46
|
def ==(other)
|
53
47
|
return false unless other.respond_to? :value
|
54
48
|
value == other.value
|
@@ -58,28 +52,27 @@ module Linked
|
|
58
52
|
|
59
53
|
# Uses the hash value of the item value.
|
60
54
|
#
|
61
|
-
#
|
62
|
-
|
55
|
+
# @return [Integer] a fixnum that can be used by Hash to identify the item.
|
63
56
|
def hash
|
64
57
|
value.hash
|
65
58
|
end
|
66
59
|
|
67
|
-
# Freezes the value, as well as making the
|
68
|
-
|
60
|
+
# Freezes the value, as well as making the item itself immutable.
|
61
|
+
#
|
62
|
+
# @return [self]
|
69
63
|
def freeze
|
70
64
|
value.freeze
|
71
65
|
super
|
72
66
|
end
|
73
67
|
|
74
68
|
# The default #inspect method becomes very cluttered the moment you start
|
75
|
-
#
|
76
|
-
# class name, object id and value (if set).
|
77
|
-
|
69
|
+
# linking objects together. This implementation fixes that and only shows
|
70
|
+
# the class name, object id and value (if set).
|
71
|
+
#
|
72
|
+
# @return [String] a string representation of the list item.
|
78
73
|
def inspect
|
79
|
-
return yield
|
80
|
-
|
81
|
-
output = format '%s:0x%0x', self.class.name, object_id
|
82
|
-
value ? output + " value=#{value.inspect}" : output
|
74
|
+
return yield(self).to_s if block_given?
|
75
|
+
value ? object_identifier + " value=#{value.inspect}" : object_identifier
|
83
76
|
end
|
84
77
|
end
|
85
78
|
end
|
data/lib/linked/list.rb
CHANGED
@@ -1,70 +1,52 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Linked
|
4
|
-
#
|
4
|
+
# This class provides a way extend the regular chain of listable items with
|
5
|
+
# the concept of an empty chain.
|
5
6
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# requirements defined by Listable.
|
9
|
-
|
7
|
+
# Lists are ment to behave more like arrays, and respond to many of the same
|
8
|
+
# methods.
|
10
9
|
class List
|
11
|
-
include
|
12
|
-
|
13
|
-
# Initializes the list. It is important that this method be called during
|
14
|
-
# the initialization of the including class, and that the instance variables
|
15
|
-
# @_item_count and @_eol never be accessed directly.
|
10
|
+
include ListEnumerable
|
11
|
+
include Util
|
16
12
|
|
13
|
+
# Initializes the list.
|
17
14
|
def initialize
|
18
|
-
|
19
|
-
@_item_count = 0
|
20
|
-
|
15
|
+
reset_list
|
21
16
|
super
|
22
17
|
end
|
23
18
|
|
24
19
|
# When copying a list its entire item chain needs to be copied as well.
|
25
20
|
# Therefore #dup will be called on each of the original lists items, making
|
26
21
|
# this operation quite expensive.
|
27
|
-
|
22
|
+
#
|
23
|
+
# @param source [List] the list to copy.
|
28
24
|
def initialize_dup(source)
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
source.each_item { |item| push item.dup }
|
25
|
+
reset_list
|
26
|
+
source.each_item { |item| push item.dup }
|
33
27
|
|
34
28
|
super
|
35
29
|
end
|
36
30
|
|
37
|
-
# Identity method that simply return the list. This method mirrors Item#list
|
38
|
-
# and allows other methods that work on List objects to easily and
|
39
|
-
# interchangebly accept both lists and items as arguments.
|
40
|
-
#
|
41
|
-
# Returns the list itself.
|
42
|
-
|
43
|
-
def list
|
44
|
-
self
|
45
|
-
end
|
46
|
-
|
47
31
|
# Access the first item in the list. If the list is empty a NoMethodError
|
48
32
|
# will be raised. This mirrors the behaviour of Item#item and allows other
|
49
33
|
# methods that work on List objects to easily and interchangeably accept
|
50
34
|
# both lists and items as arguments.
|
51
35
|
#
|
52
|
-
#
|
53
|
-
|
36
|
+
# @return [Listable] the first item in the list.
|
54
37
|
def item
|
55
38
|
raise NoMethodError if empty?
|
56
|
-
|
39
|
+
@_chain
|
57
40
|
end
|
58
41
|
|
59
42
|
# Two lists are considered equal if the n:th item from each list are equal.
|
60
43
|
#
|
61
|
-
# other
|
62
|
-
#
|
63
|
-
#
|
64
|
-
|
44
|
+
# @param other [Object] the object to compare with.
|
45
|
+
# @return [true] if the given object is a list and the items are equal.
|
46
|
+
# @return [false] otherwise.
|
65
47
|
def ==(other)
|
66
48
|
return false unless other.is_a? self.class
|
67
|
-
return false unless other.count ==
|
49
|
+
return false unless other.count == count
|
68
50
|
|
69
51
|
other_items = other.each_item
|
70
52
|
each_item.all? { |item| item == other_items.next }
|
@@ -72,86 +54,10 @@ module Linked
|
|
72
54
|
|
73
55
|
alias eql? ==
|
74
56
|
|
75
|
-
#
|
76
|
-
#
|
77
|
-
# which the block returns true and the n - 1 items directly following it
|
78
|
-
# will be returned.
|
79
|
-
#
|
80
|
-
# n - the number of items to return.
|
81
|
-
#
|
82
|
-
# Returns, for different values of n:
|
83
|
-
# n == 0) nil
|
84
|
-
# n == 1) an item if the list contains one, or nil
|
85
|
-
# n > 1) an array of between 0 and n items, depending on how many are in
|
86
|
-
# the list
|
87
|
-
|
88
|
-
def first(n = 1)
|
89
|
-
raise ArgumentError, 'n cannot be negative' if n < 0
|
90
|
-
|
91
|
-
return first_item_after head, n, count unless block_given?
|
92
|
-
|
93
|
-
item = head
|
94
|
-
items_left = count
|
95
|
-
|
96
|
-
items_left.times do
|
97
|
-
break if yield next_item = item.next
|
98
|
-
item = next_item
|
99
|
-
items_left -= 1
|
100
|
-
end
|
101
|
-
|
102
|
-
first_item_after item, n, items_left
|
103
|
-
end
|
104
|
-
|
105
|
-
# Access the last n item(s) in the list. The items will retain thier order.
|
106
|
-
# If a block is given each item, starting with the last in the list, will be
|
107
|
-
# yielded to it. The first item for which the block returns true and the
|
108
|
-
# n - 1 items directly preceding it will be returned.
|
109
|
-
#
|
110
|
-
# n - the number of items to return.
|
111
|
-
#
|
112
|
-
# Returns, for different values of n:
|
113
|
-
# n == 0) nil
|
114
|
-
# n == 1) an item if the list contains one, or nil
|
115
|
-
# n > 1) an array of between 0 and n items, depending on how many are in
|
116
|
-
# the list
|
117
|
-
|
118
|
-
def last(n = 1)
|
119
|
-
raise ArgumentError, 'n cannot be negative' if n < 0
|
120
|
-
|
121
|
-
return last_item_before tail, n, count unless block_given?
|
122
|
-
|
123
|
-
item = tail
|
124
|
-
items_left = count
|
125
|
-
|
126
|
-
items_left.times do
|
127
|
-
break if yield prev_item = item.prev
|
128
|
-
item = prev_item
|
129
|
-
items_left -= 1
|
130
|
-
end
|
131
|
-
|
132
|
-
last_item_before item, n, items_left
|
133
|
-
end
|
134
|
-
|
135
|
-
# Overrides the Enumerable#count method when given no argument to provide a
|
136
|
-
# fast item count. Instead of iterating over each item, the internal item
|
137
|
-
# count is returned.
|
138
|
-
#
|
139
|
-
# args - see Enumerable#count
|
140
|
-
#
|
141
|
-
# Returns the number of items counted.
|
142
|
-
|
143
|
-
def count(*args)
|
144
|
-
if args.empty? && !block_given?
|
145
|
-
@_item_count
|
146
|
-
else
|
147
|
-
super
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
# Returns true if the list does not contain any items.
|
152
|
-
|
57
|
+
# @return [true] if the list does not contain any items.
|
58
|
+
# @return [false] otherwise.
|
153
59
|
def empty?
|
154
|
-
@
|
60
|
+
nil.eql? @_chain
|
155
61
|
end
|
156
62
|
|
157
63
|
# Insert an item at the end of the list. If the given object is not an
|
@@ -160,12 +66,17 @@ module Linked
|
|
160
66
|
#
|
161
67
|
# See Item#append for more details.
|
162
68
|
#
|
163
|
-
# object
|
164
|
-
#
|
165
|
-
# Returns self.
|
166
|
-
|
69
|
+
# @param object [#item, Object] the item to insert, or an arbitrary object.
|
70
|
+
# @return [self]
|
167
71
|
def push(object)
|
168
|
-
|
72
|
+
item = coerce_item object
|
73
|
+
|
74
|
+
if empty?
|
75
|
+
@_chain = item
|
76
|
+
else
|
77
|
+
list_tail.append item
|
78
|
+
end
|
79
|
+
|
169
80
|
self
|
170
81
|
end
|
171
82
|
|
@@ -173,11 +84,18 @@ module Linked
|
|
173
84
|
|
174
85
|
# Pop the last item off the list.
|
175
86
|
#
|
176
|
-
#
|
177
|
-
|
87
|
+
# @return [Listable, nil] the last item in the list, or nil if the list is
|
88
|
+
# empty.
|
178
89
|
def pop
|
179
90
|
return nil if empty?
|
180
|
-
|
91
|
+
|
92
|
+
if list_tail.first?
|
93
|
+
item = last
|
94
|
+
@_chain = nil
|
95
|
+
item
|
96
|
+
else
|
97
|
+
list_tail.delete
|
98
|
+
end
|
181
99
|
end
|
182
100
|
|
183
101
|
# Insert an item at the beginning of the list. If the given object is not an
|
@@ -186,67 +104,49 @@ module Linked
|
|
186
104
|
#
|
187
105
|
# See Item#prepend for more details.
|
188
106
|
#
|
189
|
-
# object
|
190
|
-
#
|
191
|
-
# Returns self.
|
192
|
-
|
107
|
+
# @param object [#item, Object] the item to insert, or an arbitrary object.
|
108
|
+
# @return [self]
|
193
109
|
def unshift(object)
|
194
|
-
|
110
|
+
item = coerce_item object
|
111
|
+
@_chain = empty? ? item.chain : @_chain.prepend(item)
|
112
|
+
|
195
113
|
self
|
196
114
|
end
|
197
115
|
|
198
116
|
# Shift the first item off the list.
|
199
117
|
#
|
200
|
-
#
|
201
|
-
|
118
|
+
# @return [Listable, nil] the first item in the list, or nil if the list is
|
119
|
+
# empty.
|
202
120
|
def shift
|
203
121
|
return nil if empty?
|
204
|
-
|
122
|
+
|
123
|
+
if list_head.last?
|
124
|
+
item = @_chain
|
125
|
+
@_chain = nil
|
126
|
+
item
|
127
|
+
else
|
128
|
+
old_head = list_head
|
129
|
+
@_chain = list_head.next
|
130
|
+
old_head.delete
|
131
|
+
end
|
205
132
|
end
|
206
133
|
|
207
134
|
# Check if an item is in the list.
|
208
135
|
#
|
209
|
-
# item
|
210
|
-
#
|
211
|
-
#
|
212
|
-
|
136
|
+
# @param item [Object] any object that may be in the list.
|
137
|
+
# @return [true] if the given item is in the list.
|
138
|
+
# @return [false] otherwise.
|
213
139
|
def include?(item)
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
end
|
218
|
-
|
219
|
-
# Iterates over each item in the list If a block is not given an enumerator
|
220
|
-
# is returned.
|
221
|
-
|
222
|
-
def each_item
|
223
|
-
return to_enum(__method__) { count } unless block_given?
|
224
|
-
return if empty?
|
225
|
-
|
226
|
-
item = head
|
227
|
-
loop { yield item = item.next }
|
228
|
-
end
|
229
|
-
|
230
|
-
alias each each_item
|
231
|
-
|
232
|
-
# Iterates over each item in the list in reverse order. If a block is not
|
233
|
-
# given an enumerator is returned.
|
234
|
-
|
235
|
-
def reverse_each_item
|
236
|
-
return to_enum(__method__) { count } unless block_given?
|
237
|
-
return if empty?
|
238
|
-
|
239
|
-
item = tail
|
240
|
-
loop { yield item = item.prev }
|
140
|
+
return false if empty?
|
141
|
+
# TODO: This works fine, but looks wrong.
|
142
|
+
@_chain.in_chain? item
|
241
143
|
end
|
242
144
|
|
243
|
-
alias reverse_each reverse_each_item
|
244
|
-
|
245
145
|
# Calls #freeze on all items in the list, as well as the head and the tail
|
246
146
|
# (eol).
|
247
|
-
|
147
|
+
#
|
148
|
+
# @return [self]
|
248
149
|
def freeze
|
249
|
-
@_eol.freeze
|
250
150
|
each_item(&:freeze)
|
251
151
|
super
|
252
152
|
end
|
@@ -257,14 +157,13 @@ module Linked
|
|
257
157
|
# Importantly this implementation supports nested lists and will return a
|
258
158
|
# tree like structure.
|
259
159
|
|
260
|
-
def
|
261
|
-
|
262
|
-
res = [super]
|
160
|
+
def inspect_list(&block)
|
161
|
+
res = [block_given? ? yield(self) : object_identifier]
|
263
162
|
|
264
163
|
each_item do |item|
|
265
164
|
lines = item.inspect(&block).split "\n"
|
266
165
|
|
267
|
-
res.push
|
166
|
+
res.push((item.last? ? '└─╴' : '├─╴') + lines.shift)
|
268
167
|
padding = item.last? ? ' ' : '│ '
|
269
168
|
lines.each { |line| res.push padding + line }
|
270
169
|
end
|
@@ -272,125 +171,49 @@ module Linked
|
|
272
171
|
res.join("\n")
|
273
172
|
end
|
274
173
|
|
174
|
+
alias inspect inspect_list
|
175
|
+
|
275
176
|
# Protected factory method for creating items compatible with the list. This
|
276
177
|
# method is called whenever an arbitrary object is pushed or unshifted onto
|
277
178
|
# the list and need to be wraped inside an Item.
|
278
179
|
#
|
279
180
|
# This method can be overridden to support different Item types.
|
280
181
|
#
|
281
|
-
# args
|
282
|
-
#
|
283
|
-
#
|
284
|
-
|
182
|
+
# @param args [Array<Object>] the arguments that are to be passed on to
|
183
|
+
# `Item.new`.
|
184
|
+
# @return [Item] a new `Listable` item.
|
285
185
|
protected def create_item(*args)
|
286
186
|
Item.new(*args)
|
287
187
|
end
|
288
188
|
|
289
|
-
#
|
290
|
-
|
291
|
-
|
292
|
-
@_eol
|
293
|
-
end
|
294
|
-
|
295
|
-
# Returns an object that responds to #prev= and #append.
|
296
|
-
|
297
|
-
alias tail head
|
298
|
-
|
299
|
-
# Internal method to grow the list with n elements. Never call this method
|
300
|
-
# without also inserting the n elements.
|
189
|
+
# Takes an arbitrary object and coerces it into an item compliant with the
|
190
|
+
# list. If the object is already an item it will be used as is. Otherwise
|
191
|
+
# #create_item will be called with the object as an argument.
|
301
192
|
#
|
302
|
-
#
|
303
|
-
#
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
# Internal method to shrink the list with n elements. Never call this method
|
311
|
-
# without also deleting the n elements.
|
312
|
-
#
|
313
|
-
# n - the number of items that has been/will be removed from the list.
|
314
|
-
#
|
315
|
-
# Returns updated the item count.
|
316
|
-
|
317
|
-
private def shrink(n = 1)
|
318
|
-
@_item_count -= n
|
193
|
+
# @param [#item, Object] the object to coerce.
|
194
|
+
# @return [Listable] see `#create_item`.
|
195
|
+
private def coerce_item(object)
|
196
|
+
if object.respond_to? :item
|
197
|
+
object.item
|
198
|
+
else
|
199
|
+
create_item object
|
200
|
+
end
|
319
201
|
end
|
320
202
|
|
321
|
-
# Private method
|
322
|
-
#
|
323
|
-
|
324
|
-
|
325
|
-
# b) clear the `next` pointer of the last item.
|
326
|
-
|
327
|
-
private def clear
|
328
|
-
@_eol.send :reset
|
329
|
-
@_item_count = 0
|
203
|
+
# Private method for clearing the list and bringing it to a pristine
|
204
|
+
# state.
|
205
|
+
private def reset_list
|
206
|
+
@_chain = nil
|
330
207
|
end
|
331
208
|
|
332
|
-
#
|
333
|
-
|
334
|
-
|
335
|
-
# should not be lower than the actual ammount. The following must
|
336
|
-
# hold for the output to be valid:
|
337
|
-
# a) n > 0
|
338
|
-
# b) there are at least items_left items left
|
339
|
-
#
|
340
|
-
# item - the Item just before the item to start from.
|
341
|
-
# n - the number of items to return.
|
342
|
-
# items_left - the number of items left.
|
343
|
-
#
|
344
|
-
# Returns, for different values of n:
|
345
|
-
# n == 0) nil
|
346
|
-
# n == 1) an item if items_left > 0 or nil
|
347
|
-
# n > 1) an array of items if items_left > 0 or an empty array
|
348
|
-
|
349
|
-
private def first_item_after(item, n, items_left = @_item_count)
|
350
|
-
# Optimize for these cases
|
351
|
-
return nil if n == 0
|
352
|
-
return n > 1 ? [] : nil if item.last?
|
353
|
-
return item.next if n == 1
|
354
|
-
|
355
|
-
n = items_left if n > items_left
|
356
|
-
|
357
|
-
arr = Array.new n
|
358
|
-
n.times { |i| arr[i] = item = item.next }
|
359
|
-
arr
|
360
|
-
rescue StopIteration
|
361
|
-
arr.compact! || arr
|
209
|
+
# Returns the first item item in the list, or nil if empty.
|
210
|
+
private def list_head
|
211
|
+
@_chain
|
362
212
|
end
|
363
213
|
|
364
|
-
#
|
365
|
-
|
366
|
-
|
367
|
-
# should not be lower than the actual ammount. The following must hold for
|
368
|
-
# the output to be valid:
|
369
|
-
# a) n > 0
|
370
|
-
# b) there are at least items_left items left
|
371
|
-
#
|
372
|
-
# item - the Item just after the item to start from.
|
373
|
-
# n - the number of items to return.
|
374
|
-
# items_left - the number of items left.
|
375
|
-
#
|
376
|
-
# Returns, for different values of n:
|
377
|
-
# n == 0) nil
|
378
|
-
# n == 1) an item if items_left > 0 or nil
|
379
|
-
# n > 1) an array of items if items_left > 0 or an empty array
|
380
|
-
|
381
|
-
private def last_item_before(item, n, items_left = @_item_count)
|
382
|
-
# Optimize for these cases
|
383
|
-
return nil if n == 0
|
384
|
-
return n > 1 ? [] : nil if item.first?
|
385
|
-
return item.prev if n == 1
|
386
|
-
|
387
|
-
n = items_left if n > items_left
|
388
|
-
|
389
|
-
arr = Array.new n
|
390
|
-
(n - 1).downto(0) { |i| arr[i] = item = item.prev }
|
391
|
-
arr
|
392
|
-
rescue StopIteration
|
393
|
-
arr.compact! || arr
|
214
|
+
# Returns an the last item in the list, or nil if empty.
|
215
|
+
private def list_tail
|
216
|
+
@_chain.last_in_chain
|
394
217
|
end
|
395
218
|
end
|
396
219
|
end
|