jumoku 0.2.4 → 0.2.5

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.
@@ -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