jumoku 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,44 +9,59 @@ require 'hashery'
9
9
  require 'plexus'
10
10
 
11
11
  # Jumoku provides you with several modules and classes to build and manipulate
12
- # tree graphs. Two basic implementations are available: undirected trees
13
- # ({RawUndirectedTree}) and directed trees ({RawDirectedTree}).
12
+ # tree-graphs.
14
13
  #
15
- # {Tree} is derived from the undirected flavour and sticks to the mathematical
16
- # tree definition. {Arborescence} is derived from the directed flavour and is
17
- # likely to be used as the basis to modelize hierarchy structures, such as a
18
- # family tree, a file browser…
14
+ # A tree is a structure composed of branches binding nodes. Branches may be
15
+ # directed (arcs) or undirected (edges). When a there is only one general
16
+ # direction (flow), there is a root node, and one or several leaf nodes. A
17
+ # directed, uni-flow tree where each node only branches in once and branches
18
+ # out once is called a path. For more information on what a tree-graph is and
19
+ # how you could make use of it, see the README.
19
20
  #
20
- # Note that a node can be any Object. There is no "node type", therefore
21
- # arguments which re expected to be nodes are simply labelled as "`node`"
22
- # within this documentation. A nice object type to use as a node may be an
23
- # OpenStruct or an [OpenObject](http://facets.rubyforge.org/apidoc/api/more/classes/OpenObject.html)
24
- # (from the Facets library), both turning nodes into versatile handlers.
21
+ # Two basic implementations are available: undirected trees
22
+ # ({RawUndirectedTree}) and directed trees ({RawDirectedTree}). They offer
23
+ # limited features, so one will certainly drop to their civilized siblings:
24
+ #
25
+ # * {Tree} is derived from an undirected tree and sticks to the mathematical
26
+ # tree definition.
27
+ # * {Arborescence} is derived from a directed tree and is likely to be used
28
+ # as the basis to modelize hierarchy structures, such as a family tree, a file
29
+ # browser…
30
+ #
31
+ # A node can be any Object: there is no "node type". A nice object "type" to
32
+ # use as a node may be an OpenStruct or an OpenHash (from the Facets library),
33
+ # but really any object is valid.
34
+ #
35
+ # Jumoku allows you to enable some strategies when creating a new tree. For
36
+ # instance, you may enable an edge/arc labeling strategy, which will cause
37
+ # indexing of branches as they are added. Jumoku provides a few basic
38
+ # strategies mixin, and one may implement custom ones.
25
39
  #
26
40
  module Jumoku
27
41
  # core implementations
28
- autoload :Shared, 'jumoku/builders/shared'
29
- autoload :Extended, 'jumoku/builders/extended'
42
+ autoload :Shared, 'jumoku/builders/shared'
43
+ autoload :Extended, 'jumoku/builders/extended'
30
44
 
31
45
  # branch types
32
- autoload :UndirectedBranch, 'jumoku/support/branch'
33
- autoload :DirectedBranch, 'jumoku/support/branch'
46
+ autoload :UndirectedBranch, 'jumoku/support/branch'
47
+ autoload :DirectedBranch, 'jumoku/support/branch'
34
48
 
35
49
  # tree builders
36
- autoload :RawUndirectedTreeBuilder, 'jumoku/builders/raw_undirected_tree'
37
- autoload :RawDirectedTreeBuilder, 'jumoku/builders/raw_directed_tree'
38
- autoload :TreeBuilder, 'jumoku/builders/tree'
39
- autoload :ArborescenceBuilder, 'jumoku/builders/arborescence'
50
+ autoload :RawUndirectedTreeBuilder, 'jumoku/builders/raw_undirected_tree'
51
+ autoload :RawDirectedTreeBuilder, 'jumoku/builders/raw_directed_tree'
52
+ autoload :TreeBuilder, 'jumoku/builders/tree'
53
+ autoload :ArborescenceBuilder, 'jumoku/builders/arborescence'
40
54
 
41
55
  # tree classes
42
- autoload :RawDirectedTree, 'jumoku/classes/tree_classes'
43
- autoload :RawUndirectedTree, 'jumoku/classes/tree_classes'
44
- autoload :Tree, 'jumoku/classes/tree_classes'
45
- autoload :Arborescence, 'jumoku/classes/tree_classes'
56
+ autoload :RawDirectedTree, 'jumoku/classes/tree_classes'
57
+ autoload :RawUndirectedTree, 'jumoku/classes/tree_classes'
58
+ autoload :Tree, 'jumoku/classes/tree_classes'
59
+ autoload :Arborescence, 'jumoku/classes/tree_classes'
46
60
 
