puremvc: PureMVC Python Port by Toby de Havilland
64 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c)
65 | 2006-08 Futurescale, Inc., Some rights reserved.
66 |
67 |
puremvc.core: PureMVC Python Port by Toby de Havilland
68 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c)
69 | 2006-08 Futurescale, Inc., Some rights reserved.
70 |
puremvc.interfaces: PureMVC Python Port by Toby de Havilland
71 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c)
72 | 2006-08 Futurescale, Inc., Some rights reserved.
73 |
puremvc.patterns: PureMVC Python Port by Toby de Havilland
74 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c)
75 | 2006-08 Futurescale, Inc., Some rights reserved.
76 |
77 |
puremvc.patterns.command: PureMVC Python Port by Toby de Havilland
78 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c)
79 | 2006-08 Futurescale, Inc., Some rights reserved.
80 |
puremvc.patterns.facade: PureMVC Python Port by Toby de Havilland
81 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c)
82 | 2006-08 Futurescale, Inc., Some rights reserved.
83 |
puremvc.patterns.mediator: PureMVC Python Port by Toby de Havilland
84 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c)
85 | 2006-08 Futurescale, Inc., Some rights reserved.
86 |
puremvc.patterns.observer: PureMVC Python Port by Toby de Havilland
87 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c)
88 | 2006-08 Futurescale, Inc., Some rights reserved.
89 |
puremvc.patterns.proxy: PureMVC Python Port by Toby de Havilland
90 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c)
91 | 2006-08 Futurescale, Inc., Some rights reserved.
PureMVC Python Port by Toby de Havilland
64 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c) 2006-08
65 | Futurescale, Inc., Some rights reserved. Your reuse is governed by the
66 | Creative Commons Attribution 3.0 License
puremvc.core: PureMVC Python Port by Toby de Havilland
88 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c)
89 | 2006-08 Futurescale, Inc., Some rights reserved.
90 |
puremvc.interfaces: PureMVC Python Port by Toby de Havilland
91 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c)
92 | 2006-08 Futurescale, Inc., Some rights reserved.
93 |
puremvc.patterns: PureMVC Python Port by Toby de Havilland
94 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c)
95 | 2006-08 Futurescale, Inc., Some rights reserved.
96 |
97 |
puremvc.patterns.command: PureMVC Python Port by Toby de Havilland
98 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c)
99 | 2006-08 Futurescale, Inc., Some rights reserved.
100 |
puremvc.patterns.facade: PureMVC Python Port by Toby de Havilland
101 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c)
102 | 2006-08 Futurescale, Inc., Some rights reserved.
103 |
puremvc.patterns.mediator: PureMVC Python Port by Toby de Havilland
104 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c)
105 | 2006-08 Futurescale, Inc., Some rights reserved.
106 |
puremvc.patterns.observer: PureMVC Python Port by Toby de Havilland
107 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c)
108 | 2006-08 Futurescale, Inc., Some rights reserved.
109 |
puremvc.patterns.proxy: PureMVC Python Port by Toby de Havilland
110 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c)
111 | 2006-08 Futurescale, Inc., Some rights reserved.
PureMVC Python Port by Toby de Havilland
65 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c) 2006-08
66 | Futurescale, Inc., Some rights reserved. Your reuse is governed by the
67 | Creative Commons Attribution 3.0 License
PureMVC Python Port by Toby de Havilland
65 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c) 2006-08
66 | Futurescale, Inc., Some rights reserved. Your reuse is governed by the
67 | Creative Commons Attribution 3.0 License
PureMVC Python Port by Toby de Havilland
65 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c) 2006-08
66 | Futurescale, Inc., Some rights reserved. Your reuse is governed by the
67 | Creative Commons Attribution 3.0 License
puremvc.patterns.command: PureMVC Python Port by Toby de Havilland
89 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c)
90 | 2006-08 Futurescale, Inc., Some rights reserved.
91 |
puremvc.patterns.facade: PureMVC Python Port by Toby de Havilland
92 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c)
93 | 2006-08 Futurescale, Inc., Some rights reserved.
94 |
puremvc.patterns.mediator: PureMVC Python Port by Toby de Havilland
95 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c)
96 | 2006-08 Futurescale, Inc., Some rights reserved.
97 |
puremvc.patterns.observer: PureMVC Python Port by Toby de Havilland
98 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c)
99 | 2006-08 Futurescale, Inc., Some rights reserved.
100 |
puremvc.patterns.proxy: PureMVC Python Port by Toby de Havilland
101 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c)
102 | 2006-08 Futurescale, Inc., Some rights reserved.
PureMVC Python Port by Toby de Havilland
66 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c) 2006-08
67 | Futurescale, Inc., Some rights reserved. Your reuse is governed by the
68 | Creative Commons Attribution 3.0 License
PureMVC Python Port by Toby de Havilland
66 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c) 2006-08
67 | Futurescale, Inc., Some rights reserved. Your reuse is governed by the
68 | Creative Commons Attribution 3.0 License
PureMVC Python Port by Toby de Havilland
66 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c) 2006-08
67 | Futurescale, Inc., Some rights reserved. Your reuse is governed by the
68 | Creative Commons Attribution 3.0 License
PureMVC Python Port by Toby de Havilland
66 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c) 2006-08
67 | Futurescale, Inc., Some rights reserved. Your reuse is governed by the
68 | Creative Commons Attribution 3.0 License
PureMVC Python Port by Toby de Havilland
66 | <toby.de.havilland@puremvc.org> PureMVC - Copyright(c) 2006-08
67 | Futurescale, Inc., Some rights reserved. Your reuse is governed by the
68 | Creative Commons Attribution 3.0 License
When javascript is enabled, this page will redirect URLs of
22 | the form redirect.html#dotted.name to the
23 | documentation for the object with the given fully-qualified
24 | dotted name.
18 | puremvc puremvc.core puremvc.interfaces puremvc.patterns puremvc.patterns.command puremvc.patterns.facade puremvc.patterns.mediator puremvc.patterns.observer puremvc.patterns.proxy
28 | [hide private]
30 |
31 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/fabfile.py:
--------------------------------------------------------------------------------
1 | from fabric.api import *
2 |
3 |
4 | def install():
5 | local("python ./setup.py clean")
6 | local("python ./setup.py install")
7 |
8 |
9 | def clean():
10 | 'remove local .pyc files'
11 |
12 | local("find ./src/ -name '*.pyc' -exec rm -rf {} \;")
13 | local("find ./tests/ -name '*.pyc' -exec rm -rf {} \;")
14 |
15 |
16 | def docs():
17 | 'Build docs'
18 |
19 | local("rm -rf ./docs/*")
20 | local("epydoc -v --html --name 'PureMVC Python' ./src/puremvc -o ./docs/")
21 |
22 |
23 | def test():
24 | 'Run unit tests'
25 | local("python ./tests/main.py")
26 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from distutils.core import setup
2 |
3 | setup(name='PureMVC',
4 | version='1.2',
5 | description='PureMVC Python Framework',
6 | author='Toby de Havilland',
7 | author_email='toby.de.havilland@puremvc.org',
8 | url='http://www.puremvc.org',
9 | package_dir={'': 'src'},
10 | packages=['puremvc', 'puremvc.patterns'],
11 | )
12 |
--------------------------------------------------------------------------------
/src/puremvc/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | PureMVC Python Port by Toby de Havilland
3 | PureMVC - Copyright(c) 2006-08 Futurescale, Inc., Some rights reserved.
4 | Your reuse is governed by the Creative Commons Attribution 3.0 License
5 | """
6 |
--------------------------------------------------------------------------------
/src/puremvc/patterns/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | PureMVC Python Port by Toby de Havilland
3 | PureMVC - Copyright(c) 2006-08 Futurescale, Inc., Some rights reserved.
4 | Your reuse is governed by the Creative Commons Attribution 3.0 License
5 | """
6 |
--------------------------------------------------------------------------------
/src/puremvc/patterns/command.py:
--------------------------------------------------------------------------------
1 | """
2 | PureMVC Python Port by Toby de Havilland
3 | PureMVC - Copyright(c) 2006-08 Futurescale, Inc., Some rights reserved.
4 | Your reuse is governed by the Creative Commons Attribution 3.0 License
5 | """
6 |
7 | import puremvc.interfaces
8 | import puremvc.patterns.observer
9 |
10 | class MacroCommand(puremvc.patterns.observer.Notifier, puremvc.interfaces.ICommand, puremvc.interfaces.INotifier):
11 | """
12 | A base C{ICommand} implementation that executes other C{ICommand}s.
13 |
14 | A C{MacroCommand} maintains an list of C{ICommand} Class references called I{SubCommands}.
15 |
16 | When C{execute} is called, the C{MacroCommand}
17 | instantiates and calls C{execute} on each of its I{SubCommands} turn.
18 | Each I{SubCommand} will be passed a reference to the original
19 | C{INotification} that was passed to the C{MacroCommand}'s
20 | C{execute} method.
21 |
22 | Unlike C{SimpleCommand}, your subclass
23 | should not override C{execute}, but instead, should
24 | override the C{initializeMacroCommand} method,
25 | calling C{addSubCommand} once for each I{SubCommand}
26 | to be executed.
27 |
28 | @see: L{Controller}
29 | @see: L{Notification}
30 | @see: L{SimpleCommand}
31 | """
32 |
33 | def __init__(self):
34 | """
35 | MacroCommand Constructor
36 |
37 | You should not need to define a constructor,
38 | instead, override the C{initializeMacroCommand}
39 | method.
40 | """
41 | self.subCommands = []
42 | self.initializeMacroCommand()
43 |
44 | def initializeMacroCommand(self):
45 | """
46 | Initialize the C{MacroCommand}.
47 |
48 | In your subclass, override this method to
49 | initialize the C{MacroCommand}'s I{SubCommand}
50 | list with C{ICommand} class references like
51 | this:
52 |
53 | Note that I{SubCommand}s may be any C{ICommand} implementor,
54 | C{MacroCommand}s or C{SimpleCommands} are both acceptable.
55 | """
56 | pass
57 |
58 | def addSubCommand(self, commandClassRef):
59 | """
60 | Add a I{SubCommand}.
61 |
62 | The I{SubCommands} will be called in First In/First Out (FIFO)
63 | order.
64 |
65 | @param commandClassRef: a reference to the C{Class} of the C{ICommand}.
66 | """
67 | self.subCommands.append(commandClassRef)
68 |
69 |
70 | def execute(self, notification):
71 | """
72 | Execute this C{MacroCommand}'s I{SubCommands}.
73 |
74 | The I{SubCommands} will be called in First In/First Out (FIFO)
75 | order.
76 |
77 | @param notification: the C{INotification} object to be passed to each I{SubCommand}.
78 | """
79 | for commandClassRef in self.subCommands[:]:
80 | commandClassRef().execute(notification)
81 |
82 |
83 | class SimpleCommand(puremvc.patterns.observer.Notifier, puremvc.interfaces.ICommand, puremvc.interfaces.INotifier):
84 | """
85 | A base C{ICommand} implementation.
86 |
87 | Your subclass should override the C{execute}
88 | method where your business logic will handle the C{INotification}.
89 |
90 | @see: L{Controller}
91 | @see: L{Notification}
92 | @see: L{MacroCommand}
93 | """
94 |
95 | def execute(self, notification):
96 | """
97 | Fulfill the use-case initiated by the given C{INotification}.
98 |
99 | In the Command Pattern, an application use-case typically
100 | begins with some user action, which results in an C{INotification} being broadcast, which
101 | is handled by business logic in the C{execute} method of an
102 | C{ICommand}.
103 |
104 | @param notification: the C{INotification} to handle.
105 | """
106 | pass
107 |
--------------------------------------------------------------------------------
/src/puremvc/patterns/facade.py:
--------------------------------------------------------------------------------
1 | """
2 | PureMVC Python Port by Toby de Havilland
3 | PureMVC - Copyright(c) 2006-08 Futurescale, Inc., Some rights reserved.
4 | Your reuse is governed by the Creative Commons Attribution 3.0 License
5 | """
6 |
7 | import puremvc.core
8 | import puremvc.interfaces
9 | import puremvc.patterns.observer
10 |
11 | class Facade(puremvc.interfaces.IFacade):
12 | """
13 | A base Singleton C{IFacade} implementation.
14 |
15 | In PureMVC, the C{Facade} class assumes these
16 | responsibilities:
17 |
18 | Initializing the C{Model}, C{View} and C{Controller} Singletons.
19 |
20 | Providing all the methods defined by the C{IModel, IView, & IController} interfaces.
21 |
22 | Providing the ability to override the specific C{Model}, C{View} and C{Controller} Singletons created.
23 |
24 | Providing a single point of contact to the application for registering C{Commands} and notifying C{Observers}
25 |
26 |
27 | @see: L{Model}
28 | @see: L{View}
29 | @see: L{Controller}
30 | @see: L{Notification}
31 | @see: L{Mediator}
32 | @see: L{Proxy}
33 | @see: L{SimpleCommand}
34 | @see: L{MacroCommand}
35 | """
36 |
37 | instance = None
38 | controller = None
39 | model = None
40 | view = None
41 |
42 | def __new__(cls, *args, **kwargs):
43 | """
44 | This C{IFacade} implementation is a Singleton, so you should not call the constructor
45 | directly, but instead call the static Singleton method C{Facade.getInstance()}
46 | """
47 | if not cls.instance or not isinstance(cls.instance, cls):
48 | cls.instance = super(Facade, cls).__new__(cls, *args, **kwargs)
49 | cls.instance.initializeFacade()
50 | return cls.instance
51 |
52 | @staticmethod
53 | def getInstance():
54 | """
55 | C{Facade} Singleton Static method.
56 |
57 | @return: the Singleton instance of C{Facade}
58 | """
59 | return Facade()
60 |
61 | def initializeFacade(self):
62 | """
63 | Initialize the Singleton C{Facade} instance.
64 |
65 | Called automatically by the constructor. Override in your
66 | subclass to do any subclass specific initializations. Be
67 | sure to call C{Facade.initializeFacade()}, though.
68 | """
69 | self.initializeController()
70 | self.initializeModel()
71 | self.initializeView()
72 |
73 | def initializeController(self):
74 | """
75 | Initialize the C{Controller}.
76 |
77 | Called by the C{initializeFacade} method.
78 | Override this method in your subclass of C{Facade}
79 | if one or both of the following are true:
80 |
81 | You wish to initialize a different C{IController}.
82 | You have C{Commands} to register with the C{Controller} at startup.
83 |
84 | If you don't want to initialize a different C{IController},
85 | call C{super.initializeController()} at the beginning of your method, then register C{Proxy}s.
86 |
87 | Note: This method is I{rarely} overridden; in practice you are more
88 | likely to use a C{Command} to create and register C{Proxy}s
89 | with the C{Model}, since C{Proxy}s with mutable data will likely
90 | need to send C{INotification}s and thus will likely want to fetch a reference to
91 | the C{Facade} during their construction.
92 | """
93 | if self.controller is not None:
94 | return
95 | self.controller = puremvc.core.Controller.getInstance()
96 |
97 | def initializeModel(self):
98 | """
99 | Initialize the C{Model}.
100 |
101 | Called by the C{initializeFacade} method.
102 | Override this method in your subclass of C{Facade}
103 | if one or both of the following are true:
104 |
105 | You wish to initialize a different C{IModel}.
106 |
107 | You have C{Proxy}s to register with the Model that do not
108 | retrieve a reference to the Facade at construction time.
109 |
110 | If you don't want to initialize a different C{IModel},
111 | call C{super.initializeModel()} at the beginning of your
112 | method, then register C{Proxy}s.
113 |
114 | Note: This method is I{rarely} overridden; in practice you are more
115 | likely to use a C{Command} to create and register C{Proxy}s
116 | with the C{Model}, since C{Proxy}s with mutable data will likely
117 | need to send C{INotification}s and thus will likely want to fetch a reference to
118 | the C{Facade} during their construction.
119 | """
120 | if self.model is not None:
121 | return
122 | self.model = puremvc.core.Model.getInstance()
123 |
124 | def initializeView(self):
125 | """
126 | Initialize the C{View}.
127 |
128 |
129 | Called by the C{initializeFacade} method.
130 | Override this method in your subclass of C{Facade}
131 | if one or both of the following are true:
132 |
133 | You wish to initialize a different C{IView}.
134 |
135 | You have C{Observers} to register with the C{View}
136 |
137 | If you don't want to initialize a different C{IView},
138 | call C{super.initializeView()} at the beginning of your
139 | method, then register C{IMediator} instances.
140 |
141 | Note: This method is I{rarely} overridden; in practice you are more
142 | likely to use a C{Command} to create and register C{Mediator}s
143 | with the C{View}, since C{IMediator} instances will need to send
144 | C{INotification}s and thus will likely want to fetch a reference
145 | to the C{Facade} during their construction.
146 | """
147 | if self.view is not None:
148 | return
149 | self.view = puremvc.core.View.getInstance()
150 |
151 | def registerCommand(self, notificationName, commandClassRef):
152 | """
153 | Register an C{ICommand} with the C{Controller} by Notification name.
154 |
155 | @param notificationName: the name of the C{INotification} to associate the C{ICommand} with
156 | @param commandClassRef: a reference to the Class of the C{ICommand}
157 | """
158 | self.controller.registerCommand(notificationName, commandClassRef)
159 |
160 | def removeCommand(self, notificationName):
161 | """
162 | Remove a previously registered C{ICommand} to C{INotification} mapping from the Controller.
163 |
164 | @param notificationName: the name of the C{INotification} to remove the C{ICommand} mapping for
165 | """
166 | self.controller.removeCommand(notificationName)
167 |
168 | def hasCommand(self, notificationName):
169 | """
170 | Check if a Command is registered for a given Notification
171 |
172 | @param notificationName: the name of the C{INotification}
173 | @return: whether a Command is currently registered for the given C{notificationName}.
174 | """
175 | return self.controller.hasCommand(notificationName)
176 |
177 | def registerProxy(self, proxy):
178 | """
179 | Register an C{IProxy} with the C{Model} by name.
180 |
181 | @param proxy: the C{IProxy} instance to be registered with the C{Model}.
182 | """
183 | self.model.registerProxy(proxy)
184 |
185 | def retrieveProxy(self, proxyName):
186 | """
187 | Retrieve an C{IProxy} from the C{Model} by name.
188 |
189 | @param proxyName: the name of the proxy to be retrieved.
190 | @return: the C{IProxy} instance previously registered with the given C{proxyName}.
191 | """
192 | return self.model.retrieveProxy(proxyName)
193 |
194 | def removeProxy(self, proxyName):
195 | """
196 | Remove an C{IProxy} from the C{Model} by name.
197 |
198 | @param proxyName: the C{IProxy} to remove from the C{Model}.
199 | @return: the C{IProxy} that was removed from the C{Model}
200 | """
201 | if self.model is not None:
202 | return self.model.removeProxy(proxyName)
203 |
204 | def hasProxy(self, proxyName):
205 | """
206 | Check if a Proxy is registered
207 |
208 | @param proxyName: the name of the C{IProxy}
209 | @return: whether a Proxy is currently registered with the given C{proxyName}.
210 | """
211 | return self.model.hasProxy(proxyName)
212 |
213 | def registerMediator(self, mediator):
214 | """
215 | Register a C{IMediator} with the C{View}.
216 |
217 | @param mediator: a reference to the C{IMediator}
218 | """
219 | if self.view is not None:
220 | self.view.registerMediator(mediator)
221 |
222 | def retrieveMediator(self, mediatorName):
223 | """
224 | Retrieve an C{IMediator} from the C{View}.
225 |
226 | @param mediatorName: the name of the C{IMediator}
227 | @return: the C{IMediator} previously registered with the given C{mediatorName}.
228 | """
229 | return self.view.retrieveMediator(mediatorName)
230 |
231 | def removeMediator(self, mediatorName):
232 | """
233 | Remove an C{IMediator} from the C{View}.
234 |
235 | @param mediatorName: name of the C{IMediator} to be removed.
236 | @return: the C{IMediator} that was removed from the C{View}
237 | """
238 | mediator = None
239 | if self.view is not None:
240 | mediator = self.view.removeMediator(mediatorName)
241 | return mediator
242 |
243 | def hasMediator(self, mediatorName):
244 | """
245 | Check if a Mediator is registered or not
246 |
247 | @param mediatorName: the name of the C{IMediator}
248 | @return: whether a Mediator is registered with the given C{mediatorName}.
249 | """
250 | return self.view.hasMediator(mediatorName)
251 |
252 | def sendNotification(self, notificationName, body=None, noteType=None):
253 | """
254 | Create and send an C{INotification}.
255 |
256 | Keeps us from having to construct new notification
257 | instances in our implementation code.
258 |
259 | @param notificationName: the name of the notification to send
260 | @param body: the body of the notification (optional)
261 | @param noteType: the type of the notification (optional)
262 | """
263 | self.notifyObservers(
264 | puremvc.patterns.observer.Notification(
265 | notificationName, body, noteType
266 | )
267 | )
268 |
269 | def notifyObservers(self, notification):
270 | """
271 | Notify C{Observer}s.
272 |
273 | This method is left public mostly for backward
274 | compatibility, and to allow you to send custom
275 | notification classes using the facade.
276 |
277 | Usually you should just call sendNotification
278 | and pass the parameters, never having to
279 | construct the notification yourself.
280 |
281 | @param notification: the C{INotification} to have the C{View} notify C{Observers} of.
282 | """
283 | if self.view is not None:
284 | self.view.notifyObservers(notification)
285 |
--------------------------------------------------------------------------------
/src/puremvc/patterns/mediator.py:
--------------------------------------------------------------------------------
1 | """
2 | PureMVC Python Port by Toby de Havilland
3 | PureMVC - Copyright(c) 2006-08 Futurescale, Inc., Some rights reserved.
4 | Your reuse is governed by the Creative Commons Attribution 3.0 License
5 | """
6 |
7 | import puremvc.interfaces
8 | import puremvc.patterns.observer
9 | import puremvc.patterns.facade
10 |
11 | class Mediator(puremvc.patterns.observer.Notifier, puremvc.interfaces.IMediator, puremvc.interfaces.INotifier):
12 | """
13 | A base C{IMediator} implementation.
14 |
15 | @see: L{View}
16 | """
17 |
18 | NAME = "Mediator"
19 |
20 | def __init__(self, mediatorName=None, viewComponent=None):
21 | """
22 | Mediator Constructor
23 |
24 | Typically, a C{Mediator} will be written to serve
25 | one specific control or group controls and so,
26 | will not have a need to be dynamically named.
27 | """
28 | self.facade = puremvc.patterns.facade.Facade.getInstance()
29 | mediatorName = mediatorName or self.NAME
30 | if mediatorName is None:
31 | raise ValueError("Mediator name cannot be None")
32 | self.mediatorName = mediatorName
33 | self.setViewComponent(viewComponent)
34 |
35 | def getMediatorName(self):
36 | """
37 | Get the name of the C{Mediator}.
38 | @return: the Mediator name
39 | """
40 | return self.mediatorName
41 |
42 | def setViewComponent(self,viewComponent):
43 | """
44 | Set the C{IMediator}'s view component.
45 |
46 | @param viewComponent: the view component
47 | """
48 | self.viewComponent = viewComponent
49 |
50 | def getViewComponent(self):
51 | """
52 | Get the C{Mediator}'s view component.
53 |
54 | @return: the view component
55 | """
56 | return self.viewComponent
57 |
58 | def listNotificationInterests(self):
59 | """
60 | List the C{INotification} names this
61 | C{Mediator} is interested in being notified of.
62 |
63 | @return: List the list of C{INotification} names
64 | """
65 | return []
66 |
67 | def handleNotification(self,notification):
68 | """
69 | Handle C{INotification}s.
70 |
71 | Typically this will be handled in a if/else statement,
72 | with one 'comparison' entry per C{INotification}
73 | the C{Mediator} is interested in.
74 | """
75 | pass
76 |
77 | def onRegister(self):
78 | """
79 | Called by the View when the Mediator is registered
80 | """
81 | pass
82 |
83 | def onRemove(self):
84 | """
85 | Called by the View when the Mediator is removed
86 | """
87 | pass
88 |
--------------------------------------------------------------------------------
/src/puremvc/patterns/observer.py:
--------------------------------------------------------------------------------
1 | """
2 | PureMVC Python Port by Toby de Havilland
3 | PureMVC - Copyright(c) 2006-08 Futurescale, Inc., Some rights reserved.
4 | Your reuse is governed by the Creative Commons Attribution 3.0 License
5 | """
6 |
7 | import puremvc.interfaces
8 | import puremvc.patterns.facade
9 |
10 | class Observer(puremvc.interfaces.IObserver):
11 | """
12 | A base C{IObserver} implementation.
13 |
14 | An C{Observer} is an object that encapsulates information
15 | about an interested object with a method that should
16 | be called when a particular C{INotification} is broadcast.
17 |
18 | In PureMVC, the C{Observer} class assumes these responsibilities:
19 |
20 | Encapsulate the notification (callback) method of the interested object.
21 |
22 | Encapsulate the notification context (this) of the interested object.
23 |
24 | Provide methods for setting the notification method and context.
25 |
26 | Provide a method for notifying the interested object.
27 |
28 | @see: L{View}
29 | @see: L{Notification}
30 | """
31 |
32 | def __init__(self, notifyMethod, notifyContext):
33 | """
34 | Constructor.
35 |
36 | The notification method on the interested object should take
37 | one parameter of type C{INotification}
38 |
39 | @param notifyMethod: the notification method of the interested object
40 | @param notifyContext: the notification context of the interested object
41 | """
42 | self.notify = None
43 | self.context = None
44 |
45 | self.setNotifyMethod(notifyMethod)
46 | self.setNotifyContext(notifyContext)
47 |
48 | def setNotifyMethod(self, notifyMethod):
49 | """
50 | Set the notification method.
51 |
52 | The notification method should take one parameter of type C{INotification}.
53 |
54 | @param notifyMethod: the notification (callback) method of the interested object.
55 | """
56 | self.notify = notifyMethod
57 |
58 | def setNotifyContext(self, notifyContext):
59 | """
60 | Set the notification context.
61 |
62 | @param notifyContext: the notification context (this) of the interested object.
63 | """
64 | self.context = notifyContext
65 |
66 | def getNotifyMethod(self):
67 | """
68 | Get the notification method.
69 |
70 | @return: the notification (callback) method of the interested object.
71 | """
72 | return self.notify
73 |
74 | def getNotifyContext(self):
75 | """
76 | Get the notification context.
77 |
78 | @return: the notification context (C{this}) of the interested object.
79 | """
80 | return self.context
81 |
82 | def notifyObserver(self, notification):
83 | """
84 | Notify the interested object.
85 |
86 | @param notification: the C{INotification} to pass to the interested object's notification method.
87 | """
88 | self.getNotifyMethod()(notification)
89 |
90 | def compareNotifyContext(self, obj):
91 | """
92 | Compare an object to the notification context.
93 |
94 | @param obj: the object to compare
95 | @return: boolean indicating if the object and the notification context are the same
96 | """
97 | return (obj is self.context)
98 |
99 |
100 | class Notifier(puremvc.interfaces.INotifier):
101 | """
102 | A Base C{INotifier} implementation.
103 |
104 | C{MacroCommand, Command, Mediator} and C{Proxy}
105 | all have a need to send C{Notifications}.
106 |
107 | The C{INotifier} interface provides a common method called
108 | C{sendNotification} that relieves implementation code of
109 | the necessity to actually construct C{Notifications}.
110 |
111 | The C{Notifier} class, which all of the above mentioned classes
112 | extend, provides an initialized reference to the C{Facade}
113 | Singleton, which is required for the convenience method
114 | for sending C{Notifications}, but also eases implementation as these
115 | classes have frequent C{Facade} interactions and usually require
116 | access to the facade anyway.
117 |
118 | @see: L{Facade}
119 | @see: L{Mediator}
120 | @see: L{Proxy}
121 | @see: L{SimpleCommand}
122 | @see: L{MacroCommand}
123 | """
124 |
125 | def __init__(self):
126 | """
127 | Notifier Constructor
128 | """
129 | self.facade = puremvc.patterns.facade.Facade.getInstance()
130 |
131 | def sendNotification(self, notificationName, body=None, noteType=None):
132 | """
133 | Create and send an C{INotification}.
134 |
135 | Keeps us from having to construct new INotification
136 | instances in our implementation code.
137 |
138 | @param notificationName: the name of the notification to send
139 | @param body: the body of the notification (optional)
140 | @param noteType: the type of the notification (optional)
141 | """
142 | self.facade.sendNotification(notificationName, body, noteType)
143 |
144 |
145 | class Notification(puremvc.interfaces.INotification):
146 | """
147 | A base C{INotification} implementation.
148 |
149 | PureMVC does not rely upon underlying event models such
150 | as the one provided with Flash, and ActionScript 3 does
151 | not have an inherent event model.
152 |
153 | The Observer Pattern as implemented within PureMVC exists
154 | to support event-driven communication between the
155 | application and the actors of the MVC triad.
156 |
157 | Notifications are not meant to be a replacement for Events
158 | in Flex/Flash/Apollo. Generally, C{IMediator} implementors
159 | place event listeners on their view components, which they
160 | then handle in the usual way. This may lead to the broadcast of C{Notification}s to
161 | trigger C{ICommand}s or to communicate with other C{IMediators}. C{IProxy} and C{ICommand}
162 | instances communicate with each other and C{IMediator}s
163 | by broadcasting C{INotification}s.
164 |
165 | A key difference between Flash C{Event}s and PureMVC
166 | C{Notification}s is that C{Event}s follow the
167 | 'Chain of Responsibility' pattern, 'bubbling' up the display hierarchy
168 | until some parent component handles the C{Event}, while
169 | PureMVC C{Notification}s follow a 'Publish/Subscribe'
170 | pattern. PureMVC classes need not be related to each other in a
171 | parent/child relationship in order to communicate with one another
172 | using C{Notification}s.
173 |
174 | @see: L{Observer}
175 | """
176 |
177 | def __init__(self, name, body=None, noteType=None):
178 | """
179 | Constructor.
180 |
181 | @param name: name of the C{Notification} instance. (required)
182 | @param body: the C{Notification} body. (optional)
183 | @param noteType: the type of the C{Notification} (optional)
184 | """
185 | self.name = name
186 | self.body = body
187 | self.type = noteType
188 |
189 | def getName(self):
190 | """
191 | Get the name of the C{Notification} instance.
192 |
193 | @return: the name of the C{Notification} instance.
194 | """
195 | return self.name
196 |
197 | def setBody(self, body):
198 | """
199 | Set the body of the C{Notification} instance.
200 | """
201 | self.body = body
202 |
203 | def getBody(self):
204 | """
205 | Get the body of the C{Notification} instance.
206 |
207 | @return: the body object.
208 | """
209 | return self.body
210 |
211 | def setType(self, noteType):
212 | """
213 | Set the type of the C{Notification} instance.
214 | """
215 | self.type = type;
216 |
217 | def getType(self):
218 | """
219 | Get the type of the C{Notification} instance.
220 |
221 | @return: the type
222 | """
223 | return self.type;
224 |
225 | def str(self):
226 | """
227 | Get the string representation of the C{Notification} instance.
228 |
229 | @return: the string representation of the C{Notification} instance.
230 | """
231 | msg = "Notification Name: "+self.getName();
232 |
233 | bd = "None"
234 | if self.body is not None:
235 | bd = str(self.body)
236 |
237 | ty = "None"
238 | if self.type is not None:
239 | ty = self.type
240 |
241 | msg += "\nBody:"+bd
242 | msg += "\nType:"+ty
243 | return msg;
244 |
--------------------------------------------------------------------------------
/src/puremvc/patterns/proxy.py:
--------------------------------------------------------------------------------
1 | """
2 | PureMVC Python Port by Toby de Havilland
3 | PureMVC - Copyright(c) 2006-08 Futurescale, Inc., Some rights reserved.
4 | Your reuse is governed by the Creative Commons Attribution 3.0 License
5 | """
6 |
7 | import puremvc.interfaces
8 | import puremvc.patterns.observer
9 | import puremvc.patterns.facade
10 |
11 | class Proxy(puremvc.patterns.observer.Notifier, puremvc.interfaces.IProxy, puremvc.interfaces.INotifier):
12 | """
13 | A base C{IProxy} implementation.
14 |
15 | In PureMVC, C{Proxy} classes are used to manage parts of the
16 | application's data model.
17 |
18 | A C{Proxy} might simply manage a reference to a local data object,
19 | in which case interacting with it might involve setting and
20 | getting of its data in synchronous fashion.
21 |
22 | C{Proxy} classes are also used to encapsulate the application's
23 | interaction with remote services to save or retrieve data, in which case,
24 | we adopt an asyncronous idiom; setting data (or calling a method) on the
25 | C{Proxy} and listening for a C{Notification} to be sent
26 | when the C{Proxy} has retrieved the data from the service.
27 |
28 | @see: L{Model}
29 | """
30 |
31 | NAME = "Proxy"
32 |
33 | def __init__(self, proxyName=None, data=None):
34 | """
35 | Proxy Constructor
36 |
37 | @param proxyName: the name of the proxy instance (optional)
38 | @param data: the proxy data (optional)
39 | """
40 | self.facade = puremvc.patterns.facade.Facade.getInstance()
41 | proxyName = proxyName or self.NAME
42 | if proxyName is None:
43 | raise ValueError("Proxy name cannot be None")
44 | self.proxyName = proxyName
45 | if data is not None:
46 | self.setData(data)
47 |
48 | def getProxyName(self):
49 | """
50 | Get the Proxy name
51 |
52 | @return: the proxy name
53 | """
54 | return self.proxyName
55 |
56 | def setData(self, data):
57 | """
58 | Set the Proxy data
59 |
60 | @param data: the Proxy data object
61 | """
62 | self.data = data
63 |
64 | def getData(self):
65 | """
66 | Get the proxy data
67 |
68 | @return: the Proxy data object
69 | """
70 | return self.data
71 |
72 | def onRegister(self):
73 | """
74 | Called by the Model when the Proxy is registered
75 | """
76 | pass
77 |
78 | def onRemove(self):
79 | """
80 | Called by the Model when the Proxy is removed
81 | """
82 | pass
83 |
--------------------------------------------------------------------------------
/tests/core/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PureMVC/puremvc-python-standard-framework/23aec5ef46291fac1e6fe6868f25e0e12abcd149/tests/core/__init__.py
--------------------------------------------------------------------------------
/tests/core/controller.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import puremvc.interfaces
3 | import puremvc.patterns.observer
4 | import puremvc.core
5 | import utils.controller
6 |
7 | class ControllerTest(unittest.TestCase):
8 | """ControllerTest: Test Controller Singleton"""
9 | def testAssertNotNone(self):
10 | """ControllerTest: Test instance not null"""
11 | controller = puremvc.core.Controller.getInstance()
12 | self.assertNotEqual(None, controller)
13 |
14 | def testAssertIController(self):
15 | """ControllerTest: Test instance implements IController"""
16 | controller = puremvc.core.Controller.getInstance()
17 | self.assertEqual(True, isinstance(controller, puremvc.interfaces.IController))
18 |
19 | def testRegisterAndExecuteCommand(self):
20 | """ControllerTest: Test registerCommand() and executeCommand()"""
21 | controller = puremvc.core.Controller.getInstance()
22 | controller.registerCommand('ControllerTest', utils.controller.ControllerTestCommand)
23 |
24 | vo = utils.controller.ControllerTestVO(12)
25 | note = puremvc.patterns.observer.Notification('ControllerTest', vo)
26 |
27 | controller.executeCommand(note)
28 |
29 | self.assertEqual(True, vo.result == 24 )
30 |
31 | def testRegisterAndRemoveCommand(self):
32 | """ControllerTest: Test registerCommand() and removeCommand()"""
33 | controller = puremvc.core.Controller.getInstance()
34 | controller.registerCommand('ControllerRemoveTest', utils.controller.ControllerTestCommand)
35 |
36 | vo = utils.controller.ControllerTestVO(12)
37 | note = puremvc.patterns.observer.Notification('ControllerRemoveTest', vo)
38 |
39 | controller.executeCommand(note)
40 |
41 | self.assertEqual(True, vo.result == 24 )
42 |
43 | vo.result = 0
44 |
45 | controller.removeCommand('ControllerRemoveTest')
46 | controller.executeCommand(note)
47 |
48 | self.assertEqual(True, vo.result == 0)
49 |
50 | def testHasCommand(self):
51 | """ControllerTest: Test hasCommand()"""
52 |
53 | controller = puremvc.core.Controller.getInstance()
54 | controller.registerCommand('hasCommandTest', utils.controller.ControllerTestCommand)
55 |
56 | self.assertEqual(True, controller.hasCommand('hasCommandTest'))
57 |
58 | controller.removeCommand('hasCommandTest')
59 |
60 | self.assertEqual(False, controller.hasCommand('hasCommandTest'))
61 |
--------------------------------------------------------------------------------
/tests/core/model.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | import puremvc.interfaces
4 | import puremvc.patterns.proxy
5 | import puremvc.core
6 | import utils.model
7 |
8 | class ModelTest(unittest.TestCase):
9 | """ModelTest: Test Model Singleton"""
10 | def testAssertNotNone(self):
11 | """ModelTest: Test instance not null"""
12 | model = puremvc.core.Model.getInstance()
13 | self.assertNotEqual(None, model)
14 |
15 | def testAssertIModel(self):
16 | """ModelTest: Test instance implements IModel"""
17 | model = puremvc.core.Model.getInstance()
18 | self.assertEqual(True, isinstance(model, puremvc.interfaces.IModel))
19 |
20 | def testRegisterAndRetrieveProxy(self):
21 | """ModelTest: Test registerProxy() and retrieveProxy()"""
22 | model = puremvc.core.Model.getInstance()
23 | model.registerProxy(puremvc.patterns.proxy.Proxy('colors', ['red', 'green', 'blue']))
24 | testProxy = model.retrieveProxy('colors')
25 | data = testProxy.getData()
26 |
27 | self.assertNotEqual(None, data)
28 | self.assertEqual(True, isinstance(data, list))
29 | self.assertEqual(True, len(data) == 3 )
30 | self.assertEqual(True, data[0] == 'red' )
31 | self.assertEqual(True, data[1] == 'green' )
32 | self.assertEqual(True, data[2] == 'blue' )
33 |
34 | def testRegisterAndRemoveProxy(self):
35 | """ModelTest: Test registerProxy() and removeProxy()"""
36 | model = puremvc.core.Model.getInstance()
37 | testProxy = puremvc.patterns.proxy.Proxy('sizes', ['7', '13', '21'])
38 | model.registerProxy(testProxy)
39 |
40 | removedProxy = model.removeProxy('sizes')
41 |
42 | self.assertEqual(True,removedProxy.getProxyName() == 'sizes')
43 |
44 | testProxy = model.retrieveProxy('sizes')
45 |
46 | self.assertEqual(None, testProxy)
47 |
48 | def testHasProxy(self):
49 | """ModelTest: Test hasProxy()"""
50 |
51 | model = puremvc.core.Model.getInstance()
52 | testProxy = puremvc.patterns.proxy.Proxy('aces', ['clubs', 'spades', 'hearts', 'diamonds'])
53 | model.registerProxy(testProxy)
54 |
55 | self.assertEqual(True, model.hasProxy('aces'))
56 |
57 | model.removeProxy('aces')
58 |
59 | self.assertEqual(False, model.hasProxy('aces'))
60 |
61 |
62 | def testOnRegisterAndOnRemove(self):
63 | """ModelTest: Test onRegister() and onRemove()"""
64 |
65 | model = puremvc.core.Model.getInstance()
66 |
67 | testProxy = utils.model.ModelTestProxy()
68 | model.registerProxy(testProxy)
69 |
70 | self.assertEqual(True, testProxy.getData() == utils.model.ModelTestProxy.ON_REGISTER_CALLED)
71 |
72 | model.removeProxy(utils.model.ModelTestProxy.NAME)
73 |
74 | self.assertEqual(True, testProxy.getData() == utils.model.ModelTestProxy.ON_REMOVE_CALLED)
75 |
--------------------------------------------------------------------------------
/tests/core/view.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | import puremvc.interfaces
4 | import puremvc.patterns.observer
5 | import puremvc.patterns.mediator
6 | import puremvc.core
7 | import utils.view
8 |
9 | class ViewTest(unittest.TestCase):
10 | """ViewTest: Test View Singleton"""
11 |
12 | lastNotification = None
13 | onRegisterCalled = False
14 | onRemoveCalled = False
15 | counter = 0
16 |
17 | NOTE1 = "note1"
18 | NOTE2 = "note2"
19 | NOTE3 = "note3"
20 | NOTE5 = "note5"
21 |
22 | def __cleanup(self):
23 | puremvc.core.View.getInstance().removeMediator(utils.view.ViewTestMediator.NAME)
24 | puremvc.core.View.getInstance().removeMediator(utils.view.ViewTestMediator2.NAME)
25 | puremvc.core.View.getInstance().removeMediator(utils.view.ViewTestMediator3.NAME)
26 | puremvc.core.View.getInstance().removeMediator(utils.view.ViewTestMediator4.NAME)
27 | puremvc.core.View.getInstance().removeMediator(utils.view.ViewTestMediator5.NAME)
28 |
29 | def testAssertNotNone(self):
30 | """ViewTest: Test instance not null"""
31 | view = puremvc.core.View.getInstance()
32 | self.assertNotEqual(None, view)
33 |
34 | def testAssertIView(self):
35 | """ViewTest: Test instance implements IView"""
36 | view = puremvc.core.View.getInstance()
37 | self.assertEqual(True, isinstance(view, puremvc.interfaces.IView))
38 |
39 | def testRegisterAndNotifyObserver(self):
40 | """ViewTest: Test registerObserver() and notifyObservers()"""
41 |
42 | self.viewTestVar = 0
43 | def viewTestMethod(note):
44 | self.viewTestVar = note.getBody()
45 |
46 | view = puremvc.core.View.getInstance()
47 | obsvr = puremvc.patterns.observer.Observer(viewTestMethod, self)
48 | view.registerObserver(utils.view.ViewTestNote.NAME, obsvr)
49 |
50 | note = utils.view.ViewTestNote.create(10)
51 | view.notifyObservers(note)
52 |
53 | self.assertEqual(True, self.viewTestVar == 10)
54 |
55 | def testRegisterAndRetrieveMediator(self):
56 | """ViewTest: Test registerMediator() and retrieveMediator()"""
57 | view = puremvc.core.View.getInstance()
58 |
59 | viewTestMediator = utils.view.ViewTestMediator(self)
60 | view.registerMediator(viewTestMediator)
61 |
62 | mediator = view.retrieveMediator(utils.view.ViewTestMediator.NAME)
63 |
64 | self.assertEqual(True, isinstance(mediator, utils.view.ViewTestMediator))
65 | self.__cleanup()
66 |
67 | def testHasMediator(self):
68 | """ViewTest: Test hasMediator()"""
69 | view = puremvc.core.View.getInstance()
70 | meditr = puremvc.patterns.mediator.Mediator('hasMediatorTest', self)
71 | view.registerMediator(meditr)
72 |
73 | self.assertEqual(True, view.hasMediator('hasMediatorTest'))
74 |
75 | view.removeMediator('hasMediatorTest')
76 |
77 | self.assertEqual(False, view.hasMediator('hasMediatorTest'))
78 | self.__cleanup()
79 |
80 | def testRegisterAndRemoveMediator(self):
81 | """ViewTest: Test registerMediator() and removeMediator()"""
82 | view = puremvc.core.View.getInstance()
83 |
84 | meditr = puremvc.patterns.mediator.Mediator('testing', self)
85 | view.registerMediator(meditr)
86 |
87 | removedMediator = view.removeMediator('testing')
88 |
89 | self.assertEqual(True, removedMediator.getMediatorName() == 'testing')
90 |
91 | self.assertEqual(True, view.retrieveMediator('testing') == None)
92 | self.__cleanup()
93 |
94 | def testOnRegisterAndOnRemove(self):
95 | """ViewTest: Test onRegsiter() and onRemove()"""
96 | view = puremvc.core.View.getInstance()
97 |
98 | mediator = utils.view.ViewTestMediator4(self)
99 | view.registerMediator(mediator)
100 |
101 | self.assertEqual(True, self.onRegisterCalled)
102 |
103 | view.removeMediator(utils.view.ViewTestMediator4.NAME)
104 |
105 | self.assertEqual(True, self.onRemoveCalled)
106 | self.__cleanup()
107 |
108 |
109 | def testSuccessiveRegisterAndRemoveMediator(self):
110 | """ViewTest: Test Successive registerMediator() and removeMediator()"""
111 | view = puremvc.core.View.getInstance()
112 |
113 | view.registerMediator(utils.view.ViewTestMediator(self))
114 |
115 | self.assertEqual(True, isinstance(view.retrieveMediator(utils.view.ViewTestMediator.NAME), utils.view.ViewTestMediator))
116 |
117 | view.removeMediator(utils.view.ViewTestMediator.NAME)
118 |
119 | self.assertEqual(True, view.retrieveMediator(utils.view.ViewTestMediator.NAME) == None)
120 |
121 | self.assertEqual(True, view.removeMediator(utils.view.ViewTestMediator.NAME) == None)
122 |
123 | view.registerMediator(utils.view.ViewTestMediator(self))
124 |
125 | self.assertEqual(True, isinstance(view.retrieveMediator(utils.view.ViewTestMediator.NAME), utils.view.ViewTestMediator))
126 |
127 | view.removeMediator(utils.view.ViewTestMediator.NAME)
128 |
129 | self.assertEqual(True, view.retrieveMediator(utils.view.ViewTestMediator.NAME) == None)
130 |
131 | self.__cleanup()
132 |
133 | def testRemoveMediatorAndSubsequentNotify(self):
134 | """ViewTest: Test removeMediator() and subsequent nofity()"""
135 |
136 | view = puremvc.core.View.getInstance()
137 |
138 | view.registerMediator(utils.view.ViewTestMediator2(self))
139 |
140 | view.notifyObservers(puremvc.patterns.observer.Notification(self.NOTE1))
141 | self.assertEqual(True, self.lastNotification == self.NOTE1)
142 |
143 | view.notifyObservers(puremvc.patterns.observer.Notification(self.NOTE2))
144 | self.assertEqual(True, self.lastNotification == self.NOTE2)
145 |
146 | view.removeMediator(utils.view.ViewTestMediator2.NAME)
147 |
148 | self.assertEqual(True, view.retrieveMediator(utils.view.ViewTestMediator2.NAME) == None)
149 |
150 | self.lastNotification = None
151 |
152 | view.notifyObservers(puremvc.patterns.observer.Notification(self.NOTE1))
153 | self.assertEqual(True, self.lastNotification != self.NOTE1)
154 |
155 | view.notifyObservers(puremvc.patterns.observer.Notification(self.NOTE2))
156 | self.assertEqual(True, self.lastNotification != self.NOTE2)
157 |
158 | self.__cleanup()
159 |
160 | def testRemoveOneOfTwoMediatorsAndSubsequentNotify(self):
161 | """ViewTest: Test removing one of two Mediators and subsequent notify()"""
162 |
163 | view = puremvc.core.View.getInstance()
164 |
165 | view.registerMediator(utils.view.ViewTestMediator2(self))
166 |
167 | view.registerMediator(utils.view.ViewTestMediator3(self))
168 |
169 | view.notifyObservers(puremvc.patterns.observer.Notification(self.NOTE1))
170 | self.assertEqual(True, self.lastNotification == self.NOTE1)
171 |
172 | view.notifyObservers(puremvc.patterns.observer.Notification(self.NOTE2))
173 | self.assertEqual(True, self.lastNotification == self.NOTE2)
174 |
175 | view.notifyObservers(puremvc.patterns.observer.Notification(self.NOTE3))
176 | self.assertEqual(True, self.lastNotification == self.NOTE3)
177 |
178 | view.removeMediator(utils.view.ViewTestMediator2.NAME)
179 |
180 | self.assertEqual(True, view.retrieveMediator(utils.view.ViewTestMediator2.NAME) == None)
181 |
182 | self.lastNotification = None
183 |
184 | view.notifyObservers(puremvc.patterns.observer.Notification(self.NOTE1))
185 | self.assertEqual(True, self.lastNotification != self.NOTE1)
186 |
187 | view.notifyObservers(puremvc.patterns.observer.Notification(self.NOTE2))
188 | self.assertEqual(True, self.lastNotification != self.NOTE2)
189 |
190 | view.notifyObservers(puremvc.patterns.observer.Notification(self.NOTE3))
191 | self.assertEqual(True, self.lastNotification == self.NOTE3)
192 |
193 | self.__cleanup()
194 |
195 | def testMediatorReregistration(self):
196 | """
197 | Tests registering the same mediator twice.
198 | A subsequent notification should only illicit
199 | one response. Also, since reregistration
200 | was causing 2 observers to be created, ensure
201 | that after removal of the mediator there will
202 | be no further response.
203 |
204 | Added for the fix deployed in version 2.0.4
205 | """
206 |
207 | view = puremvc.core.View.getInstance()
208 |
209 | view.registerMediator(utils.view.ViewTestMediator5(self))
210 |
211 | # try to register another instance of that mediator (uses the same NAME constant).
212 | view.registerMediator(utils.view.ViewTestMediator5(self))
213 |
214 | self.counter = 0
215 | view.notifyObservers(puremvc.patterns.observer.Notification(self.NOTE5))
216 | self.assertEqual(1, self.counter)
217 |
218 | view.removeMediator(utils.view.ViewTestMediator5.NAME)
219 |
220 | self.assertEqual(True, view.retrieveMediator(utils.view.ViewTestMediator5.NAME ) == None)
221 |
222 | self.counter=0
223 | view.notifyObservers(puremvc.patterns.observer.Notification(self.NOTE5))
224 | self.assertEqual(0, self.counter)
225 |
226 | def testRemoveSelf(self):
227 | view = puremvc.core.View.getInstance()
228 | view.registerMediator(utils.view.ViewTestMediator2(self))
229 | view.registerMediator(utils.view.ViewTestMediator3(self))
230 |
231 | self.assertTrue(self.NOTE5 in view.observerMap)
232 | view.notifyObservers(puremvc.patterns.observer.Notification(self.NOTE5))
233 | self.assertFalse(self.NOTE5 in view.observerMap)
234 |
--------------------------------------------------------------------------------
/tests/main.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Add src folder to Python search paths
4 | import sys
5 |
6 | sys.path.insert(1, "src")
7 |
8 | # Normal imports
9 | import core.controller
10 | import core.model
11 | import core.view
12 | import patterns.command
13 | import patterns.facade
14 | import patterns.mediator
15 | import patterns.observer
16 | import patterns.proxy
17 | import unittest
18 |
19 |
20 | if __name__ == '__main__':
21 | # List of test cases classes
22 | testCases = (core.controller.ControllerTest,
23 | core.model.ModelTest,
24 | core.view.ViewTest,
25 |
26 | patterns.command.CommandTest,
27 | patterns.facade.FacadeTest,
28 | patterns.mediator.MediatorTest,
29 | patterns.observer.ObserverTest,
30 | patterns.proxy.ProxyTest)
31 |
32 | # Discover all the test cases
33 | loader = unittest.TestLoader()
34 | suites = []
35 |
36 | for testCase in testCases:
37 | suites.append(loader.loadTestsFromTestCase(testCase))
38 |
39 | # Run test suite
40 | unittest.TextTestRunner(verbosity=2).run(unittest.TestSuite(suites))
41 |
--------------------------------------------------------------------------------
/tests/patterns/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PureMVC/puremvc-python-standard-framework/23aec5ef46291fac1e6fe6868f25e0e12abcd149/tests/patterns/__init__.py
--------------------------------------------------------------------------------
/tests/patterns/command.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | import puremvc.patterns.observer
4 | import utils.command
5 |
6 | class CommandTest(unittest.TestCase):
7 | """CommandTest: Test Command Pattern"""
8 |
9 | def testMacroCommandExecute(self):
10 | """CommandTest: Test MacroCommand execute()"""
11 |
12 | vo = utils.command.MacroCommandTestVO(5)
13 | note = puremvc.patterns.observer.Notification('MacroCommandTest', vo)
14 | command = utils.command.MacroCommandTestCommand()
15 | command.execute(note);
16 |
17 | self.assertEqual(True, vo.result1 == 10)
18 | self.assertEqual(True, vo.result2 == 25)
19 |
20 | def testSimpleCommandExecute(self):
21 | """CommandTest: Test SimpleCommand execute()"""
22 |
23 | vo = utils.command.SimpleCommandTestVO(5)
24 | note = puremvc.patterns.observer.Notification('SimpleCommandTestNote', vo)
25 | command = utils.command.SimpleCommandTestCommand()
26 | command.execute(note);
27 |
28 | self.assertEqual(True, vo.result == 10)
29 |
--------------------------------------------------------------------------------
/tests/patterns/facade.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | import puremvc.interfaces
4 | import puremvc.patterns.proxy
5 | import puremvc.patterns.mediator
6 | import puremvc.patterns.facade
7 | import utils.facade
8 |
9 | class FacadeTest(unittest.TestCase):
10 | """FacadeTest: Test Facade Pattern"""
11 |
12 | def testAssertNotNone(self):
13 | """FacadeTest: Test instance not null"""
14 | fcde = puremvc.patterns.facade.Facade.getInstance()
15 | self.assertNotEqual(None, fcde)
16 |
17 | def testAssertIFacade(self):
18 | """FacadeTest: Test instance implements IFacade"""
19 | fcde = puremvc.patterns.facade.Facade.getInstance()
20 | self.assertEqual(True, isinstance(fcde, puremvc.interfaces.IFacade))
21 |
22 | def testRegisterCommandAndSendNotification(self):
23 | """FacadeTest: Test registerCommand() and sendNotification()"""
24 |
25 | fcde = puremvc.patterns.facade.Facade.getInstance()
26 | fcde.registerCommand('FacadeTestNote', utils.facade.FacadeTestCommand)
27 |
28 | vo = utils.facade.FacadeTestVO(32)
29 | fcde.sendNotification('FacadeTestNote', vo)
30 |
31 | self.assertEqual(True, vo.result == 64)
32 |
33 | def testRegisterAndRemoveCommandAndSendNotification(self):
34 | """FacadeTest: Test removeCommand() and subsequent sendNotification()"""
35 | fcde = puremvc.patterns.facade.Facade.getInstance()
36 | fcde.registerCommand('FacadeTestNote', utils.facade.FacadeTestCommand)
37 | fcde.removeCommand('FacadeTestNote')
38 |
39 | vo = utils.facade.FacadeTestVO(32)
40 | fcde.sendNotification('FacadeTestNote', vo)
41 |
42 | self.assertEqual(True, vo.result != 64)
43 |
44 | def testRegisterAndRetrieveProxy(self):
45 | """FacadeTest: Test registerProxy() and retrieveProxy()"""
46 | fcde = puremvc.patterns.facade.Facade.getInstance()
47 | fcde.registerProxy(puremvc.patterns.proxy.Proxy('colors', ['red', 'green', 'blue']))
48 | pxy = fcde.retrieveProxy('colors')
49 |
50 | self.assertEqual(True, isinstance(pxy, puremvc.interfaces.IProxy))
51 |
52 | data = pxy.getData()
53 |
54 | self.assertEqual(True, data is not None)
55 | self.assertEqual(True, isinstance(data, list))
56 | self.assertEqual(True, len(data) == 3)
57 | self.assertEqual(True, data[0] == 'red')
58 | self.assertEqual(True, data[1] == 'green')
59 | self.assertEqual(True, data[2] == 'blue')
60 |
61 | def testRegisterAndRemoveProxy(self):
62 | """FacadeTest: Test registerProxy() and removeProxy()"""
63 |
64 | fcde = puremvc.patterns.facade.Facade.getInstance()
65 | pxy = puremvc.patterns.proxy.Proxy('sizes', ['7', '13', '21'])
66 | fcde.registerProxy(pxy)
67 |
68 | removedProxy = fcde.removeProxy('sizes')
69 |
70 | self.assertEqual(True, removedProxy.getProxyName() == 'sizes')
71 |
72 | pxy = fcde.retrieveProxy('sizes')
73 |
74 | self.assertEqual(True, pxy == None)
75 |
76 | def testRegisterRetrieveAndRemoveMediator(self):
77 | """FacadeTest: Test registerMediator() retrieveMediator() and removeMediator()"""
78 |
79 | fcde = puremvc.patterns.facade.Facade.getInstance()
80 | fcde.registerMediator(puremvc.patterns.mediator.Mediator(puremvc.patterns.mediator.Mediator.NAME, object()))
81 |
82 | self.assertEqual(True, fcde.retrieveMediator(puremvc.patterns.mediator.Mediator.NAME) is not None)
83 |
84 | removedMediator = fcde.removeMediator(puremvc.patterns.mediator.Mediator.NAME)
85 |
86 | self.assertEqual(True, removedMediator.getMediatorName() == puremvc.patterns.mediator.Mediator.NAME)
87 |
88 | self.assertEqual(True, fcde.retrieveMediator(puremvc.patterns.mediator.Mediator.NAME) == None)
89 |
90 | def testHasProxy(self):
91 | """FacadeTest: Test hasProxy()"""
92 |
93 | fcde = puremvc.patterns.facade.Facade.getInstance()
94 | fcde.registerProxy(puremvc.patterns.proxy.Proxy('hasProxyTest', [1,2,3]))
95 |
96 | self.assertEqual(True, fcde.hasProxy('hasProxyTest'))
97 |
98 | fcde.removeProxy('hasProxyTest')
99 |
100 | self.assertEqual(False, fcde.hasProxy('hasProxyTest'))
101 |
102 | def testHasMediator(self):
103 | """FacadeTest: Test hasMediator()"""
104 |
105 | fcde = puremvc.patterns.facade.Facade.getInstance()
106 | fcde.registerMediator(puremvc.patterns.mediator.Mediator('facadeHasMediatorTest', object()))
107 |
108 | self.assertEqual(True, fcde.hasMediator('facadeHasMediatorTest'))
109 |
110 | fcde.removeMediator('facadeHasMediatorTest')
111 |
112 | self.assertEqual(False, fcde.hasMediator('facadeHasMediatorTest'))
113 |
114 | def testHasCommand(self):
115 | """FacadeTest: Test hasCommand()"""
116 | fcde = puremvc.patterns.facade.Facade.getInstance()
117 | fcde.registerCommand('facadeHasCommandTest', utils.facade.FacadeTestCommand)
118 |
119 | self.assertEqual(True, fcde.hasCommand('facadeHasCommandTest'))
120 |
121 | fcde.removeCommand('facadeHasCommandTest')
122 |
123 | self.assertEqual(False, fcde.hasCommand('facadeHasCommandTest'))
124 |
--------------------------------------------------------------------------------
/tests/patterns/mediator.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | import puremvc.patterns.mediator as mediator
4 |
5 | class MediatorTest(unittest.TestCase):
6 | """MediatorTest: Test Mediator Pattern"""
7 |
8 | def testNameAccessor(self):
9 | """MediatorTest: Test getMediatorName()"""
10 | mdiatr = mediator.Mediator();
11 | self.assertEqual(True, mdiatr.getMediatorName() == mediator.Mediator.NAME );
12 |
13 | def testViewAccessor(self):
14 | """MediatorTest: Test getViewComponent()"""
15 |
16 | view = object()
17 | mdiatr = mediator.Mediator(mediator.Mediator.NAME, view);
18 | self.assertEqual(True, mdiatr.getViewComponent() is not None)
19 |
--------------------------------------------------------------------------------
/tests/patterns/observer.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | import puremvc.patterns.observer
4 |
5 | class ObserverTest(unittest.TestCase):
6 | """ObserverTest: Test Observer Pattern"""
7 |
8 | __observerTestVar = None
9 |
10 | def __observerTestMethod(self,note):
11 | self.__observerTestVar = note.getBody()
12 |
13 | def testObserverAccessors(self):
14 | """ObserverTest: Test Observer Accessors"""
15 |
16 | obsrvr = puremvc.patterns.observer.Observer(None,None)
17 | obsrvr.setNotifyContext(self)
18 |
19 | obsrvr.setNotifyMethod(self.__observerTestMethod)
20 |
21 | note = puremvc.patterns.observer.Notification('ObserverTestNote',10)
22 | obsrvr.notifyObserver(note)
23 |
24 | self.assertEqual(True, self.__observerTestVar == 10)
25 |
26 | def testObserverConstructor(self):
27 | """ObserverTest: Test Observer Constructor"""
28 |
29 | obsrvr = puremvc.patterns.observer.Observer(self.__observerTestMethod,self)
30 |
31 | note = puremvc.patterns.observer.Notification('ObserverTestNote',5)
32 | obsrvr.notifyObserver(note)
33 |
34 | self.assertEqual(True, self.__observerTestVar == 5)
35 |
36 | def testCompareNotifyContext(self):
37 | """ObserverTest: Test compareNotifyContext()"""
38 |
39 | obsrvr = puremvc.patterns.observer.Observer(self.__observerTestMethod, self)
40 |
41 | negTestObj = object()
42 |
43 | self.assertEqual(False, obsrvr.compareNotifyContext(negTestObj))
44 | self.assertEqual(True, obsrvr.compareNotifyContext(self))
45 |
46 | def testNameAccessors(self):
47 | """NotificationTest: Test Name Accessors"""
48 |
49 | note = puremvc.patterns.observer.Notification('TestNote')
50 |
51 | self.assertEqual(True, note.getName() == 'TestNote')
52 |
53 | def testBodyAccessors(self):
54 | """NotificationTest: Test Body Accessors"""
55 |
56 | note = puremvc.patterns.observer.Notification(None)
57 | note.setBody(5)
58 |
59 | self.assertEqual(True, note.getBody() == 5)
60 |
61 | def testConstructor(self):
62 | """NotificationTest: Test Constructor"""
63 |
64 | note = puremvc.patterns.observer.Notification('TestNote',5,'TestNoteType')
65 |
66 | self.assertEqual(True, note.getName() == 'TestNote')
67 | self.assertEqual(True, note.getBody() == 5)
68 | self.assertEqual(True, note.getType() == 'TestNoteType')
69 |
--------------------------------------------------------------------------------
/tests/patterns/proxy.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | import puremvc.patterns.proxy
4 |
5 | class ProxyTest(unittest.TestCase):
6 | """ProxyTest: Test Proxy Pattern"""
7 |
8 | def testNameAccessor(self):
9 | """ProxyTest: Test Name Accessor"""
10 |
11 | prxy = puremvc.patterns.proxy.Proxy('TestProxy')
12 | self.assertEqual(True, prxy.getProxyName() == 'TestProxy')
13 |
14 | def testDataAccessors(self):
15 | """ProxyTest: Test Data Accessors"""
16 |
17 | prxy = puremvc.patterns.proxy.Proxy('colors')
18 | prxy.setData(['red', 'green', 'blue'])
19 | data = prxy.getData()
20 |
21 | self.assertEqual(True, len(data) == 3)
22 | self.assertEqual(True, data[0] == 'red')
23 | self.assertEqual(True, data[1] == 'green')
24 | self.assertEqual(True, data[2] == 'blue')
25 |
26 | def testConstructor(self):
27 | """ProxyTest: Test Constructor"""
28 |
29 | prxy = puremvc.patterns.proxy.Proxy('colors',['red', 'green', 'blue'])
30 | data = prxy.getData()
31 |
32 | self.assertEqual(True, prxy is not None)
33 | self.assertEqual(True, prxy.getProxyName() == 'colors')
34 | self.assertEqual(True, len(data) == 3)
35 | self.assertEqual(True, data[0] == 'red')
36 | self.assertEqual(True, data[1] == 'green')
37 | self.assertEqual(True, data[2] == 'blue')
38 |
39 | def testEmptyData(self):
40 | """ProxyTest: Test Constructor with not-null empty data"""
41 |
42 | values = ["", (), []]
43 |
44 | for value in values:
45 | proxy = puremvc.patterns.proxy.Proxy("empty", value)
46 |
47 | self.assertEqual(proxy.data, value)
48 |
--------------------------------------------------------------------------------
/tests/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PureMVC/puremvc-python-standard-framework/23aec5ef46291fac1e6fe6868f25e0e12abcd149/tests/utils/__init__.py
--------------------------------------------------------------------------------
/tests/utils/command.py:
--------------------------------------------------------------------------------
1 | import puremvc.patterns.command
2 |
3 | class MacroCommandTestCommand(puremvc.patterns.command.MacroCommand):
4 | def initializeMacroCommand(self):
5 | self.addSubCommand(MacroCommandTestSub1Command)
6 | self.addSubCommand(MacroCommandTestSub2Command)
7 |
8 | class MacroCommandTestSub1Command(puremvc.patterns.command.SimpleCommand):
9 | def execute(self,note):
10 | vo = note.getBody()
11 | vo.result1 = 2 * vo.input
12 |
13 | class MacroCommandTestSub2Command(puremvc.patterns.command.SimpleCommand):
14 | def execute(self,note):
15 | vo = note.getBody()
16 | vo.result2 = vo.input * vo.input
17 |
18 | class MacroCommandTestVO(object):
19 |
20 | input = None
21 | result1 = None
22 | result2 = None
23 |
24 | def __init__(self, input_):
25 | self.input = input_
26 |
27 | class SimpleCommandTestCommand(puremvc.patterns.command.SimpleCommand):
28 | def execute(self,note):
29 | vo = note.getBody()
30 | vo.result = 2 * vo.input
31 |
32 | class SimpleCommandTestVO(object):
33 |
34 | input = None
35 | result = None
36 |
37 | def __init__(self, input_):
38 | self.input = input_
39 |
--------------------------------------------------------------------------------
/tests/utils/controller.py:
--------------------------------------------------------------------------------
1 | import puremvc.patterns.command
2 |
3 | class ControllerTestCommand(puremvc.patterns.command.SimpleCommand):
4 |
5 | def execute(self, note):
6 | vo = note.getBody()
7 | vo.result = 2 * vo.input;
8 |
9 | class ControllerTestVO(object):
10 |
11 | input = 0
12 | result = 0
13 |
14 | def __init__(self, num=0):
15 | self.input = num
16 |
--------------------------------------------------------------------------------
/tests/utils/facade.py:
--------------------------------------------------------------------------------
1 | import puremvc.patterns.command
2 |
3 | class FacadeTestCommand(puremvc.patterns.command.SimpleCommand):
4 | def execute(self,note):
5 | vo = note.getBody()
6 | vo.result = 2 * vo.input;
7 |
8 | class FacadeTestVO(object):
9 |
10 | input = None
11 | result = None
12 |
13 | def __init__(self,input):
14 | self.input = input
15 |
--------------------------------------------------------------------------------
/tests/utils/model.py:
--------------------------------------------------------------------------------
1 | import puremvc.patterns.proxy
2 |
3 | class ModelTestProxy(puremvc.patterns.proxy.Proxy):
4 | NAME = 'ModelTestProxy'
5 | ON_REGISTER_CALLED = 'onRegister Called'
6 | ON_REMOVE_CALLED = 'onRemove Called'
7 |
8 | def __init__(self):
9 | puremvc.patterns.proxy.Proxy.__init__(self, ModelTestProxy.NAME, object())
10 |
11 | def onRegister(self):
12 | self.setData(ModelTestProxy.ON_REGISTER_CALLED)
13 |
14 | def onRemove(self):
15 | self.setData(ModelTestProxy.ON_REMOVE_CALLED)
16 |
--------------------------------------------------------------------------------
/tests/utils/view.py:
--------------------------------------------------------------------------------
1 | import puremvc.patterns.observer
2 | import puremvc.interfaces
3 | import puremvc.patterns.mediator
4 |
5 | class ViewTestNote(puremvc.patterns.observer.Notification, puremvc.interfaces.INotification):
6 |
7 | NAME = "ViewTestNote"
8 |
9 | def __init__(self, anme, body):
10 | puremvc.patterns.observer.Notification.__init__(self, ViewTestNote.NAME, body)
11 |
12 | @staticmethod
13 | def create(body):
14 | return ViewTestNote(ViewTestNote.NAME, body)
15 |
16 | class ViewTestMediator(puremvc.patterns.mediator.Mediator, puremvc.interfaces.IMediator):
17 |
18 | NAME = 'ViewTestMediator'
19 |
20 | def __init__(self, view):
21 | puremvc.patterns.mediator.Mediator.__init__(self, ViewTestMediator.NAME, view)
22 |
23 | def listNotificationInterests(self):
24 | return ['ABC', 'DEF', 'GHI']
25 |
26 | class ViewTestMediator2(puremvc.patterns.mediator.Mediator, puremvc.interfaces.IMediator):
27 |
28 | NAME = 'ViewTestMediator2'
29 |
30 | def __init__(self, view):
31 | puremvc.patterns.mediator.Mediator.__init__(self, ViewTestMediator2.NAME, view)
32 |
33 | def listNotificationInterests(self):
34 | return [
35 | self.viewComponent.NOTE1,
36 | self.viewComponent.NOTE2,
37 | self.viewComponent.NOTE5,
38 | ]
39 |
40 | def handleNotification(self, notification):
41 | self.viewComponent.lastNotification = notification.getName()
42 | if notification.getName() == self.viewComponent.NOTE5:
43 | self.facade.removeMediator(self.NAME)
44 |
45 | class ViewTestMediator3(puremvc.patterns.mediator.Mediator, puremvc.interfaces.IMediator):
46 |
47 | NAME = 'ViewTestMediator3'
48 |
49 | def __init__(self, view):
50 | puremvc.patterns.mediator.Mediator.__init__(self, ViewTestMediator3.NAME, view)
51 |
52 | def listNotificationInterests(self):
53 | return [
54 | self.viewComponent.NOTE3,
55 | self.viewComponent.NOTE5,
56 | ]
57 |
58 | def handleNotification(self, notification):
59 | self.viewComponent.lastNotification = notification.getName()
60 | if notification.getName() == self.viewComponent.NOTE5:
61 | self.facade.removeMediator(self.NAME)
62 |
63 | class ViewTestMediator4(puremvc.patterns.mediator.Mediator, puremvc.interfaces.IMediator):
64 |
65 | NAME = 'ViewTestMediator4'
66 |
67 | def __init__(self, view):
68 | puremvc.patterns.mediator.Mediator.__init__(self, ViewTestMediator4.NAME, view)
69 |
70 | def onRegister(self):
71 | self.viewComponent.onRegisterCalled = True
72 |
73 | def onRemove(self):
74 | self.viewComponent.onRemoveCalled = True
75 |
76 | class ViewTestMediator5(puremvc.patterns.mediator.Mediator, puremvc.interfaces.IMediator):
77 |
78 | NAME = 'ViewTestMediator5'
79 |
80 | def __init__(self, view):
81 | puremvc.patterns.mediator.Mediator.__init__(self, ViewTestMediator5.NAME, view)
82 |
83 | def listNotificationInterests(self):
84 | return [self.viewComponent.NOTE5]
85 |
86 | def handleNotification(self, notification):
87 | self.viewComponent.counter += 1
88 |
--------------------------------------------------------------------------------