Skip to content
Clean and fast Object state transitions in Ruby using the Mixology C extension
Ruby
Find file
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.
lib
test
CHANGELOG
LICENSE
README.markdown
sample.rb
stateology.gemspec

README.markdown

Stateology

Clean and fast Object state transitions in Ruby using the Mixology C extension.

Supports:

  • Dynamic switching between states (mixing and unmixing modules)
  • Optional state_entry() and state_exit() hooks for each state (automatically called upon state entry and exit)
  • support for subclassing of classes that include Stateology (see below)
  • support for nested states, i.e states defined within other states
  • Clean DSL-style syntax
  • NO MAGIC! Stateology does not use any behind-the-scenes method aliasing nor does it make use of any hooks.

Use as in the following:

class Sample
    include Stateology

    state(:Happy) {
        def state_entry
            puts "entering Happy state"
        end

        def do_something
            puts "Pets a puppy"
        end

        def state_exit
            puts "exiting Happy state"
        end
    }

    state(:Angry) {
        def state_entry
            puts "entering Angry state"
        end

        def do_something
            puts "Kicks a puppy"
        end

        def state_exit
            puts "exiting Angry state"
        end
    }
end

s = Sample.new

# now switch to Happy state
s.state :Happy
s.do_something  #=> "Pets a puppy"

# now switch to Angry state
s.state :Angry
s.do_something  #=> "Kicks a puppy"

UPDATE:

  • made it so subclasses can inherit states from their superclasses e.g

    class A
        include Stateology
    
        state(:Happy) {
            def state_entry
                puts "entering Happy state"
            end
    
            def hello
                puts "hello from A"
            end
        }
    end
    
    class B < A
        state(:Happy) {
            def hello
                puts "hello from B"
            end
        }
    end
    
    b = B.new
    
    b.state :Happy
    #=> "entering Happy state"
    
    b.hello
    #=> "hello from B"
    
  • prior behaviour was for state_entry not to exist in class B as Happy module from class A was overwritten by the new Happy module in B

  • how does this fix work? the Happy module in B just includes any extant Happy module accessible in B

A FEW THINGS TO NOTE

  • When an object is instantiated it begins life in no state and only ordinary instance methods are accessible (The ordinary instance methods are those defined outside of any state() {} block)

  • The ordinary instance methods are available to any state so long as they are not overridden by the state.

  • To change from any given state to 'no state' pass nil as a parameter to the state method e.g s.state nil

  • 'no state', while not a state, may nonetheless have state_entry() and state_exit() methods; and these methods will be invoked on 'entry' and exit from 'no state'

  • The state_entry method for 'no state' is not automatically called on object instantiation. If you wish state_entry to run when the object is instantiated invoke it in the initialize() method.

  • The state_entry method can also accept parameters: e.g s.state :Happy, "hello" In the above the string "hello" is passed as a parameter to the state_entry() method of the Happy state.

  • The #state method can accept either a Symbol (e.g :Happy) or a Module (e.g Happy or Sample::Happy). The following are equivalent: s.state :Happy #=> change state to Happy s.state Sample::Happy #=> equivalent to above (note the fully qualified name; as Happy is a module defined under the Sample class)

  • The #state method can take a block; the block will be executed after the successful change of state: e.g s.state(:Happy) { s.hello } #=> hello method invoked immediately after change of state as it's in the block

  • alternatively; if the #state method is invoked internally by another instance method of the Sample class then a fully qualified module name is not required: state Happy #=> Fully qualified module name not required when #state invoked in an instance method

  • The #state method can also act as a 'getter' method when invoked with no parameters. It will return the current state name in Symbol form (e.g :Happy)

  • The #state_mod method works similarly to the #state 'getter' except it returns the Module representing the current state (e.g Sample::Happy)

  • The #state?(state_name) returns boolean true if the current state is equal to state_name, and false if not. state_name can be either a Module or a Symbol

Something went wrong with that request. Please try again.