rubytree 0.8.3 → 0.9.0

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.
@@ -8,7 +8,7 @@
8
8
  # Author:: Anupam Sengupta (anupamsg@gmail.com)
9
9
  #
10
10
 
11
- # Copyright (c) 2007, 2008, 2009, 2010, 2012 Anupam Sengupta
11
+ # Copyright (c) 2007, 2008, 2009, 2010, 2012, 2013 Anupam Sengupta
12
12
  #
13
13
  # All rights reserved.
14
14
  #
@@ -51,21 +51,7 @@ module Tree
51
51
  #
52
52
  class BinaryTreeNode < TreeNode
53
53
 
54
- # Adds the specified child node to the receiver node. The child node's parent is set to be the receiver.
55
- #
56
- # The child nodes are added in the order of addition, i.e., the first child added becomes the left node, and the
57
- # second child added will be the second node.
58
- #
59
- # If only one child is present, then this will be the left child.
60
- #
61
- # @param [Tree::BinaryTreeNode] child The child to add.
62
- #
63
- # @raise [ArgumentError] This exception is raised if two children are already present.
64
- def add(child)
65
- raise ArgumentError, "Already has two child nodes" if @children.size == 2
66
-
67
- super(child)
68
- end
54
+ # @!group Core Attributes
69
55
 
70
56
  # @!attribute [rw] left_child
71
57
  # Left child of the receiver node. Note that left Child == first Child.
@@ -89,6 +75,78 @@ module Tree
89
75
  children[1]
90
76
  end
91
77
 
78
+ # @!attribute is_left_child?
79
+ # +true+ if the receiver node is the left child of its parent.
80
+ # Always returns +false+ if it is a root node.
81
+ #
82
+ # @return [Boolean] +true+ if this is the left child of its parent.
83
+ def is_left_child?
84
+ return false if is_root?
85
+ self == parent.left_child
86
+ end
87
+
88
+ # @!attribute [r] is_right_child?
89
+ # +true+ if the receiver node is the right child of its parent.
90
+ # Always returns +false+ if it is a root node.
91
+ #
92
+ # @return [Boolean] +true+ if this is the right child of its parent.
93
+ def is_right_child?
94
+ return false if is_root?
95
+ self == parent.right_child
96
+ end
97
+
98
+ # @!group Structure Modification
99
+
100
+ # Adds the specified child node to the receiver node. The child node's parent is set to be the receiver.
101
+ #
102
+ # The child nodes are added in the order of addition, i.e., the first child added becomes the left node, and the
103
+ # second child added will be the second node.
104
+ #
105
+ # If only one child is present, then this will be the left child.
106
+ #
107
+ # @param [Tree::BinaryTreeNode] child The child to add.
108
+ #
109
+ # @raise [ArgumentError] This exception is raised if two children are already present.
110
+ def add(child)
111
+ raise ArgumentError, "Already has two child nodes" if @children.size == 2
112
+
113
+ super(child)
114
+ end
115
+
116
+ # Performs inorder traversal (including this node).
117
+ #
118
+ # @yieldparam node [Tree::BinaryTreeNode] Each node (in-order).
119
+ #
120
+ # @return [Tree::BinaryTreeNode] This node, if a block is given
121
+ # @return [Enumerator] An enumerator on this tree, if a block is *not* given
122
+ #
123
+ # @since 0.9.0
124
+ #
125
+ # @see #each
126
+ # @see #preordered_each
127
+ # @see #postordered_each
128
+ def inordered_each(&block)
129
+
130
+ return self.to_enum unless block_given?
131
+
132
+ node_stack = []
133
+ current_node = self
134
+
135
+ until node_stack.empty? and current_node == nil
136
+ if current_node
137
+ node_stack.push(current_node)
138
+ current_node = current_node.left_child
139
+ else
140
+ current_node = node_stack.pop()
141
+ yield current_node
142
+ current_node = current_node.right_child
143
+ end
144
+ end
145
+
146
+ return self if block_given?
147
+
148
+ end
149
+
92
150
  # A protected method to allow insertion of child nodes at the specified location.
93
151
  # Note that this method allows insertion of +nil+ nodes.
94
152
  #
@@ -107,6 +165,8 @@ module Tree
107
165
  child
108
166
  end
109
167
 
168
+ protected :set_child_at
169
+
110
170
  # Sets the left child of the receiver node. If a previous child existed, it is replaced.
111
171
  #
112
172
  # @param [Tree::BinaryTreeNode] child The child to add as the left-side node.
@@ -131,31 +191,11 @@ module Tree
131
191
  set_child_at child, 1
132
192
  end
133
193
 
