added simple 'stateful' (sort of mini-workflow) package

git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@1539 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2007-01-04 11:11:32 +00:00
parent ac2a48f72f
commit 0dda2f4b12
11 changed files with 284 additions and 16 deletions

View file

@ -28,6 +28,11 @@ body {
scrollbar-arrow-color: Black;
}
textarea, input, select {
font-family: Verdana, Arial, Helvetica, "sans serif";
font-size: 100%;
}
table {
border-collapse: collapse;
font-size: 100%;

16
stateful/README.txt Normal file
View file

@ -0,0 +1,16 @@
================
Stateful objects
================
($Id$)
>>> from cybertools.stateful.definition import StatesDefinition
>>> from cybertools.stateful.definition import State, Transition
>>> from cybertools.stateful.base import Stateful
>>> class Demo(Stateful):
... pass
>>> demo = Demo()
>>> demo.getState()
'started'

3
stateful/__init__.py Normal file
View file

@ -0,0 +1,3 @@
"""
$Id$
"""

56
stateful/base.py Normal file
View file

@ -0,0 +1,56 @@
#
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
State definition implementation.
$Id$
"""
from zope.interface import implements
from cybertools.stateful.interfaces import IStateful
from cybertools.stateful.definition import statesDefinitions
class Stateful:
implements(IStateful)
_statesDefinition = 'default'
_state = None
def getState(self):
if self._state is None:
self._state = self.getStatesDefinition()._initialState
return self._state
def doTransition(self, transition):
""" execute transition.
"""
sd = self.getStatesDefinition()
sd.doTransitionFor(self, transition)
def getAvailableTransitions(self):
sd = self.getStatesDefinition()
return sd.getAvailableTransitionsFor(self)
def getStatesDefinition(self):
return statesDefinitions.get(self._statesDefinition, None)

70
stateful/definition.py Normal file
View file

@ -0,0 +1,70 @@
#
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
State definition implementation.
$Id$
"""
from zope.interface import implements
from cybertools.stateful.interfaces import IStatesDefinition
class State(object):
def __init__(self, id, title, transitions):
self.id = id
self.title = title
self.transitions = transitions
class Transition(object):
def __init__(self, id, title, targetState):
self.id = id
self.title = title
self.targetState = targetState
class StatesDefinition(object):
implements(IStatesDefinition)
# Basic/example states definition:
_states = {
'started': State('started', 'Started', ('finish',)),
'finished': State('finished', 'Finished', ()),
}
_transitions = {
'finish': Transition('finish', 'Finish', 'finished')
}
_initialState = 'started'
def doTransitionFor(self, object, transition):
object._state = self._transitions[transition].targetState
def getAvailableTransitionsFor(self, object):
state = object.getState()
return [ self._transitions[t] for t in self._states[state].transitions ]
statesDefinitions = {
'default': StatesDefinition(),
}

61
stateful/interfaces.py Normal file
View file

@ -0,0 +1,61 @@
#
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
"""
Interfaces for the `stateful` package.
$Id$
"""
from zope.interface import Interface
class IStateful(Interface):
""" Provides basic methods for stateful objects.
"""
def getState():
""" Return the workflow state of the object.
"""
def doTransition(transition):
""" Execute a transition; the transition is specified by its id.
"""
def getAvailableTransitions():
""" Return the transitions for this object that are available in
the current state. The implementation of the returned transition
objects is not specified, they may be an action dictionaries or
special Transition objects.
"""
class IStatesDefinition(Interface):
""" A simple definition for a set of states and transitions between them,
Similar to an entity-based workflow definition.
"""
def doTransitionFor(object, transition):
""" Execute a transition for the object given;
the transition is specified by its id.
"""
def getAvailableTransitionsFor(object):
""" Return the transitions available for this object in its current state.
"""

28
stateful/tests.py Executable file
View file

@ -0,0 +1,28 @@
#! /usr/bin/python
"""
Tests for the 'cybertools.stateful' package.
$Id$
"""
import unittest, doctest
from zope.testing.doctestunit import DocFileSuite
class Test(unittest.TestCase):
"Basic tests for the storage package."
def testBasicStuff(self):
pass
def test_suite():
flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
return unittest.TestSuite((
unittest.makeSuite(Test),
DocFileSuite('README.txt', optionflags=flags),
))
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')

View file

@ -10,18 +10,19 @@ Generic Views
OK, there aren't really generic views. Already the first implementation we
want to look at is a specic one: It is based on Zope Page Templates and
uses the classic CMF/Zope 3 approach: The template belonging to a view
calls a `main` macro and fills a slot there. But at least the template
implementation is decoupled from the view, so we are able to put a lot of
generic functionality into the view.
uses the classic CMF/Zope 3 approach: The template belonging to a view - more
precisely a page - calls a `main` macro and fills a slot there. But at least
the template implementation is decoupled from the view, so we are able to
put a lot of generic functionality into the view.
In order to make a ZPT work we need a Zope-compatible request, so we use
the standard Zope 3 TestRequest.
>>> from zope.publisher.browser import TestRequest
>>> from cybertools.view.web.base import View
>>> view = View(None, TestRequest())
>>> from cybertools.view.web.base import Page, Content
>>> request = TestRequest()
>>> view = Page(None, request)
>>> view.render()
u'< html...>...<body...>...</body>...</html>...'
u'...<html...>...<body...>...</body>...</html>...'

View file

@ -30,7 +30,16 @@ class View(object):
def __init__(self, context, request):
self.context = context
self.request = request
self.parentView = self.rootView = None
self.childViews = []
self.setUp()
def setUp(self):
pass
def render(self, *args, **kw):
return ''
raise NotImplementedError
def add(self, factory):
self.childViews.append(factory(self.context, self.request))

View file

@ -17,7 +17,7 @@
#
"""
Generic view base class.
Generic base classes for web-based views.
$Id$
"""
@ -28,12 +28,15 @@ from cybertools.view.web.zpt.template import Template
class View(base.View):
""" A generic web-based view - not necessarily HTML-/browser-based.
"""
templateFactory = Template
def __init__(self, context, request):
self.context = context
self.request = request
class BrowserView(View):
""" A browser-based view (i.e. a view using HTML as presentation language).
"""
templateFactory = None
@Lazy
def template(self):
@ -42,3 +45,19 @@ class View(base.View):
def render(self, *args, **kw):
return self.template.render(*args, **kw)
class Page(BrowserView):
""" A browser-based view that renders a full web page.
"""
templateFactory = Template
def setUp(self):
super(Page, self).setUp()
self.add(Content)
class Content(BrowserView):
""" A browser-based view that renders the content region of a web page.
"""

View file

@ -8,7 +8,7 @@
<browser:page
for="*"
name="dummy.html"
class="cybertools.view.web.base.View"
class="cybertools.view.web.base.Page"
attribute="render"
permission="zope.View"
/>