linked 0.4.0 → 0.5.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.
- 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
|