134
- # Returns +true+ if the receiver node is the left child of its parent.
135
- # Always returns +false+ if it is a root node.
136
- #
137
- # @return [Boolean] +true+ if this is the left child of its parent.
138
- def is_left_child?
139
- return false if is_root?
140
- self == parent.left_child
141
- end
142
-
143
- # Returns +true+ if the receiver node is the right child of its parent.
144
- # Always returns +false+ if it is a root node.
145
- #
146
- # @return [Boolean] +true+ if this is the right child of its parent.
147
- def is_right_child?
148
- return false if is_root?
149
- self == parent.right_child
150
- end
151
-
152
194
  # Swaps the left and right child nodes of the receiver node with each other.
153
195
  def swap_children
154
196
  self.left_child, self.right_child = self.right_child, self.left_child
155
197
  end
156
198
 
157
- protected :set_child_at
158
-
159
199
  end
160
200
 
161
201
  end
@@ -0,0 +1,78 @@
1
+ # camel_case_methods.rb - This file is part of the RubyTree package.
2
+ #
3
+ # = camel_case_methods.rb - Provides conversion from CamelCase to snake_case.
4
+ #
5
+ # Author:: Anupam Sengupta (anupamsg@gmail.com)
6
+ #
7
+ # Time-stamp: <2013-12-31 20:48:13 anupam>
8
+ #
9
+ # Copyright (C) 2012, 2013 Anupam Sengupta <anupamsg@gmail.com>
10
+ #
11
+ # All rights reserved.
12
+ #
13
+ # Redistribution and use in source and binary forms, with or without modification,
14
+ # are permitted provided that the following conditions are met:
15
+ #
16
+ # - Redistributions of source code must retain the above copyright notice, this
17
+ # list of conditions and the following disclaimer.
18
+ #
19
+ # - Redistributions in binary form must reproduce the above copyright notice, this
20
+ # list of conditions and the following disclaimer in the documentation and/or
21
+ # other materials provided with the distribution.
22
+ #
23
+ # - Neither the name of the organization nor the names of its contributors may
24
+ # be used to endorse or promote products derived from this software without
25
+ # specific prior written permission.
26
+ #
27
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
30
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
31
+ # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
33
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
34
+ # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37
+ #
38
+
39
+ require 'structured_warnings'
40
+
41
+ module Tree::Utils
42
+ # Provides utility functions to handle CamelCase methods, and redirect
43
+ # invocation of such methods to the snake_case equivalents.
44
+ module CamelCaseMethodHandler
45
+ def self.included(base)
46
+ # @!visibility private
47
+ # Allow the deprecated CamelCase method names. Display a warning.
48
+ # :nodoc:
49
+ def method_missing(meth, *args, &blk)
50
+ if self.respond_to?(new_method_name = to_snake_case(meth))
51
+ warn DeprecatedMethodWarning,
52
+ "The camelCased methods are deprecated. Please use #{new_method_name} instead of #{meth}"
53
+ return send(new_method_name, *args, &blk)
54
+ else
55
+ super
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ # @!visibility private
62
+ # Convert a CamelCasedWord to a underscore separated camel_cased_word.
63
+ #
64
+ # @param [String] camel_cased_word The word to be converted to snake_case.
65
+ # @return [String] the snake_cased_word.
66
+ def to_snake_case(camel_cased_word)
67
+ word = camel_cased_word.to_s.dup
68
+ word.gsub!(/::/, '/')
69
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
70
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
71
+ word.tr!("-", "_")
72
+ word.downcase!
73
+ word
74
+ end
75
+
76
+ end # self.included
77
+ end
78
+ end
@@ -0,0 +1,125 @@
1
+ # json.rb - This file is part of the RubyTree package.
2
+ #
3
+ # = json.rb - Provides conversion to and from JSON.
4
+ #
5
+ # Author:: Anupam Sengupta (anupamsg@gmail.com)
6
+ #
7
+ # Time-stamp: <2013-12-31 21:57:42 anupam>
8
+ #
9
+ # Copyright (C) 2012, 2013 Anupam Sengupta <anupamsg@gmail.com>
10
+ #
11
+ # All rights reserved.
12
+ #
13
+ # Redistribution and use in source and binary forms, with or without modification,
14
+ # are permitted provided that the following conditions are met:
15
+ #
16
+ # - Redistributions of source code must retain the above copyright notice, this
17
+ # list of conditions and the following disclaimer.
18
+ #
19
+ # - Redistributions in binary form must reproduce the above copyright notice, this
20
+ # list of conditions and the following disclaimer in the documentation and/or
21
+ # other materials provided with the distribution.
22
+ #
23
+ # - Neither the name of the organization nor the names of its contributors may
24
+ # be used to endorse or promote products derived from this software without
25
+ # specific prior written permission.
26
+ #
27
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
30
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
31
+ # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
33
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
34
+ # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37
+
38
+ require 'json'
39
+
40
+ # Provides utility methods to convert a {Tree::TreeNode} to and from
41
+ # JSON[http://flori.github.com/json/].
42
+ module Tree::Utils::JSONConverter
43
+
44
+ def self.included(base)
45
+ base.extend(ClassMethods)
46
+ end
47
+
48
+ # @!group Converting to/from JSON
49
+
50
+ # Creates a JSON ready Hash for the #to_json method.
51
+ #
52
+ # @author Eric Cline (https://github.com/escline)
53
+ # @since 0.8.3
54
+ #
55
+ # @return A hash based representation of the JSON
56
+ #
57
+ # Rails uses JSON in ActiveSupport, and all Rails JSON encoding goes through +as_json+.
58
+ #
59
+ # @see #to_json
60
+ # @see http://stackoverflow.com/a/6880638/273808
61
+ def as_json(options = {})
62
+
63
+ json_hash = {
64
+ "name" => name,
65
+ "content" => content,
66
+ JSON.create_id => self.class.name
67
+ }
68
+
69
+ if has_children?
70
+ json_hash["children"] = children
71
+ end
72
+
73
+ return json_hash
74
+
75
+ end
76
+
77
+ # Creates a JSON representation of this node including all it's children.
78
+ # This requires the JSON gem to be available, or else the operation fails with
79
+ # a warning message. Uses the Hash output of #as_json method.
80
+ #
81
+ # @author Dirk Breuer (http://github.com/railsbros-dirk)
82
+ # @since 0.7.0
83
+ #
84
+ # @return The JSON representation of this subtree.
85
+ #
86
+ # @see ClassMethods#json_create
87
+ # @see #as_json
88
+ # @see http://flori.github.com/json
89
+ def to_json(*a)
90
+ as_json.to_json(*a)
91
+ end
92
+
93
+ # ClassMethods for the {JSONConverter} module. Will become class methods in the +include+ target.
94
+ module ClassMethods
95
+ # Helper method to create a Tree::TreeNode instance from the JSON hash
96
+ # representation. Note that this method should *NOT* be called directly.
97
+ # Instead, to convert the JSON hash back to a tree, do:
98
+ #
99
+ # tree = JSON.parse(the_json_hash)
100
+ #
101
+ # This operation requires the {JSON gem}[http://flori.github.com/json/] to
102
+ # be available, or else the operation fails with a warning message.
103
+ #
104
+ # @author Dirk Breuer (http://github.com/railsbros-dirk)
105
+ # @since 0.7.0
106
+ #
107
+ # @param [Hash] json_hash The JSON hash to convert from.
108
+ #
109
+ # @return [Tree::TreeNode] The created tree.
110
+ #
111
+ # @see #to_json
112
+ # @see http://flori.github.com/json
113
+ def json_create(json_hash)
114
+
115
+ node = new(json_hash["name"], json_hash["content"])
116
+
117
+ json_hash["children"].each do |child|
118
+ node << child
119
+ end if json_hash["children"]
120
+
121
+ return node
122
+
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,172 @@
1
+ # metrics_methods.rb - This file is part of the RubyTree package.
2
+ #
3
+ # = metrics_methods.rb - Provides methods for various tree measurements
4
+ #
5
+ # Author:: Anupam Sengupta (anupamsg@gmail.com)
6
+ #
7
+ # Time-stamp: <2013-12-31 21:45:35 anupam>
8
+ #
9
+ # Copyright (C) 2013 Anupam Sengupta <anupamsg@gmail.com>
10
+ #
11
+ # All rights reserved.
12
+ #
13
+ # Redistribution and use in source and binary forms, with or without modification,
14
+ # are permitted provided that the following conditions are met:
15
+ #
16
+ # - Redistributions of source code must retain the above copyright notice, this
17
+ # list of conditions and the following disclaimer.
18
+ #
19
+ # - Redistributions in binary form must reproduce the above copyright notice, this
20
+ # list of conditions and the following disclaimer in the documentation and/or
21
+ # other materials provided with the distribution.
22
+ #
23
+ # - Neither the name of the organization nor the names of its contributors may
24
+ # be used to endorse or promote products derived from this software without
25
+ # specific prior written permission.
26
+ #
27
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
30
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
31
+ # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
33
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
34
+ # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37
+ #
38
+
39
+ require 'structured_warnings'
40
+
41
+ module Tree::Utils
42
+ # Provides utility functions to measure various tree metrics.
43
+ module TreeMetricsHandler
44
+ def self.included(base)
45
+
46
+ # @!group Metrics and Measures
47
+
48
+ # @!attribute [r] size
49
+ # Total number of nodes in this (sub)tree, including this node.
50
+ #
51
+ # Size of the tree is defined as:
52
+ #
53
+ # Size:: Total number nodes in the subtree including this node.
54
+ #
55
+ # @return [Integer] Total number of nodes in this (sub)tree.
56
+ def size
57
+ inject(0) {|sum, node| sum + 1 if node}
58
+ end
59
+
60
+ # @!attribute [r] length
61
+ # Convenience synonym for {#size}.
62
+ #
63
+ # @deprecated This method name is ambiguous and may be removed. Use {#size} instead.
64
+ #
65
+ # @return [Integer] The total number of nodes in this (sub)tree.
66
+ # @see #size
67
+ def length
68
+ size()
69
+ end
70
+
71
+ # @!attribute [r] node_height
72
+ # Height of the (sub)tree from this node. Height of a node is defined as:
73
+ #
74
+ # Height:: Length of the longest downward path to a leaf from the node.
75
+ #
76
+ # - Height from a root node is height of the entire tree.
77
+ # - The height of a leaf node is zero.
78
+ #
79
+ # @return [Integer] Height of the node.
80
+ def node_height
81
+ return 0 if is_leaf?
82
+ 1 + @children.collect { |child| child.node_height }.max
83
+ end
84
+
85
+ # @!attribute [r] node_depth
86
+ # Depth of this node in its tree. Depth of a node is defined as:
87
+ #
88
+ # Depth:: Length of the node's path to its root. Depth of a root node is zero.
89
+ #
90
+ # *Note* that the deprecated method {#depth} was incorrectly computing this value.
91
+ # Please replace all calls to the old method with {#node_depth} instead.
92
+ #
93
+ # {#level} is an alias for this method.
94
+ #
95
+ # @return [Integer] Depth of this node.
96
+ def node_depth
97
+ return 0 if is_root?
98
+ 1 + parent.node_depth
99
+ end
100
+
101
+ # @!attribute [r] level
102
+ # Alias for {#node_depth}
103
+ #
104
+ # @see #node_depth
105
+ def level
106
+ node_depth
107
+ end
108
+
109
+ # @!attribute [r] depth
110
+ # Depth of the tree from this node. A single leaf node has a depth of 1.
111
+ #
112
+ # This method is *DEPRECATED* and may be removed in the subsequent releases.
113
+ # Note that the value returned by this method is actually the:
114
+ #
115
+ # _height_ + 1 of the node, *NOT* the _depth_.
116
+ #
117
+ # For correct and conventional behavior, please use {#node_depth} and
118
+ # {#node_height} methods instead.
119
+ #
120
+ # @return [Integer] depth of the node.
121
+ # @deprecated This method returns an incorrect value. Use the {#node_depth} method instead.
122
+ #
123
+ # @see #node_depth
124
+ def depth
125
+ warn DeprecatedMethodWarning, 'This method is deprecated. Please use node_depth() or node_height() instead (bug # 22535)'
126
+
127
+ return 1 if is_leaf?
128
+ 1 + @children.collect { |child| child.depth }.max
129
+ end
130
+
131
+ # @!attribute [r] breadth
132
+ # Breadth of the tree at this node's level.
133
+ # A single node without siblings has a breadth of 1.
134
+ #
135
+ # Breadth is defined to be:
136
+ # Breadth:: Number of sibling nodes to this node + 1 (this node itself),
137
+ # i.e., the number of children the parent of this node has.
138
+ #
139
+ # @return [Integer] breadth of the node's level.
140
+ def breadth
141
+ is_root? ? 1 : parent.children.size
142
+ end
143
+
144
+ # @!attribute [r] in_degree
145
+ # The incoming edge-count of this node.
146
+ #
147
+ # In-degree is defined as:
148
+ # In-degree:: Number of edges arriving at the node (0 for root, 1 for all other nodes)
149
+ #
150
+ # - In-degree = 0 for a root or orphaned node
151
+ # - In-degree = 1 for a node which has a parent
152
+ #
153
+ # @return [Integer] The in-degree of this node.
154
+ def in_degree
155
+ is_root? ? 0 : 1
156
+ end
157
+
158
+ # @!attribute [r] out_degree
159
+ # The outgoing edge-count of this node.
160
+ #
161
+ # Out-degree is defined as:
162
+ # Out-degree:: Number of edges leaving the node (zero for leafs)
163
+ #
164
+ # @return [Integer] The out-degree of this node.
165
+ def out_degree
166
+ is_leaf? ? 0 : children.size
167
+ end
168
+
169
+ # @!endgroup
170
+ end # self.included
171
+ end
172
+ end