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.



71
72
73
74
75
76
77
78
79
80
81
# File 'lib/machine.rb', line 71

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



69
70
71
# File 'lib/machine.rb', line 69

def events
  @events
end

#helpersObject (readonly)

Instance Methods



69
70
71
# File 'lib/machine.rb', line 69

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



69
70
71
# File 'lib/machine.rb', line 69

def named_procs
  @named_procs
end

#optionsObject (readonly)

Instance Methods



69
70
71
# File 'lib/machine.rb', line 69

def options
  @options
end

#requirement_messagesObject (readonly)

Instance Methods



69
70
71
# File 'lib/machine.rb', line 69

def requirement_messages
  @requirement_messages
end

#statesObject (readonly)

Instance Methods



69
70
71
# File 'lib/machine.rb', line 69

def states
  @states
end

#toolsObject (readonly)

Instance Methods



69
70
71
# File 'lib/machine.rb', line 69

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
# 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
    owner.state_fu_machines[name] = machine
    owner.state_fu_options[name]  = options
    # 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
        # 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 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)
    machine.bind! klass, name, options
  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.



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

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



128
129
130
# File 'lib/machine.rb', line 128

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



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

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

#empty?Boolean

Returns:

  • (Boolean)


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

def empty?
  states.empty?
end

#event_namesObject



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

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)


163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/machine.rb', line 163

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



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

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.



116
117
118
# File 'lib/machine.rb', line 116

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

#helper_modulesObject



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

def helper_modules
  helpers.modules
end

#initial_stateObject



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

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

#initial_state=(s) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/machine.rb', line 136

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



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

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

#inject_methods_into(obj) ⇒ Object



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

def inject_methods_into( obj )
  #puts 'inject_methods_into'
end

#inject_tools_into(obj) ⇒ Object



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

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

#inspectObject



183
184
185
# File 'lib/machine.rb', line 183

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

#state_namesObject



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

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.



122
123
124
# File 'lib/machine.rb', line 122

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