fc8dc751add0a9bece435ca37c27673ecbd853d0
[hooke.git] / hooke / command_stack.py
1 # Copyright (C) 2010 W. Trevor King <wking@drexel.edu>
2 #
3 # This file is part of Hooke.
4 #
5 # Hooke is free software: you can redistribute it and/or modify it
6 # under the terms of the GNU Lesser General Public License as
7 # published by the Free Software Foundation, either version 3 of the
8 # License, or (at your option) any later version.
9 #
10 # Hooke is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General
13 # Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with Hooke.  If not, see
17 # <http://www.gnu.org/licenses/>.
18
19 """The ``command_stack`` module provides tools for managing and
20 executing stacks of :class:`~hooke.engine.CommandMessage`\s.
21 """
22
23
24 class CommandStack (list):
25     """Store a stack of commands.
26
27     Examples
28     --------
29     >>> from .engine import CommandMessage
30
31     Define two dummy commands for testing.
32
33     >>> class CommandA (object):
34     ...     name = 'CommandA'
35     >>> ca = CommandA()
36     >>> class CommandB (CommandA):
37     ...     name = 'CommandB'
38     >>> cb = CommandB()
39
40     Show off `CommandStack`\s functionality.
41
42     >>> c = CommandStack([CommandMessage(ca, {'param':'A'})])
43     >>> c.append(CommandMessage(cb, {'param':'B'}))
44     >>> c.append(CommandMessage(ca, {'param':'C'}))
45     >>> c.append(CommandMessage(cb, {'param':'D'}))
46
47     Implement a dummy :meth:`_execute` for testing.  This would
48     usually call :meth:`hooke.command.Command.run` with appropriate
49     arguments.
50     
51     >>> def execute(command_message):
52     ...     cm = command_message
53     ...     print 'EXECUTE', cm.command.name, cm.arguments
54     >>> c._execute = execute
55
56     >>> c.execute()  # doctest: +ELLIPSIS
57     EXECUTE CommandA {'param': 'A'}
58     EXECUTE CommandB {'param': 'B'}
59     EXECUTE CommandA {'param': 'C'}
60     EXECUTE CommandB {'param': 'D'}
61
62     :meth:`filter` allows you to select which commands get executed.
63     If, for example, you are applying a set of commands to the current
64     :class:`~hooke.curve.Curve`, you may only want to execute
65     instances of :class:`~hooke.plugin.curve.CurveCommand`.  Here we
66     only execute instances of `CommandB`.
67     
68     >>> def filter(command_message):
69     ...     return isinstance(command_message.command, CommandB)
70     >>> c.filter = filter
71
72     Apply the stack to the current curve
73     >>> c.execute()  # doctest: +ELLIPSIS
74     EXECUTE CommandB {'param': 'B'}
75     EXECUTE CommandB {'param': 'D'}
76     """
77     def execute(self, *args, **kwargs):
78         """Execute a stack of commands.
79
80         See Also
81         --------
82         _execute, filter
83         """
84         for command_message in self:
85             if self.filter(command_message) == True:
86                 self._execute(command_message, *args, **kwargs)
87
88     def filter(self, command_message):
89         """Any commands in the stack that are not subclasses of
90         :class:`~hooke.plugin.curve.CurveCommand` are ignored.
91         """
92         return True
93
94     def _execute(self, command_message):
95         raise NotImplementedError()