anthonyw-simple_state 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.markdown +104 -24
- data/VERSION.yml +2 -2
- data/lib/simple_state/builder.rb +29 -10
- data/lib/simple_state/mixins.rb +1 -1
- metadata +2 -2
    
        data/README.markdown
    CHANGED
    
    | @@ -10,38 +10,49 @@ There are several existing implementations of state machines in Ruby, notably | |
| 10 10 | 
             
            cumbersome, when all I really needed was a lightweight means for setting the
         | 
| 11 11 | 
             
            state of a class instance, and transitioning from one state to another.
         | 
| 12 12 |  | 
| 13 | 
            -
            There is no support for adding your own code to customise | 
| 14 | 
            -
            is there any support for callbacks or event guards | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
            permitted, and that's | 
| 13 | 
            +
            There is no explicit support for adding your own code to customise
         | 
| 14 | 
            +
            transitions, nor is there any support for callbacks or event guards (although
         | 
| 15 | 
            +
            you can still do similar things fairly trivially). It's called **Simple**State
         | 
| 16 | 
            +
            for a reason! The library adds some helper methods to your class, keeps track
         | 
| 17 | 
            +
            of the valid states, makes sure that a transition is permitted, and that's
         | 
| 18 | 
            +
            about it.
         | 
| 18 19 |  | 
| 19 20 | 
             
            ## Why use SimpleState?
         | 
| 20 21 |  | 
| 21 | 
            -
              | 
| 22 | 
            -
             | 
| 23 | 
            -
              | 
| 24 | 
            -
             | 
| 25 | 
            -
              | 
| 26 | 
            -
              | 
| 27 | 
            -
             | 
| 22 | 
            +
            <ul style="margin-top: 1em">
         | 
| 23 | 
            +
              <li>Lightweight.</li>
         | 
| 24 | 
            +
              <li>method_missing isn't used. ;)</li>
         | 
| 25 | 
            +
              <li>No dependencies.</li>
         | 
| 26 | 
            +
              <li>No extensions to core classes.</li>
         | 
| 27 | 
            +
              <li>Tested on Ruby 1.8.6 (p287), 1.8.7 (p72), and 1.9.1 (p0).</li>
         | 
| 28 | 
            +
              <li>Uses an API similar to Workflow, which I find to be more logical than
         | 
| 29 | 
            +
                that in the acts_as_state_machine family.</li>
         | 
| 30 | 
            +
            </ul>
         | 
| 28 31 |  | 
| 29 32 | 
             
            ## Why use something else?
         | 
| 30 33 |  | 
| 31 | 
            -
              | 
| 32 | 
            -
              | 
| 33 | 
            -
              | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
              | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 34 | 
            +
            <ul style="margin-top: 1em">
         | 
| 35 | 
            +
              <li>The three libraries mentioned above make available, as part of their
         | 
| 36 | 
            +
                DSL, a means of customising events/transitions with your own code.
         | 
| 37 | 
            +
                SimpleState makes no such provision, however you can mimic the behaviour
         | 
| 38 | 
            +
                quite easily as documented in example 3, below.</li>
         | 
| 39 | 
            +
              <li>Similarly, some other libraries provide the ability to add guard
         | 
| 40 | 
            +
                conditions -- a condition which must be satisfied before a transition
         | 
| 41 | 
            +
                can take place. SimpleState also does explicitly support this, however it
         | 
| 42 | 
            +
                is possible by adapting example 3.
         | 
| 43 | 
            +
              <li>SimpleState forces you to use an attribute called `state` - other libraries
         | 
| 44 | 
            +
                let you choose whatever name you want.</li>
         | 
| 45 | 
            +
              <li>Uses a class variable to keep track of transitions - doesn't lend itself
         | 
| 46 | 
            +
                all that well to subclassing your state machines.</li>
         | 
| 47 | 
            +
            </ul>
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            If SimpleState's limitations are too much for you, then you are probably
         | 
| 50 | 
            +
            better off choosing one of the other libraries instead.
         | 
| 42 51 |  | 
| 43 52 | 
             
            ## Examples
         | 
| 44 53 |  | 
| 54 | 
            +
            ### Example 1: Basic usage
         | 
| 55 | 
            +
             | 
| 45 56 | 
             
                require 'rubygems'
         | 
| 46 57 | 
             
                require 'simple_state'
         | 
| 47 58 |  | 
| @@ -96,13 +107,15 @@ state of the instance. | |
| 96 107 |  | 
| 97 108 | 
             
                # etc...
         | 
| 98 109 |  | 
| 110 | 
            +
            ### Example 2: Events in multiple states
         | 
| 111 | 
            +
             | 