47
61
  # strategies
48
- autoload :EdgeLabeling, 'jumoku/strategies/edge_labeling'
49
- EdgeLabeling.autoload :Simple, 'jumoku/strategies/edge_labeling/simple'
62
+ autoload :Strategies, 'jumoku/strategies'
63
+ Strategies.autoload :EdgeLabelingBackend, 'jumoku/strategies/edge_labeling'
64
+ Strategies.autoload :SimpleEdgeLabeling, 'jumoku/strategies/edge_labeling/simple'
50
65
 
51
66
  # support
52
67
  require 'jumoku/support/ruby_compatibility'
@@ -4,6 +4,17 @@ module Jumoku
4
4
  # It provides a directed tree which acts as a hierarchical structure, known
5
5
  # as an arborescence.
6
6
  #
7
+ # By default, it is ensured that new arcs remain in the same general flow
8
+ # direction, based on the first arc added (binding the node known as root
9
+ # to its first children, known as leaf). This constraint may be relaxed by
10
+ # passing the `:free_flow` option to true when initializing:
11
+ #
12
+ # Arborescence.new(:free_flow => true)
13
+ #
14
+ # This way, the tree remains directed (nodes are bound using arcs, not
15
+ # undirected edges), but a node may be either a pure source (only outing
16
+ # arcs), a pure target (only arcs pointing at it), or a mix.
17
+ #
7
18
  module ArborescenceBuilder
8
19
  include RawDirectedTreeBuilder
9
20
  include Extended
@@ -64,9 +75,8 @@ module Jumoku
64
75
  end
65
76
  alias parent_of parent
66
77
 
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.
78
+ # Check whether a node is a parent. If another node is provided as second
79
+ # parameter, check whether the former is the parent of the latter.
70
80
  #
71
81
  # @overload parent?(node)
72
82
  # @param [Node] node
@@ -137,7 +147,7 @@ module Jumoku
137
147
  alias cousins neighbours
138
148
 
139
149
  # Check whether two nodes are neighbours. To include the node's siblings
140
- # in the matching candidates, pass the :siblings option to true.
150
+ # in the matching candidates, pass the `:siblings` option to true.
141
151
  #
142
152
  # @param [Node] node1
143
153
  # @param [Node] node2
@@ -298,7 +298,7 @@ module Jumoku
298
298
  end
299
299
  alias has_branches_among? branches_among?
300
300
 
301
- # Number of nodes.
301
+ # Number of nodes. Just `#nodes.size` really.
302
302
  #
303
303
  # @return [Integer]
304
304
  #
@@ -1,11 +1,12 @@
1
1
  module Jumoku
2
2
  # A {RawDirectedTree} relax the undirected constraint of trees as defined in
3
- # Graph Theory. They remain connected and acyclic.
3
+ # Graph Theory. They remain connected and acyclic though.
4
4
  #
5
- # It thus uses Plexus::DirectedGraphBuilder as its backend.
5
+ # It thus uses `Plexus::DirectedGraphBuilder` as its backend.
6
6
  #
7
7
  # It offers limited functionalities, therefore the main tree structure you'll likely to
8
8
  # use is its extended version, {Arborescence}.
9
+ #
9
10
  module RawDirectedTreeBuilder
10
11
  include Plexus::DirectedGraphBuilder
11
12
  include Shared
@@ -22,14 +23,8 @@ module Jumoku
22
23
  #
23
24
  def initialize(*params)
24
25
  super(*params) # Delegates to Plexus.
25
- args = (params.pop if params.last.is_a? Hash) || {}
26
- @_options = args
27
- strategies = _extract_strategies(args)
28
-
29
- class << self; self; end.module_eval do
30
- strategies.each { |strategy| include strategy }
31
- alias has_branch? has_arc?
32
- end
26
+ @_options = (params.pop if params.last.is_a? Hash) || {}
27
+ _delay { alias has_branch? has_arc? }
33
28
  end
34
29
 
35
30
  # Checks whether the tree is *really* a valid tree, that is if the
@@ -39,7 +34,8 @@ module Jumoku
39
34
  # * acyclic
40
35
  # * connected
41
36
  #
42
- # @return [Boolean]
37
+ # @return [true, false]
38
+ #
43
39
  def valid?
