rooted_tree 0.3.3 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
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.