Class StateMachine::NodeCollection
In: lib/state_machine/node_collection.rb
Parent: Object

Represents a collection of nodes in a state machine, be it events or states. Nodes will not differentiate between the String and Symbol versions of the values being indexed.

Methods

<<   []   add_to_index   at   concat   context   each   eval_context   fetch   index   keys   length   machine=   new   remove_from_index   to_sym?   update   update_index   value  

Included Modules

Enumerable Assertions

Attributes

machine  [R]  The machine associated with the nodes

Public Class methods

Creates a new collection of nodes for the given state machine. By default, the collection is empty.

Configuration options:

  • :index - One or more attributes to automatically generate hashed indices for in order to perform quick lookups. Default is to index by the :name attribute

[Source]

    # File lib/state_machine/node_collection.rb, line 21
21:     def initialize(machine, options = {})
22:       assert_valid_keys(options, :index)
23:       options = {:index => :name}.merge(options)
24:       
25:       @machine = machine
26:       @nodes = []
27:       @index_names = Array(options[:index])
28:       @indices = @index_names.inject({}) do |indices, name|
29:         indices[name] = {}
30:         indices["#{name}_to_s""#{name}_to_s"] = {}
31:         indices["#{name}_to_sym""#{name}_to_sym"] = {}
32:         indices
33:       end
34:       @default_index = Array(options[:index]).first
35:       @contexts = []
36:     end

Public Instance methods

Adds a new node to the collection. By doing so, this will also add it to the configured indices. This will also evaluate any existings contexts that match the new node.

[Source]

    # File lib/state_machine/node_collection.rb, line 88
88:     def <<(node)
89:       @nodes << node
90:       @index_names.each {|name| add_to_index(name, value(node, name), node)}
91:       @contexts.each {|context| eval_context(context, node)}
92:       self
93:     end

Gets the node indexed by the given key. By default, this will look up the key in the first index configured for the collection. A custom index can be specified like so:

  collection['parked', :value]

The above will look up the "parked" key in a hash indexed by each node‘s value attribute.

If the key cannot be found, then nil will be returned.

[Source]

     # File lib/state_machine/node_collection.rb, line 145
145:     def [](key, index_name = @default_index)
146:       self.index(index_name)[key] ||
147:       self.index("#{index_name}_to_s""#{index_name}_to_s")[key.to_s] ||
148:       to_sym?(key) && self.index("#{index_name}_to_sym""#{index_name}_to_sym")["#{key}""#{key}"] ||
149:       nil
150:     end

Gets the node at the given index.

  states = StateMachine::NodeCollection.new
  states << StateMachine::State.new(machine, :parked)
  states << StateMachine::State.new(machine, :idling)

  states.at(0).name    # => :parked
  states.at(1).name    # => :idling

[Source]

     # File lib/state_machine/node_collection.rb, line 131
131:     def at(index)
132:       @nodes[index]
133:     end

Appends a group of nodes to the collection

[Source]

    # File lib/state_machine/node_collection.rb, line 96
96:     def concat(nodes)
97:       nodes.each {|node| self << node}
98:     end

Tracks a context that should be evaluated for any nodes that get added which match the given set of nodes. Matchers can be used so that the context can get added once and evaluated after multiple adds.

[Source]

    # File lib/state_machine/node_collection.rb, line 75
75:     def context(nodes, &block)
76:       nodes = nodes.first.is_a?(Matcher) ? nodes.first : WhitelistMatcher.new(nodes)
77:       @contexts << context = {:nodes => nodes, :block => block}
78:       
79:       # Evaluate the new context for existing nodes
80:       each {|node| eval_context(context, node)}
81:       
82:       context
83:     end

Calls the block once for each element in self, passing that element as a parameter.

  states = StateMachine::NodeCollection.new
  states << StateMachine::State.new(machine, :parked)
  states << StateMachine::State.new(machine, :idling)
  states.each {|state| puts state.name, ' -- '}

…produces:

  parked -- idling --

[Source]

     # File lib/state_machine/node_collection.rb, line 118