| 99 112 | 
             
            It is possible for the same event to be used in multiple states:
         | 
| 100 113 |  | 
| 101 114 | 
             
                state :not_started do
         | 
| 102 115 | 
             
                  event :start,  :transitions_to => :started
         | 
| 103 116 | 
             
                  event :cancel, :transitions_to => :cancelled  # <--
         | 
| 104 117 | 
             
                end
         | 
| 105 | 
            -
             | 
| 118 | 
            +
             | 
| 106 119 | 
             
                state :started do
         | 
| 107 120 | 
             
                  event :finish, :transitions_to => :finished
         | 
| 108 121 | 
             
                  event :cancel, :transitions_to => :cancelled  # <--
         | 
| @@ -125,6 +138,73 @@ current state: | |
| 125 138 | 
             
                state :cancelled
         | 
| 126 139 | 
             
                state :cancelled_before_start
         | 
| 127 140 |  | 
| 141 | 
            +
            ### Example 3: Customising event transitions
         | 
| 142 | 
            +
             | 
| 143 | 
            +
            If the built in event methods aren't sufficient and you need to do extra stuff
         | 
| 144 | 
            +
            to your class during a particular event, you can simply override the method;
         | 
| 145 | 
            +
            the original method is available via `super`:
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                class OverriddenEvent
         | 
| 148 | 
            +
                  extend SimpleState
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                  state_machine do
         | 
| 151 | 
            +
                    state :start do
         | 
| 152 | 
            +
                      event :start, :transitions_to => :started
         | 
| 153 | 
            +
                    end
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                    state :started
         | 
| 156 | 
            +
                  end
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                  def start!
         | 
| 159 | 
            +
                    puts "Before super() : state=#{self.state}"
         | 
| 160 | 
            +
                    ret = super
         | 
| 161 | 
            +
                    puts "After super() : state=#{self.state}"
         | 
| 162 | 
            +
                    ret
         | 
| 163 | 
            +
                  end
         | 
| 164 | 
            +
                end
         | 
| 165 | 
            +
             | 
| 166 | 
            +
                OverriddenEvent.new.start!
         | 
| 167 | 
            +
                # => Before super() : state=start
         | 
| 168 | 
            +
                # => After super() : state=finished
         | 
| 169 | 
            +
                # => :started
         | 
| 170 | 
            +
             | 
| 171 | 
            +
            If the event transition isn't valid, super will simply return false, otherwise
         | 
| 172 | 
            +
            it will return the symbol representing the new state.
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                def start!
         | 
| 175 | 
            +
                  if new_state = super
         | 
| 176 | 
            +
                    puts "Started! The new state is #{self.state}"
         | 
| 177 | 
            +
                  else
         | 
| 178 | 
            +
                    puts "Could not start!"
         | 
| 179 | 
            +
                  end
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                  new_state
         | 
| 182 | 
            +
                end
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                machine = OverriddenEvent.new
         | 
| 185 | 
            +
                machine.start!
         | 
| 186 | 
            +
                => Started! The new state is finished
         | 
| 187 | 
            +
                => :started
         | 
| 188 | 
            +
             | 
| 189 | 
            +
                machine.start!
         | 
| 190 | 
            +
                => Could not start!
         | 
| 191 | 
            +
                => false
         | 
| 192 | 
            +
             | 
| 193 | 
            +
            If you need to know whether a transition will be permitted before you call
         | 
| 194 | 
            +
            super(), SimpleState provides `#event_permitted?`, expecting you to provide a
         | 
| 195 | 
            +
            symbol representing the event.
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                machine.event_permitted?(:start)
         | 
| 198 | 
            +
                # => true|false
         | 
| 199 | 
            +
             | 
| 200 | 
            +
            This also provides an easy means for creating guard conditions:
         | 
| 201 | 
            +
             | 
| 202 | 
            +
                def start!
         | 
| 203 | 
            +
                  if event_permitted?(:start) && SomeExternalService.can_start?(self)
         | 
| 204 | 
            +
                    super
         | 
| 205 | 
            +
                  end
         | 
| 206 | 
            +
                end
         | 
| 207 | 
            +
             | 
| 128 208 | 
             
            ## ORM Integration
         | 
| 129 209 |  | 
| 130 210 | 
             
            SimpleState should play nicely with your ORM of choice. When an object's state
         | 
    
        data/VERSION.yml
    CHANGED
    
    
    
        data/lib/simple_state/builder.rb
    CHANGED
    
    | @@ -32,11 +32,31 @@ module SimpleState | |
| 32 32 |  | 
| 33 33 | 
             
                  # Create an anonymous module which will be added to the state machine
         | 