44
40
  super and directed?
45
41
  end
@@ -1,8 +1,10 @@
1
1
  module Jumoku
2
2
  # A {RawUndirectedTree} sticks to the standard definition of trees in Graph Theory:
3
+ #
3
4
  # * undirected,
4
5
  # * connected,
5
6
  # * acyclic.
7
+ #
6
8
  # It thus uses Plexus::UndirectedGraphBuilder as its backend, which ensure the first
7
9
  # constraint.
8
10
  #
@@ -26,16 +28,8 @@ module Jumoku
26
28
  #
27
29
  def initialize(*params)
28
30
  super(*params) # Delegates to Plexus.
29
- args = (params.pop if params.last.is_a? Hash) || {}
30
- @_options = args
31
- strategies = _extract_strategies(args)
32
-
33
- super(*params) # Delegates to Plexus.
34
-
35
- class << self; self; end.module_eval do
36
- strategies.each { |strategy| include strategy }
37
- alias has_branch? has_edge?
38
- end
31
+ @_options = (params.pop if params.last.is_a? Hash) || {}
32
+ _delay { alias has_branch? has_edge? }
39
33
  end
40
34
 
41
35
  # Checks whether the tree is *really* a valid tree, that is if the
@@ -1,14 +1,13 @@
1
1
  module Jumoku
2
2
  # This module provides the basic routines needed to implement the specialized
3
- # builders: {UndirectedTreeBuilder} and {DirectedTreeBuilder}.
3
+ # builders: {RawUndirectedTreeBuilder} and {RawDirectedTreeBuilder}.
4
4
  #
5
5
  module Shared
6
- STRATEGIES = [:edge_labeling, :node_labeling]
7
-
8
6
  def self.included(base)
9
7
  base.class_eval do
10
8
  # Late aliasing as it references methods provided by Plexus modules.
11
9
  alias has_node? has_vertex?
10
+ include Jumoku::Strategies
12
11
  end
13
12
  end
14
13
 
@@ -211,16 +210,10 @@ module Jumoku
211
210
  self.class.new(self)
212
211
  end
213
212
 
214
- def _extract_strategies(options)
215
- options = options.dup.select! { |k,v| STRATEGIES.include?(k) } || options.dup
216
- options.inject([]) do |strategies, (k,v)|
217
- begin
218
- strategies << Jumoku.const_get(k.to_s.constantize).const_get(v.to_s.constantize)
219
- strategies
220
- rescue NameError # silently ignored
221
- strategies
222
- end
223
- end
213
+ # Some code chunks must be module evaled at runtime.
214
+ #
215
+ def _delay
216
+ class << self; self; end.module_eval { yield }
224
217
  end
225
218
  end
226
219
  end
@@ -1,9 +1,15 @@
1
1
  module Jumoku
2
2
  # "Raw" implementations.
3
+
4
+ # See {RawUndirectedTreeBuilder}.
3
5
  class RawUndirectedTree; include RawUndirectedTreeBuilder; end
6
+ # See {RawDirectedTreeBuilder}.
4
7
  class RawDirectedTree; include RawDirectedTreeBuilder; end
5
8
 
6
9
  # "Useful" implementations.
10
+
11
+ # See {TreeBuilder}.
7
12
  class Tree; include TreeBuilder; end
13
+ # See {ArborescenceBuilder}.
8
14
  class Arborescence; include ArborescenceBuilder; end
9
15
  end
@@ -0,0 +1,32 @@
1
+ module Jumoku
2
+ # Strategies are decorators implemented as mixins which taint a tree object
3
+ # with new or extended features. For instance, the `SimpleEdgeLabeling`
4
+ # strategy alters the way new nodes and branches are added to a tree,
5
+ # enforcing labeling of edges with increasing integers, `Binary` enforces
6
+ # a tree to grow as a binary tree, etc.
7
+ #
8
+ # It is easy to develop your own decorators and `use` them.
9
+ #
10
+ module Strategies
11
+ # Activate a strategy.
12
+ #
13
+ # @param [#to_s, Module] strategy strategy's name, either as a module
14
+ # namespace (like `Strategies::SimpleEdgeLabeling`) or a symbol (like
15
+ # `:simple_edge_labeling`)
16
+ #
17
+ def use(strategy)
18
+ strategy = catch(:unknown_strategy) do
19
+ begin
20
+ if strategy.is_a?(Symbol) || strategy.is_a?(String)
21
+ strategy = Strategies.const_get(strategy.to_s.constantize)
22
+ end
23
+ rescue NameError
24
+ throw :unknown_strategy, nil
25
+ end
26
+ strategy
27
+ end
28
+ extend strategy unless strategy.nil?
29
+ end
30
+ alias strategy use
31
+ end
32
+ end
@@ -1,6 +1,14 @@
1
1
  module Jumoku
