Class: ConfigToolkit::HashArrayVisitor

Inherits:
Object
  • Object
show all
Defined in:
lib/configtoolkit/hasharrayvisitor.rb

Overview

This class allows each node of an Array or Hash containing arbitrarily deeply nested Arrays or Hashes to be visited. Users extend this class for a particular task and override some or all of the visit_*, enter_*, and leave_* methods to accomplish the task. They then call the visit method in order to visit the nodes of a structure.

This class was written in order to factor out (fairly complicated) iteration logic from methods that need to process each element of one of these structures. Such methods almost invariably require a stack to store information for all levels above the level currently being processed. Previously, when these methods recursively iterated over these structures, they used the call stack for this purpose. The HashArrayVisitor, however, provides its own stack. All of the enter_* methods are expected to return a new stack frame for the container being entered, which will be accessible via the stack_top method while processing elements of the container. When the leave_* method is called for the container, the associated stack frame will be popped from the top of the stack and passed to the leave_* method. An initial stack frame can be specified in new, which often is handy as it ensures that there always will be at least one element in the stack. The HashArrayVisitor base class has no knowledge of the contents of a stack frame, and so the stack frames can be of any type (even nil if no stack is required).

PrettyPrintWriter, YAMLWriter, and KeyValueWriter all use this class.

Instance Method Summary collapse

Constructor Details

#initialize(initial_stack_frame = nil) ⇒ HashArrayVisitor

Description:

This constructs the visitor with initial stack frame initial_stack_frame.

Parameters:

initial_stack_frame

The initial stack frame; if not specified, then the stack initially will be empty. Specifying an initial stack frame ensures that the stack never will be empty while visiting a structure (this frame never will be popped).



44
45
46
47
48
49
50
# File 'lib/configtoolkit/hasharrayvisitor.rb', line 44

def initialize(initial_stack_frame = nil)
  @stack = []

  if(initial_stack_frame != nil)
    @stack.push(initial_stack_frame)
  end
end

Instance Method Details

#enter_array(array) ⇒ Object

Description:

This method is called when an Array is visited for the first time. It must return the new stack frame for the Array.

This can be overridden; the default implementation is a no-op and returns nil.

Parameters:

array

The Array being visited

Returns:

The new stack frame for the Array



200
201
202
# File 'lib/configtoolkit/hasharrayvisitor.rb', line 200

def enter_array(array)
  return nil
end

#enter_hash(hash) ⇒ Object

Description:

This method is called when a Hash is visited for the first time. It must return the new stack frame for the Hash.

This can be overridden; the default implementation is a no-op and returns nil.

Parameters:

hash

The Hash being visited

Returns:

The new stack frame for the Hash



141
142
143
# File 'lib/configtoolkit/hasharrayvisitor.rb', line 141

def enter_hash(hash)
  return nil
end

#leave_array(popped_stack_frame) ⇒ Object

Description:

This method is called when finished visiting all elements of an Array. The stack will be popped before the call (so stack_top will return the frame for the Array’s parent), but the popped stack frame is passed as popped_stack_frame.

This can be overridden; the default implementation is a no-op.

Parameters:

popped_stack_frame

The stack frame associated with the Array, which has been popped from the stack.



218
219
# File 'lib/configtoolkit/hasharrayvisitor.rb', line 218

def leave_array(popped_stack_frame)
end

#leave_hash(popped_stack_frame) ⇒ Object

Description:

This method is called when finished visiting all elements of a Hash. The stack will be popped before the call (so stack_top will return the frame for the Hash’s parent), but the popped stack frame is passed as popped_stack_frame.

This can be overridden; the default implementation is a no-op.

Parameters:

popped_stack_frame

The stack frame associated with the Hash, which has been popped from the stack.



159
160
# File 'lib/configtoolkit/hasharrayvisitor.rb', line 159

def leave_hash(popped_stack_frame)
end

#stack_empty?Boolean

Returns:

True if and only if the stack is empty

Returns:



122
123
124
# File 'lib/configtoolkit/hasharrayvisitor.rb', line 122

def stack_empty?
  return @stack.empty?()
end

#stack_topObject

Returns:

The top stack frame



114
115
116
# File 'lib/configtoolkit/hasharrayvisitor.rb', line 114

def stack_top
  return @stack[-1]
end

#visit(structure) ⇒ Object

Description:

This method visits the nodes of structure, calling the appropriate method for each node.

Parameters:

structure

The Hash or Array whose nodes are to be visited (if structure‘s nodes themselves are Arrays or Hashes, the nodes of these nested structures also will be visited).



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/configtoolkit/hasharrayvisitor.rb', line 72

def visit(structure)
  if(structure.is_a?(Hash))
    @stack.push(enter_hash(structure))

    structure.each_with_index do |key_value, index|
      key = key_value[0]
      value = key_value[1]

      is_container_value = container_value?(value)
      visit_hash_element(key, value, index, is_container_value)

      if(is_container_value)
        visit(value)
      end
    end

    leave_hash(@stack.pop())
  elsif(structure.is_a?(Array))
    @stack.push(enter_array(structure))
    
    structure.each_with_index do |element, index|
      is_container_value = container_value?(element)
      visit_array_element(element, index, is_container_value)

      if(is_container_value)
        visit(element)
      end
    end

    leave_array(@stack.pop())
  else
    message  = "The argument to HashArrayVisitor::visit must be a "
    message << "Hash or Array; instead, a #{structure.class} "
    message << "(#{structure}) was passed!"
    raise ArgumentError, message
  end
end

#visit_array_element(value, index, is_container_value) ⇒ Object

Description:

This method is called when visiting an Array element value at index in the Array.

This can be overridden; the default implementation is a no-op.

Parameters:

value

The Array element

index

The index of value in the Array

is_container_value

Whether or not value is a container (in which case, it will be visited next with a call to enter_array or enter_hash)



238
239
# File 'lib/configtoolkit/hasharrayvisitor.rb', line 238

def visit_array_element(value, index, is_container_value)
end

#visit_hash_element(key, value, index, is_container_value) ⇒ Object

Description:

This method is called when visiting a Hash element mapping key to value.

This can be overridden; the default implementation is a no-op.

Parameters:

key

The key in the Hash element

value

The value in the Hash element

index

The index of key=>value in the Hash (the index that is returned by each_with_index)

is_container_value

Whether or not value is a container (in which case, it will be visited next with a call to enter_array or enter_hash)



182
183
# File 'lib/configtoolkit/hasharrayvisitor.rb', line 182

def visit_hash_element(key, value, index, is_container_value)
end