Class: Sycamore::Tree
- Inherits:
-
Object
- Object
- Sycamore::Tree
- Includes:
- Enumerable
- Defined in:
- lib/sycamore/tree.rb
Overview
Direct Known Subclasses
CQS reflection collapse
- ADDITIVE_COMMAND_METHODS =
the names of all command methods, which add elements to a Tree
%i[add << replace add_node_with_empty_child clear_child_of_node] << :[]=
- DESTRUCTIVE_COMMAND_METHODS =
the names of all command methods, which delete elements from a Tree
%i[delete >> clear compact replace clear_child_of_node] << :[]=
- PURE_ADDITIVE_COMMAND_METHODS =
the names of all additive command methods, which only add elements from a Tree
ADDITIVE_COMMAND_METHODS - DESTRUCTIVE_COMMAND_METHODS
- PURE_DESTRUCTIVE_COMMAND_METHODS =
the names of all destructive command methods, which only delete elements from a Tree
DESTRUCTIVE_COMMAND_METHODS - ADDITIVE_COMMAND_METHODS
- COMMAND_METHODS =
the names of all methods, which change the state of a Tree
ADDITIVE_COMMAND_METHODS + DESTRUCTIVE_COMMAND_METHODS + %i[freeze]
- PREDICATE_METHODS =
the names of all query methods, which return a boolean
%i[nothing? absent? existent? present? blank? empty? include? include_node? member? key? has_key? include_path? path? >= > < <= leaf? leaves? internal? external? flat? nested? sleaf? sleaves? strict_leaf? strict_leaves? eql? matches? === ==]
- QUERY_METHODS =
the names of all methods, which side-effect-freeze return only a value
PREDICATE_METHODS + %i[new_child dup hash to_native_object to_h to_s inspect node node! nodes keys child_of child_at dig fetch fetch_path search size total_size tsize height each each_path paths each_node each_key each_pair] << :[]
Construction collapse
-
#data ⇒ Object
readonly
protected
the internal hash representation of this tree.
Construction collapse
-
.with(*args) ⇒ Tree
(also: from, [])
Creates a new Tree and initializes it with the given data.
- #clear_data ⇒ Object protected
-
#initialize ⇒ Tree
constructor
Creates a new empty Tree.
-
#new_child(parent_node, *args) ⇒ Tree
private
Creates a new tree meant to be used as a child.
Absence and Nothing predicates collapse
- #absent? ⇒ Boolean
- #existent? ⇒ Boolean
-
#nothing? ⇒ Boolean
Checks if this is the Nothing tree.
-
#present? ⇒ Boolean
Checks if this is not blank, i.e.
Element access collapse
-
#[]=(*args) ⇒ Object
Replaces the contents of a child tree.
- #_search(query, current_path: Path::ROOT, results: []) ⇒ Object protected
-
#add(nodes_or_tree) ⇒ Object
(also: #<<)
Adds nodes or a tree structure to this tree.
- #add_child(node, children) ⇒ Object private
- #add_node(node) ⇒ Object protected
- #add_node_with_empty_child(node) ⇒ Object private
- #add_path(path) ⇒ Object private
- #add_tree(tree) ⇒ Object private
-
#child_at(*path) ⇒ Tree, Absence
(also: #[], #dig)
The child tree of a node at a path.
-
#child_of(node) ⇒ Tree, Absence
The child tree of a node.
-
#clear ⇒ Object
Deletes all nodes and their children.
- #clear_child_of_node(node) ⇒ Object private
-
#compact ⇒ Object
Deletes all empty child trees recursively.
-
#delete(nodes_or_tree) ⇒ Object
(also: #>>)
Remove nodes or a tree structure from this tree.
- #delete_node(node) ⇒ Object protected
- #delete_path(path) ⇒ Object protected
- #delete_tree(tree) ⇒ Object protected
-
#each_node(&block) ⇒ Object
(also: #each_key)
Iterates over all nodes of this tree.
-
#each_pair(&block) ⇒ Object
(also: #each)
Iterates over all nodes and their child trees.
-
#each_path(with_ancestor: Path::ROOT, &block) ⇒ Object
(also: #paths)
Iterates over the paths to all leaves of this tree.
-
#empty? ⇒ Boolean
(also: #blank?)
Checks if this tree is empty.
-
#external?(*nodes) ⇒ Object
(also: #leaves?, #flat?)
Checks if all given nodes or that of the tree have no children.
-
#fetch(node, *default, &block) ⇒ Tree, default
The child tree of a node.
-
#fetch_path(path, *default, &block) ⇒ Tree, default
The child tree of a node at a path.
-
#height ⇒ Fixnum
The length of the longest path of this tree.
-
#include?(*elements) ⇒ Boolean
Checks if some nodes or a full tree-like structure is included in this tree.
-
#include_node?(node) ⇒ Boolean
(also: #member?, #has_key?, #key?)
Checks if a node exists in the nodes set of this tree.
-
#include_path?(*args) ⇒ Boolean
(also: #path?)
Checks if a path of nodes exists in this tree.
-
#internal?(*nodes) ⇒ Object
(also: #nested?)
Checks if all given nodes or that of the tree have children.
-
#leaf?(node) ⇒ Boolean
Checks if the given node has no children.
-
#node ⇒ Object?
The only node of this tree or an exception, if more nodes present.
-
#node! ⇒ Object
The only node of this tree or an exception, if none or more nodes present.
-
#nodes ⇒ Array<Object>
(also: #keys)
The nodes of this tree (without their children).
-
#replace(nodes_or_tree) ⇒ Object
Replaces the contents of this tree.
-
#search(nodes_or_tree) ⇒ Array<Path>
Searches the tree for one or multiple nodes or a complete tree.
-
#size ⇒ Fixnum
The number of nodes in this tree.
-
#strict_leaf?(node) ⇒ Boolean
(also: #sleaf?)
Checks if the given node has no children, even not an empty child tree.
-
#strict_leaves?(*nodes) ⇒ Object
(also: #sleaves?)
Checks if all given nodes or that of the tree have no children, even not an empty child tree.
-
#total_size ⇒ Fixnum
(also: #tsize)
The number of nodes in this tree and all of their children.
Comparison collapse
-
#<(other) ⇒ Boolean
Checks if this tree is a subtree of another tree.
-
#<=(other) ⇒ Boolean
Checks if this tree is a subtree or equal to another tree.
-
#==(other) ⇒ Boolean
Checks if this tree has the same content as another tree, but ignores empty child trees.
-
#>(other) ⇒ Boolean
Checks if another tree is a subtree or equal to this tree.
-
#>=(other) ⇒ Boolean
Checks if another tree is a subtree or equal to this tree.
-
#eql?(other) ⇒ Boolean
Checks if this tree has the same content as another tree.
-
#hash ⇒ Fixnum
A hash code of this tree.
-
#matches?(other) ⇒ Boolean
(also: #===)
Checks if another object matches this tree structurally and by content.
- #matches_atom?(other) ⇒ Boolean private
- #matches_enumerable?(other) ⇒ Boolean private
- #matches_tree?(other) ⇒ Boolean private
Conversion collapse
-
.tree_like?(object) ⇒ Boolean
(also: like?)
private
Checks if the given object can be converted into a Tree.
-
#inspect ⇒ String
A developer-friendly string representation of this tree in the usual Ruby Object#inspect style.
-
#to_h(*args) ⇒ Hash
A hash representation of this tree.
-
#to_native_object(sleaf_child_as: nil, special_nil: false) ⇒ Object
private
A native Ruby object representing the content of the tree.
-
#to_s ⇒ String
A string representation of this tree.
- #valid_node!(node) ⇒ Object private private
- #valid_node_element!(node) ⇒ Object private private
- #valid_tree!(tree) ⇒ Object private private
Other standard Ruby methods collapse
-
#dup ⇒ Tree
Duplicates the whole tree.
-
#freeze ⇒ Object
Deep freezes the whole tree.
-
#initialize_clone(other) ⇒ Object
Clones the whole tree.
Constructor Details
#initialize ⇒ Tree
Creates a new empty Tree.
67 68 |
# File 'lib/sycamore/tree.rb', line 67 def initialize end |
Instance Attribute Details
#data ⇒ Object (readonly, protected)
the internal hash representation of this tree
13 14 15 |
# File 'lib/sycamore/tree.rb', line 13 protected def data @data ||= Hash.new end |
Class Method Details
.tree_like?(object) ⇒ Boolean Also known as: like?
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Checks if the given object can be converted into a Tree.
Ideally these would be implemented with Refinements, but since they aren’t available anywhere (I’m looking at you, JRuby), we have to be content with this.
1421 1422 1423 1424 1425 1426 1427 1428 |
# File 'lib/sycamore/tree.rb', line 1421 def self.tree_like?(object) case object when Hash, Tree, Absence # ... ?! true else (object.respond_to? :tree_like? and object.tree_like?) # or ... end end |
.with(*args) ⇒ Tree Also known as: from, []
Creates a new Tree and initializes it with the given data.
90 91 92 93 94 |
# File 'lib/sycamore/tree.rb', line 90 def self.with(*args) tree = new tree.add( args.size == 1 ? args.first : args ) unless args.empty? tree end |
Instance Method Details
#<(other) ⇒ Boolean
Checks if this tree is a subtree of another tree.
1214 1215 1216 |
# File 'lib/sycamore/tree.rb', line 1214 def <(other) (other.is_a?(Tree) or other.is_a?(Absence)) and other.include?(self) and self != other end |
#<=(other) ⇒ Boolean
Checks if this tree is a subtree or equal to another tree.
1231 1232 1233 |
# File 'lib/sycamore/tree.rb', line 1231 def <=(other) (other.is_a?(Tree) or other.is_a?(Absence)) and other.include?(self) end |
#==(other) ⇒ Boolean
Checks if this tree has the same content as another tree, but ignores empty child trees.
1194 1195 1196 1197 1198 1199 |
# File 'lib/sycamore/tree.rb', line 1194 def ==(other) (other.instance_of?(self.class) and size == other.size and all? { |node, child| other.include?(node) and other[node] == child }) or ((other.equal?(Nothing) or other.instance_of?(Absence)) and other == self) end |
#>(other) ⇒ Boolean
Checks if another tree is a subtree or equal to this tree.
1265 1266 1267 |
# File 'lib/sycamore/tree.rb', line 1265 def >(other) (other.is_a?(Tree) or other.is_a?(Absence)) and self.include?(other) and self != other end |
#>=(other) ⇒ Boolean
Checks if another tree is a subtree or equal to this tree.
1248 1249 1250 |
# File 'lib/sycamore/tree.rb', line 1248 def >=(other) (other.is_a?(Tree) or other.is_a?(Absence)) and self.include?(other) end |
#[]=(*path, node) ⇒ Object #[]=(*path, node_collection) ⇒ Object #[]=(*path, tree_structure) ⇒ Object #[]=(*path, another_object) ⇒ Object
Replaces the contents of a child tree.
As this is just a call of #replace on the child tree, you can assign content to not existing child trees. Just like #child_at you can reference a deeper node with a path of nodes.
Note that even if you assign a Sycamore::Tree directly the given tree will not become part of this tree by reference.
An exception is the assignment of nil
or the Nothing tree: it will delete the child tree at the given path entirely. If you really want to overwrite the current child nodes with a single nil
node, you’ll have to assign an array containing only nil
.
tree[:foo] = [nil]
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 |
# File 'lib/sycamore/tree.rb', line 473 def []=(*args) path, nodes_or_tree = args[0..-2], args[-1] raise ArgumentError, 'wrong number of arguments (given 1, expected 2)' if path.empty? if Nothing.like? nodes_or_tree if path.size == 1 clear_child_of_node(path.first) else path, node = path[0..-2], path[-1] child_at(*path).clear_child_of_node(node) end else child_at(*path).replace(nodes_or_tree) end end |
#_search(query, current_path: Path::ROOT, results: []) ⇒ Object (protected)
941 942 943 944 945 946 947 |
# File 'lib/sycamore/tree.rb', line 941 protected def _search(query, current_path: Path::ROOT, results: []) results << current_path if include?(query) each do |node, child| child._search(query, current_path: current_path/node, results: results) end results end |
#absent? ⇒ Boolean
137 138 139 |
# File 'lib/sycamore/tree.rb', line 137 def absent? false end |
#add(node) ⇒ Object #add(node_collection) ⇒ Object #add(tree_structure) ⇒ Object #add(path) ⇒ Object Also known as: <<
Adds nodes or a tree structure to this tree.
210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/sycamore/tree.rb', line 210 def add(nodes_or_tree) case when nodes_or_tree.equal?(Nothing) then # do nothing when nodes_or_tree.is_a?(Tree) then add_tree(nodes_or_tree) when Tree.like?(nodes_or_tree) then add_tree(valid_tree! nodes_or_tree) when nodes_or_tree.is_a?(Path) then add_path(nodes_or_tree) when nodes_or_tree.is_a?(Enumerable) nodes_or_tree.all? { |node| valid_node_element! node } nodes_or_tree.each { |node| add(node) } else add_node(nodes_or_tree) end self end |
#add_child(node, children) ⇒ Object (private)
255 256 257 258 259 260 261 262 |
# File 'lib/sycamore/tree.rb', line 255 private def add_child(node, children) return add_node(node) if Nothing.like?(children) add_node_with_empty_child(node) data[node] << children self end |
#add_node(node) ⇒ Object (protected)
227 228 229 230 231 |
# File 'lib/sycamore/tree.rb', line 227 protected def add_node(node) data[node] ||= Nothing self end |
#add_node_with_empty_child(node) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
245 246 247 248 249 250 251 252 253 |
# File 'lib/sycamore/tree.rb', line 245 def add_node_with_empty_child(node) valid_node! node if data.fetch(node, Nothing).nothing? data[node] = new_child(node) end self end |
#add_path(path) ⇒ Object (private)
270 271 272 273 274 275 276 277 278 279 |
# File 'lib/sycamore/tree.rb', line 270 private def add_path(path) return self if path.root? path.parent.inject(self) { |tree, node| # using a {} block to circumvent this Rubinius issue: https://github.com/rubinius/rubinius-code/issues/7 tree.add_node_with_empty_child(node) tree[node] }.add_node path.node self end |
#add_tree(tree) ⇒ Object (private)
264 265 266 267 268 |
# File 'lib/sycamore/tree.rb', line 264 private def add_tree(tree) tree.each { |node, child| add_child(node, child) } self end |
#child_at(*nodes) ⇒ Tree, Absence #child_at(path) ⇒ Tree, Absence Also known as: [], dig
Should we differentiate the case of a leaf and a not present node? How?
The child tree of a node at a path.
When a child at the given node path is not a present, an Absence object representing the missing tree is returned.
640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 |
# File 'lib/sycamore/tree.rb', line 640 def child_at(*path) first = path.first case path.length when 0 raise ArgumentError, 'wrong number of arguments (given 0, expected 1+)' when 1 if first.is_a? Enumerable child_at(*first) else child_of(*path) end else child_of(first).child_at(*path[1..-1]) end end |
#child_of(node) ⇒ Tree, Absence
Should we differentiate the case of a leaf and a not present node? How?
The child tree of a node.
When a child to the given node is not a present, an Absence object representing the missing tree is returned.
612 613 614 615 616 |
# File 'lib/sycamore/tree.rb', line 612 def child_of(node) valid_node! node Nothing.like?(child = data[node]) ? Absence.at(self, node) : child end |
#clear ⇒ Object
Deletes all nodes and their children.
500 501 502 503 504 |
# File 'lib/sycamore/tree.rb', line 500 def clear data.clear self end |
#clear_child_of_node(node) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
236 237 238 239 240 |
# File 'lib/sycamore/tree.rb', line 236 def clear_child_of_node(node) data[valid_node! node] = Nothing self end |
#clear_data ⇒ Object (protected)
74 75 76 |
# File 'lib/sycamore/tree.rb', line 74 protected def clear_data @data = nil end |
#compact ⇒ Object
Deletes all empty child trees recursively.
518 519 520 521 522 523 524 525 526 527 |
# File 'lib/sycamore/tree.rb', line 518 def compact data.each do |node, child| case when child.nothing? then next when child.empty? then clear_child_of_node(node) else child.compact end end self end |
#delete(node) ⇒ Object #delete(node_collection) ⇒ Object #delete(tree_structure) ⇒ Object #delete(path) ⇒ Object Also known as: >>
Remove nodes or a tree structure from this tree.
If a given node is in the #nodes set, it gets deleted, otherwise it is silently ignored.
321 322 323 324 325 326 327 328 329 330 331 332 333 334 |
# File 'lib/sycamore/tree.rb', line 321 def delete(nodes_or_tree) case when nodes_or_tree.is_a?(Tree) then delete_tree(nodes_or_tree) when Tree.like?(nodes_or_tree) then delete_tree(valid_tree! nodes_or_tree) when nodes_or_tree.is_a?(Path) then delete_path(nodes_or_tree) when nodes_or_tree.is_a?(Enumerable) nodes_or_tree.all? { |node| valid_node_element! node } nodes_or_tree.each { |node| delete node } else delete_node valid_node!(nodes_or_tree) end self end |
#delete_node(node) ⇒ Object (protected)
338 339 340 341 342 |
# File 'lib/sycamore/tree.rb', line 338 protected def delete_node(node) data.delete(node) self end |
#delete_path(path) ⇒ Object (protected)
369 370 371 372 373 374 375 376 377 378 379 380 |
# File 'lib/sycamore/tree.rb', line 369 protected def delete_path(path) case path.length when 0 then return self when 1 then return delete_node(path.node) end parent = fetch_path(path.parent) { return self } parent.delete_node(path.node) delete_path(path.parent) if parent.empty? and not path.parent.root? self end |
#delete_tree(tree) ⇒ Object (protected)
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 |
# File 'lib/sycamore/tree.rb', line 344 protected def delete_tree(tree) tree.each { |node_to_delete, child_to_delete| # using a {} block to circumvent this Rubinius issue: https://github.com/rubinius/rubinius-code/issues/7 next unless include? node_to_delete if Nothing.like?(child_to_delete) or (child_to_delete.respond_to?(:empty?) and child_to_delete.empty?) delete_node node_to_delete else fetch(node_to_delete, Nothing).tap do |child| case when child.empty? then next when Tree.like?(child_to_delete) child.delete_tree(child_to_delete) when child_to_delete.is_a?(Enumerable) child_to_delete.each { |node| child.delete_node node } else child.delete_node child_to_delete end delete_node(node_to_delete) if child.empty? end end } self end |
#dup ⇒ Tree
Duplicates the whole tree.
1443 1444 1445 1446 1447 |
# File 'lib/sycamore/tree.rb', line 1443 def dup duplicate = self.class.new.add(self) duplicate.taint if tainted? duplicate end |
#each_node {|Object| ... } ⇒ Tree #each_node ⇒ Enumerator<Object> Also known as: each_key
Iterates over all nodes of this tree.
Note that does not include the nodes of the child trees.
761 762 763 764 765 766 767 |
# File 'lib/sycamore/tree.rb', line 761 def each_node(&block) return enum_for(__callee__) unless block_given? data.each_key(&block) self end |
#each_pair {|Object, Tree| ... } ⇒ Tree #each_pair ⇒ Enumerator<Array(Object, Tree)> Also known as: each
Iterates over all nodes and their child trees.
790 791 792 793 794 795 796 |
# File 'lib/sycamore/tree.rb', line 790 def each_pair(&block) return enum_for(__callee__) unless block_given? data.each_pair(&block) self end |
#each_path {|path| ... } ⇒ Tree #each_path ⇒ Enumerator<Path> Also known as: paths
Iterates over the paths to all leaves of this tree.
820 821 822 823 824 825 826 827 828 829 830 831 832 |
# File 'lib/sycamore/tree.rb', line 820 def each_path(with_ancestor: Path::ROOT, &block) return enum_for(__callee__) unless block_given? each do |node, child| if child.empty? yield Path[with_ancestor, node] else child.each_path(with_ancestor: with_ancestor.branch(node), &block) end end self end |
#empty? ⇒ Boolean Also known as: blank?
Checks if this tree is empty.
1010 1011 1012 |
# File 'lib/sycamore/tree.rb', line 1010 def empty? data.empty? end |
#eql?(other) ⇒ Boolean
Checks if this tree has the same content as another tree.
1174 1175 1176 1177 |
# File 'lib/sycamore/tree.rb', line 1174 def eql?(other) (other.instance_of?(self.class) and data.eql?(other.data)) or (other.instance_of?(Absence) and other.eql?(self)) end |
#existent? ⇒ Boolean
146 147 148 |
# File 'lib/sycamore/tree.rb', line 146 def existent? not absent? end |
#external? ⇒ Boolean #external?(*nodes) ⇒ Boolean Also known as: leaves?, flat?
Checks if all given nodes or that of the tree have no children.
1102 1103 1104 1105 1106 |
# File 'lib/sycamore/tree.rb', line 1102 def external?(*nodes) nodes = self.nodes if nodes.empty? nodes.all? { |node| leaf?(node) } end |
#fetch(node, *default, &block) ⇒ Tree, default
Should we differentiate the case of a leaf and a not present node? How?
The child tree of a node.
If the node can’t be found or has no child tree, there are several options:
-
With no other arguments, it will raise a
KeyError
exception when the node can’t be found or a ChildError exception (which is a subclass ofKeyError
) when the node has no child tree -
if
default
is given, then that will be returned; -
if the optional code block is specified, then that will be run and its result returned.
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 |
# File 'lib/sycamore/tree.rb', line 690 def fetch(node, *default, &block) return fetch_path(node, *default, &block) if node.is_a? Path valid_node! node child = data.fetch(node, *default, &block) if child.equal? Nothing child = case when block_given? then yield when !default.empty? then default.first else raise ChildError, "node #{node.inspect} has no child tree" end end child end |
#fetch_path(path, *default, &block) ⇒ Tree, default
The child tree of a node at a path.
If the node at the given path can’t be found or has no child tree, it behaves like #fetch.
729 730 731 732 733 734 735 736 737 738 |
# File 'lib/sycamore/tree.rb', line 729 def fetch_path(path, *default, &block) default_case = block_given? || !default.empty? path.inject(self) do |tree, node| if default_case tree.fetch(node) { return block_given? ? yield : default.first } else tree.fetch(node) end end end |
#freeze ⇒ Object
Deep freezes the whole tree.
1463 1464 1465 1466 1467 |
# File 'lib/sycamore/tree.rb', line 1463 def freeze data.freeze each { |_, child| child.freeze } super end |
#hash ⇒ Fixnum
A hash code of this tree.
1155 1156 1157 |
# File 'lib/sycamore/tree.rb', line 1155 def hash data.hash ^ self.class.hash end |
#height ⇒ Fixnum
The length of the longest path of this tree.
996 997 998 999 |
# File 'lib/sycamore/tree.rb', line 996 def height return 0 if empty? paths.map(&:length).max end |
#include?(*elements) ⇒ Boolean
Checks if some nodes or a full tree-like structure is included in this tree.
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 |
# File 'lib/sycamore/tree.rb', line 901 def include?(*elements) raise ArgumentError, 'wrong number of arguments (given 0, expected 1+)' if elements.size == 0 return elements.all? { |element| include? element } if elements.size > 1 elements = elements.first case when Tree.like?(elements) elements.all? do |node, child| include_node?(node) and ( child.nil? or child.equal?(Nothing) or self.child_of(node).include?(child) ) end when elements.is_a?(Path) include_path? elements when elements.is_a?(Enumerable) elements.all? { |element| include_node? element } else include_node? elements end end |
#include_node?(node) ⇒ Boolean Also known as: member?, has_key?, key?
Checks if a node exists in the nodes set of this tree.
877 878 879 880 881 |
# File 'lib/sycamore/tree.rb', line 877 def include_node?(node) return include_path?(node) if node.is_a? Path data.include?(node) end |
#include_path?(*args) ⇒ Boolean Also known as: path?
Checks if a path of nodes exists in this tree.
849 850 851 852 853 854 855 856 857 858 859 860 861 862 |
# File 'lib/sycamore/tree.rb', line 849 def include_path?(*args) case args.count when 0 then raise ArgumentError, 'wrong number of arguments (given 0, expected 1+)' when 1 then path = args.first else return include_path?(args) end path = [path] unless path.is_a? Enumerable if path.is_a? Path fetch_path(path.parent) { return false }.include? path.node else fetch_path(path[0..-2]) { return false }.include? path.last end end |
#initialize_clone(other) ⇒ Object
Clones the whole tree.
1452 1453 1454 1455 1456 |
# File 'lib/sycamore/tree.rb', line 1452 def initialize_clone(other) super clear_data add other end |
#inspect ⇒ String
A developer-friendly string representation of this tree in the usual Ruby Object#inspect style.
1374 1375 1376 1377 |
# File 'lib/sycamore/tree.rb', line 1374 def inspect "#<#{self.class}:0x#{object_id.to_s(16)} #{ to_h(sleaf_child_as: Sycamore::NothingTree::NestedString).inspect}>" end |
#internal? ⇒ Boolean #internal?(*nodes) ⇒ Boolean Also known as: nested?
Does it make sense to support the no arguments variant here and with this semantics? One would expect it to be the negation of #external? without arguments.
Checks if all given nodes or that of the tree have children.
1136 1137 1138 1139 1140 1141 |
# File 'lib/sycamore/tree.rb', line 1136 def internal?(*nodes) return false if self.empty? nodes = self.nodes if nodes.empty? nodes.all? { |node| not leaf?(node) and include_node?(node) } end |
#leaf?(node) ⇒ Boolean
Checks if the given node has no children.
1029 1030 1031 |
# File 'lib/sycamore/tree.rb', line 1029 def leaf?(node) include_node?(node) && child_at(node).empty? end |
#matches?(other) ⇒ Boolean Also known as: ===
This should probably apply a less strict equivalence comparison on the nodes. Problem: Requires a solution which doesn’t use Hash#include?.
Checks if another object matches this tree structurally and by content.
1283 1284 1285 1286 1287 1288 1289 |
# File 'lib/sycamore/tree.rb', line 1283 def matches?(other) case when Tree.like?(other) then matches_tree?(other) when other.is_a?(Enumerable) then matches_enumerable?(other) else matches_atom?(other) end end |
#matches_atom?(other) ⇒ Boolean (private)
1293 1294 1295 |
# File 'lib/sycamore/tree.rb', line 1293 private def matches_atom?(other) not other.nil? and (size == 1 and nodes.first == other and leaf? other) end |
#matches_enumerable?(other) ⇒ Boolean (private)
1297 1298 1299 1300 |
# File 'lib/sycamore/tree.rb', line 1297 private def matches_enumerable?(other) size == other.size and all? { |node, child| child.empty? and other.include?(node) } end |
#matches_tree?(other) ⇒ Boolean (private)
1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 |
# File 'lib/sycamore/tree.rb', line 1302 private def matches_tree?(other) size == other.size and all? { |node, child| if child.nothing? other.include?(node) and begin other_child = other.fetch(node, nil) not other_child or (other_child.respond_to?(:empty?) and other_child.empty?) end else child.matches? other[node] end } end |
#new_child(parent_node, *args) ⇒ Tree
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Creates a new tree meant to be used as a child.
This method is used for instantiation of child trees. When you want to a tree with different types child trees, maybe depending on the parent node, you can subclass Sycamore::Tree and override this method to your needs. By default it creates trees of the same type as this tree.
114 115 116 |
# File 'lib/sycamore/tree.rb', line 114 def new_child(parent_node, *args) self.class.new(*args) end |
#node ⇒ Object?
The only node of this tree or an exception, if more nodes present.
566 567 568 569 570 571 |
# File 'lib/sycamore/tree.rb', line 566 def node nodes = self.nodes raise NonUniqueNodeSet, "multiple nodes present: #{nodes}" if nodes.size > 1 nodes.first end |
#node! ⇒ Object
The only node of this tree or an exception, if none or more nodes present.
589 590 591 592 |
# File 'lib/sycamore/tree.rb', line 589 def node! raise EmptyNodeSet, 'no node present' if empty? node end |
#nodes ⇒ Array<Object> Also known as: keys
The nodes of this tree (without their children).
544 545 546 |
# File 'lib/sycamore/tree.rb', line 544 def nodes data.keys end |
#nothing? ⇒ Boolean
Checks if this is the Nothing tree.
128 129 130 |
# File 'lib/sycamore/tree.rb', line 128 def nothing? false end |
#present? ⇒ Boolean
This is not the negation of #absent?, since this would result in a different behaviour than ActiveSupports present? method. For the negation of #absent?, see #existent?.
159 160 161 |
# File 'lib/sycamore/tree.rb', line 159 def present? not blank? end |
#replace(node) ⇒ Object #replace(node_collection) ⇒ Object #replace(tree_structure) ⇒ Object #replace(path) ⇒ Object
Replaces the contents of this tree.
410 411 412 |
# File 'lib/sycamore/tree.rb', line 410 def replace(nodes_or_tree) clear.add(nodes_or_tree) end |
#search(nodes_or_tree) ⇒ Array<Path>
Searches the tree for one or multiple nodes or a complete tree.
937 938 939 |
# File 'lib/sycamore/tree.rb', line 937 def search(nodes_or_tree) _search(nodes_or_tree) end |
#size ⇒ Fixnum
The number of nodes in this tree.
Note, this does not count the nodes in the child trees.
963 964 965 |
# File 'lib/sycamore/tree.rb', line 963 def size data.size end |
#strict_leaf?(node) ⇒ Boolean Also known as: sleaf?
Checks if the given node has no children, even not an empty child tree.
1046 1047 1048 |
# File 'lib/sycamore/tree.rb', line 1046 def strict_leaf?(node) include_node?(node) && child_at(node).absent? end |
#strict_leaves? ⇒ Boolean #strict_leaves?(*nodes) ⇒ Boolean Also known as: sleaves?
Checks if all given nodes or that of the tree have no children, even not an empty child tree.
1073 1074 1075 1076 1077 |
# File 'lib/sycamore/tree.rb', line 1073 def strict_leaves?(*nodes) nodes = self.nodes if nodes.empty? nodes.all? { |node| strict_leaf?(node) } end |
#to_h(*args) ⇒ Hash
A hash representation of this tree.
1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 |
# File 'lib/sycamore/tree.rb', line 1343 def to_h(*args) return {} if empty? # not the nicest, but fastest way to inject on hashes, as noted here: # http://stackoverflow.com/questions/3230863/ruby-rails-inject-on-hashes-good-style hash = {} data.each do |node, child| hash[node] = child.to_native_object(*args) end hash end |
#to_native_object(sleaf_child_as: nil, special_nil: false) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
A native Ruby object representing the content of the tree.
It is used by #to_h to produce flattened representations of child trees.
1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 |
# File 'lib/sycamore/tree.rb', line 1327 def to_native_object(sleaf_child_as: nil, special_nil: false) case when empty? [] when strict_leaves? size == 1 && (!special_nil || !nodes.first.nil?) ? nodes.first : nodes else to_h(sleaf_child_as: sleaf_child_as, special_nil: special_nil) end end |
#to_s ⇒ String
A string representation of this tree.
1361 1362 1363 1364 1365 1366 1367 |
# File 'lib/sycamore/tree.rb', line 1361 def to_s if (content = to_native_object(special_nil: true)).is_a? Enumerable "Tree[#{content.inspect[1..-2]}]" else "Tree[#{content.inspect}]" end end |
#total_size ⇒ Fixnum Also known as: tsize
The number of nodes in this tree and all of their children.
979 980 981 982 983 |
# File 'lib/sycamore/tree.rb', line 979 def total_size total = size data.each { |_, child| total += child.total_size } total end |
#valid_node!(node) ⇒ Object (private)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
1403 1404 1405 1406 1407 |
# File 'lib/sycamore/tree.rb', line 1403 private def valid_node!(node) raise InvalidNode, "#{node} is not a valid tree node" if node.is_a? Enumerable node end |
#valid_node_element!(node) ⇒ Object (private)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
1393 1394 1395 1396 1397 1398 |
# File 'lib/sycamore/tree.rb', line 1393 private def valid_node_element!(node) raise InvalidNode, "#{node} is not a valid tree node" if node.is_a?(Enumerable) and not node.is_a?(Path) and not Tree.like?(node) node end |
#valid_tree!(tree) ⇒ Object (private)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
1381 1382 1383 1384 1385 1386 1387 1388 1389 |
# File 'lib/sycamore/tree.rb', line 1381 private def valid_tree!(tree) tree.each { |node, child| # using a {} block to circumvent this Rubinius issue: https://github.com/rubinius/rubinius-code/issues/7 next if child.nil? valid_node!(node) valid_tree!(child) if Tree.like?(child) } tree end |