Class: StateFu::Machine

Inherits:
Object show all
Includes:
HasOptions, Applicable
Defined in:
lib/machine.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from HasOptions

#[], #[]=, included

Methods included from Applicable

included

Constructor Details

#initialize(options = {}, &block) ⇒ Machine

Returns a new instance of Machine.



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

def initialize( options={}, &block )
  @states               = [].extend( StateArray  )
  @events               = [].extend( EventArray  )
  @helpers              = [].extend( HelperArray )
  @tools                = [].extend( ToolArray   )
  @named_procs          = {}
  @requirement_messages = {}
  @options              = options
  @hooks                = Hooks.for( self )
  apply!( &block ) if block_given?
end

Instance Attribute Details

#eventsObject (readonly)

Instance Methods



60
61
62
# File 'lib/machine.rb', line 60

def events
  @events
end

#helpersObject (readonly)

Instance Methods



60
61
62
# File 'lib/machine.rb', line 60

def helpers
  @helpers
end

#hooksObject (readonly)

Returns the value of attribute hooks.



11
12
13
# File 'lib/machine.rb', line 11

def hooks
  @hooks
end

#named_procsObject (readonly)

Instance Methods



60
61
62
# File 'lib/machine.rb', line 60

def named_procs
  @named_procs
end

#optionsObject (readonly)

Instance Methods



60
61
62
# File 'lib/machine.rb', line 60

def options
  @options
end

#requirement_messagesObject (readonly)

Instance Methods



60
61
62
# File 'lib/machine.rb', line 60

def requirement_messages
  @requirement_messages
end

#statesObject (readonly)

Instance Methods



60
61
62
# File 'lib/machine.rb', line 60

def states
  @states
end

#toolsObject (readonly)

Instance Methods



60
61
62
# File 'lib/machine.rb', line 60

def tools
  @tools
end

Class Method Details

.bind!(machine, owner, name, field_name) ⇒ Object

make it so that a class which has included StateFu has a binding to this machine



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/machine.rb', line 33

def self.bind!( machine, owner, name, field_name)
  name = name.to_sym
  # define an accessor method with the given name
  if owner.class == Class
    owner.state_fu_machines[name]    = machine
    owner.state_fu_field_names[name] = field_name
    # method_missing to catch NoMethodError for event methods, etc
    StateFu::MethodFactory.define_once_only_method_missing( owner )
    unless owner.respond_to?(name)
      owner.class_eval do
        define_method name do
          state_fu( name )
        end
      end
    end
    # prepare the persistence field
    StateFu::Persistence.prepare_field owner, field_name 
  else
    _binding = StateFu::Binding.new machine, owner, name, :field_name => field_name, :singleton => true 
    MethodFactory.define_singleton_method(owner, name) { _binding }
  end
end

.BINDINGSObject



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

def self.BINDINGS
  @@_bindings ||= {}
end

.for_class(klass, name, options = {}, &block) ⇒ Object

Class methods



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

def self.for_class(klass, name, options={}, &block)
  options.symbolize_keys!
  name = name.to_sym

  unless machine = klass.state_fu_machines[ name ]
    machine = new(options)
    machine.bind! klass, name, options[:field_name] 
  end
  if block_given?
    machine.apply! &block 
  end
  machine
end

Instance Method Details

#apply!(&block) ⇒ Object Also known as: lathe

merge the commands in &block with the existing machine; returns a lathe for the machine.



76
77
78
# File 'lib/machine.rb', line 76

def apply!( &block )
  StateFu::Lathe.new( self, &block )
end

#bind!(owner, name = DEFAULT, field_name = nil) ⇒ Object

make it so a class which has included StateFu has a binding to this machine



119
120
121
122
# File 'lib/machine.rb', line 119

def bind!( owner, name= DEFAULT, field_name = nil )
  field_name ||= Persistence.default_field_name( name )
  self.class.bind!(self, owner, name, field_name)
end

#deep_cloneObject Also known as: deep_copy

Marshal, the poor man’s X-Ray photocopier. TODO: a version which will not break its teeth on procs



170
171
172
# File 'lib/machine.rb', line 170

def deep_clone
  Marshal::load(Marshal.dump(self))
end

#empty?Boolean

Returns:

  • (Boolean)


124
125
126
# File 'lib/machine.rb', line 124

def empty?
  states.empty?
end

#event_namesObject



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

def event_names
  events.map(&:name)
end

#find_or_create_states_by_name(*args) ⇒ Object

given a messy bunch of symbols, find or create a list of matching States.

Raises:

  • (ArgumentError)


155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/machine.rb', line 155

def find_or_create_states_by_name( *args )
  args = args.compact.flatten
  raise ArgumentError.new( args.inspect ) unless args.all? { |a| [Symbol, StateFu::State].include? a.class }
  args.map do |s|
    unless state = states[s.to_sym]
      # TODO clean this line up
      state = s.is_a?( StateFu::State ) ? s : StateFu::State.new( self, s )
      self.states << state
    end
    state
  end
end

#graphvizObject



179
180
181
# File 'lib/machine.rb', line 179

def graphviz
  @graphviz ||= Plotter.new(self).output
end

#helper(*modules_to_add) ⇒ Object

the modules listed here will be mixed into Binding and Transition objects for this machine. use this to define methods, references or data useful to you during transitions, event hooks, or in general use of StateFu.

They can be supplied as a string/symbol (as per rails controller helpers), or a Module.

To do this globally, just duck-punch StateFu::Machine / StateFu::Binding.



107
108
109
# File 'lib/machine.rb', line 107

def helper *modules_to_add
  modules_to_add.each { |mod| helpers << mod }
end

#helper_modulesObject



81
82
83
# File 'lib/machine.rb', line 81

def helper_modules
  helpers.modules
end

#initial_stateObject



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

def initial_state()
  @initial_state ||= states.first
end

#initial_state=(s) ⇒ Object



128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/machine.rb', line 128

def initial_state=( s )
  case s
  when Symbol, String, StateFu::State
    unless init_state = states[ s.to_sym ]
      init_state = StateFu::State.new( self, s.to_sym )
      states << init_state
    end
    @initial_state = init_state
  else
    raise( ArgumentError, s.inspect )
  end
end

#inject_helpers_into(obj) ⇒ Object



85
86
87
# File 'lib/machine.rb', line 85

def inject_helpers_into( obj )
  helpers.inject_into( obj )
end

#inject_methods_into(obj) ⇒ Object



93
94
95
# File 'lib/machine.rb', line 93

def inject_methods_into( obj )
  #puts 'inject_methods_into'
end

#inject_tools_into(obj) ⇒ Object



89
90
91
# File 'lib/machine.rb', line 89

def inject_tools_into( obj )
  tools.inject_into( obj )
end

#inspectObject



175
176
177
# File 'lib/machine.rb', line 175

def inspect
  "#<#{self.class} ##{__id__} states=#{state_names.inspect} events=#{event_names.inspect} options=#{options.inspect}>"
end

#state_namesObject



145
146
147
# File 'lib/machine.rb', line 145

def state_names
  states.map(&:name)
end

#tool(*modules_to_add) ⇒ Object

same as helper, but for extending Lathes rather than the Bindings / Transitions. use this to extend the Lathe DSL to suit your problem domain.



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

def tool *modules_to_add
  modules_to_add.each { |mod| tools << mod }
end