2
- module EdgeLabeling
3
- module Backend
2
+ module Strategies
3
+ # Edge labeling strategies are concerned with the way edges or arcs are
4
+ # labeled. Simple labeling schemes are based on indexing with increasing
5
+ # integers, whereas some other strategies elaborate on notions such as
6
+ # weight or symetry.
7
+ #
8
+ # This module provides basic implementation for the common ground used
9
+ # by custom strategies.
10
+ #
11
+ module EdgeLabelingBackend
4
12
  # Sort edges by the provided block's logic. The block takes edge as
5
13
  # parameter, and this method delegates to Enumerable#sort_by for
6
14
  # sorting edges. Return unsorted edges list if no block is provided.
@@ -1,5 +1,5 @@
1
1
  module Jumoku
2
- module EdgeLabeling
2
+ module Strategies
3
3
  # A simple edge labeling strategy: as new nodes are added, new edges are
4
4
  # assigned increasing integers. Removed edges do not trigger reindexing,
5
5
  # so for a tree with n nodes, the labeling set is ||n|| but indexes are
@@ -8,8 +8,8 @@ module Jumoku
8
8
  # This simple strategy allows for using simple search algorithms as well
9
9
  # for simple ordering schemes.
10
10
  #
11
- module Simple
12
- include EdgeLabeling::Backend
11
+ module SimpleEdgeLabeling
12
+ include Strategies::EdgeLabelingBackend
13
13
 
14
14
  attr_accessor :next_simple_edge_label_number
15
15
 
@@ -1,11 +1,17 @@
1
1
  module Jumoku
2
- # Namespacing. Useful for duck typing criteria.
2
+ # Used only as a namespace. Useful for duck typing criteria: both
3
+ # `Plexus::Edge` and `Plexus::Arc` are seen as `Branch`.
4
+ #
3
5
  module Branch; end
4
6
 
7
+ # Delegates to `Plexus::Edge`.
8
+ #
5
9
  class UndirectedBranch < Plexus::Edge
6
10
  include Branch
7
11
  end
8
12
 
13
+ # Delegates to `Plexus::Arc`.
14
+ #
9
15
  class DirectedBranch < Plexus::Arc
10
16
  include Branch
11
17
  end
@@ -1,4 +1,4 @@
1
- module Jukomu
1
+ module Jumoku
2
2
  if RUBY_VERSION < "1.9"
3
3
  def ruby_18
4
4
  yield
@@ -1,8 +1,14 @@
1
1
  module Jumoku
2
+ # Generic error class.
2
3
  class JumokuError < StandardError; end
4
+ # Generic error class for raw builders.
3
5
  class RawTreeError < JumokuError; end
6
+ # Node-related error class for raw builders.
4
7
  class RawTreeNodeError < RawTreeError; end
8
+ # Raised when a branch already exist, causing an operation to abort.
5
9
  class BranchAlreadyExistError < JumokuError; end
10
+ # Raised when breaking the connexion constraint, causing an operation to abort.
6
11
  class ForbiddenCycle < JumokuError; end
12
+ # Raised when a referenced node does not exist.
7
13
  class UndefinedNode < JumokuError; end
8
14
  end
@@ -1,6 +1,6 @@
1
1
  module Jumoku
2
2
  MAJOR = 0
3
3
  MINOR = 2
4
- PATCH = 4
4
+ PATCH = 5
5
5
  VERSION = [MAJOR, MINOR, PATCH].join('.')
6
6
  end
@@ -1,8 +1,8 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe EdgeLabeling::Simple do
4
- let(:tree) { Tree.new(:edge_labeling => :simple) }
5
- let(:arbo) { Arborescence.new(:edge_labeling => :simple) }
3
+ describe Strategies::SimpleEdgeLabeling do
4
+ let(:tree) { Tree.new.use Strategies::SimpleEdgeLabeling }
5
+ let(:arbo) { Arborescence.new.use :simple_edge_labeling }
6
6
 
7
7
  describe 'a tree with simple edge labeling' do
