class StateMachine::NodeCollection

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.

Attributes

machine[R]

The machine associated with the nodes

Public Class Methods

new(machine, options = {}) click to toggle source

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

   # File lib/state_machine/node_collection.rb
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"] = {}
31     indices[:"#{name}_to_sym"] = {}
32     indices
33   end
34   @default_index = Array(options[:index]).first
35   @contexts = []
36 end

Public Instance Methods

<<(node) click to toggle source

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.

   # File lib/state_machine/node_collection.rb
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
[](key, index_name = @default_index) click to toggle source

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.

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

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
    # File lib/state_machine/node_collection.rb
131 def at(index)
132   @nodes[index]
133 end
concat(nodes) click to toggle source

Appends a group of nodes to the collection

   # File lib/state_machine/node_collection.rb
96 def concat(nodes)
97   nodes.each {|node| self << node}
98 end
context(nodes, &block) click to toggle source

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.

   # File lib/state_machine/node_collection.rb
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
each() { |node| ... } click to toggle source

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 --
    # File lib/state_machine/node_collection.rb
118 def each
119   @nodes.each {|node| yield node}
120   self
121 end
fetch(key, index_name = @default_index) click to toggle source

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
    # File lib/state_machine/node_collection.rb
164 def fetch(key, index_name = @default_index)
165   self[key, index_name] || raise(IndexError, "#{key.inspect} is an invalid #{index_name}")
166 end
keys(index_name = @default_index) click to toggle source

Gets the set of unique keys for the given index

   # File lib/state_machine/node_collection.rb
68 def keys(index_name = @default_index)
69   index(index_name).keys
70 end
length() click to toggle source

Gets the number of nodes in this collection

   # File lib/state_machine/node_collection.rb
63 def length
64   @nodes.length
65 end
machine=(new_machine) click to toggle source

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

   # File lib/state_machine/node_collection.rb
57 def machine=(new_machine)
58   @machine = new_machine
59   each {|node| node.machine = new_machine}
60 end
update(node) click to toggle source

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.

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

Protected Instance Methods

add_to_index(name, key, node) click to toggle source

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

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

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

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

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

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

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

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

Determines whether the given value can be converted to a symbol

    # File lib/state_machine/node_collection.rb
212 def to_sym?(value)
213   "#{value}" != ''
214 end
update_index(name, node) click to toggle source

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

    # File lib/state_machine/node_collection.rb
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
value(node, attribute) click to toggle source

Gets the value for the given attribute on the node

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