puremvc 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +11 -0
- data/LICENSE +28 -0
- data/README.md +88 -0
- data/lib/core/controller.rb +177 -0
- data/lib/core/model.rb +133 -0
- data/lib/core/view.rb +230 -0
- data/lib/patterns/command/macro_command.rb +81 -0
- data/lib/patterns/command/simple_command.rb +28 -0
- data/lib/patterns/facade/facade.rb +267 -0
- data/lib/patterns/mediator/mediator.rb +56 -0
- data/lib/patterns/observer/notification.rb +68 -0
- data/lib/patterns/observer/notifier.rb +87 -0
- data/lib/patterns/observer/observer.rb +55 -0
- data/lib/patterns/proxy/proxy.rb +50 -0
- data/lib/puremvc.rb +19 -0
- data/sig/lib/core/controller.rbs +40 -0
- data/sig/lib/core/model.rbs +38 -0
- data/sig/lib/core/view.rbs +42 -0
- data/sig/lib/interfaces/i_command.rbs +18 -0
- data/sig/lib/interfaces/i_controller.rbs +52 -0
- data/sig/lib/interfaces/i_facade.rbs +116 -0
- data/sig/lib/interfaces/i_mediator.rbs +60 -0
- data/sig/lib/interfaces/i_model.rbs +38 -0
- data/sig/lib/interfaces/i_notification.rbs +52 -0
- data/sig/lib/interfaces/i_notifier.rbs +48 -0
- data/sig/lib/interfaces/i_observer.rbs +56 -0
- data/sig/lib/interfaces/i_proxy.rbs +36 -0
- data/sig/lib/interfaces/i_view.rbs +71 -0
- data/sig/lib/patterns/command/macro_command.rbs +18 -0
- data/sig/lib/patterns/command/simple_command.rbs +11 -0
- data/sig/lib/patterns/facade/facade.rbs +48 -0
- data/sig/lib/patterns/mediator/mediator.rbs +18 -0
- data/sig/lib/patterns/observer/notification.rbs +18 -0
- data/sig/lib/patterns/observer/notifier.rbs +17 -0
- data/sig/lib/patterns/observer/observer.rbs +20 -0
- data/sig/lib/patterns/proxy/proxy.rbs +19 -0
- metadata +71 -56
- data/puremvc.rb +0 -13
- data/src/org/puremvc/ruby/core/controller.rb +0 -76
- data/src/org/puremvc/ruby/core/model.rb +0 -57
- data/src/org/puremvc/ruby/core/view.rb +0 -112
- data/src/org/puremvc/ruby/patterns/command/macro_command.rb +0 -59
- data/src/org/puremvc/ruby/patterns/command/simple_command.rb +0 -17
- data/src/org/puremvc/ruby/patterns/facade/facade.rb +0 -161
- data/src/org/puremvc/ruby/patterns/mediator/mediator.rb +0 -37
- data/src/org/puremvc/ruby/patterns/observer/notification.rb +0 -38
- data/src/org/puremvc/ruby/patterns/observer/notifier.rb +0 -27
- data/src/org/puremvc/ruby/patterns/observer/observer.rb +0 -31
- data/src/org/puremvc/ruby/patterns/proxy/proxy.rb +0 -32
- data/version.txt +0 -12
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
# macro_command.rb
|
5
|
+
# PureMVC Ruby Multicore
|
6
|
+
#
|
7
|
+
# Copyright(c) 2025 Saad Shams <saad.shams@puremvc.org>
|
8
|
+
# Your reuse is governed by the BSD 3-Clause License
|
9
|
+
|
10
|
+
module PureMVC
|
11
|
+
# A base ICommand implementation that executes other ICommand instances.
|
12
|
+
#
|
13
|
+
# A MacroCommand maintains a list of ICommand class references called SubCommands.
|
14
|
+
#
|
15
|
+
# When <code>execute</code>is called, the MacroCommand instantiates and calls <code>execute</code>on each of its
|
16
|
+
# SubCommands in turn. Each SubCommand will be passed a reference to the original INotification that was passed to
|
17
|
+
# the MacroCommand's <code>execute</code>method.
|
18
|
+
#
|
19
|
+
# Unlike SimpleCommand, your subclass should not override <code>execute</code> but instead override
|
20
|
+
# <code>initialize_macro_command</code> calling <code>add_sub_command</code>once for each SubCommand to be executed.
|
21
|
+
#
|
22
|
+
# @see Controller
|
23
|
+
# @see Notification
|
24
|
+
# @see SimpleCommand
|
25
|
+
class MacroCommand < Notifier
|
26
|
+
# Constructor.
|
27
|
+
#
|
28
|
+
# You should not need to define a constructor,
|
29
|
+
# instead, override the <code>initialize_macro_command</code> method.
|
30
|
+
#
|
31
|
+
# If your subclass does define a constructor,
|
32
|
+
# be sure to call <code>super</code>
|
33
|
+
def initialize
|
34
|
+
super()
|
35
|
+
@sub_commands = []
|
36
|
+
initialize_macro_command
|
37
|
+
end
|
38
|
+
|
39
|
+
# Initialize the MacroCommand.
|
40
|
+
#
|
41
|
+
# In your subclass, override this method to
|
42
|
+
# initialize the MacroCommand's SubCommand list with
|
43
|
+
# ICommand factory references, like this:
|
44
|
+
#
|
45
|
+
# @example
|
46
|
+
# def initialize_macro_command
|
47
|
+
# add_sub_command { FirstCommand.new }
|
48
|
+
# add_sub_command { SecondCommand.new }
|
49
|
+
# add_sub_command { ThirdCommand.new }
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# Note that SubCommands may be any ICommand implementor;
|
53
|
+
# MacroCommands or SimpleCommands are both acceptable.
|
54
|
+
def initialize_macro_command; end
|
55
|
+
|
56
|
+
# Add a SubCommand.
|
57
|
+
# SubCommands will be called in First In/First Out (FIFO) order.
|
58
|
+
#
|
59
|
+
# @param factory [^() -> _ICommand] A block or callable that returns an instance of ICommand when called.
|
60
|
+
def add_sub_command(&factory)
|
61
|
+
@sub_commands << factory
|
62
|
+
end
|
63
|
+
|
64
|
+
# Execute this MacroCommand's SubCommands.
|
65
|
+
#
|
66
|
+
# The SubCommands will be called in First In/First Out (FIFO) order.
|
67
|
+
#
|
68
|
+
# @param notification [INotification] the notification object to be passed to each SubCommand.
|
69
|
+
def execute(notification)
|
70
|
+
while @sub_commands.any?
|
71
|
+
# @type factory: [^() -> ICommand]
|
72
|
+
factory = @sub_commands.shift
|
73
|
+
|
74
|
+
# @type var command: _ICommand
|
75
|
+
command = factory.call
|
76
|
+
command.initialize_notifier(@multiton_key)
|
77
|
+
command.execute(notification)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# simple_command.rb
|
4
|
+
# PureMVC Ruby Multicore
|
5
|
+
#
|
6
|
+
# Copyright(c) 2025 Saad Shams <saad.shams@puremvc.org>
|
7
|
+
# Your reuse is governed by the BSD 3-Clause License
|
8
|
+
|
9
|
+
module PureMVC
|
10
|
+
# A base <code>ICommand</code>implementation.
|
11
|
+
#
|
12
|
+
# Subclasses should override the <code>execute</code>method, where business logic
|
13
|
+
# will handle the <code>INotification</code>.
|
14
|
+
#
|
15
|
+
# @see Controller
|
16
|
+
# @see Notification
|
17
|
+
# @see MacroCommand
|
18
|
+
class SimpleCommand < Notifier
|
19
|
+
# Fulfill the use-case initiated by the given <code>INotification</code>.
|
20
|
+
#
|
21
|
+
# In the Command Pattern, an application use-case typically begins with some user action,
|
22
|
+
# which results in an <code>INotification</code>being broadcast, handled by business logic in the
|
23
|
+
# <code>execute</code>method of an <code>ICommand</code>.
|
24
|
+
#
|
25
|
+
# @param notification [_INotification] the notification to handle
|
26
|
+
def execute(notification); end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,267 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# facade.rb
|
4
|
+
# PureMVC Ruby Multicore
|
5
|
+
#
|
6
|
+
# Copyright(c) 2025 Saad Shams <saad.shams@puremvc.org>
|
7
|
+
# Your reuse is governed by the BSD 3-Clause License
|
8
|
+
|
9
|
+
module PureMVC
|
10
|
+
# A base Multiton <code>IFacade</code> implementation.
|
11
|
+
#
|
12
|
+
# @see Model
|
13
|
+
# @see View
|
14
|
+
# @see Controller
|
15
|
+
class Facade
|
16
|
+
# Message Constants
|
17
|
+
MULTITON_MSG = 'Facade instance for this Multiton key already constructed!'
|
18
|
+
private_constant :MULTITON_MSG
|
19
|
+
|
20
|
+
class << self
|
21
|
+
# The Multiton IFacade instanceMap.
|
22
|
+
# @return [Hash{String => IFacade}]
|
23
|
+
def instance_map = (@instance_map ||= {})
|
24
|
+
|
25
|
+
# Mutex used to synchronize access to the instance map for thread safety.
|
26
|
+
# @return [Mutex]
|
27
|
+
def mutex = (@mutex ||= Mutex.new)
|
28
|
+
|
29
|
+
# Facade Multiton Factory method.
|
30
|
+
#
|
31
|
+
# @param key [String] the unique key identifying the Multiton instance
|
32
|
+
# @param factory [^(String) -> _IFacade] the unique key passed to the factory block
|
33
|
+
# @return [IFacade] the Multiton instance of the Facade
|
34
|
+
def get_instance(key, &factory)
|
35
|
+
mutex.synchronize do
|
36
|
+
instance_map[key] ||= factory.call(key)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Check if a Core is registered or not.
|
41
|
+
#
|
42
|
+
# @param key [String] the multiton key for the Core in question
|
43
|
+
# @return [Boolean] whether a Core is registered with the given <code>key</code>.
|
44
|
+
def has_core?(key)
|
45
|
+
instance_map.key?(key)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Remove a Core.
|
49
|
+
#
|
50
|
+
# Removes the Model, View, Controller, and Facade
|
51
|
+
# instances associated with the given key.
|
52
|
+
#
|
53
|
+
# @param key [String] the key of the Core to remove
|
54
|
+
def remove_core(key)
|
55
|
+
mutex.synchronize do
|
56
|
+
Model.remove_model(key)
|
57
|
+
View.remove_view(key)
|
58
|
+
Controller.remove_controller(key)
|
59
|
+
instance_map.delete(key)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Constructor.
|
65
|
+
#
|
66
|
+
# This <code>IFacade</code> implementation is a Multiton, so you should not call the constructor
|
67
|
+
# directly. Instead, use the static factory method and pass the unique key for this instance with factory:
|
68
|
+
# <code>PureMVC::Facade.get_instance(key) { |key| PureMVC::Facade.new(key) }</code>.
|
69
|
+
#
|
70
|
+
# @param key [String]
|
71
|
+
# @raise [RuntimeError] if an instance for this Multiton key has already been constructed.
|
72
|
+
def initialize(key)
|
73
|
+
raise MULTITON_MSG if self.class.instance_map[key]
|
74
|
+
|
75
|
+
self.class.instance_map[key] = self
|
76
|
+
@model = @view = @controller = nil
|
77
|
+
initialize_notifier(key)
|
78
|
+
initialize_facade
|
79
|
+
end
|
80
|
+
|
81
|
+
# Initialize the Multiton <code>Facade</code> instance.
|
82
|
+
#
|
83
|
+
# This method is called automatically by the constructor. Override it in your
|
84
|
+
# subclass to perform any subclass-specific initialization.
|
85
|
+
#
|
86
|
+
# @note Be sure to call <code>super.initialize_facade</code> when overriding.
|
87
|
+
def initialize_facade
|
88
|
+
initialize_model
|
89
|
+
initialize_controller
|
90
|
+
initialize_view
|
91
|
+
end
|
92
|
+
|
93
|
+
# Initialize the <code>Controller</code>.
|
94
|
+
#
|
95
|
+
# Called by the <code>initialize_facade</code> method.
|
96
|
+
#
|
97
|
+
# Override this method in your subclass of <code>Facade</code> if one or both of the following are true:
|
98
|
+
# - You wish to initialize a different <code>IController</code>.
|
99
|
+
# - You have <code>Commands</code> to register with the <code>Controller</code> at startup.
|
100
|
+
#
|
101
|
+
# If you don't want to initialize a different<code>IController</code>,call<code>super.initialize_controller()</code>
|
102
|
+
# at the beginning of your method, then register <code>Command</code>s.
|
103
|
+
def initialize_controller
|
104
|
+
@controller = Controller.get_instance(@multiton_key) { |key| Controller.new(key) }
|
105
|
+
end
|
106
|
+
|
107
|
+
# Initialize the <code>Model</code>.
|
108
|
+
#
|
109
|
+
# Called by the <code>initializeFacade</code> method.
|
110
|
+
#
|
111
|
+
# Override this method in your subclass of <code>Facade</code> if one or both of the following are true:
|
112
|
+
# - You wish to initialize a different <code>IModel</code>.
|
113
|
+
# - You have <code>Proxy</code>s to register with the Model that do not retrieve a reference to the
|
114
|
+
# <code>Facade</code> at construction time.
|
115
|
+
#
|
116
|
+
# If you don't want to initialize a different <code>IModel</code>, call <code>super.initialize_model()</code> at
|
117
|
+
# the beginning of your method, then register <code>Proxy</code>s.
|
118
|
+
#
|
119
|
+
# Note: This method is <i>rarely</i> overridden; in practice you are more likely to use a <code>Command</code> to
|
120
|
+
# create and register <code>Proxy</code>s with the <code>Model</code>, since <code>Proxy</code>s with mutable data
|
121
|
+
# will likely need to send <code>INotification</code>s and thus will likely want to fetch a reference to the
|
122
|
+
# <code>Facade</code> during their construction.
|
123
|
+
def initialize_model
|
124
|
+
@model = Model.get_instance(@multiton_key) { |key| Model.new(key) }
|
125
|
+
end
|
126
|
+
|
127
|
+
# Initialize the <code>View</code>.
|
128
|
+
#
|
129
|
+
# Called by the <code>initializeFacade</code> method.
|
130
|
+
#
|
131
|
+
# Override this method in your subclass of <code>Facade</code> if one or both of the following are true:
|
132
|
+
# - You wish to initialize a different <code>IView</code>.
|
133
|
+
# - You have <code>Observers</code> to register with the <code>View</code>.
|
134
|
+
#
|
135
|
+
# If you don't want to initialize a different <code>IView</code>, call <code>super.initialize_view()</code> at the
|
136
|
+
# beginning of your method, then register <code>IMediator</code> instances.
|
137
|
+
#
|
138
|
+
# Note: This method is <i>rarely</i> overridden; in practice you are more likely to use a <code>Command</code> to
|
139
|
+
# create and register <code>Mediator</code>s with the <code>View</code>, since <code>IMediator</code> instances will
|
140
|
+
# need to send <code>INotification</code>s and thus will likely want to fetch a reference to the <code>Facade</code>
|
141
|
+
# during their construction.
|
142
|
+
def initialize_view
|
143
|
+
@view = View.get_instance(@multiton_key) { |key| View.new(key) }
|
144
|
+
end
|
145
|
+
|
146
|
+
# Register an <code>ICommand</code> with the <code>Controller</code> by Notification name.
|
147
|
+
#
|
148
|
+
# @param notification_name [String] name of the <code>INotification</code> to associate with <code>ICommand</code>
|
149
|
+
# @param factory [^<() -> ICommand>] a reference to the Class of the <code>ICommand</code>
|
150
|
+
def register_command(notification_name, &factory)
|
151
|
+
@controller&.register_command(notification_name, &factory)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Check if a Command is registered for a given Notification
|
155
|
+
#
|
156
|
+
# @param notification_name [String] The name of the Notification to check
|
157
|
+
# @return [Boolean] whether a Command is currently registered for the given <code>notification_name</code>.
|
158
|
+
def has_command?(notification_name)
|
159
|
+
!!@controller&.has_command?(notification_name)
|
160
|
+
end
|
161
|
+
|
162
|
+
# Remove a previously registered <code>ICommand</code> to <code>INotification</code> mapping from the Controller.
|
163
|
+
#
|
164
|
+
# @param notification_name [String] the name of the <code>INotification</code> to remove the <code>ICommand</code>
|
165
|
+
def remove_command(notification_name)
|
166
|
+
@controller&.remove_command(notification_name)
|
167
|
+
end
|
168
|
+
|
169
|
+
# Register an <code>IProxy</code> with the <code>Model</code> by name.
|
170
|
+
#
|
171
|
+
# @param proxy [IProxy] the <code>IProxy</code> instance to be registered with the <code>Model</code>.
|
172
|
+
def register_proxy(proxy)
|
173
|
+
@model&.register_proxy(proxy)
|
174
|
+
end
|
175
|
+
|
176
|
+
# Retrieve an <code>IProxy</code> from the <code>Model</code> by name.
|
177
|
+
#
|
178
|
+
# @param proxy_name [String] the name of the proxy to be retrieved.
|
179
|
+
# @return [IProxy, nil] the <code>IProxy</code> instance previously registered with the <code>proxy_name</code>
|
180
|
+
def retrieve_proxy(proxy_name)
|
181
|
+
@model&.retrieve_proxy(proxy_name)
|
182
|
+
end
|
183
|
+
|
184
|
+
# Check if a Proxy is registered
|
185
|
+
#
|
186
|
+
# @param proxy_name [String] the name of the Proxy
|
187
|
+
# @return [Boolean] whether a Proxy is currently registered with the given <code>proxyName</code>.
|
188
|
+
def has_proxy?(proxy_name)
|
189
|
+
!!@model&.has_proxy?(proxy_name)
|
190
|
+
end
|
191
|
+
|
192
|
+
# Remove an <code>IProxy</code> from the <code>Model</code> by name.
|
193
|
+
#
|
194
|
+
# @param proxy_name [String] the <code>IProxy</code> to remove from the <code>Model</code>.
|
195
|
+
# @return [IProxy, nil] the <code>IProxy</code> that was removed from the <code>Model</code>, or nil
|
196
|
+
def remove_proxy(proxy_name)
|
197
|
+
@model&.remove_proxy(proxy_name)
|
198
|
+
end
|
199
|
+
|
200
|
+
# Register an <code>IMediator</code> with the <code>View</code>.
|
201
|
+
#
|
202
|
+
# @param mediator [IMediator] a reference to the <code>IMediator</code>
|
203
|
+
def register_mediator(mediator)
|
204
|
+
@view&.register_mediator(mediator)
|
205
|
+
end
|
206
|
+
|
207
|
+
# Retrieve an <code>IMediator</code> from the <code>View</code>.
|
208
|
+
#
|
209
|
+
# @param mediator_name [String] the name of the <code>IMediator</code> to retrieve
|
210
|
+
# @return [IMediator, nil] the <code>IMediator</code> previously registered with the <code>mediator_name</code>
|
211
|
+
def retrieve_mediator(mediator_name)
|
212
|
+
@view&.retrieve_mediator(mediator_name)
|
213
|
+
end
|
214
|
+
|
215
|
+
# Check if a Mediator is registered or not
|
216
|
+
#
|
217
|
+
# @param mediator_name [String] the name of the Mediator
|
218
|
+
# @return [Boolean] whether a Mediator is registered with the given <code>mediator_name</code>.
|
219
|
+
def has_mediator?(mediator_name)
|
220
|
+
!!@view&.has_mediator?(mediator_name)
|
221
|
+
end
|
222
|
+
|
223
|
+
# Remove an <code>IMediator</code> from the <code>View</code>.
|
224
|
+
#
|
225
|
+
# @param mediator_name [String] name of the <code>IMediator</code> to be removed.
|
226
|
+
# @return [IMediator, nil] the <code>IMediator</code> that was removed from the <code>View</code>, or nil
|
227
|
+
def remove_mediator(mediator_name)
|
228
|
+
@view&.remove_mediator(mediator_name)
|
229
|
+
end
|
230
|
+
|
231
|
+
# Notify <code>Observer</code>s.
|
232
|
+
#
|
233
|
+
# This method is left public mostly for backward compatibility
|
234
|
+
# and to allow you to send custom notification classes using the facade.
|
235
|
+
#
|
236
|
+
# Usually you should call <code>send_notification</code>
|
237
|
+
# and pass the parameters, never having to
|
238
|
+
# construct the notification yourself.
|
239
|
+
#
|
240
|
+
# @param notification [INotification] the notification to have the <code>View</code> notify <code>Observers</code>.
|
241
|
+
def notify_observers(notification)
|
242
|
+
@view&.notify_observers(notification)
|
243
|
+
end
|
244
|
+
|
245
|
+
# Create and send an <code>INotification</code>.
|
246
|
+
#
|
247
|
+
# Keeps us from having to construct new notification instances in our implementation code.
|
248
|
+
#
|
249
|
+
# @param name [String] the name of the notification to send
|
250
|
+
# @param body [Object, nil] the body of the notification (optional)
|
251
|
+
# @param type [String, nil] the type of the notification (optional)
|
252
|
+
def send_notification(name, body = nil, type = nil)
|
253
|
+
notify_observers(Notification.new(name, body, type))
|
254
|
+
end
|
255
|
+
|
256
|
+
# Set the Multiton key for this facade instance.
|
257
|
+
#
|
258
|
+
# This is not meant to be called directly. It is invoked internally by the
|
259
|
+
# constructor when <code>get_instance</code> is called. However, it must be public
|
260
|
+
# to implement <code>INotifier</code>.
|
261
|
+
#
|
262
|
+
# @param key [String] the multiton key for this instance
|
263
|
+
def initialize_notifier(key)
|
264
|
+
@multiton_key = key
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# mediator.rb
|
4
|
+
# PureMVC Ruby Multicore
|
5
|
+
#
|
6
|
+
# Copyright(c) 2025 Saad Shams <saad.shams@puremvc.org>
|
7
|
+
# Your reuse is governed by the BSD 3-Clause License
|
8
|
+
|
9
|
+
module PureMVC
|
10
|
+
# A base <code>IMediator</code> implementation.
|
11
|
+
#
|
12
|
+
# @see View
|
13
|
+
class Mediator < Notifier
|
14
|
+
# The name of the <code>Mediator</code>.
|
15
|
+
#
|
16
|
+
# Typically, a <code>Mediator</code> will be written to serve
|
17
|
+
# one specific control or group controls and so,
|
18
|
+
# will not have a need to be dynamically named.
|
19
|
+
NAME = 'Mediator'
|
20
|
+
public_constant :NAME
|
21
|
+
|
22
|
+
# @return [String] The name of the Mediator.
|
23
|
+
attr_reader :name
|
24
|
+
|
25
|
+
# @return [Object, nil] The component associated with this Mediator.
|
26
|
+
attr_accessor :component
|
27
|
+
|
28
|
+
# Initializes a new Mediator instance.
|
29
|
+
#
|
30
|
+
# @param name [String | nil] the name of the mediator
|
31
|
+
# @param component [Object, nil] the component this mediator manages
|
32
|
+
def initialize(name = nil, component = nil)
|
33
|
+
super()
|
34
|
+
@name = name || NAME
|
35
|
+
@component = component
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns an array of notification names this mediator is interested in.
|
39
|
+
#
|
40
|
+
# @return [Array<String>] list of notification names
|
41
|
+
def list_notification_interests
|
42
|
+
[]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Handles a notification.
|
46
|
+
#
|
47
|
+
# @param notification [_INotification] the notification to handle
|
48
|
+
def handle_notification(notification); end
|
49
|
+
|
50
|
+
# Called when the mediator is registered.
|
51
|
+
def on_register; end
|
52
|
+
|
53
|
+
# Called when the mediator is removed.
|
54
|
+
def on_remove; end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# notification.rb
|
4
|
+
# PureMVC Ruby Multicore
|
5
|
+
#
|
6
|
+
# Copyright(c) 2025 Saad Shams <saad.shams@puremvc.org>
|
7
|
+
# Your reuse is governed by the BSD 3-Clause License
|
8
|
+
|
9
|
+
module PureMVC
|
10
|
+
# A base <code>INotification</code>implementation.
|
11
|
+
#
|
12
|
+
# PureMVC does not rely upon underlying event models such
|
13
|
+
# as the one provided with Flash, and ActionScript 3 does
|
14
|
+
# not have an inherent event model.
|
15
|
+
#
|
16
|
+
# The Observer Pattern as implemented within PureMVC exists
|
17
|
+
# to support event-driven communication between the
|
18
|
+
# application and the actors of the MVC triad.
|
19
|
+
#
|
20
|
+
# Notifications are not meant to be a replacement for Events
|
21
|
+
# in Flex/Flash/Apollo. Generally, <code>IMediator</code>implementors
|
22
|
+
# place event listeners on their view components, which they
|
23
|
+
# then handle in the usual way. This may lead to the broadcast of <code>Notification</code>s to
|
24
|
+
# trigger <code>ICommand</code>s or to communicate with other <code>IMediator</code>s. <code>IProxy</code> and
|
25
|
+
# <code>ICommand</code> instances communicate with each other and <code>IMediator</code>s
|
26
|
+
# by broadcasting <code>INotification</code>s.
|
27
|
+
#
|
28
|
+
# A key difference between Flash <code>Event</code>s and PureMVC
|
29
|
+
# <code>Notification</code>s is that <code>Event</code>s follow the
|
30
|
+
# 'Chain of Responsibility' pattern, 'bubbling' up the display hierarchy
|
31
|
+
# until some parent component handles the <code>Event</code>, while
|
32
|
+
# PureMVC <code>Notification</code>s follow a 'Publish/Subscribe'
|
33
|
+
# pattern. PureMVC classes need not be related to each other in a
|
34
|
+
# parent/child relationship to communicate with one another
|
35
|
+
# using <code>Notification</code>s.
|
36
|
+
#
|
37
|
+
# @see Observer
|
38
|
+
class Notification
|
39
|
+
# @return [String] the name of the notification
|
40
|
+
attr_reader :name
|
41
|
+
|
42
|
+
# @return [Object, nil] the body of the notification
|
43
|
+
attr_accessor :body
|
44
|
+
|
45
|
+
# @return [String, nil] the type of the notification
|
46
|
+
attr_accessor :type
|
47
|
+
|
48
|
+
# The Notification class represents a message with a name, optional body, and optional type.
|
49
|
+
#
|
50
|
+
# @param name [String] the name of the notification
|
51
|
+
# @param body [Object, nil] optional data to pass with the notification
|
52
|
+
# @param type [String, nil] optional type identifier
|
53
|
+
def initialize(name, body = nil, type = nil)
|
54
|
+
@name = name
|
55
|
+
@body = body
|
56
|
+
@type = type
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns a string representation of the notification.
|
60
|
+
#
|
61
|
+
# @return [String]
|
62
|
+
def to_s
|
63
|
+
"Notification Name: #{@name}" \
|
64
|
+
"\nBody: #{@body.inspect}" \
|
65
|
+
"\nType: #{@type}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# notifier.rb
|
4
|
+
# PureMVC Ruby Multicore
|
5
|
+
#
|
6
|
+
# Copyright(c) 2025 Saad Shams <saad.shams@puremvc.org>
|
7
|
+
# Your reuse is governed by the BSD 3-Clause License
|
8
|
+
|
9
|
+
module PureMVC
|
10
|
+
# A base <code>INotifier</code> implementation.
|
11
|
+
#
|
12
|
+
# <code>MacroCommand</code>, <code>SimpleCommand</code>, <code>Mediator</code>, and <code>Proxy</code>
|
13
|
+
# all need to send <code>Notification</code>s.
|
14
|
+
#
|
15
|
+
# The <code>INotifier</code> interface provides a common method called
|
16
|
+
# <code>send_notification</code> that relieves implementation code of
|
17
|
+
# the necessity to actually construct <code>Notification</code>s.
|
18
|
+
#
|
19
|
+
# The <code>Notifier</code> class, which all the above-mentioned classes
|
20
|
+
# extend, provides an initialized reference to the <code>Facade</code>
|
21
|
+
# Multiton, which is required for the convenience method
|
22
|
+
# for sending <code>Notification</code>s. It also eases implementation,
|
23
|
+
# as these classes have frequent <code>Facade</code> interactions and
|
24
|
+
# usually require access to it anyway.
|
25
|
+
#
|
26
|
+
# NOTE: In the MultiCore version of the framework, there is one caveat:
|
27
|
+
# notifiers cannot send notifications or reach the facade until they
|
28
|
+
# have a valid <code>multiton_key</code>.
|
29
|
+
#
|
30
|
+
# The <code>multiton_key</code> is set:
|
31
|
+
# * on a <code>SimpleCommand</code> when it is executed by the <code>Controller<c/ode>
|
32
|
+
# * on a <code>Mediator</code> when registered with the <code>View</code>
|
33
|
+
# * on a <code>Proxy</code> when registered with the <code>Model</code>
|
34
|
+
#
|
35
|
+
# @see Proxy
|
36
|
+
# @see Facade
|
37
|
+
# @see Mediator
|
38
|
+
# @see MacroCommand
|
39
|
+
# @see SimpleCommand
|
40
|
+
class Notifier
|
41
|
+
# Message Constants
|
42
|
+
MULTITON_MSG = 'multitonKey for this Notifier not yet initialized!'
|
43
|
+
private_constant :MULTITON_MSG
|
44
|
+
|
45
|
+
# @attr_reader [String] The Multiton Key for this app
|
46
|
+
attr_reader :multiton_key
|
47
|
+
|
48
|
+
# Initialize this INotifier instance.
|
49
|
+
#
|
50
|
+
# This is how a Notifier receives its <code>multiton_key</code>.
|
51
|
+
# Any calls to <code>send_notification</code> or attempts to access the <code>facade</code>
|
52
|
+
# will fail until this method has been called.
|
53
|
+
#
|
54
|
+
# Subclasses such as <code>Mediator</code>, <code>Command</code>, or <code>Proxy</code> may override this
|
55
|
+
# method if they need to send notifications or access the <code>Facade</code> instance
|
56
|
+
# as early as possible. However, note that the <code>Facade</code> cannot be accessed
|
57
|
+
# within the constructor of these classes, because <code>initialize_notifier</code>
|
58
|
+
# will not yet have been called at that point.
|
59
|
+
#
|
60
|
+
# @param key [String] the <code>multiton_key</code> this <code>INotifier</code> will use
|
61
|
+
def initialize_notifier(key)
|
62
|
+
@multiton_key = key
|
63
|
+
end
|
64
|
+
|
65
|
+
# Create and send an INotification.
|
66
|
+
#
|
67
|
+
# This method eliminates the need to manually construct
|
68
|
+
# INotification instances in your implementation code.
|
69
|
+
#
|
70
|
+
# @param name [String] the name of the notification
|
71
|
+
# @param body [Object, nil] optional body
|
72
|
+
# @param type [String, nil] optional type
|
73
|
+
def send_notification(name, body = nil, type = nil)
|
74
|
+
facade.send_notification(name, body, type)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Return the Multiton Facade instance
|
78
|
+
#
|
79
|
+
# @raise [RuntimeError] if the <code>multiton_key</code> is not set
|
80
|
+
# @return [IFacade] the facade instance for the notifier's key
|
81
|
+
def facade
|
82
|
+
raise MULTITON_MSG if @multiton_key.nil?
|
83
|
+
|
84
|
+
Facade.get_instance(@multiton_key) { |key| Facade.new(key) }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# observer.rb
|
4
|
+
# PureMVC Ruby Multicore
|
5
|
+
#
|
6
|
+
# Copyright(c) 2025 Saad Shams <saad.shams@puremvc.org>
|
7
|
+
# Your reuse is governed by the BSD 3-Clause License
|
8
|
+
|
9
|
+
module PureMVC
|
10
|
+
# A base <code>IObserver</code> implementation.
|
11
|
+
#
|
12
|
+
# An <code>Observer</code> is an object that encapsulates information
|
13
|
+
# about an interested object with a method that should
|
14
|
+
# be called when a particular <code>INotification</code> is broadcast.
|
15
|
+
#
|
16
|
+
# In PureMVC, the <code>Observer</code> class assumes these responsibilities:
|
17
|
+
# - Encapsulate the notification (callback) method of the interested object.
|
18
|
+
# - Encapsulate the notification context (<code>self</code>) of the interested object.
|
19
|
+
# - Provide methods for setting the notification method and context.
|
20
|
+
# - Provide a method for notifying the interested object.
|
21
|
+
#
|
22
|
+
# @see View
|
23
|
+
# @see Notification
|
24
|
+
class Observer
|
25
|
+
# @return [Method | nil] notify The callback method to be called on notification.
|
26
|
+
attr_accessor :notify
|
27
|
+
|
28
|
+
# @return [Object | nil] context The object context for the callback.
|
29
|
+
attr_accessor :context
|
30
|
+
|
31
|
+
# Initialize an Observer with a notify method and context.
|
32
|
+
#
|
33
|
+
# @param notify [Method, nil] the callback method to invoke on notification.
|
34
|
+
# @param context [Object, nil] the object context for the callback.
|
35
|
+
def initialize(notify = nil, context = nil)
|
36
|
+
@notify = notify
|
37
|
+
@context = context
|
38
|
+
end
|
39
|
+
|
40
|
+
# Calls the notify method with the given notification.
|
41
|
+
#
|
42
|
+
# @param notification [INotification] the notification to send.
|
43
|
+
def notify_observer(notification)
|
44
|
+
@notify&.call(notification)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Compares the given object with the Observer's context.
|
48
|
+
#
|
49
|
+
# @param object [Object] the object to compare.
|
50
|
+
# @return [Boolean] true if the given object is the same as the context.
|
51
|
+
def compare_notify_context?(object)
|
52
|
+
object.equal?(@context)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|