Class: StateFu::Binding

Inherits:
Object show all
Defined in:
lib/binding.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(machine, object, method_name, options = {}) ⇒ Binding

the constructor should not be called manually; a binding is returned when an instance of a class with a StateFu::Machine calls:

instance.#state_fu (for the default machine which is called :state_fu), instance.#state_fu( :<machine_name> ) ,or instance.#<machine_name>



15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/binding.rb', line 15

def initialize( machine, object, method_name, options={} )
  @machine       = machine
  @object        = object
  @method_name   = method_name
  @transitions   = []
  @options       = options.symbolize_keys!
  @target        = singleton? ? object : object.class
  @field_name    = options[:field_name] || @target.state_fu_field_names[method_name]
  @persister     = Persistence.for( self )

  # define event methods on this binding and its @object
  MethodFactory.new( self ).install!
  @machine.helpers.inject_into( self )
end

Instance Attribute Details

#field_nameObject (readonly)

Returns the value of attribute field_name.



4
5
6
# File 'lib/binding.rb', line 4

def field_name
  @field_name
end

#machineObject (readonly) Also known as: workflow, state_machine

Returns the value of attribute machine.



4
5
6
# File 'lib/binding.rb', line 4

def machine
  @machine
end

#method_nameObject (readonly)

Returns the value of attribute method_name.



4
5
6
# File 'lib/binding.rb', line 4

def method_name
  @method_name
end

#objectObject (readonly) Also known as: o, obj, model, instance

Returns the value of attribute object.



4
5
6
# File 'lib/binding.rb', line 4

def object
  @object
end

#optionsObject (readonly)

Returns the value of attribute options.



4
5
6
# File 'lib/binding.rb', line 4

def options
  @options
end

#persisterObject (readonly)

Returns the value of attribute persister.



4
5
6
# File 'lib/binding.rb', line 4

def persister
  @persister
end

#targetObject (readonly)

Returns the value of attribute target.



4
5
6
# File 'lib/binding.rb', line 4

def target
  @target
end

#transitions(opts = {}) ⇒ Object (readonly)

transition validation



113
114
115
# File 'lib/binding.rb', line 113

def transitions
  @transitions
end

Instance Method Details

#==(other) ⇒ Object

let’s be == (and hence ===) the current_state_name as a symbol. a nice little convenience.



249
250
251
252
253
254
255
# File 'lib/binding.rb', line 249

def == other
  if other.respond_to?( :to_sym ) && current_state
    current_state_name == other.to_sym || super( other )
  else
    super( other )
  end
end

#can_transition?(event, target = nil, *args) ⇒ Boolean

event_name? [target], *args

Returns:

  • (Boolean)


75
76
77
78
79
80
81
82
83
# File 'lib/binding.rb', line 75

def can_transition?(event, target=nil, *args)
  begin
    if t = find_transition(event, target, *args) 
      t.valid?(*args)
    end
  rescue IllegalTransition, UnknownTarget
    nil
  end      
end

#current_stateObject Also known as: now, state

the current State



43
44
45
# File 'lib/binding.rb', line 43

def current_state
  persister.current_state
end

#current_state_nameObject Also known as: name, state_name, to_sym

the name, as a Symbol, of the binding’s current_state



50
51
52
53
54
55
56
# File 'lib/binding.rb', line 50

def current_state_name
  begin
    current_state.name.to_sym
  rescue NoMethodError
    nil
  end
end

#cycle(event_or_array = nil, *args, &block) ⇒ Object

if there is one possible cyclical event, return a transition there otherwise, maybe we got an event name as an argument?



198
199
200
201
202
203
204
205
# File 'lib/binding.rb', line 198

def cycle(event_or_array=nil, *args, &block)
  if event_or_array.nil?
    transitions.cyclic.with(*args, &block).singular ||
      transitions.cyclic.with(*args, &block).valid.singular
  else
    transitions.cyclic.with(*args, &block).find(event_or_array)
  end
end

#cycle!(event_or_array = nil, *args, &block) ⇒ Object

if there is a single possible cycle() transition, fire and return it otherwise raise an IllegalTransition



209
210
211
212
213
214
215
# File 'lib/binding.rb', line 209

def cycle!(event_or_array=nil, *args, &block )
  returning cycle(event_or_array, *args, &block ) do |t|
    raise TransitionNotFound.new( self, transitions.cyclic.with(*args,&block), "Cannot cycle! unless there is exactly one cyclic event") \
      if t.nil?
    t.fire!
  end
end

#cycle?(event_or_array = nil, *args) ⇒ Boolean

if there is one possible cyclical event, evaluate its requirements (true/false), else nil

Returns:

  • (Boolean)


219
220
221
222
223
# File 'lib/binding.rb', line 219

def cycle?(event_or_array=nil, *args )
  if t = cycle(event_or_array, *args )
    t.requirements_met?
  end
end

#eventsObject Also known as: events_from_current_state

returns a list of Events which can fire from the current_state



96
97
98
99
100
# File 'lib/binding.rb', line 96

def events
  machine.events.select do |e|
    e.can_transition_from? current_state
  end.extend EventArray
