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.

[View source]

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

[View source]

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

[View source]

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

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

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

Class methods

[View source]

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.

[View source]

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

[View source]

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

[View source]

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

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

#empty?Boolean

Returns:

  • (Boolean)
[View source]

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

def empty?
  states.empty?
end

#event_namesObject

[View source]

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)
[View source]

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

[View source]

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.

[View source]

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

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

#helper_modulesObject

[View source]

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

def helper_modules
  helpers.modules
end

#initial_stateObject

[View source]

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

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

#initial_state=(s) ⇒ Object

[View source]

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

[View source]

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

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

#inject_methods_into(obj) ⇒ Object

[View source]

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

def inject_methods_into( obj )
  #puts 'inject_methods_into'
end

#inject_tools_into(obj) ⇒ Object

[View source]

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

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

#inspectObject

[View source]

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

[View source]

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.

[View source]

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

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