118:     def each
119:       @nodes.each {|node| yield node}
120:       self
121:     end

Gets the node indexed by the given key. By default, this will look up the key in the first index configured for the collection. A custom index can be specified like so:

  collection['parked', :value]

The above will look up the "parked" key in a hash indexed by each node‘s value attribute.

If the key cannot be found, then an IndexError exception will be raised:

  collection['invalid', :value]   # => IndexError: "invalid" is an invalid value

[Source]

     # File lib/state_machine/node_collection.rb, line 164
164:     def fetch(key, index_name = @default_index)
165:       self[key, index_name] || raise(IndexError, "#{key.inspect} is an invalid #{index_name}")
166:     end

Gets the set of unique keys for the given index

[Source]

    # File lib/state_machine/node_collection.rb, line 68
68:     def keys(index_name = @default_index)
69:       index(index_name).keys
70:     end

Gets the number of nodes in this collection

[Source]

    # File lib/state_machine/node_collection.rb, line 63
63:     def length
64:       @nodes.length
65:     end

Changes the current machine associated with the collection. In turn, this will change the state machine associated with each node in the collection.

[Source]

    # File lib/state_machine/node_collection.rb, line 57
57:     def machine=(new_machine)
58:       @machine = new_machine
59:       each {|node| node.machine = new_machine}
60:     end

Updates the indexed keys for the given node. If the node‘s attribute has changed since it was added to the collection, the old indexed keys will be replaced with the updated ones.

[Source]

     # File lib/state_machine/node_collection.rb, line 103
103:     def update(node)
104:       @index_names.each {|name| update_index(name, node)}
105:     end

Protected Instance methods

Adds the given key / node combination to an index, including the string and symbol versions of the index

[Source]

     # File lib/state_machine/node_collection.rb, line 183
183:       def add_to_index(name, key, node)
184:         index(name)[key] = node
185:         index("#{name}_to_s""#{name}_to_s")[key.to_s] = node
186:         index("#{name}_to_sym""#{name}_to_sym")["#{key}""#{key}"] = node if to_sym?(key)
187:       end

Evaluates the given context for a particular node. This will only evaluate the context if the node matches.

[Source]

     # File lib/state_machine/node_collection.rb, line 218
218:       def eval_context(context, node)
219:         node.context(&context[:block]) if context[:nodes].matches?(node.name)
220:       end

Gets the given index. If the index does not exist, then an ArgumentError is raised.

[Source]

     # File lib/state_machine/node_collection.rb, line 171
171:       def index(name)
172:         raise ArgumentError, 'No indices configured' unless @indices.any?
173:         @indices[name] || raise(ArgumentError, "Invalid index: #{name.inspect}")
174:       end

Removes the given key from an index, including the string and symbol versions of the index

[Source]

     # File lib/state_machine/node_collection.rb, line 191
191:       def remove_from_index(name, key)
192:         index(name).delete(key)
193:         index("#{name}_to_s""#{name}_to_s").delete(key.to_s)
194:         index("#{name}_to_sym""#{name}_to_sym").delete("#{key}""#{key}") if to_sym?(key)
195:       end

Determines whether the given value can be converted to a symbol

[Source]

     # File lib/state_machine/node_collection.rb, line 212
212:       def to_sym?(value)
213:         "#{value}" != ''
214:       end

Updates the node for the given index, including the string and symbol versions of the index

[Source]

     # File lib/state_machine/node_collection.rb, line 199
199:       def update_index(name, node)
200:         index = self.index(name)
201:         old_key = RUBY_VERSION < '1.9' ? index.index(node) : index.key(node)
202:         new_key = value(node, name)
203:         
204:         # Only replace the key if it's changed
205:         if old_key != new_key
206:           remove_from_index(name, old_key)
207:           add_to_index(name, new_key, node)
208:         end
209:       end

Gets the value for the given attribute on the node

[Source]

     # File lib/state_machine/node_collection.rb, line 177
177:       def value(node, attribute)
178:         node.send(attribute)
179:       end

[Validate]