end

#find_transition(event, target = nil, *args) ⇒ Object

event_name [target], *args



67
68
69
70
71
# File 'lib/binding.rb', line 67

def find_transition(event, target=nil, *args)
  target ||= args.last[:to].to_sym rescue nil      
  query = transitions.for_event(event).to(target).with(*args)
  query.find || query.valid.singular || nil 
end

#fire_transition!(event, target = nil, *args) ⇒ Object

event_name! [target], *args



87
88
89
# File 'lib/binding.rb', line 87

def fire_transition!(event, target=nil, *args)
  find_transition(event, target, *args).fire!
end

#inspectObject

display something sensible that doesn’t take up the whole screen



237
238
239
240
241
242
243
244
245
# File 'lib/binding.rb', line 237

def inspect
  '<#' + self.class.to_s + ' ' +
    attrs = [[:current_state, state_name.inspect],
             [:object_type , @object.class],
             [:method_name , method_name.inspect],
             [:field_name  , field_name.inspect],
             [:machine     , machine.to_s]].
    map {|x| x.join('=') }.join( " " ) + '>'
end

#invalid_events(*args) ⇒ Object



129
130
131
# File 'lib/binding.rb', line 129

def invalid_events(*args)
  (events - valid_events(*args)).extend StateArray
end

#next!(*args, &block) ⇒ Object Also known as: next_transition!, next_event!, next_state!

if there is a next_transition, create, fire & return it otherwise raise an IllegalTransition



172
173
174
175
176
177
178
# File 'lib/binding.rb', line 172

def next!( *args, &block )
  if t = next_transition( *args, &block )
    t.fire!
  else
    raise TransitionNotFound.new( self, valid_transitions(*args), "Exactly 1 valid transition required.")
  end
end

#next?(*args, &block) ⇒ Boolean

if there is a next_transition, return true / false depending on whether its requirements are met otherwise, nil

Returns:

  • (Boolean)


186
187
188
189
190
# File 'lib/binding.rb', line 186

def next?( *args, &block )
  if t = next_transition( *args, &block )
    t.requirements_met?
  end
end

#next_event(*args) ⇒ Object

if there is exactly one event which is valid with the given optional arguments, return it



166
167
168
# File 'lib/binding.rb', line 166

def next_event( *args )
  transitions.with(*args, &block).next_event
end

#next_state(*args, &block) ⇒ Object

if there is exactly one state reachable via a transition which is valid with the given optional arguments, return it.



160
161
162
# File 'lib/binding.rb', line 160

def next_state(*args, &block)
  transitions.with(*args, &block).next_state 
end

#next_statesObject

all states which can be reached from the current_state. Does not check transition requirements, etc.



105
106
107
# File 'lib/binding.rb', line 105

def next_states
  events.map(&:targets).compact.flatten.uniq.extend StateArray
end

#next_transition(*args, &block) ⇒ Object

if there is exactly one legal & valid transition which can be fired with the given (optional) arguments, return it.



149
150
151
# File 'lib/binding.rb', line 149

def next_transition( *args, &block )
  transitions.with(*args, &block).next
end

#next_transition_excluding_cycles(*args, &block) ⇒ Object

as above but ignoring any transitions whose origin and target are the same



154
155
156
# File 'lib/binding.rb', line 154

def next_transition_excluding_cycles( *args, &block )
  transitions.not_cyclic.with(*args, &block).next
end

#reloadObject

SPECME DOCME OR KILLME



264
265
266
267
268
269
270
# File 'lib/binding.rb', line 264

def reload()
  if persister.is_a?( Persistence::ActiveRecord )
    object.reload
  end
  persister.reload
  self
end

#singleton?Boolean

TODO better name is this a binding unique to a specific instance (not bound to a class)?

Returns:

  • (Boolean)


259
260
261
# File 'lib/binding.rb', line 259

def singleton?
  options[:singleton]
end

#teleport!(target) ⇒ Object

change the current state of the binding without any requirements or other sanity checks, or any hooks firing. Useful for test / spec scenarios, and abusing the framework.



232
233
234
# File 'lib/binding.rb', line 232

def teleport!( target )
  persister.current_state=( machine.states[target] )
end

#transition(event_or_array, *args, &block) ⇒ Object

initializes a new Transition to the given destination, with the given *args (to be passed to requirements and hooks).

If a block is given, it yields the Transition or is executed in its evaluation context, depending on the arity of the block.



139
140
141
# File 'lib/binding.rb', line 139

def transition( event_or_array, *args, &block )
  return transitions.with(*args, &block).find(event_or_array)
end

#valid_events(*args) ⇒ Object



125
126
127
# File 'lib/binding.rb', line 125

def valid_events(*args)
  valid_transitions(*args).events
end

#valid_next_states(*args) ⇒ Object



121
122
123
# File 'lib/binding.rb', line 121

def valid_next_states(*args)
  valid_transitions(*args).targets
end

#valid_transitions(*args) ⇒ Object



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

def valid_transitions(*args)
  transitions.valid.with(*args)
end