Class: StateFu::MethodFactory

Inherits:
Object
  • Object
show all
Defined in:
lib/method_factory.rb

Overview

This class is responsible for defining methods at runtime.

TODO: all events, simple or complex, should get the same method signature simple events will be called as: event_name! nil, *args complex events will be called as: event_name! :state, *args

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(_binding) ⇒ MethodFactory

An instance of MethodFactory is created to define methods on a specific StateFu::Binding, and on the object / class it is bound to.


15
16
17
18
19
20
# File 'lib/method_factory.rb', line 15

def initialize(_binding)
  @binding            = _binding
  @machine            = binding.machine
  @method_definitions = MethodFactory.method_definitions_for(@machine, @binding.method_name)
  self
end

Instance Attribute Details

#bindingObject (readonly)

Returns the value of attribute binding


10
11
12
# File 'lib/method_factory.rb', line 10

def binding
  @binding
end

#machineObject (readonly)

Returns the value of attribute machine


10
11
12
# File 'lib/method_factory.rb', line 10

def machine
  @machine
end

#method_definitionsObject

Returns the value of attribute method_definitions


9
10
11
# File 'lib/method_factory.rb', line 9

def method_definitions
  @method_definitions
end

Class Method Details

.method_definitions_for(machine, name) ⇒ Object


22
23
24
25
26
27
28
29
30
31
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/method_factory.rb', line 22

def self.method_definitions_for(machine, name)
  returning Hash.new do |method_definitions|
    simple_events, complex_events = machine.events.partition &:simple?

    # simple event methods
    # all arguments are passed into the transition / transition query

    simple_events.each do |event|
      method_definitions["#{event.name}"]      = lambda do |*args|
        state_fu(name).find_transition(event, event.target, *args)
      end

      method_definitions["can_#{event.name}?"] = lambda do |*args|
        state_fu(name).can_transition?(event, event.target, *args)
      end

      method_definitions["#{event.name}!"]     = lambda do |*args|
        state_fu(name).fire_transition!(event, event.target, *args)
      end
    end

    # "complex" event methods (for events with more than one possible target)
    # the first argument is the target state
    # any remaining arguments are passed into the transition / transition query

    # object.event_name [:target], *arguments
    #
    # returns a new transition. Will raise an IllegalTransition if
    # it is not given arguments which result in a valid combination
    # of event and target state being deducted.
    #
    # object.event_name [nil] suffices if the event has only one valid
    # target (ie only one transition which would not raise a
    # RequirementError if fired)

    # object.event_name! [:target], *arguments
    #
    # as per the method above, except that it also fires the event

    # object.can_<event_name>? [:target], *arguments
    #
    # tests that calling event_name or event_name! would not raise an error
    # ie, the transition is legal and is valid with the arguments supplied

    complex_events.each do |event|
      method_definitions["#{event.name}"]      = lambda do |target, *args|
        state_fu(name).find_transition(event, target, *args)
      end

      method_definitions["can_#{event.name}?"] = lambda do |target, *args|
        begin
          t = state_fu(name).find_transition(event, target, *args)
          t.valid?
        rescue IllegalTransition
          false
        end
      end

      method_definitions["#{event.name}!"]     = lambda do |target, *args|
        state_fu(name).fire_transition!(event, target, *args)
      end
    end

    # methods for a "complex" event with a specific target
    # eg progress_to_deleted!(*args)
    # is equivalent to progress!(:deleted, *args)
    
    (simple_events + complex_events).each do |event|
      event.targets.each do |target|
        method_definitions["#{event.name}_to_#{target.name}"]      = lambda do |*args|
          state_fu(name).find_transition(event, target, *args)
        end

        method_definitions["can_#{event.name}_to_#{target.name}?"] = lambda do |*args|
          state_fu(name).can_transition?(event, target, *args)
        end

        method_definitions["#{event.name}_to_#{target.name}!"]     = lambda do |*args|
          state_fu(name).fire_transition!(event, target, *args)
        end
      end unless event.targets.nil?
    end
    
    # sugar: query methods for determining the current state

    machine.states.each do |state|
      method_definitions["#{state.name}?"] = lambda do
        state_fu(name).current_state == state
      end
    end
  end
end

.prepare_class_machine(klass, machine, name, options) ⇒ Object

This should be called once per machine bound to a class. It defines methods for the machine as standard methods, if the machine is the default machine or the options passed to the machine include :define_methods => true .

Note this happens when a machine is first bound to the class, not when StateFu is included.


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

def self.prepare_class_machine(klass, machine, name, options)
  return unless options[:define_methods]

  method_definitions_for(machine, name).each do |method_name, block|
    unless klass.respond_to? method_name, true
      klass.class_eval do
        define_method method_name, &block
      end
    end
  end

end

Instance Method Details

#install!Object

Define the same helper methods on the StateFu::Binding and its object. Any existing methods will not be tampered with, but a warning will be issued in the logs if any methods cannot be defined.


143
144
145
146
# File 'lib/method_factory.rb', line 143

def install!
  define_event_methods_on @binding
  define_event_methods_on @binding.object if @binding.options[:define_methods] && @binding.options[:singleton]
end