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.



78
79
80
81
82
83
84
85
86
87
88
# File 'lib/machine.rb', line 78

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



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

def events
  @events
end

#helpersObject (readonly)

Instance Methods



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

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



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

def named_procs
  @named_procs
end

#optionsObject (readonly)

Instance Methods



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

def options
  @options
end

#requirement_messagesObject (readonly)

Instance Methods



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

def requirement_messages
  @requirement_messages
end

#statesObject (readonly)

Instance Methods



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

def states
  @states
end

#toolsObject (readonly)

Instance Methods



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

def tools
  @tools
end

Class Method Details

.bind!(machine, owner, name, options = {}) ⇒ Object

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



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/machine.rb', line 32

def self.bind!(machine, owner, name, options={})
  name = name.to_sym
  options[:define_methods] = (name == DEFAULT) unless options.symbolize_keys!.has_key?(:define_methods)
  options[:field_name] ||= Persistence.default_field_name(name)
  options[:singleton] = true unless owner.is_a?(Class)
  # define an accessor method with the given name
  if options[:singleton]
    _binding = StateFu::Binding.new machine, owner, name, options
    MethodFactory.define_singleton_method(owner, name) { _binding }
    if alias_name = options[:alias] || options[:as]
      MethodFactory.define_singleton_method(owner, alias_name) { _binding }
    end
  else
    klass = owner
    klass.state_fu_machines[name] = machine
    klass.state_fu_options[name]  = options

    # prepare the state machine accessor method
    if owner.respond_to? name
      raise "FIXME " + name
    else
      klass.class_eval do
        define_method name do
          state_fu name
        end
        # allow aliases to be set up, e.g. machine(:as => :status)
        if alias_name = options[:alias] || options[:as]
          alias_method alias_name, name
        end
      end
    end

    # prepare event / state class methods
    StateFu::MethodFactory.prepare_class_machine klass, machine, name, options

    # prepare the persistence field
    StateFu::Persistence.prepare_field owner, options[:field_name]
  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
# 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)
  end
  if block_given?
    machine.apply! &block
  end
  machine.bind! klass, name, options
  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.



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

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

#bind!(owner, name = DEFAULT, options = {}) ⇒ Object

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



131
132
133
# File 'lib/machine.rb', line 131

def bind!(owner, name=DEFAULT, options={})
  self.class.bind!(self, owner, name, options)
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



181
182
183
# File 'lib/machine.rb', line 181

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

#empty?Boolean

Returns:

  • (Boolean)


135
136
137
# File 'lib/machine.rb', line 135

def empty?
  states.empty?
end

#event_namesObject



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

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)


166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/machine.rb', line 166

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



190
191
192
# File 'lib/machine.rb', line 190

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.



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

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

#helper_modulesObject



97
98
99
# File 'lib/machine.rb', line 97

def helper_modules
  helpers.modules
end

#initial_stateObject



152
153
154
# File 'lib/machine.rb', line 152

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

#initial_state=(s) ⇒ Object



139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/machine.rb', line 139

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



101
102
103
# File 'lib/machine.rb', line 101

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

#inject_tools_into(obj) ⇒ Object



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

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

#inspectObject



186
187
188
# File 'lib/machine.rb', line 186

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

#state_namesObject



156
157
158
# File 'lib/machine.rb', line 156

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.



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

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