rooted_tree 0.3.3 → 0.3.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2f48a3504fb307daf09d507cd556d7b622d6e17d
4
- data.tar.gz: 363432cb0d5a2b69f0f4f0980de16ec03451619b
3
+ metadata.gz: 4500aef4d9f3aaa7483ec8908d63c594d2b1f8c9
4
+ data.tar.gz: c45b17013a6aa680e023a86e8b927841d22259b8
5
5
  SHA512:
6
- metadata.gz: 08d262553a452128cceedda189e57d7118184f253c5106696a9a5b98751392091717dbd8551670cb81c6743e08ecd9876442c3592f4ff1e6ea9b67e9a581d5ee
7
- data.tar.gz: f1de5a0b40607102934aaea8ecbb94d98de13aed5a5805bfed6e2ff34d41121a4cb600cc8a1f05dbf88b4c6001432c54e75621dde560ac4e79937cdd5f2a55f4
6
+ metadata.gz: 88bbe928aa3515c11a0c11d150c17286ca38e4bf4e69331eb1c4e4847743262978fd59ce4e1d126f898ab252fe6214ee7b21cf93be94666e6aa563f53ad5c03f
7
+ data.tar.gz: 568e6025fb468c1f7477a6425c140e506564ba4b84500f92fce9dd8fb174f77cdaae98f5d725b5f12a07ccaee359392a01bb0f339a9db6fb5fb725c6c175604d
data/.gitignore CHANGED
@@ -1,6 +1,5 @@
1
1
  /.bundle/
2
2
  /.yardoc
3
- /Gemfile.lock
4
3
  /_yardoc/
5
4
  /coverage/
6
5
  /doc/
