statu 0.3.1
Python State Machines for Humans
License: MIT

statu - a python state machine
==============================

state machine for humans

|Build Status|

There are two types of developers in this world: those who love state
machines and those who *will* eventually.

We fall in the first camp. We think it is really important to have a
declarative way to define the states of an object. That’s why we are
continuing development of ``statu``.

Install
-------

.. code:: bash

    pip install state_machine

.. |Build Status| image:: https://travis-ci.org/DisruptiveLabs/statu.svg?branch=master
   :target: https://travis-ci.org/DisruptiveLabs/statu

Basic Usage
-----------

.. code:: python


    @acts_as_state_machine
    class Person():
        name = 'Billy'

        sleeping = State(initial=True)
        running = State()
        cleaning = State()

        run = Event(from_states=sleeping, to_state=running)
        cleanup = Event(from_states=running, to_state=cleaning)
        sleep = Event(from_states=(running, cleaning), to_state=sleeping)

        @before('sleep')
        def do_one_thing(self):
            print "{} is sleepy".format(self.name)

        @before('sleep')
        def do_another_thing(self):
            print "{} is REALLY sleepy".format(self.name)

        @after('sleep')
        def snore(self):
            print "Zzzzzzzzzzzz"

        @after('sleep')
        def big_snore(self):
            print "Zzzzzzzzzzzzzzzzzzzzzz"

    person = Person()
    print person.current_state == Person.sleeping       # True
    print person.is_sleeping                            # True
    print person.is_running                             # False
    person.run()
    print person.is_running                             # True
    person.sleep()

    # Billy is sleepy
    # Billy is REALLY sleepy
    # Zzzzzzzzzzzz
    # Zzzzzzzzzzzzzzzzzzzzzz

    print person.is_sleeping                            # True

Features
--------

Before / After Callback Decorators
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can add callback hooks that get executed before or after an event
(see example above).

*Important:* if the *before* event causes an exception or returns
``False``, the state will not change (transition is blocked) and the
*after* event will not be executed.

Blocks invalid state transitions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

An *InvalidStateTransition Exception* will be thrown if you try to move
into an invalid state.

ORM support
-----------

We have basic support for `mongoengine`_, and `sqlalchemy`_.

Mongoengine
~~~~~~~~~~~

Just have your object inherit from ``mongoengine.Document`` and
state\_machine will add a StringField for state.

*Note:* You must explicitly call #save to persist the document to the
datastore.

.. code:: python

        @acts_as_state_machine
        class Person(mongoengine.Document):
            name = mongoengine.StringField(default='Billy')

            sleeping = State(initial=True)
            running = State()
            cleaning = State()

            run = Event(from_states=sleeping, to_state=running)
            cleanup = Event(from_states=running, to_state=cleaning)
            sleep = Event(from_states=(running, cleaning), to_state=sleeping)

            @before('sleep')
            def do_one_thing(self):
                print "{} is sleepy".format(self.name)

            @before('sleep')
            def do_another_thing(self):
                print "{} is REALLY sleepy".format(self.name)

            @after('sleep')
            def snore(self):
                print "Zzzzzzzzzzzz"

            @after('sleep')
            def snore(self):
                print "Zzzzzzzzzzzzzzzzzzzzzz"


        person = Person()
        person.save()
        eq_(person.current_state, Person.sleeping)
        assert person.is_sleeping
        assert not person.is_running
        person.run()
        assert person.is_running
        person.sleep()
        assert person.is_sleeping
        person.run()
        person.save()

        person2 = Person.objects(id=person.id).first()
        assert person2.is_running

.. _mongoengine: http://mongoengine.org/
.. _sqlalchemy: http://www.sqlalchemy.org/

Sqlalchemy
~~~~~~~~~~

All you need to do is have sqlalchemy manage your object. For example:

.. code:: python

        from sqlalchemy.ext.declarative import declarative_base
        Base = declarative_base()
        @acts_as_state_machine
        class Puppy(Base):
           ...

Issues / Roadmap:
-----------------

-  Allow multiple state\_machines per object
-  Be able to configure the state field

Questions / Issues
------------------

Feel free to open an issue, PR, or shoot us an email at `pypi@comanage.com`

Thank you
---------

to `jtushman` for getting this library off the ground, and letting us fork
and continue the development here

.. _@tushman: http://twitter.com/tushman

and to `aasm`_ and ruby’s `state\_machine`_ and all other state machines
that I loved before

.. _aasm: https://github.com/aasm/aasm
.. _state\_machine: https://github.com/pluginaweek/state_machine