dcf2ce2a64a58e0794f282b20aed55c859fdc359
[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     >>> c = CommandStack([CommandMessage('CommandA', {'param':'A'})])
31     >>> c.append(CommandMessage('CommandB', {'param':'B'}))
32     >>> c.append(CommandMessage('CommandA', {'param':'C'}))
33     >>> c.append(CommandMessage('CommandB', {'param':'D'}))
34
35     Implement a dummy :meth:`execute_command` for testing.
36     
37     >>> def execute_cmd(hooke, command_message):
38     ...     cm = command_message
39     ...     print 'EXECUTE', cm.command, cm.arguments
40     >>> c.execute_command = execute_cmd
41
42     >>> c.execute(hooke=None)  # doctest: +ELLIPSIS
43     EXECUTE CommandA {'param': 'A'}
44     EXECUTE CommandB {'param': 'B'}
45     EXECUTE CommandA {'param': 'C'}
46     EXECUTE CommandB {'param': 'D'}
47
48     :meth:`filter` allows you to select which commands get executed.
49     If, for example, you are applying a set of commands to the current
50     :class:`~hooke.curve.Curve`, you may only want to execute
51     instances of :class:`~hooke.plugin.curve.CurveCommand`.  Here we
52     only execute commands named `CommandB`.
53     
54     >>> def filter(hooke, command_message):
55     ...     return command_message.command == 'CommandB'
56     >>> c.filter = filter
57
58     Apply the stack to the current curve.
59
60     >>> c.execute(hooke=None)  # doctest: +ELLIPSIS
61     EXECUTE CommandB {'param': 'B'}
62     EXECUTE CommandB {'param': 'D'}
63
64     Execute a new command and add it to the stack.
65
66     >>> cm = CommandMessage('CommandC', {'param':'E'})
67     >>> c.execute_command(hooke=None, command_message=cm)
68     EXECUTE CommandC {'param': 'E'}
69     >>> c.append(cm)
70     >>> print [repr(cm) for cm in c]  # doctest: +NORMALIZE_WHITESPACE
71     ["<CommandMessage CommandA {'param': 'A'}>",
72      "<CommandMessage CommandB {'param': 'B'}>",
73      "<CommandMessage CommandA {'param': 'C'}>",
74      "<CommandMessage CommandB {'param': 'D'}>",
75      "<CommandMessage CommandC {'param': 'E'}>"]
76     """
77     def execute(self, hooke):
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(hooke, command_message) == True:
86                 self.execute_command(
87                     hooke=hooke, command_message=command_message)
88
89     def filter(self, hooke, command_message):
90         """Return `True` to execute `command_message`, `False` otherwise.
91
92         The default implementation always returns `True`.
93         """
94         return True
95
96     def execute_command(self, hooke, command_message):
97         hooke.run_command(command=command_message.command,
98                           arguments=command_message.arguments)