data/.rubocop.yml ADDED
@@ -0,0 +1,65 @@
1
+ AllCops:
2
+ Exclude:
3
+ - 'vendor/**/*'
4
+ - 'tmp/**/*'
5
+ TargetRubyVersion: 2.4
6
+
7
+ Style/FrozenStringLiteralComment:
8
+ EnforcedStyle: always
9
+
10
+ Layout/EndOfLine:
11
+ EnforcedStyle: lf
12
+
13
+ Layout/ClassStructure:
14
+ Enabled: true
15
+ Categories:
16
+ module_inclusion:
17
+ - include
18
+ - prepend
19
+ - extend
20
+ ExpectedOrder:
21
+ - module_inclusion
22
+ - constants
23
+ - public_class_methods
24
+ - initializer
25
+ - instance_methods
26
+ - protected_methods
27
+ - private_methods
28
+
29
+ Layout/IndentHeredoc:
30
+ EnforcedStyle: squiggly
31
+
32
+ Lint/AmbiguousBlockAssociation:
33
+ Exclude:
34
+ - 'test/**/*.rb'
35
+
36
+ Lint/InterpolationCheck:
37
+ Exclude:
38
+ - 'test/**/*.rb'
39
+
40
+ Metrics/BlockLength:
41
+ Exclude:
42
+ - 'Rakefile'
43
+ - '**/*.rake'
44
+ - 'test/**/*.rb'
45
+ - '*.gemspec'
46
+
47
+ Metrics/ModuleLength:
48
+ Exclude:
49
+ - 'test/**/*.rb'
50
+
51
+ Metrics/ParameterLists:
52
+ CountKeywordArgs: false
53
+
54
+ Naming/UncommunicativeMethodParamName:
55
+ AllowedNames:
56
+ - x
57
+ - y
58
+ - i
59
+ - p
60
+ - n
61
+ - r
62
+ - g
63
+ - b
64
+ - to
65
+ - '_'
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in rooted_tree.gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,54 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rooted_tree (0.3.4)
5
+ linked (~> 0.1.1)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ast (2.4.0)
11
+ docile (1.3.1)
12
+ jaro_winkler (1.5.1)
13
+ json (2.1.0)
14
+ linked (0.1.3)
15
+ minitest (5.9.0)
16
+ parallel (1.12.1)
17
+ parser (2.5.1.2)
18
+ ast (~> 2.4.0)
19
+ powerpack (0.1.2)
20
+ rainbow (3.0.0)
21
+ rake (10.5.0)
22
+ redcarpet (3.4.0)
23
+ rubocop (0.58.2)
24
+ jaro_winkler (~> 1.5.1)
25
+ parallel (~> 1.10)
26
+ parser (>= 2.5, != 2.5.1.1)
27
+ powerpack (~> 0.1)
28
+ rainbow (>= 2.2.2, < 4.0)
29
+ ruby-progressbar (~> 1.7)
30
+ unicode-display_width (~> 1.0, >= 1.0.1)
31
+ ruby-progressbar (1.9.0)
32
+ simplecov (0.16.1)
33
+ docile (~> 1.1)
34
+ json (>= 1.8, < 3)
35
+ simplecov-html (~> 0.10.0)
36
+ simplecov-html (0.10.2)
37
+ unicode-display_width (1.4.0)
38
+ yard (0.9.15)
39
+
40
+ PLATFORMS
41
+ ruby
42
+
43
+ DEPENDENCIES
44
+ bundler (~> 1.16)
45
+ minitest (~> 5.0)
46
+ rake (~> 10.0)
47
+ redcarpet (~> 3.4)
48
+ rooted_tree!
49
+ rubocop (~> 0.52)
50
+ simplecov (~> 0.16)
51
+ yard (~> 0.9)
52
+
53
+ BUNDLED WITH
54
+ 1.16.3
data/README.md CHANGED
@@ -1,13 +1,11 @@
1
- # RootedTree
1
+ # 🌳 RootedTree
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/rooted_tree.png)](http://badge.fury.io/rb/rooted_tree)
4
4
  [![Build Status](https://travis-ci.org/seblindberg/ruby-rooted_tree.svg?branch=master)](https://travis-ci.org/seblindberg/ruby-rooted_tree)
5
- [![Coverage Status](https://coveralls.io/repos/github/seblindberg/ruby-rooted_tree/badge.svg?branch=master)](https://coveralls.io/github/seblindberg/ruby-rooted_tree?branch=master)
6
5
  [![Inline docs](http://inch-ci.org/github/seblindberg/ruby-rooted_tree.svg?branch=master)](http://inch-ci.org/github/seblindberg/ruby-rooted_tree)
6
+ [![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](http://www.rubydoc.info/gems/rooted_tree/)
7
7
 
8
- A tree is a connected graph with no cycles. There, that is plenty of explanation. Please refer to https://en.wikipedia.org/wiki/Tree_structure for a more in depth description, but if you need one this library probably is not for you.
9
-
10
- This gem technically implements a _rooted, ordered tree_, but that name is a mouthful. It is ment to be used as a building block when working with any tree shaped data. For a brief recap of the terminology please see below.
8
+ This gem implements a _rooted, ordered tree_, but that name is a bit of a mouthful. It is ment to be used as a building block when working with any tree shaped data. For a brief recap of the terminology please see below. Please refer to https://en.wikipedia.org/wiki/Tree_structure for a more in depth description.
11
9
 
12
10
  A A is the root.
13
11
  ┌────┼──┐ B, C and D are all children of A.
data/Rakefile CHANGED
@@ -1,10 +1,24 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
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 << "test"
6
- t.libs << "lib"
9
+ t.libs << 'test'
10
+ t.libs << 'lib'
7
11
  t.test_files = FileList['test/**/*_test.rb']
8
12
  end
9
13
 
10
- task :default => :test
14
+ RuboCop::RakeTask.new(:rubocop)
15
+
16
+ YARD::Rake::YardocTask.new(:yard) do |t|
17
+ t.stats_options = %w[--list-undoc]
18
+ t.files = ['lib/**/*.rb', '-', 'CHANGELOG.md']
19
+ end
20
+
21
+ desc 'Generate Ruby documentation'
22
+ task doc: %w[yard]
23
+
24
+ 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 "bundler/setup"
4
- require "rooted_tree"
4
+ require 'bundler/setup'
5
+ require 'rooted_tree'
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 "rooted_tree"
10
11
  # require "pry"
11
12
  # Pry.start
12
13
 
13
- require "irb"
14
+ require 'irb'
14
15
  IRB.start
@@ -1,20 +1,18 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'rooted_tree'
4
5
 
5
- # FileSystemItem
6
- #
7
6
  # Maps the entries in the file system to `Node` objects via .map_to_path. The
8
7
  # Node#inspect method is then exploited in #display to show the resulting tree
9
8
  # structure. The name of each entry in the filesystem is stored in the value
10
9
  # field of the Node.
11
-
12
10
  class FileSystemItem < RootedTree::Node
13
11
  def display
14
- inspect { |item| item.value }
12
+ inspect(&:value)
15
13
  end
16
-
17
- def self.map_to_path path = '.', root: new(path)
14
+
15
+ def self.map_to_path(path = '.', root: new(path))
18
16
  # Iterate over all of the files in the directory
19
17
  Dir[path + '/*'].each do |entry|
20
18
  # Create a new FileSystemItem for the entry
@@ -24,7 +22,7 @@ class FileSystemItem < RootedTree::Node
24
22
  # entry, if it is a directory
25
23
  map_to_path entry, root: item unless File.file? entry
26
24
  end
27
-
25
+
28
26
  root
29
27
  end
30
28
  end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RootedTree
4
+ # This module contains methods for mutating a tree node.
5
+ module Mutable
6
+ # Insert a child between this node and the one after it.
7
+ #
8
+ # @raise [StructureException] if this node has no parent.
9
+ #
10
+ # @param value [Object] the value of the new sibling.
11
+ # @return [self]
12
+ def append_sibling(value = nil)
13
+ raise StructureException, 'Root node can not have siblings' if root?
14
+
15
+ append value
16
+ self
17
+ end
18
+
19
+ # Insert a child between this node and the one before it.
20
+ #
21
+ # @raise [StructureException] if this node has no parent.
22
+ #
23
+ # @param value [Object] the value of the new sibling.
24
+ # @return [self]
25
+ def prepend_sibling(value = nil)
26
+ raise StructureException, 'Root node can not have siblings' if root?
27
+
28
+ prepend value
29
+ self
30
+ end
31
+
32
+ # Insert a child after the last one.
33
+ #
34
+ # @param value [Object] the value of the new sibling.
35
+ # @return [self]
36
+ def append_child(value = nil)
37
+ push value
38
+ end
39
+
40
+ # @see #append_child.
41
+ alias << append_child
42
+
43
+ # Insert a child before the first one.
44
+ #
45
+ # @param value [Object] the value of the new sibling.
46
+ # @return [self]
47
+ def prepend_child(value = nil)
48
+ unshift value
49
+ end
50
+
51
+ # Extracts the node and its subtree from the larger structure.
52
+ #
53
+ # @return [self] the node will now be root.
54
+ def extract
55
+ return self if root?
56
+
57
+ method(:delete).super_method.call
58
+ self
59
+ end
60
+
61
+ # Removes the node from the tree.
62
+ #
63
+ # @return [Array<Node>] an array of the children to the deleted node, now
64
+ # made roots.
65
+ def delete
66
+ extract.children.to_a.each(&:extract)
67
+ end
68
+ end
69
+ end
@@ -1,335 +1,111 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Node
4
- #
5
- # Nodes are mutable by default, since creating anyting but simple leafs would
6
- # otherwise be imposible. Calling #freeze on a node makes the entire subtree
7
- # immutable. This is used by the Tree class which only operates on frozen node
8
- # structures.
9
- #
10
- # The following is an example of a rooted tree with maximum depth 2.
11
- #
12
- # r - r, a, b, c, and d are internal vertices
13
- # +--+---+ - vertices e, f, g, h, i, and j are leaves
14
- # a b c - vertices g, h, and i are siblings
15
- # +++ | +-+-+ - node a is an ancestor of j
16
- # d e f g h i - j is a descendant of a
17
- # |
18
- # j
19
- #
20
- # The terminology is mostly referenced from
21
- # http://www.cs.columbia.edu/~cs4203/files/GT-Lec4.pdf.
22
-
23
3
  module RootedTree
24
- class Node
25
- include Enumerable
26
-
27
- attr_accessor :first_child, :last_child, :degree, :value
28
- attr_writer :next, :prev, :parent
29
-
30
- protected :next=, :prev=, :parent=, :first_child=, :last_child=, :degree=
31
-
32
- alias arity degree
4
+ # rubocop:disable Metrics/ClassLength
5
+
6
+ # Nodes are mutable by default, since creating anyting but simple leafs would
7
+ # otherwise be imposible. Calling #freeze on a node makes the entire subtree
8
+ # immutable. This is used by the Tree class which only operates on frozen node
9
+ # structures.
10
+ #
11
+ # The following is an example of a rooted tree with maximum depth 2.
12
+ #
13
+ # r - r, a, b, c, and d are internal vertices
14
+ # +--+---+ - vertices e, f, g, h, i, and j are leaves
15
+ # a b c - vertices g, h, and i are siblings
16
+ # +++ | +-+-+ - node a is an ancestor of j
17
+ # d e f g h i - j is a descendant of a
18
+ # |
19
+ # j
20
+ #
21
+ # The terminology is mostly referenced from
22
+ # http://www.cs.columbia.edu/~cs4203/files/GT-Lec4.pdf.
23
+ class Node < Linked::Item
24
+ extend Forwardable
25
+ include Linked::List
26
+ include Mutable
33
27
 
34
28
  # Creates a new node with the given object as its value, unless a Node is
35
29
  # passed, in which case it will be returned.
36
30
  #
37
- # value - the object to be used as value for a new Node, or a Node object.
38
- #
39
- # Returns a Node object.
40
-
31
+ # @param value [Object] the object to be used as value for a new Node, or a
32
+ # Node object.
33
+ # @return [Node] a Node object.
41
34
  def self.[](value = nil)
42
35
  return value if value.is_a? self
43
36
  new value
44
37
  end
45
-
46
- # Create a new, unconnected Node object with an optional value. The value is
47
- # owned by the Node instance and will be duped along with it.
48
- #
49
- # value - arbitrary object that is owned by the Node instance.
50
-
51
- def initialize(value = nil)
52
- @parent = nil
53
- @next = nil
54
- @prev = nil
55
- @first_child = nil
56
- @last_child = nil
57
- @degree = 0
58
- @value = value
59
- end
60
-
61
- # When copying a node the child nodes are copied as well, along with the
62
- # value.
63
-
64
- def initialize_dup(source)
65
- # Dup each child and link them to the new parent
66
- duped_children = source.children.map do |child|
67
- child.dup.tap { |n| n.parent = self }
68
- end
69
-
70
- # Connect each child to its adjecent siblings
71
- duped_children.each_cons(2) { |a, b| a.next, b.prev = b, a }
72
-
73
- @parent = nil
74
- @first_child = duped_children.first
75
- @last_child = duped_children.last
76
- @value = begin
77
- source.value.dup
78
- rescue TypeError
79
- source.value
80
- end
81
-
82
- super
83
- end
84
-
85
- # Freezes the value as well as each of the children, outside of the normal
86
- # behaviour.
87
-
88
- def freeze
89
- @value.freeze
90
- children.each(&:freeze)
91
- super
92
- end
93
38
 
94
- # Returns true if this node is a leaf. A leaf is a node with no children.
39
+ # @return [Integer] the number of children of the node.
40
+ alias degree count
95
41
 
96
- def leaf?
97
- @degree == 0
98
- end
42
+ # @return [Integer] see #degree.
43
+ alias arity degree
99
44
 
100
- # Returns true if the node is internal, which is equivalent to it having
101
- # children.
45
+ # @return [Node] the first child.
46
+ alias first_child first
102
47
 
103
- def internal?
104
- !leaf?
105
- end
48
+ # @return [Node] the last child.
49
+ alias last_child last
106
50
 
107
- # Returns true if the node has no parent.
51
+ # @return [true] if this node has no children.
52
+ # @return [false] otherwise.
53
+ def_delegator :degree, :zero?, :leaf?
108
54
 
109
- def root?
110
- @parent.nil?
111
- end
55
+ # @return [true] if the node has children.
56
+ # @return [false] otherwise.
57
+ def_delegator :degree, :positive?, :internal?
112
58
 
113
- # Returns the root of the tree structure that the node is part of.
59
+ # @return [true] if the node has no parent.
60
+ # @return [false] otherwise.
61
+ def_delegator :list, :nil?, :root?
114
62
 
63
+ # @return [Node] the root of the tree structure that the node is part of.
115
64
  def root
116
65
  return self if root?
117
-
118
- node = self
119
- loop { node = node.parent }
120
- node
121
- end
122
-
123
- # Returns true if this node is the first of its siblings.
124
-
125
- def first?
126
- @prev.nil?
127
- end
128
-
129
- # Returns true if this node is the last of its siblings.
130
-
131
- def last?
132
- @next.nil?
66
+ loop.reduce(self) { |node,| node.parent }
133
67
  end
134
68
 
135
- # Returns the depth of the node within the tree
136
-
137
- def depth
138
- ancestors.count
139
- end
69
+ # @return [Integer] the depth of the node within the tree.
70
+ def_delegator :ancestors, :count, :depth
140
71
 
141
72
  alias level depth
142
73
 
143
- # Returns the maximum node depth under this node.
144
-
74
+ # @return [Integer] the maximum node depth under this node.
145
75
  def max_depth(offset = depth)
146
76
  return offset if leaf?
147
77
 
148
78
  children.map { |c| c.max_depth offset + 1 }.max
149
79
  end
150
80
 
151
- # Returns the highest child count of the nodes in the subtree.
152
-
81
+ # @return [Integer] the highest child count of the nodes in the subtree.
153
82
  def max_degree
154
83
  children.map(&:degree).push(degree).max
155
84
  end
156
85
 
157
86
  # Calculate the size in vertecies of the subtree.
158
87
  #
159
- # Returns the number of nodes under this node, including self.
160
-
88
+ # @return [Integer] the number of nodes under this node, including self.
161
89
  def size
162
90
  children.reduce(1) { |a, e| a + e.size }
163
91
  end
164
92
 
165
- # Access the next sibling. Raises a StopIteration if this node is the last
166
- # one.
167
- #
168
- # Returns the next sibling node.
169
-
170
- def next
171
- raise StopIteration if last?
172
- @next
173
- end
174
-
175
- # Dangerous accessor of the next sibling. Unlike the regular #next this
176
- # method will return nil if this node is the last one.
177
- #
178
- # Returns the next sibling node or nil if this node is last.
179
-
180
- def next!
181
- @next
182
- end
183
-
184
- # Access the previous sibling. Raises a StopIteration if this node is the
185
- # first one.
186
- #
187
- # Returns the previous sibling node.
188
-
189
- def prev
190
- raise StopIteration if first?
191
- @prev
192
- end
193
-
194
- alias previous prev
195
-
196
- # Dangerous accessor of the previous sibling. Unlike the regular #prev this
197
- # method will return nil if this node is the first one.
198
-
199
- def prev!
200
- @prev
201
- end
202
-
203
- alias previous! prev!
204
-
205
93
  # Access the parent node. Raises a StopIteration if this node is the
206
94
  # root.
207
95
  #
208
- # Returns the parent node.
209
-
96
+ # @raise [StopIteration] if this node is the root.
97
+ #
98
+ # @return [Node] the parent node.
210
99
  def parent
211
100
  raise StopIteration if root?
212
- @parent
213
- end
214
-
215
- # Insert a child between this node and the one after it.
216
- #
217
- # Returns self.
218
-
219
- def append_sibling(value = nil)
220
- raise StructureException, 'Root node can not have siblings' if root?
221
-
222
- node = self.class[value]
223
- node.next = @next
224
- node.prev = self
225
- node.parent = @parent
226
- @parent.degree += 1
227
-
228
- if @next
229
- @next.prev = node
230
- else
231
- @parent.last_child = node
232
- end
233
- @next = node
234
- end
235
-
236
- # Insert a child between this node and the one before it.
237
- #
238
- # Returns self.
239
-
240
- def prepend_sibling(value = nil)
241
- raise StructureException, 'Root node can not have siblings' if root?
242
-
243
- node = self.class[value]
244
- node.next = self
245
- node.prev = @prev
246
- node.parent = @parent
247
- @parent.degree += 1
248
-
249
- if @prev
250
- @prev.next = node
251
- else
252
- @parent.first_child = node
253
- end
254
- @prev = node
255
- end
256
-
257
- private def add_child_to_leaf(value)
258
- node = self.class[value]
259
- @first_child = @last_child = node
260
- node.next = node.prev = nil
261
- @degree = 1
262
- node.parent = self
263
- end
264
-
265
- # Insert a child after the last one.
266
- #
267
- # Returns self.
268
-
269
- def append_child(value = nil)
270
- if leaf?
271
- add_child_to_leaf value
272
- else
273
- @last_child.append_sibling value
274
- end
275
- self
276
- end
277
-
278
- alias << append_child
279
-
280
- # Insert a child before the first one.
281
- #
282
- # Returns self.
283
-
284
- def prepend_child(value = nil)
285
- if leaf?
286
- add_child_to_leaf value
287
- else
288
- @first_child.prepend_sibling value
289
- end
290
- end
291
-
292
- # Extracts the node and its subtree from the larger structure.
293
- #
294
- # Returns self, now made root.
295
-
296
- def extract
297
- return self if root?
298
-
299
- if last?
300
- parent.last_child = @prev
301
- else
302
- @next.prev = @prev
303
- end
304
-
305
- if first?
306
- parent.first_child = @next
307
- else
308
- @prev.next = @next
309
- end
310
-
311
- @parent.degree -= 1
312
- @prev = @next = @parent = nil
313
- self
314
- end
315
-
316
- # Removes the node from the tree.
317
- #
318
- # Returns an array of the children to the deleted node, now made roots.
319
-
320
- def delete
321
- extract.children.to_a.each do |child|
322
- child.parent = nil
323
- child.next = child.prev = nil
324
- end
101
+ list
325
102
  end
326
103
 
327
104
  # Iterates over the nodes above this in the tree hierarchy and yields them
328
105
  # to a block. If no block is given an enumerator is returned.
329
106
  #
330
- # Returns an enumerator that will iterate over the parents of this node
331
- # until the root is reached.
332
-
107
+ # @yield [Node] each parent node.
108
+ # @return [Enumerator] if no block is given.
333
109
  def ancestors
334
110
  return to_enum(__callee__) unless block_given?
335
111
  node = self
@@ -346,59 +122,41 @@ module RootedTree
346
122
  # Note that the block will catch any StopIteration that is raised and
347
123
  # terminate early, returning the value of the exception.
348
124
  #
349
- # rtl - reverses the iteration order if true.
350
-
351
- def children(rtl: false)
352
- return to_enum(__callee__, rtl: rtl) unless block_given?
353
- return if leaf?
354
-
355
- child, advance = if rtl
356
- [@last_child, :prev]
357
- else
358
- [@first_child, :next]
359
- end
360
-
361
- loop do
362
- yield child
363
- child = child.send advance
364
- end
125
+ # @param rtl [true, false] reverses the iteration order if true.
126
+ # @return see #each_item
127
+ def children(rtl: false, &block)
128
+ rtl ? reverse_each_item(&block) : each_item(&block)
365
129
  end
366
130
 
367
131
  # Accessor method for any of the n children under this node. If called
368
132
  # without an argument and the node has anything but exactly one child an
369
133
  # exception will be raised.
370
134
  #
371
- # n - the n:th child to be returned. If n is negative the indexing will be
372
- # reversed and the children counted from the last to the first.
373
- #
374
- # Returns the child at the n:th index.
375
-
376
- def child(n = nil)
377
- if n.nil?
378
- if @degree != 1
379
- raise ArgumentError, 'No argument given for node with degree != 1'
380
- end
381
- return @first_child
135
+ # @param index [Integer] the n:th child to be returned. If the index is
136
+ # negative the indexing will be reversed and the children counted from the
137
+ # last to the first.
138
+ # @return [Node] the child at the n:th index.
139
+ def child(index = nil)
140
+ unless index
141
+ raise ArgumentError, 'No index for node with degree != 1' if degree != 1
142
+ return first
382
143
  end
383
144
 
384
- rtl = if n < 0
385
- n = -1 - n
386
- true
387
- else
388
- false
389
- end
145
+ rtl, index = wrap_index index
390
146
 
391
- raise RangeError, 'Child index out of range' if n >= @degree
147
+ raise RangeError, 'Child index out of range' if index >= degree
392
148
 
393
149
  children(rtl: rtl).each do |c|
394
- break c if n == 0
395
- n -= 1
150
+ break c if index.zero?
151
+ index -= 1
396
152
  end
397
153
  end
398
154
 
399
- # Yields first to self and then to each child. If a block is not given an
400
- # enumerator is returned.
155
+ alias [] child
401
156
 
157
+ # @yield [Node] first self is yielded and then the children who in turn
158
+ # yield their children.
159
+ # @return [Enumerator] if no block is given.
402
160
  def each(&block)
403
161
  return to_enum(__callee__) unless block_given?
404
162
  yield self
@@ -409,9 +167,7 @@ module RootedTree
409
167
  # node is placed at index zero of its own array, followed by an array of its
410
168
  # children. Leaf nodes are not wraped in arrays but inserted directly.
411
169
  #
412
- # flatten - flattens the array if true.
413
- #
414
- # Example
170
+ # == Example
415
171
  #
416
172
  # r
417
173
  # / \
@@ -419,29 +175,28 @@ module RootedTree
419
175
  # |
420
176
  # c
421
177
  #
422
- # Returns a nested array of nodes.
423
-
424
- def to_a flatten: false
425
- return super() if flatten
178
+ # @param flatten [true, false] the array is flattened if true.
179
+ # @return [Array<Node, Array>] a nested array of nodes.
180
+ def to_a(flatten: false)
181
+ return each.to_a if flatten
426
182
  return self if leaf?
427
183
  [self, children.map(&:to_a)]
428
184
  end
429
185
 
430
186
  # Iterates over each of the leafs.
431
187
  #
432
- # rtl - if true the iteration order is switched to right to left.
433
-
188
+ # @param rtl [true, false] if true the iteration order is switched to right
189
+ # to left.
434
190
  def leafs(rtl: false, &block)
435
191
  return to_enum(__callee__, rtl: rtl) unless block_given?
436
192
  return yield self if leaf?
437
193
  children(rtl: rtl) { |v| v.leafs(rtl: rtl, &block) }
438
194
  end
439
195
 
440
- # Iterates over each of the edges and yields the parent and the child. If no
441
- # block is given an enumerator is returned.
196
+ # Iterates over each edge in the tree.
442
197
  #
443
- # block - an optional block that will be yielded to, if given.
444
-
198
+ # @yield [Array<Node>] each connected node pair.
199
+ # @return [Enumerator] if no block is given.
445
200
  def edges(&block)
446
201
  return to_enum(__callee__) unless block_given?
447
202
 
@@ -455,10 +210,8 @@ module RootedTree
455
210
  # created and returned. Note that if the any of the root nodes are not
456
211
  # frozen they will be modified, and as a result seize to be roots.
457
212
  #
458
- # other - a Node-like object that responds true to #root?
459
- #
460
- # Returns a new root with the two nodes as children.
461
-
213
+ # @param other [Node] a Node-like object that responds true to #root?
214
+ # @return [Node] a new root with the two nodes as children.
462
215
  def +(other)
463
216
  unless root? && other.root?
464
217
  raise StructureException, 'Only roots can be added'
@@ -473,8 +226,9 @@ module RootedTree
473
226
 
474
227
  # Compare one node (sub)structure with another.
475
228
  #
476
- # Returns true if the two vertecies form identical subtrees
477
-
229
+ # @param other [Object]
230
+ # @return [true] if the two vertecies form identical subtrees.
231
+ # @reutrn [false] otherwise.
478
232
  def ==(other)
479
233
  return false unless other.is_a? self.class
480
234
  return false unless degree == other.degree
@@ -483,36 +237,19 @@ module RootedTree
483
237
  children.to_a == other.children.to_a
484
238
  end
485
239
 
486
- # Visalizes the tree structure in a style very similar to the cli tool tree.
487
- # An example of the output can be seen below. Note that the output string
488
- # contains unicode characters.
489
- #
490
- # Node:0x3ffd64c22abc
491
- # |--Node:0x3ffd64c1fd30
492
- # | |--Node:0x3ffd64c1f86c
493
- # | +--Node:0x3ffd64c1f63c
494
- # +--Node:0x3ffd64c1f40c
495
- #
496
- # By passing `as_array: true` the method will instead return an array
497
- # containing each of the output lines. The method also accepts a block
498
- # which, if given, will be yielded to once for every node, and the output
499
- # will be used as node labels instead of the default identifier.
500
-
501
- def inspect(as_array: false, &block)
502
- unless block_given?
503
- block = proc { |v| format '%s:0x%0x', v.class.name, v.object_id }
504
- end
505
-
506
- res = [block.call(self)]
240
+ private
507
241
 
508
- children do |child|
509
- lines = child.inspect(as_array: true, &block).each
510
- res << ((child.last? ? '└─╴' : '├─╴') + lines.next)
511
- prep = child.last? ? '   ' : '│  '
512
- loop { res << (prep + lines.next) }
242
+ def wrap_index(index)
243
+ if index.negative?
244
+ [true, -1 - index]
245
+ else
246
+ [false, index]
513
247
  end
514
-
515
- as_array ? res : res.join("\n")
516
248
  end
249
+
250
+ protected :first, :last, :each_item
251
+ private :push, :unshift, :list, :count
517
252
  end
253
+
254
+ # rubocop:enable Metrics/ClassLength
518
255
  end
@@ -1,54 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Tree
4
- #
5
- # Include this module in any object that responds to #root with a Node
6
- # structure. The mixin provides some methods for describing the tree as well as
7
- # direct access to some of the iteration methods in Node.
8
-
9
3
  module RootedTree
4
+ # Include this module in any object that responds to #root with a Node
5
+ # structure. The mixin provides some methods for describing the tree as well
6
+ # as direct access to some of the iteration methods in Node.
10
7
  module Tree
11
-
8
+ extend Forwardable
9
+
12
10
  # Freezes the node structure that is part of the tree.
13
-
14
11
  def freeze
15
12
  root.freeze
16
13
  super
17
14
  end
18
-
19
- # Returns the maximum degree (highest number of children) in the tree.
20
15
 
21
- def degree
22
- root.max_degree
23
- end
16
+ # Returns the maximum degree (highest number of children) in the tree.
17
+ def_delegator :root, :max_degree, :degree
24
18
 
25
19
  # Returns the maximum depth of the tree.
26
-
27
- def depth
28
- root.max_depth
29
- end
20
+ def_delegator :root, :max_depth, :depth
30
21
 
31
22
  # Iterates over each node in the tree. When given a block it will be yielded
32
23
  # to once for each node. If no block is given an enumerator is returned.
33
-
34
- def each_node(&block)
35
- root.each(&block)
36
- end
24
+ def_delegator :root, :each, :each_node
37
25
 
38
26
  # Iterates over each leaf in the tree. When given a block it will be yielded
39
27
  # to once for leaf node. If no block is given an enumerator is returned.
40
-
41
- def each_leaf(&block)
42
- root.leafs(&block)
43
- end
28
+ def_delegator :root, :leafs, :each_leaf
44
29
 
45
30
  # Iterates over each edge in the tree. An edge is composed of the parent
46
31
  # node and the child, always in that order. When given a block it will be
47
32
  # yielded to once for each node. If no block is given an enumerator is
48
33
  # returned.
49
-
50
- def each_edge(&block)
51
- root.edges(&block)
52
- end
34
+ def_delegator :root, :edges, :each_edge
53
35
  end
54
36
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RootedTree
4
- VERSION = '0.3.3'
4
+ # @return [String] the semantic version number of the library.
5
+ VERSION = '0.3.4'
5
6
  end
data/lib/rooted_tree.rb CHANGED
@@ -1,9 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'forwardable'
4
+ require 'linked'
5
+
3
6
  require 'rooted_tree/version'
7
+ require 'rooted_tree/mutable'
4
8
  require 'rooted_tree/node'
5
9
  require 'rooted_tree/tree'
6
10
 
11
+ # A basic implementation of a tree data structure.
7
12
  module RootedTree
8
13
  class StructureException < StandardError; end
14
+
15
+ private_constant :Mutable
9
16
  end
data/rooted_tree.gemspec CHANGED
@@ -1,26 +1,38 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'rooted_tree/version'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = "rooted_tree"
8
+ spec.name = 'rooted_tree'
8
9
  spec.version = RootedTree::VERSION
9
- spec.authors = ["Sebastian Lindberg"]
10
- spec.email = ["seb.lindberg@gmail.com"]
10
+ spec.authors = ['Sebastian Lindberg']
11
+ spec.email = ['seb.lindberg@gmail.com']
12
+
13
+ spec.summary = 'A basic implementation of a tree data structure.'
14
+ spec.description = 'This gem implements a rooted, ordered tree, with a ' \
15
+ 'focus on easy iteration over nodes and access to ' \
16
+ 'basic tree properties.'
17
+ spec.homepage = 'https://github.com/seblindberg/ruby-rooted_tree'
18
+ spec.license = 'MIT'
11
19
 
12
- spec.summary = %q{A basic implementation of a tree data structure.}
13
- spec.description = %q{This gem implements a rooted, ordered tree, with a focus on easy iteration over nodes and access to basic tree properties.}
14
- spec.homepage = "https://github.com/seblindberg/ruby-rooted_tree"
15
- spec.license = "MIT"
20
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
21
+ `git ls-files -z`.split("\x0")
22
+ .reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ end
16
24
 
17
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
- spec.bindir = "exe"
25
+ spec.bindir = 'exe'
19
26
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
- spec.require_paths = ["lib"]
27
+ spec.require_paths = ['lib']
28
+
29
+ spec.add_dependency 'linked', '~> 0.1.1'
21
30
 
22
- spec.add_development_dependency "bundler", "~> 1.12"
23
- spec.add_development_dependency "rake", "~> 10.0"
24
- spec.add_development_dependency "minitest", "~> 5.0"
25
- spec.add_development_dependency "coveralls", "~> 0.8"
31
+ spec.add_development_dependency 'bundler', '~> 1.16'
32
+ spec.add_development_dependency 'minitest', '~> 5.0'
33
+ spec.add_development_dependency 'rake', '~> 10.0'
34
+ spec.add_development_dependency 'redcarpet', '~> 3.4'
35
+ spec.add_development_dependency 'rubocop', '~> 0.52'
36
+ spec.add_development_dependency 'simplecov', '~> 0.16'
37
+ spec.add_development_dependency 'yard', '~> 0.9'
26
38
  end
metadata CHANGED
@@ -1,29 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rooted_tree
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.3.4
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-03 00:00:00.000000000 Z
11
+ date: 2018-07-24 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: linked
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.1.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.1.1
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
16
30
  requirements:
17
31
  - - "~>"
18
32
  - !ruby/object:Gem::Version
19
- version: '1.12'
33
+ version: '1.16'
20
34
  type: :development
21
35
  prerelease: false
22
36
  version_requirements: !ruby/object:Gem::Requirement
23
37
  requirements:
24
38
  - - "~>"
25
39
  - !ruby/object:Gem::Version
26
- version: '1.12'
40
+ version: '1.16'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
27
55
  - !ruby/object:Gem::Dependency
28
56
  name: rake
29
57
  requirement: !ruby/object:Gem::Requirement
@@ -39,33 +67,61 @@ dependencies:
39
67
  - !ruby/object:Gem::Version
40
68
  version: '10.0'
41
69
  - !ruby/object:Gem::Dependency
42
- name: minitest
70
+ name: redcarpet
43
71
  requirement: !ruby/object:Gem::Requirement
44
72
  requirements:
45
73
  - - "~>"
46
74
  - !ruby/object:Gem::Version
47
- version: '5.0'
75
+ version: '3.4'
48
76
  type: :development
49
77
  prerelease: false
50
78
  version_requirements: !ruby/object:Gem::Requirement
51
79
  requirements:
52
80
  - - "~>"
53
81
  - !ruby/object:Gem::Version
54
- version: '5.0'
82
+ version: '3.4'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.52'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.52'
97
+ - !ruby/object:Gem::Dependency
98
+ name: simplecov
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.16'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.16'
55
111
  - !ruby/object:Gem::Dependency
56
- name: coveralls
112
+ name: yard
57
113
  requirement: !ruby/object:Gem::Requirement
58
114
  requirements:
59
115
  - - "~>"
60
116
  - !ruby/object:Gem::Version
61
- version: '0.8'
117
+ version: '0.9'
62
118
  type: :development
63
119
  prerelease: false
64
120
  version_requirements: !ruby/object:Gem::Requirement
65
121
  requirements:
66
122
  - - "~>"
67
123
  - !ruby/object:Gem::Version
68
- version: '0.8'
124
+ version: '0.9'
69
125
  description: This gem implements a rooted, ordered tree, with a focus on easy iteration
70
126
  over nodes and access to basic tree properties.
71
127
  email:
@@ -75,8 +131,10 @@ extensions: []
75
131
  extra_rdoc_files: []
76
132
  files:
77
133
  - ".gitignore"
134
+ - ".rubocop.yml"
78
135
  - ".travis.yml"
79
136
  - Gemfile
137
+ - Gemfile.lock
80
138
  - LICENSE.txt
81
139
  - README.md
82
140
  - Rakefile
@@ -84,6 +142,7 @@ files:
84
142
  - bin/setup
85
143
  - examples/filesystem_tree.rb
86
144
  - lib/rooted_tree.rb
145
+ - lib/rooted_tree/mutable.rb
87
146
  - lib/rooted_tree/node.rb
88
147
  - lib/rooted_tree/tree.rb
89
148
  - lib/rooted_tree/version.rb
@@ -108,7 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
108
167
  version: '0'
109
168
  requirements: []
110
169
  rubyforge_project:
111
- rubygems_version: 2.5.1
170
+ rubygems_version: 2.6.11
112
171
  signing_key:
113
172
  specification_version: 4
114
173
  summary: A basic implementation of a tree data structure.