8
8
  it "should label its edges with increasing integers when new nodes are added or removed" do
metadata CHANGED
@@ -1,8 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jumoku
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 29
4
5
  prerelease:
5
- version: 0.2.4
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 5
10
+ version: 0.2.5
6
11
  platform: ruby
7
12
  authors:
8
13
  - Jean-Denis Vauguet <jd@vauguet.fr>
@@ -10,7 +15,7 @@ autorequire:
10
15
  bindir: bin
11
16
  cert_chain: []
12
17
 
13
- date: 2011-07-12 00:00:00 +02:00
18
+ date: 2011-07-14 00:00:00 +02:00
14
19
  default_executable:
15
20
  dependencies:
16
21
  - !ruby/object:Gem::Dependency
@@ -21,6 +26,9 @@ dependencies:
21
26
  requirements:
22
27
  - - ">="
23
28
  - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
24
32
  version: "0"
25
33
  type: :runtime
26
34
  version_requirements: *id001
@@ -32,6 +40,9 @@ dependencies:
32
40
  requirements:
33
41
  - - ">="
34
42
  - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
35
46
  version: "0"
36
47
  type: :runtime
37
48
  version_requirements: *id002
@@ -43,6 +54,9 @@ dependencies:
43
54
  requirements:
44
55
  - - ">="
45
56
  - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
46
60
  version: "0"
47
61
  type: :runtime
48
62
  version_requirements: *id003
@@ -54,6 +68,9 @@ dependencies:
54
68
  requirements:
55
69
  - - ">="
56
70
  - !ruby/object:Gem::Version
71
+ hash: 3
72
+ segments:
73
+ - 0
57
74
  version: "0"
58
75
  type: :runtime
59
76
  version_requirements: *id004
@@ -65,6 +82,9 @@ dependencies:
65
82
  requirements:
66
83
  - - ">="
67
84
  - !ruby/object:Gem::Version
85
+ hash: 3
86
+ segments:
87
+ - 0
68
88
  version: "0"
69
89
  type: :development
70
90
  version_requirements: *id005
@@ -76,6 +96,9 @@ dependencies:
76
96
  requirements:
77
97
  - - ">="
78
98
  - !ruby/object:Gem::Version
99
+ hash: 3
100
+ segments:
101
+ - 0
79
102
  version: "0"
80
103
  type: :development
81
104
  version_requirements: *id006
@@ -96,7 +119,7 @@ files:
96
119
  - lib/jumoku/builders/shared.rb
97
120
  - lib/jumoku/builders/arborescence.rb
98
121
  - lib/jumoku/builders/raw_directed_tree.rb
99
- - lib/jumoku/raw_tree_node.rb
122
+ - lib/jumoku/strategies.rb
100
123
  - lib/jumoku/classes/tree_classes.rb
101
124
  - lib/jumoku/strategies/edge_labeling/simple.rb
102
125
  - lib/jumoku/strategies/edge_labeling.rb
@@ -131,12 +154,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
131
154
  requirements:
132
155
  - - ">="
133
156
  - !ruby/object:Gem::Version
157
+ hash: 3
158
+ segments:
159
+ - 0
134
160
  version: "0"
135
161
  required_rubygems_version: !ruby/object:Gem::Requirement
136
162
  none: false
137
163
  requirements:
138
164
  - - ">="
139
165
  - !ruby/object:Gem::Version
166
+ hash: 3
167
+ segments:
168
+ - 0
140
169
  version: "0"
141
170
  requirements: []
142
171
 
@@ -1,27 +0,0 @@
1
- module Jumoku
2
- module RawTree
3
- # This module describes RawTree nodes.
4
- #
5
- # You may also use this module as a mixin to have a class behave
6
- # as a RawTree node, while customizing behaviors.
7
- #
8
- module Node
9
- # Creates a new node.
10
- #
11
- # @param [#to_s, #to_sym] name a valid name, considering the :names_as options
12
- # passed to the raw tree you want to create a node for.
13
- # @param [Object] data the data field type to use. Defaults to an empty OpenObject
14
- # @param [Array(#to_s)] children an array of children nodes ids
15
- #
16
- # @return [OpenObject] the node as an OpenObject instance
17
- #
18
- def create name, data = OpenObject.new, children = []
19
- @name = name
20
- @data = data
21
- @children = children
22
-
23
- OpenObject.new(:name => @name, :data => @data, :children => @children)
24
- end
25
- end
26
- end
27
- end