jumoku 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +12 -7
- data/lib/jumoku/builders/arborescence.rb +157 -0
- data/lib/jumoku/builders/raw_directed_tree.rb +2 -2
- data/lib/jumoku/builders/raw_undirected_tree.rb +2 -0
- data/lib/jumoku/builders/shared.rb +5 -0
- data/lib/jumoku/strategies/edge_labeling.rb +1 -1
- data/lib/jumoku/strategies/edge_labeling/simple.rb +15 -2
- data/lib/jumoku/version.rb +1 -1
- data/spec/arborescence_spec.rb +141 -0
- data/spec/raw_directed_tree_spec.rb +1 -0
- data/spec/strategies/simple_edge_labeling.rb +10 -3
- data/spec/tree_spec.rb +2 -0
- metadata +2 -2
data/README.md
CHANGED
@@ -2,21 +2,22 @@
|
|
2
2
|
|
3
3
|
## Synopsis
|
4
4
|
|
5
|
-
Jumoku provides you with tree structures and related tools to perform manipulation and computation the easy way. Trees are
|
5
|
+
Jumoku provides you with tree structures and related tools to perform manipulation and computation, the easy way. Trees are frequently used to mimic hierarchal structures such as filesystems, or modelize decisionnal patterns, for instance.
|
6
6
|
|
7
7
|
Jumoku is built upon [Plexus](http://github.com/chikamichi/plexus "Plexus on Github"), a ruby-powered Graph Theory library, and aims at being a fully-fledged, pithy solution for tree-like structures managment. See below for additionnal information about graphs, trees, arborescences, why they're different and how to make good use of them.
|
8
8
|
|
9
9
|
## A few words about *trees*
|
10
10
|
|
11
|
-
A Tree is a graph subject to three basic constraints: nodes are all connected, they must not form any loop, and the branches binding nodes have no preferential direction.
|
11
|
+
A Tree is a graph subject to three basic constraints: nodes are all connected, they must not form any loop, and the branches binding nodes have no preferential direction. Trees are an important subset of graphs used in a whole slew of computer science and mathematical problems: network modelization, datasets storage, scientific computation, load balancing, games, AI designs, etc. They however are not limited to directed patterns: a tree is not compelled to have a "root" node, nor to have "leaves" as you may think *prima facie*. Trees with such features are called arborescences and Jumoku has support for them, too.
|
12
12
|
|
13
13
|
Jumoku (*currently under early development stage*) provides you with the following structures:
|
14
14
|
|
15
|
-
*
|
16
|
-
*
|
15
|
+
* RawUndirectedTree: a pure tree-graph with only basic features
|
16
|
+
* RawDirectedTree: a tree that's an arborescence, that is, a tree with a flow from its root to its leaf nodes
|
17
|
+
* **Tree**: a tree-graph with extended features built upon RawUndirectedTree. That's what you'd want to use as a fully-fledged tree structure
|
18
|
+
* **Arborescence**: an arbo-graph with extended features built upon RawDirectedTree. That's what you'd want to use as a fully-fledged arborescence structure
|
17
19
|
* AVLTree (*not yet*)
|
18
20
|
* RedBlackTree (*not yet*)
|
19
|
-
* **Arborescence** (*not yet*): the structure everybody thinks of when asked about "trees", with a root, internal nodes and leaves
|
20
21
|
|
21
22
|
You can also extend those structures with hybrid behaviors (not Graph Theory compliant but may be useful):
|
22
23
|
|
@@ -24,6 +25,10 @@ You can also extend those structures with hybrid behaviors (not Graph Theory com
|
|
24
25
|
* Loopy (*not yet*): relax the *acyclic* constraint
|
25
26
|
* Atomic (*not yet*): relax the *connected* constraint
|
26
27
|
|
28
|
+
There are also strategies one may enable:
|
29
|
+
|
30
|
+
* a simple edge labeling scheme (increasing integer indexes), providing edges and nodes sorting facilities
|
31
|
+
|
27
32
|
## Basic usage
|
28
33
|
|
29
34
|
To create an instance of a tree, you may use either inheritance or mixins.
|
@@ -42,7 +47,7 @@ tree = Jumoku::Tree.new
|
|
42
47
|
|
43
48
|
``` ruby
|
44
49
|
class MyTree
|
45
|
-
include Jumoku::
|
50
|
+
include Jumoku::TreeBuilder
|
46
51
|
end
|
47
52
|
tree = MyTree.new
|
48
53
|
```
|
@@ -51,7 +56,7 @@ The RawTree class is actually implemented this way.
|
|
51
56
|
|
52
57
|
### What you get
|
53
58
|
|
54
|
-
`tree` is now a Tree object, shipping with some default options:
|
59
|
+
Following the previous code example, `tree` is now a Tree object, shipping with some default options:
|
55
60
|
|
56
61
|
* it is a valid tree *per se* (an undirected, acyclic, connected graph)
|
57
62
|
* Jumoku API's methods will ensure it remains so as you manipulate it
|
@@ -8,16 +8,173 @@ module Jumoku
|
|
8
8
|
include RawDirectedTreeBuilder
|
9
9
|
include Extended
|
10
10
|
|
11
|
+
def add_branch!(u, v = nil, l = nil)
|
12
|
+
return super(u,v,l) if @_options[:free_flow]
|
13
|
+
return add_branch!(u.source, u.target, l) if u.is_a? _branch_type
|
14
|
+
return super(u,v,l) if nodes.size < 2
|
15
|
+
|
16
|
+
nodes.include?(u) ? super(u,v,l) : super(v,u,l)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Find the root node.
|
20
|
+
#
|
21
|
+
# @return [Node]
|
22
|
+
#
|
23
|
+
def root
|
24
|
+
return nodes.first if nodes.size == 1
|
25
|
+
nodes.find { |node| root?(node) }
|
26
|
+
end
|
27
|
+
|
28
|
+
# Return the list of arcs branched out from the root node.
|
29
|
+
#
|
30
|
+
# @return [Array<Plexus::Arc>]
|
31
|
+
#
|
32
|
+
def root_edges
|
33
|
+
adjacent(root, :type => :edges)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Check whether a node is the root.
|
37
|
+
#
|
38
|
+
# @return [true, false]
|
39
|
+
#
|
40
|
+
def root?(node)
|
41
|
+
in_degree(node) == 0 && out_degree(node) > 0
|
42
|
+
end
|
43
|
+
|
44
|
+
# Return the list of a node's children.
|
45
|
+
#
|
46
|
+
# @param [Node] node
|
47
|
+
# @return [Array<Node>]
|
48
|
+
#
|
49
|
+
def children(parent)
|
50
|
+
adjacent(parent)
|
51
|
+
end
|
52
|
+
alias children_of children
|
53
|
+
|
54
|
+
# Return the parent node of another.
|
55
|
+
#
|
56
|
+
# @param [Node] node
|
57
|
+
# @raise if the node has more than one parent (should never occur!)
|
58
|
+
# @return [Node, nil] nil if the node is root
|
59
|
+
#
|
60
|
+
def parent(node)
|
61
|
+
parent = adjacent(node, :direction => :in)
|
62
|
+
raise JumokuError, "Inconsistent directed tree (more than one parent for the node!)" if parent.size > 1
|
63
|
+
parent.empty? ? nil : parent.first
|
64
|
+
end
|
65
|
+
alias parent_of parent
|
66
|
+
|
67
|
+
# Check whether a node is a parent. If another node is provided as
|
68
|
+
# second parameter, check whether the former node is the parent of the
|
69
|
+
# latter node.
|
70
|
+
#
|
71
|
+
# @overload parent?(node)
|
72
|
+
# @param [Node] node
|
73
|
+
# @return [true, false]
|
74
|
+
# @overload parent?(node, maybe_child)
|
75
|
+
# @param [Node] node
|
76
|
+
# @param [Node] maybe_child
|
77
|
+
# @return [true, false]
|
78
|
+
#
|
79
|
+
def parent?(node, maybe_child = nil)
|
80
|
+
if maybe_child.nil?
|
81
|
+
!children(node).empty?
|
82
|
+
else
|
83
|
+
children(node).include? maybe_child
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Return the siblings for a node. Siblings are other node's parent children.
|
88
|
+
#
|
89
|
+
# @param [Node] node
|
90
|
+
# @return [Array<Node>] empty list if the node is root
|
91
|
+
#
|
92
|
+
def siblings(node)
|
93
|
+
return [] if root?(node)
|
94
|
+
siblings = children(parent(node))
|
95
|
+
siblings.delete(node)
|
96
|
+
siblings
|
97
|
+
end
|
98
|
+
alias siblings_of siblings
|
99
|
+
|
100
|
+
# Check whether two nodes are siblings.
|
101
|
+
#
|
102
|
+
# @param [Node] node1
|
103
|
+
# @param [Node] node2
|
104
|
+
# @return [true, false]
|
105
|
+
#
|
106
|
+
def siblings?(node1, node2)
|
107
|
+
siblings(node1).include?(node2)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Return the list of a node's neighbours, that is, children of node's
|
111
|
+
# parent siblings (cousins).
|
112
|
+
#
|
113
|
+
# @param [Node] node
|
114
|
+
# @param [Hash] options
|
115
|
+
# @option options [true, false] :siblings whether to include the node's
|
116
|
+
# siblings in the list
|
117
|
+
# @return [Array<Node>]
|
118
|
+
#
|
119
|
+
def neighbours(node, options = {:siblings => false})
|
120
|
+
# special case when the node is the root
|
121
|
+
return [] if root?(node)
|
122
|
+
|
123
|
+
# special case when the node is a root's child
|
124
|
+
if root?(parent(node))
|
125
|
+
nghb = children(parent(node))
|
126
|
+
nghb.delete_if { |child| child == node } unless options[:siblings]
|
127
|
+
return nghb.map { |child| [child] }
|
128
|
+
end
|
129
|
+
|
130
|
+
# general case
|
131
|
+
nghb = siblings(parent(node)).map do |sibling|
|
132
|
+
children(sibling)
|
133
|
+
end
|
134
|
+
nghb << siblings(node) if options[:siblings]
|
135
|
+
nghb
|
136
|
+
end
|
137
|
+
alias cousins neighbours
|
138
|
+
|
139
|
+
# Check whether two nodes are neighbours. To include the node's siblings
|
140
|
+
# in the matching candidates, pass the :siblings option to true.
|
141
|
+
#
|
142
|
+
# @param [Node] node1
|
143
|
+
# @param [Node] node2
|
144
|
+
# @param [Hash] options
|
145
|
+
# @option options [true, false] :siblings whether to include the node's
|
146
|
+
# siblings in the list
|
147
|
+
# @return [true, false]
|
148
|
+
#
|
149
|
+
def neighbours?(node1, node2, options = {:siblings => false})
|
150
|
+
neighbours(node1, options).any? { |set| set.include? node2 }
|
151
|
+
end
|
152
|
+
alias cousins? neighbours?
|
153
|
+
|
154
|
+
# Return the list of leaf nodes.
|
155
|
+
#
|
156
|
+
# @return [Array<Node>]
|
157
|
+
#
|
11
158
|
def leaves
|
12
159
|
terminal_nodes.delete_if do |node|
|
13
160
|
out_degree(node) > 0
|
14
161
|
end
|
15
162
|
end
|
16
163
|
|
164
|
+
# Check whether a node is a leaf.
|
165
|
+
#
|
166
|
+
# @param [Node]
|
167
|
+
# @return [true, false]
|
168
|
+
#
|
17
169
|
def leaf?(node)
|
18
170
|
terminal?(node) && out_degree(node) == 0
|
19
171
|
end
|
20
172
|
|
173
|
+
# Check whether all nodes from a list are leaves.
|
174
|
+
#
|
175
|
+
# @param [#to_a] nodes
|
176
|
+
# @return [true, false]
|
177
|
+
#
|
21
178
|
def leaves?(*nodes)
|
22
179
|
nodes.to_a.flatten.all? { |node| leaf?(node) }
|
23
180
|
end
|
@@ -21,11 +21,11 @@ module Jumoku
|
|
21
21
|
# @return enhanced Plexus::DirectedGraph
|
22
22
|
#
|
23
23
|
def initialize(*params)
|
24
|
+
super(*params) # Delegates to Plexus.
|
24
25
|
args = (params.pop if params.last.is_a? Hash) || {}
|
26
|
+
@_options = args
|
25
27
|
strategies = _extract_strategies(args)
|
26
28
|
|
27
|
-
super(*params) # Delegates to Plexus.
|
28
|
-
|
29
29
|
class << self; self; end.module_eval do
|
30
30
|
strategies.each { |strategy| include strategy }
|
31
31
|
alias has_branch? has_arc?
|
@@ -25,7 +25,9 @@ module Jumoku
|
|
25
25
|
# @return enhanced Plexus::UndirectedGraph
|
26
26
|
#
|
27
27
|
def initialize(*params)
|
28
|
+
super(*params) # Delegates to Plexus.
|
28
29
|
args = (params.pop if params.last.is_a? Hash) || {}
|
30
|
+
@_options = args
|
29
31
|
strategies = _extract_strategies(args)
|
30
32
|
|
31
33
|
super(*params) # Delegates to Plexus.
|
@@ -3,6 +3,8 @@ module Jumoku
|
|
3
3
|
# builders: {UndirectedTreeBuilder} and {DirectedTreeBuilder}.
|
4
4
|
#
|
5
5
|
module Shared
|
6
|
+
STRATEGIES = [:edge_labeling, :node_labeling]
|
7
|
+
|
6
8
|
def self.included(base)
|
7
9
|
base.class_eval do
|
8
10
|
# Late aliasing as it references methods provided by Plexus modules.
|
@@ -10,6 +12,8 @@ module Jumoku
|
|
10
12
|
end
|
11
13
|
end
|
12
14
|
|
15
|
+
attr_accessor :_options
|
16
|
+
|
13
17
|
# Adds the node to the tree.
|
14
18
|
#
|
15
19
|
# For convenience, you may pass a branch as the parameter,
|
@@ -208,6 +212,7 @@ module Jumoku
|
|
208
212
|
end
|
209
213
|
|
210
214
|
def _extract_strategies(options)
|
215
|
+
options = options.dup.select! { |k,v| STRATEGIES.include?(k) } || options.dup
|
211
216
|
options.inject([]) do |strategies, (k,v)|
|
212
217
|
begin
|
213
218
|
strategies << Jumoku.const_get(k.to_s.constantize).const_get(v.to_s.constantize)
|
@@ -31,9 +31,18 @@ module Jumoku
|
|
31
31
|
#
|
32
32
|
# @return [Array]
|
33
33
|
#
|
34
|
-
def
|
34
|
+
def sorted_edges(&block)
|
35
35
|
return super(&block) if block_given?
|
36
|
-
edges
|
36
|
+
_sort_edges(edges)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Sort a set of edges.
|
40
|
+
#
|
41
|
+
# @param [Array]
|
42
|
+
# @return [Array]
|
43
|
+
#
|
44
|
+
def sort_edges(set)
|
45
|
+
_sort_edges(set)
|
37
46
|
end
|
38
47
|
|
39
48
|
# Only for directed trees.
|
@@ -70,6 +79,10 @@ module Jumoku
|
|
70
79
|
self.next_simple_edge_label_number += 1
|
71
80
|
super
|
72
81
|
end
|
82
|
+
|
83
|
+
def _sort_edges(set)
|
84
|
+
set.sort { |a,b| a.label._weight <=> b.label._weight }
|
85
|
+
end
|
73
86
|
end
|
74
87
|
end
|
75
88
|
end
|
data/lib/jumoku/version.rb
CHANGED
data/spec/arborescence_spec.rb
CHANGED
@@ -14,6 +14,147 @@ describe Arborescence do
|
|
14
14
|
|
15
15
|
it_should_behave_like "a tree with extended features"
|
16
16
|
|
17
|
+
context "arcs flow" do
|
18
|
+
it "should by default be forced to match direction of the first arc added" do
|
19
|
+
tree.add_branch! 1, 2, 0
|
20
|
+
tree.add_branch! 2, 3, 1
|
21
|
+
tree.add_branch! 4, 3, 2 # will be reversed actually
|
22
|
+
tree.edges.sort_by { |edge| edge.label }.map { |edge| edge.target }.should == [2, 3, 4]
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should be possible to relax this constraint" do
|
26
|
+
tree = Arborescence.new(:free_flow => true)
|
27
|
+
tree.add_branch! 1, 2, 0
|
28
|
+
tree.add_branch! 2, 3, 1
|
29
|
+
tree.add_branch! 4, 3, 2 # will *not* be reversed
|
30
|
+
tree.edges.sort_by { |edge| edge.label }.map { |edge| edge.target }.should == [2, 3, 3]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "root helpers" do
|
35
|
+
before :each do
|
36
|
+
tree.add_branches! 1,2, 2,3, 1,4, 4,5, 4,6
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#root" do
|
40
|
+
it "should return the root node" do
|
41
|
+
tree.root.should == 1
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#root_edges" do
|
46
|
+
it "should return the edge(s) branched from the root" do
|
47
|
+
tree.root_edges.size.should == 2
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#root?" do
|
52
|
+
it "should report whether a node is the root" do
|
53
|
+
tree.root?(1).should be_true
|
54
|
+
(3..6).all? { |i| !tree.root?(i) }.should be_true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "children helpers" do
|
60
|
+
before :each do
|
61
|
+
tree.add_branches! 1,2, 2,3, 1,4, 4,5, 4,6
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "#children" do
|
65
|
+
it "should return the list of children nodes for a node" do
|
66
|
+
tree.children(1).sort.should == [2,4]
|
67
|
+
tree.children_of(4).sort.should == [5,6]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "parent helpers" do
|
73
|
+
before :each do
|
74
|
+
tree.add_branches! 1,2, 2,3, 1,4, 4,5, 4,6
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#parent" do
|
78
|
+
it "should return the parent of a node" do
|
79
|
+
tree.parent(1).should be_nil
|
80
|
+
tree.parent(2).should == 1
|
81
|
+
tree.parent_of(6).should == 4
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "parent?" do
|
86
|
+
it "should check whether a node is the parent of another (which may be specified)" do
|
87
|
+
tree.parent?(1).should be_true
|
88
|
+
tree.parent?(6).should be_false
|
89
|
+
tree.parent?(1,2).should be_true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context "siblings helpers" do
|
95
|
+
before :each do
|
96
|
+
tree.add_branches! 1,2, 2,3, 1,4, 4,5, 4,6, 1,7
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "#siblings" do
|
100
|
+
it "should return the siblings for a node" do
|
101
|
+
tree.siblings(1).should be_empty
|
102
|
+
tree.siblings_of(2).sort.should == [4,7]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "#siblings?" do
|
107
|
+
it "should check whether two nodes are siblings" do
|
108
|
+
tree.siblings?(1,2).should be_false
|
109
|
+
tree.siblings?(2,3).should be_false
|
110
|
+
tree.siblings?(2,4).should be_true
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "#neighbours" do
|
115
|
+
before :each do
|
116
|
+
tree.add_node! 2, 8
|
117
|
+
tree.add_node! 7, 9
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should return the grand-siblings for a node" do
|
121
|
+
tree.neighbours(1).should be_empty
|
122
|
+
tree.neighbours(2).size.should == 2
|
123
|
+
[4,7].each { |node| tree.neighbours(2).should include([node]) }
|
124
|
+
neighbours = tree.neighbours(8)
|
125
|
+
neighbours.size.should == 2
|
126
|
+
neighbours.should include [5,6]
|
127
|
+
neighbours.should include [9]
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should be able to include siblings as well" do
|
131
|
+
neighbours = tree.neighbours(8, :siblings => true)
|
132
|
+
neighbours.size.should == 3
|
133
|
+
neighbours.should include [3]
|
134
|
+
neighbours.should include [5,6]
|
135
|
+
neighbours.should include [9]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe "#neighbours?" do
|
140
|
+
before :each do
|
141
|
+
tree.add_node! 2, 8
|
142
|
+
tree.add_node! 7, 9
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should check whether two nodes are neighbours" do
|
146
|
+
tree.neighbours?(1,2).should be_false
|
147
|
+
tree.neighbours?(2,8).should be_false
|
148
|
+
tree.neighbours?(3,8).should be_false
|
149
|
+
tree.neighbours?(5,8).should be_true
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should be possible to include siblings in the match" do
|
153
|
+
tree.neighbours?(3,8, :siblings => true).should be_true
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
17
158
|
context "leaf nodes inspection" do
|
18
159
|
before :each do
|
19
160
|
tree.add_branches! 1,2, 2,3, 1,4, 1,5
|
@@ -7,6 +7,7 @@ describe RawDirectedTree do
|
|
7
7
|
let(:branch_type) { subject.send :_branch_type }
|
8
8
|
|
9
9
|
it_should_behave_like "a legacy tree"
|
10
|
+
|
10
11
|
it "should not allow to add a reverse arc" do
|
11
12
|
tree.add_branch! 1, 2
|
12
13
|
lambda { tree.add_branch! 2, 1 }.should raise_error ForbiddenCycle
|
@@ -25,21 +25,28 @@ describe EdgeLabeling::Simple do
|
|
25
25
|
tree.edges.map { |e| e.label._weight }.sort.should == [1,2,3,5]
|
26
26
|
end
|
27
27
|
|
28
|
-
describe "#
|
28
|
+
describe "#sorted_edges" do
|
29
29
|
it "should by default return the list of edges in (increasing) order" do
|
30
30
|
tree.add_branches! 1,2, 1,3, 2,4, 1,5, 3,6, 1,7
|
31
|
-
tree.
|
31
|
+
tree.sorted_edges.map { |e| e.label._weight }.should == (0..5).to_a
|
32
32
|
end
|
33
33
|
|
34
34
|
it "should accept a block to sort edges" do
|
35
35
|
# for the sake of this specs, one'd actually #reverse the default sorting ;)
|
36
36
|
tree.add_branches! 1,2, 1,3, 2,4, 1,5, 3,6, 1,7
|
37
|
-
tree.
|
37
|
+
tree.sorted_edges do |edge|
|
38
38
|
-edge.label._weight
|
39
39
|
end.map { |e| e.label._weight }.should == (0..5).to_a.reverse
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
+
describe "#sort_edges" do
|
44
|
+
it "should sort a set of edges using the simple scheme" do
|
45
|
+
tree.add_branches! 1,2, 1,3, 2,4, 1,5, 3,6, 1,7
|
46
|
+
tree.sort_edges(tree.adjacent(1, :type => :edges)).map { |e| e.target }.should == [2,3,5,7]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
43
50
|
it "should thus allow for local edge and children ordering (directed trees only)" do
|
44
51
|
arbo.add_branches! 1,2, 1,3, 2,4, 1,5, 3,6, 1,7
|
45
52
|
arbo.sorted_arcs_from(1).map { |e| e.target }.should == [2,3,5,7]
|
data/spec/tree_spec.rb
CHANGED
@@ -7,8 +7,10 @@ describe Tree do
|
|
7
7
|
let(:branch_type) { subject.send :_branch_type }
|
8
8
|
|
9
9
|
it_should_behave_like "a legacy tree"
|
10
|
+
|
10
11
|
it "should be a undirected graph" do
|
11
12
|
tree.class.ancestors.should include RawUndirectedTreeBuilder
|
12
13
|
end
|
14
|
+
|
13
15
|
it_should_behave_like "a tree with extended features"
|
14
16
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: jumoku
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.2.
|
5
|
+
version: 0.2.4
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Jean-Denis Vauguet <jd@vauguet.fr>
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-07-
|
13
|
+
date: 2011-07-12 00:00:00 +02:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|