Class: Traversal::Description

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Enumerable
Defined in:
lib/traversal/description.rb

Overview

Traversal description

Defined Under Namespace

Classes: EmptyArgument

Constant Summary collapse

DEPTH_FIRST =
0
BREADTH_FIRST =
1

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeDescription

Create blank traversal description



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/traversal/description.rb', line 22

def initialize
  @exclude        = []
  @include_only   = []

  @prune          = []
  @expand_only    = []
  @stop_before    = []
  @stop_after     = []

  @start_node     = nil
  @relations      = []

  @order          = DEPTH_FIRST
  @uniq           = true
end

Instance Attribute Details

#relationsObject (readonly)

Returns the value of attribute relations.



16
17
18
# File 'lib/traversal/description.rb', line 16

def relations
  @relations
end

#start_nodeObject (readonly)

Returns the value of attribute start_node.



16
17
18
# File 'lib/traversal/description.rb', line 16

def start_node
  @start_node
end

Instance Method Details

#==(other) ⇒ Object

Tests equality of traversal descriptions



39
40
41
42
43
44
45
46
47
# File 'lib/traversal/description.rb', line 39

def ==(other)
  return super unless other.is_a?(Description)

  [:@start_node, :@relations, :@include_only,
   :@exclude, :@prune, :@stop_before, :@uniq,
   :@stop_after, :@order, :@expand_only].all? do |sym|
    instance_variable_get(sym) == other.instance_variable_get(sym)
  end
end

#breadth_firstObject

Declare traversal order strategy as breadth first



127
128
129
# File 'lib/traversal/description.rb', line 127

def breadth_first
  tap { @order = BREADTH_FIRST }
end

#breadth_first?Boolean

:nodoc:

Returns:

  • (Boolean)


177
178
179
# File 'lib/traversal/description.rb', line 177

def breadth_first? #:nodoc:
  @order == BREADTH_FIRST
end

#depth_firstObject

Declare traversal order strategy as depth first



122
123
124
# File 'lib/traversal/description.rb', line 122

def depth_first
  tap { @order = DEPTH_FIRST }
end

#eachObject

Iterate through nodes defined by DSL and optionally execute given block for each node.



138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/traversal/description.rb', line 138

def each # :yields: node
  assert_complete_description

  iter = Traversal::Iterator.new(self)

  if iterator?
    iter.each do |node|
      yield node
    end
  else
    iter
  end
end

#exclude(*nodes, &blk) ⇒ Object

Declare exclude condition. Which nodes you want to exclude (ignore them but not their relations) from your traversal?



77
78
79
# File 'lib/traversal/description.rb', line 77

def exclude(*nodes, &blk)
  tap { @exclude << condition(*nodes, &blk) }
end

#exclude_and_prune(*nodes, &blk) ⇒ Object Also known as: prune_and_exclude

Declare exclude AND prune condition. Matching node and its relations will be excluded from traversal.



103
104
105
106
# File 'lib/traversal/description.rb', line 103

def exclude_and_prune(*nodes, &blk)
  exclude(*nodes, &blk)
  prune(*nodes, &blk)
end

#exclude_node?(node) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


164
165
166
# File 'lib/traversal/description.rb', line 164

def exclude_node?(node) #:nodoc:
  !include_node?(node)
end

#expand_node?(node) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


168
169
170
171
# File 'lib/traversal/description.rb', line 168

def expand_node?(node) #:nodoc:
  @expand_only.all? { |cond| cond[node] } &&
  @prune.none? { |cond| cond[node] }
end

#expand_only(*nodes, &blk) ⇒ Object

Declare which nodes you want to expand. Others will be pruned.



88
89
90
# File 'lib/traversal/description.rb', line 88

def expand_only(*nodes, &blk)
  tap { @expand_only << condition(*nodes, &blk) }
end

#follow(*relations, &blk) ⇒ Object

Declare which relation you want to follow in your traversal. It can be Symbol, Method, Proc or block.

Relation should return something enumerable, otherwise it will be ignored in traversal.

Example

traversal.follow(:children)               # for each node will call node#children method
traversal.follow { |node| node.children } # same effect

Raises:

  • (ArgumentError)


62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/traversal/description.rb', line 62

def follow(*relations, &blk)
  raise ArgumentError, 'arguments or block expected' if relations.empty? && !block_given?

  tap do
    relations << blk if block_given?

    relations.each do |relation|
      @relations << condition(relation)
    end
    #@relations = condition(*relations, &blk)
  end
end

#include_node?(node) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


159
160
161
162
# File 'lib/traversal/description.rb', line 159

def include_node?(node) #:nodoc:
  @include_only.all? { |cond| cond[node] } &&
  @exclude.none? { |cond| cond[node] }
end

#include_only(*nodes, &blk) ⇒ Object Also known as: exclude_unless

Declare inverted exclude condition. Which nodes you want to keep?



82
83
84
# File 'lib/traversal/description.rb', line 82

def include_only(*nodes, &blk)
  tap { @include_only << condition(*nodes, &blk) }
end

#prune(*nodes, &blk) ⇒ Object

Declare prune condition. Which nodes relations you want to ignore?

Example:

traversal.follow(:children).
prune { |node| node.name == "A" } # node "A" will be included, but not its children


97
98
99
# File 'lib/traversal/description.rb', line 97

def prune(*nodes, &blk)
  tap { @prune << condition(*nodes, &blk) }
end

#prune_node?(node) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


173
174
175
# File 'lib/traversal/description.rb', line 173

def prune_node?(node) #:nodoc:
  !expand_node?(node)
end

#stop?(node, type = :before) ⇒ Boolean

Does node matches one of stop conditions?

Returns:

  • (Boolean)


155
156
157
# File 'lib/traversal/description.rb', line 155

def stop?(node, type = :before) #:nodoc:
  (type == :after ? @stop_after : @stop_before).any? { |cond| cond[node] }
end

#stop_after(*nodes, &blk) ⇒ Object

Declare stop post-condition. When met, matched node will be included in traversal and iteration will be stopped.



117
118
119
# File 'lib/traversal/description.rb', line 117

def stop_after(*nodes, &blk)
  tap { @stop_after << condition(*nodes, &blk) }
end

#stop_before(*nodes, &blk) ⇒ Object

Declare stop pre-condition. When met, matched node will be excluded from traversal and iteration will be stopped.



111
112
113
# File 'lib/traversal/description.rb', line 111

def stop_before(*nodes, &blk)
  tap { @stop_before << condition(*nodes, &blk) }
end

#traverse(start_node) ⇒ Object

Declare a traversal start point. From which node you want to follow relations?



50
51
52
# File 'lib/traversal/description.rb', line 50

def traverse(start_node)
  tap { @start_node = start_node }
end

#uniq(v = true) ⇒ Object

Set uniqueness behaviour By default it is set to true



133
134
135
# File 'lib/traversal/description.rb', line 133

def uniq(v = true)
  tap { @uniq = !!v }
end

#uniq?Boolean

:nodoc:

Returns:

  • (Boolean)


181
182
183
# File 'lib/traversal/description.rb', line 181

def uniq? #:nodoc:
  @uniq
end