linked 0.2.0 → 0.2.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 +4 -4
- data/lib/linked/item.rb +177 -46
- data/lib/linked/list.rb +21 -0
- data/lib/linked/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fefa402916a5d9db25add5e34a0d95cfbfe1440f
|
4
|
+
data.tar.gz: 3f17115bffeb8777ba904292f98c8fffbed1b34b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6dbf54c87e2ca417d725e40f12eff566439fc9212c5f13c12bd2c089c0018a09cf671de635c2aa800151e3d6474de793e6b3991ab51c7e7fa3f224903ad34cb2
|
7
|
+
data.tar.gz: 83a352fb6f3f00e5bb7245075d47609521dbeb7c508a1ea066acb2fb120e6affdbc2ed9c54cbcc15c881353b7c716653976eec02e138f7b8131e51c6699ac7c9
|
data/lib/linked/item.rb
CHANGED
@@ -6,10 +6,10 @@ module Linked
|
|
6
6
|
# This class implements doubly linked list items, designed to work both on
|
7
7
|
# their own and as children of list.
|
8
8
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
9
|
+
# +- - - + +------+------+ +- - - +
|
10
|
+
# | Head | <--| prev | next |--> ... --> | Tail |
|
11
|
+
# + - - -+ +------+------+ + - - -+
|
12
|
+
# (optional) First Item N Items (optional)
|
13
13
|
#
|
14
14
|
# An object is considered a list if it responds to #head, #tail, #grow and
|
15
15
|
# #shrink. The latter facilitate counting of the items and will be called
|
@@ -17,6 +17,19 @@ module Linked
|
|
17
17
|
# expected to return two objects that, respectivly
|
18
18
|
# a) responds to #next= and #append, or #prev= and #prepend and
|
19
19
|
# b) returns true for #nil?.
|
20
|
+
#
|
21
|
+
# Notation
|
22
|
+
# --------
|
23
|
+
#
|
24
|
+
# Some methods operate on chains of items, and to describe the effects of an
|
25
|
+
# operation the following syntax is used.
|
26
|
+
#
|
27
|
+
# A ( A <> B ) [ A <> B ]
|
28
|
+
# (i) (ii) (iii)
|
29
|
+
#
|
30
|
+
# Single items are denoted with capital letters (i), while chains are written
|
31
|
+
# as multiple connected items (ii). The parenthesis are optional. To show that
|
32
|
+
# one or more nodes are wraped in a list, angle brackets are used (iii).
|
20
33
|
|
21
34
|
class Item
|
22
35
|
# Access the list (if any) that the item belongs to. Writing to this
|
@@ -191,16 +204,17 @@ module Linked
|
|
191
204
|
# the argument after: true, the split will instead happen after this item
|
192
205
|
# and it will instead be kept with those before it.
|
193
206
|
#
|
194
|
-
# Example
|
207
|
+
# Example for the chain (A <> B <> C)
|
195
208
|
#
|
196
|
-
#
|
197
|
-
#
|
209
|
+
# B.split(after: false) => (A), (B <> C)
|
210
|
+
# B.split(after: true) => (A <> B), (C)
|
198
211
|
#
|
199
212
|
# after - determine wheter to split the chain before or after this item.
|
200
213
|
#
|
201
214
|
# Returns self.
|
202
215
|
|
203
216
|
def split after: false
|
217
|
+
warn '[DEPRECATION] this method will be removed in the next major update. Please use #delete_before and #delete_after instead'
|
204
218
|
if after
|
205
219
|
unless last?
|
206
220
|
if @list
|
@@ -246,6 +260,10 @@ module Linked
|
|
246
260
|
# the given item is part of a chain, all items following it will be moved to
|
247
261
|
# this one, and added to the list if one is set.
|
248
262
|
#
|
263
|
+
# Example for the chain (A <> C)
|
264
|
+
#
|
265
|
+
# A.append B # => (A <> B <> C)
|
266
|
+
#
|
249
267
|
# Alternativly the argument can be an arbitrary object, in which case a new
|
250
268
|
# item will be created around it.
|
251
269
|
#
|
@@ -258,33 +276,31 @@ module Linked
|
|
258
276
|
#
|
259
277
|
# Returns the last item that was appended.
|
260
278
|
|
261
|
-
def append(
|
262
|
-
if
|
263
|
-
|
279
|
+
def append(object)
|
280
|
+
if object.respond_to? :item
|
281
|
+
first_item = object.item
|
282
|
+
last_item = first_item.send :extract_beginning_with, @list
|
264
283
|
else
|
265
|
-
|
284
|
+
first_item = last_item = self.class.new object
|
285
|
+
first_item.list = @list
|
286
|
+
@list.send :grow if @list
|
266
287
|
end
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
@next =
|
271
|
-
|
272
|
-
|
273
|
-
sibling.list = @list
|
274
|
-
sibling = sibling.next
|
275
|
-
end
|
276
|
-
|
277
|
-
@list.send :grow, count if @list
|
278
|
-
|
279
|
-
sibling.next = after_sibling
|
280
|
-
after_sibling.prev = sibling if after_sibling
|
281
|
-
sibling
|
288
|
+
|
289
|
+
first_item.prev = self
|
290
|
+
@next.prev = last_item if @next
|
291
|
+
@next, last_item.next = first_item, @next
|
292
|
+
|
293
|
+
last_item
|
282
294
|
end
|
283
295
|
|
284
296
|
# Inserts the given item between this one and the one before it (if any). If
|
285
297
|
# the given item is part of a chain, all items preceeding it will be moved
|
286
298
|
# to this one, and added to the list if one is set.
|
287
299
|
#
|
300
|
+
# Example for the chain (A <> C)
|
301
|
+
#
|
302
|
+
# C.prepend B # => (A <> B <> C)
|
303
|
+
#
|
288
304
|
# Alternativly the argument can be an arbitrary object, in which case a new
|
289
305
|
# item will be created around it.
|
290
306
|
#
|
@@ -297,29 +313,23 @@ module Linked
|
|
297
313
|
#
|
298
314
|
# Returns the last item that was prepended.
|
299
315
|
|
300
|
-
def prepend(
|
301
|
-
if
|
302
|
-
|
316
|
+
def prepend(object)
|
317
|
+
if object.respond_to? :item
|
318
|
+
last_item = object.item
|
319
|
+
first_item = last_item.send :extract_ending_with, @list
|
303
320
|
else
|
304
|
-
|
321
|
+
first_item = last_item = self.class.new object
|
322
|
+
first_item.list = @list
|
323
|
+
@list.send :grow, 1 if @list
|
305
324
|
end
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
@prev =
|
310
|
-
|
311
|
-
|
312
|
-
sibling.list = @list
|
313
|
-
sibling = sibling.prev
|
314
|
-
end
|
315
|
-
|
316
|
-
@list.send :grow, count if @list
|
317
|
-
|
318
|
-
sibling.prev = before_sibling
|
319
|
-
before_sibling.next = sibling if before_sibling
|
320
|
-
sibling
|
325
|
+
|
326
|
+
last_item.next = self
|
327
|
+
@prev.next = first_item if @prev
|
328
|
+
@prev, first_item.prev = last_item, @prev
|
329
|
+
|
330
|
+
first_item
|
321
331
|
end
|
322
|
-
|
332
|
+
|
323
333
|
# Remove an item from the chain. If this item is part of a list and is
|
324
334
|
# either first, last or both in that list, #next= and #prev= will be called
|
325
335
|
# on the list head and tail respectivly.
|
@@ -336,9 +346,32 @@ module Linked
|
|
336
346
|
@next = @prev = @list = nil
|
337
347
|
self
|
338
348
|
end
|
349
|
+
|
350
|
+
# Remove all items before this one in the chain. If the items are part of a
|
351
|
+
# list they will be removed from it.
|
352
|
+
#
|
353
|
+
# Returns the last item in the chain that was just deleted, or nil if this
|
354
|
+
# is the first item.
|
355
|
+
|
356
|
+
def delete_before
|
357
|
+
@prev.send :extract_ending_with unless first?
|
358
|
+
end
|
359
|
+
|
360
|
+
# Remove all items after this one in the chain. If the items are part of a
|
361
|
+
# list they will be removed from it.
|
362
|
+
#
|
363
|
+
# Returns the last item in the chain that was just deleted, or nil if this
|
364
|
+
# is the first item.
|
365
|
+
|
366
|
+
def delete_after
|
367
|
+
@next.send :extract_beginning_with unless last?
|
368
|
+
end
|
339
369
|
|
340
370
|
# Iterates over each item before this, in reverse order. If a block is not
|
341
371
|
# given an enumerator is returned.
|
372
|
+
#
|
373
|
+
# Note that raising a StopIteraion inside the block will cause the loop to
|
374
|
+
# silently stop the iteration early.
|
342
375
|
|
343
376
|
def before
|
344
377
|
return to_enum(__callee__) unless block_given?
|
@@ -354,6 +387,9 @@ module Linked
|
|
354
387
|
|
355
388
|
# Iterates over each item after this. If a block is not given an enumerator
|
356
389
|
# is returned.
|
390
|
+
#
|
391
|
+
# Note that raising a StopIteraion inside the block will cause the loop to
|
392
|
+
# silently stop the iteration early.
|
357
393
|
|
358
394
|
def after
|
359
395
|
return to_enum(__callee__) unless block_given?
|
@@ -384,5 +420,100 @@ module Linked
|
|
384
420
|
output = format '%s:0x%0x', self.class.name, object_id
|
385
421
|
value ? output + " value=#{value.inspect}" : output
|
386
422
|
end
|
423
|
+
|
424
|
+
# PRIVATE DANGEROUS METHOD. This method should never be called directly
|
425
|
+
# since it may leave the extracted item chain in an invalid state.
|
426
|
+
#
|
427
|
+
# This method extracts the item, together with the chain following it, from
|
428
|
+
# the list they are in (if any) and optionally facilitates moving them to a
|
429
|
+
# new list.
|
430
|
+
#
|
431
|
+
# Given the two lists
|
432
|
+
# [ A <> B <> C ] [ D ]
|
433
|
+
# (I) (II)
|
434
|
+
#
|
435
|
+
# calling B.extract_beginning_with(II) will result in (B <> C) being removed
|
436
|
+
# from (I), and (II) to be grown by two. (B <> C) will now reference (II)
|
437
|
+
# but they will not yet be linked to any of the items in it. It is therefore
|
438
|
+
# necessary to insert them directly after calling this method, or (II) will
|
439
|
+
# be left in an invalid state.
|
440
|
+
#
|
441
|
+
# Returns the last item of the chain.
|
442
|
+
|
443
|
+
private def extract_beginning_with(new_list = nil)
|
444
|
+
old_list = @list
|
445
|
+
# Count items and move them to the new list
|
446
|
+
last_item = self
|
447
|
+
count = 1 + loop.count do
|
448
|
+
last_item.list = new_list
|
449
|
+
last_item = last_item.next
|
450
|
+
end
|
451
|
+
|
452
|
+
# Make sure the old list is in a valid state
|
453
|
+
if old_list
|
454
|
+
if first?
|
455
|
+
old_list.send :clear
|
456
|
+
else
|
457
|
+
old_list.send :shrink, count
|
458
|
+
# Fix the links within in the list
|
459
|
+
@prev.next = last_item.next!
|
460
|
+
last_item.next!.prev = @prev
|
461
|
+
end
|
462
|
+
else
|
463
|
+
# Disconnect the item directly after the chain
|
464
|
+
@prev.next = nil unless first?
|
465
|
+
end
|
466
|
+
|
467
|
+
# Disconnect the chain from the list
|
468
|
+
@prev = last_item.next = nil
|
469
|
+
|
470
|
+
# Preemptivly tell the new list to grow
|
471
|
+
new_list.send :grow, count if new_list
|
472
|
+
|
473
|
+
last_item
|
474
|
+
end
|
475
|
+
|
476
|
+
# PRIVATE DANGEROUS METHOD. This method should never be called directly
|
477
|
+
# since it may leave the extracted item chain in an invalid state.
|
478
|
+
#
|
479
|
+
# This method extracts the item, together with the chain preceding it, from
|
480
|
+
# the list they are in (if any) and optionally facilitates moving them to a
|
481
|
+
# new list. See #extract_beginning_with for a description of the side
|
482
|
+
# effects from calling this method.
|
483
|
+
#
|
484
|
+
# Returns the first item in the chain.
|
485
|
+
|
486
|
+
private def extract_ending_with(new_list = nil)
|
487
|
+
old_list = @list
|
488
|
+
# Count items and move them to the new list
|
489
|
+
first_item = self
|
490
|
+
count = 1 + loop.count do
|
491
|
+
first_item.list = new_list
|
492
|
+
first_item = first_item.prev
|
493
|
+
end
|
494
|
+
|
495
|
+
# Make sure the old list is in a valid state
|
496
|
+
if old_list
|
497
|
+
if last?
|
498
|
+
old_list.send :clear
|
499
|
+
else
|
500
|
+
old_list.send :shrink, count
|
501
|
+
# Fix the links within in the list
|
502
|
+
@next.prev = first_item.prev!
|
503
|
+
first_item.prev!.next = @next
|
504
|
+
end
|
505
|
+
else
|
506
|
+
# Disconnect the item directly after the chain
|
507
|
+
@next.prev = nil unless last?
|
508
|
+
end
|
509
|
+
|
510
|
+
# Disconnect the chain from the list
|
511
|
+
first_item.prev = @next = nil
|
512
|
+
|
513
|
+
# Preemptivly tell the new list to grow
|
514
|
+
new_list.send :grow, count if new_list
|
515
|
+
|
516
|
+
first_item
|
517
|
+
end
|
387
518
|
end
|
388
519
|
end
|
data/lib/linked/list.rb
CHANGED
@@ -306,6 +306,19 @@ module Linked
|
|
306
306
|
private def shrink(n = 1)
|
307
307
|
@item_count -= n
|
308
308
|
end
|
309
|
+
|
310
|
+
# Private method to clear the list. Never call this method without also
|
311
|
+
# modifying the items in the list, as this operation leavs them in an
|
312
|
+
# inconsistant state. If the list items are kept, make sure to
|
313
|
+
# a) clear the `prev` pointer of the first item and
|
314
|
+
# b) clear the `next` pointer of the last item.
|
315
|
+
|
316
|
+
private def clear
|
317
|
+
head.send :next=, tail
|
318
|
+
tail.send :prev=, head
|
319
|
+
|
320
|
+
@item_count = 0
|
321
|
+
end
|
309
322
|
|
310
323
|
# Protected helper method that returns the first n items, starting just
|
311
324
|
# after item, given that there are items_left items left. Knowing the exact
|
@@ -370,5 +383,13 @@ module Linked
|
|
370
383
|
rescue StopIteration
|
371
384
|
arr.compact! || arr
|
372
385
|
end
|
386
|
+
|
387
|
+
# This method is called whenever the module is included somewhere. In the
|
388
|
+
# special case when List is included in an Item the #item method must be
|
389
|
+
# changed to return self.
|
390
|
+
|
391
|
+
def self.included(klass)
|
392
|
+
klass.send(:define_method, :item) { self } if klass < Item
|
393
|
+
end
|
373
394
|
end
|
374
395
|
end
|
data/lib/linked/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: linked
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sebastian Lindberg
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-08-
|
11
|
+
date: 2016-08-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|