| 34 34 | 
             
                  # class's inheritance chain.
         | 
| 35 | 
            -
                  mod = @mod = Module.new | 
| 35 | 
            +
                  mod = @mod = Module.new
         | 
| 36 | 
            +
                  mod.class_eval <<-RUBY, __FILE__, __LINE__ + 1
         | 
| 36 37 | 
             
                    def self.inspect
         | 
| 37 | 
            -
                      "SimpleState::#{@klass} | 
| 38 | 
            +
                      "SimpleState::#{@klass}AnonMixin"
         | 
| 38 39 | 
             
                    end
         | 
| 39 | 
            -
             | 
| 40 | 
            +
             | 
| 41 | 
            +
                    # Handles the change of state.
         | 
| 42 | 
            +
                    # @api private
         | 
| 43 | 
            +
                    def _change_state_using_event!(event)
         | 
| 44 | 
            +
                      self.state = self.class._determine_new_state(self.state, event)
         | 
| 45 | 
            +
                    end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                    # Returns if the passed event is permitted with the instance in it's
         | 
| 48 | 
            +
                    # current state.
         | 
| 49 | 
            +
                    # @api public
         | 
| 50 | 
            +
                    def event_permitted?(event)
         | 
| 51 | 
            +
                      self.class._event_permitted?(self.state, event)
         | 
| 52 | 
            +
                    end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                    # Returns true if the given symbol matches the current state.
         | 
| 55 | 
            +
                    # @api public
         | 
| 56 | 
            +
                    def in_state?(state)
         | 
| 57 | 
            +
                      self.state == state
         | 
| 58 | 
            +
                    end
         | 
| 59 | 
            +
                  RUBY
         | 
| 40 60 |  | 
| 41 61 | 
             
                  # Declare the state machine rules.
         | 
| 42 62 | 
             
                  instance_eval(&blk)
         | 
| @@ -61,9 +81,9 @@ module SimpleState | |
| 61 81 | 
             
                  @klass.initial_state ||= name
         | 
| 62 82 |  | 
| 63 83 | 
             
                  @mod.class_eval <<-RUBY, __FILE__, __LINE__ + 1
         | 
| 64 | 
            -
                    def #{name}? | 
| 65 | 
            -
                       | 
| 66 | 
            -
                    end | 
| 84 | 
            +
                    def #{name}?           # def prepared?
         | 
| 85 | 
            +
                      in_state?(:#{name})  #   self.state == :prepared
         | 
| 86 | 
            +
                    end                    # end
         | 
| 67 87 | 
             
                  RUBY
         | 
| 68 88 |  | 
| 69 89 | 
             
                  # Define transitions for this state.
         | 
| @@ -107,7 +127,7 @@ module SimpleState | |
| 107 127 | 
             
                      # Example:
         | 
| 108 128 | 
             
                      #
         | 
| 109 129 | 
             
                      # def process!
         | 
| 110 | 
            -
                      #   if self.class. | 
| 130 | 
            +
                      #   if self.class._transition_permitted?(self.state, :process)
         | 
| 111 131 | 
             
                      #     self.state =
         | 
| 112 132 | 
             
                      #       self.class._determine_new_state(self.state, :process)
         | 
| 113 133 | 
             
                      #   else
         | 
| @@ -116,9 +136,8 @@ module SimpleState | |
| 116 136 | 
             
                      # end
         | 
| 117 137 | 
             
                      @module.class_eval <<-RUBY, __FILE__, __LINE__ + 1
         | 
| 118 138 | 
             
                        def #{event}!
         | 
| 119 | 
            -
                          if  | 
| 120 | 
            -
                             | 
| 121 | 
            -
                              self.class._determine_new_state(self.state, :#{event})
         | 
| 139 | 
            +
                          if event_permitted?(:#{event})
         | 
| 140 | 
            +
                            _change_state_using_event!(:#{event})
         | 
| 122 141 | 
             
                          else
         | 
| 123 142 | 
             
                            false
         | 
| 124 143 | 
             
                          end
         | 
    
        data/lib/simple_state/mixins.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification 
         | 
| 2 2 | 
             
            name: anthonyw-simple_state
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.2.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors: 
         | 
| 7 7 | 
             
            - Anthony Williams
         | 
| @@ -9,7 +9,7 @@ autorequire: | |
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 11 |  | 
| 12 | 
            -
            date: 2009-03- | 
| 12 | 
            +
            date: 2009-03-11 00:00:00 -07:00
         | 
| 13 13 | 
             
            default_executable: 
         | 
| 14 14 | 
             
            dependencies: []
         | 
| 15 15 |  |