puremvc 1.0.0 → 1.0.2

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.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +15 -0
  3. data/LICENSE +28 -0
  4. data/README.md +88 -0
  5. data/sig/core/controller.rbs +40 -0
  6. data/sig/core/model.rbs +38 -0
  7. data/sig/core/view.rbs +42 -0
  8. data/sig/interfaces/i_command.rbs +18 -0
  9. data/sig/interfaces/i_controller.rbs +52 -0
  10. data/sig/interfaces/i_facade.rbs +116 -0
  11. data/sig/interfaces/i_mediator.rbs +60 -0
  12. data/sig/interfaces/i_model.rbs +38 -0
  13. data/sig/interfaces/i_notification.rbs +52 -0
  14. data/sig/interfaces/i_notifier.rbs +48 -0
  15. data/sig/interfaces/i_observer.rbs +56 -0
  16. data/sig/interfaces/i_proxy.rbs +36 -0
  17. data/sig/interfaces/i_view.rbs +71 -0
  18. data/sig/patterns/command/macro_command.rbs +18 -0
  19. data/sig/patterns/command/simple_command.rbs +11 -0
  20. data/sig/patterns/facade/facade.rbs +48 -0
  21. data/sig/patterns/mediator/mediator.rbs +18 -0
  22. data/sig/patterns/observer/notification.rbs +18 -0
  23. data/sig/patterns/observer/notifier.rbs +17 -0
  24. data/sig/patterns/observer/observer.rbs +20 -0
  25. data/sig/patterns/proxy/proxy.rbs +19 -0
  26. data/src/core/controller.rb +177 -0
  27. data/src/core/model.rb +133 -0
  28. data/src/core/view.rb +230 -0
  29. data/src/patterns/command/macro_command.rb +81 -0
  30. data/src/patterns/command/simple_command.rb +28 -0
  31. data/src/patterns/facade/facade.rb +267 -0
  32. data/src/patterns/mediator/mediator.rb +56 -0
  33. data/src/patterns/observer/notification.rb +68 -0
  34. data/src/patterns/observer/notifier.rb +87 -0
  35. data/src/patterns/observer/observer.rb +55 -0
  36. data/src/patterns/proxy/proxy.rb +50 -0
  37. data/src/puremvc.rb +19 -0
  38. metadata +71 -56
  39. data/puremvc.rb +0 -13
  40. data/src/org/puremvc/ruby/core/controller.rb +0 -76
  41. data/src/org/puremvc/ruby/core/model.rb +0 -57
  42. data/src/org/puremvc/ruby/core/view.rb +0 -112
  43. data/src/org/puremvc/ruby/patterns/command/macro_command.rb +0 -59
  44. data/src/org/puremvc/ruby/patterns/command/simple_command.rb +0 -17
  45. data/src/org/puremvc/ruby/patterns/facade/facade.rb +0 -161
  46. data/src/org/puremvc/ruby/patterns/mediator/mediator.rb +0 -37
  47. data/src/org/puremvc/ruby/patterns/observer/notification.rb +0 -38
  48. data/src/org/puremvc/ruby/patterns/observer/notifier.rb +0 -27
  49. data/src/org/puremvc/ruby/patterns/observer/observer.rb +0 -31
  50. data/src/org/puremvc/ruby/patterns/proxy/proxy.rb +0 -32
  51. data/version.txt +0 -12
data/src/core/view.rb ADDED
@@ -0,0 +1,230 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ # view.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 Multiton <code>IView</code> implementation.
12
+ #
13
+ # In PureMVC, the <code>IView</code> class assumes these responsibilities:
14
+ #
15
+ # - Maintains a cache of <code>IMediator</code>instances.
16
+ # - Provides methods for registering, retrieving, and removing <code>IMediators</code>.
17
+ # - Notifies <code>IMediators</code>when they are registered or removed.
18
+ # - Manages the observer lists for each <code>INotification</code>in the application.
19
+ # - Provides a method for attaching <code>IObservers</code>to an <code>INotification</code>'s observer list.
20
+ # - Provides a method for broadcasting an <code>INotification</code>.
21
+ # - Notifies the <code>IObservers</code>of a given <code>INotification</code>when it is broadcast.
22
+ #
23
+ # @see Mediator
24
+ # @see Observer
25
+ # @see Notification
26
+ class View
27
+ MULTITON_MSG = 'View instance for this Multiton key already constructed!'
28
+ private_constant :MULTITON_MSG
29
+
30
+ class << self
31
+ # The Multiton IModel instanceMap.
32
+ # @return [Hash{String => IView}]
33
+ def instance_map = (@instance_map ||= {})
34
+
35
+ # Mutex used to synchronize access to the instance map for thread safety.
36
+ # @return [Mutex]
37
+ def mutex = (@mutex ||= Mutex.new)
38
+
39
+ # View Multiton Factory method.
40
+ #
41
+ # @param key [String] the unique key identifying the Multiton instance
42
+ # @param factory [^(String) -> _IView] the unique key passed to the factory block
43
+ # @return [_IView] the Multiton instance of <code>View</code>
44
+ def get_instance(key, &factory)
45
+ mutex.synchronize do
46
+ instance_map[key] ||= factory.call(key)
47
+ end
48
+ end
49
+
50
+ # Remove an <code>IView</code> instance.
51
+ #
52
+ # @param key [String] the key of the <code>IView</code> instance to remove
53
+ def remove_view(key)
54
+ mutex.synchronize do
55
+ instance_map.delete(key)
56
+ end
57
+ end
58
+ end
59
+
60
+ # Constructor.
61
+ #
62
+ # This <code>IView</code> implementation is a Multiton,
63
+ # so you should not call the constructor directly. Instead, call the static
64
+ # Multiton factory method <code>View.get_instance(multiton_key) { |key| View.new(key) }</code>.
65
+ #
66
+ # @param key [String]
67
+ # @raise [RuntimeError] if an instance for this Multiton key has already been constructed.
68
+ def initialize(key)
69
+ raise MULTITON_MSG if self.class.instance_map[key]
70
+
71
+ self.class.instance_map[key] = self
72
+ # The Multiton Key for this Core
73
+ # @type var multiton_key: String
74
+ @multiton_key = key
75
+ # Mapping of Notification names to Observer lists
76
+ # @type var observer_map: Hash[String, Array[_IObserver]]
77
+ @observer_map = {}
78
+ # Mutex used to synchronize access to the observer_map
79
+ # @type var observer_mutex: Mutex
80
+ @observer_mutex = Mutex.new
81
+ # Mapping of Mediator names to Mediator instances
82
+ # @type var mediator_map: Hash[String, _IMediator]
83
+ @mediator_map = {}
84
+ # Mutex used to synchronize access to the mediator_map
85
+ # @type var mediator_mutex: Mutex
86
+ @mediator_mutex = Mutex.new
87
+ initialize_view
88
+ end
89
+
90
+ # Initialize the Multiton <code>View</code> instance.
91
+ #
92
+ # Called automatically by the constructor, this
93
+ # is your opportunity to initialize the Multiton
94
+ # instance in your subclass without overriding the
95
+ # constructor.
96
+ def initialize_view; end
97
+
98
+ # Register an <code>IObserver</code> to be notified
99
+ # of <code>INotifications</code> with a given name.
100
+ #
101
+ # @param notification_name [String] the name of the <code>INotifications</code> to notify <code>IObserver</code> of
102
+ # @param observer [IObserver] the <code>IObserver</code> to register
103
+ def register_observer(notification_name, observer)
104
+ @observer_mutex.synchronize do
105
+ observers = (@observer_map[notification_name] ||= [])
106
+ observers << observer
107
+ end
108
+ end
109
+
110
+ # Notify the <code>IObservers</code> for a particular <code>INotification</code>.
111
+ #
112
+ # All previously attached <code>IObservers</code> for this <code>INotification</code>'s
113
+ # list are notified and are passed a reference to the <code>INotification</code> in
114
+ # the order in which they were registered.
115
+ #
116
+ # @param notification [INotification] the <code>INotification</code> to notify <code>IObservers</code> of.
117
+ def notify_observers(notification)
118
+ # @type var observers: Array[_IObserver]?
119
+ observers = nil
120
+ @observer_mutex.synchronize do
121
+ # Get a reference to the observers list for this notification name
122
+ # Iteration safe, copy observers from reference array to working array,
123
+ # since the reference array may change during the notification loop
124
+ observers = @observer_map[notification.name].dup
125
+ end
126
+ # Notify Observers from the working array
127
+ observers&.each { |observer| observer.notify_observer(notification) }
128
+ end
129
+
130
+ # Remove the observer for a given notifyContext from an observer list for a given Notification name.
131
+ #
132
+ # @param notification_name [String] which observer list to remove from
133
+ # @param notify_context [Object] remove the observer with this object as its notifyContext
134
+ def remove_observer(notification_name, notify_context)
135
+ @observer_mutex.synchronize do
136
+ # the observer list for the notification under inspection
137
+ # @type var observers: Array[_IObserver]?
138
+ observers = @observer_map[notification_name]
139
+ # find and remove the sole Observer for the given notify_context
140
+ # there can only be one Observer for a given notify_context
141
+ # in any given Observer list, so remove it
142
+ observers&.reject! { |observer| observer.compare_notify_context?(notify_context) }
143
+ # Also, when a Notification's Observer list length falls to
144
+ # zero, delete the notification key from the observer map
145
+ @observer_map.delete(notification_name) if observers&.empty?
146
+ end
147
+ end
148
+
149
+ # Register an <code>IMediator</code> instance with the <code>View</code>.
150
+ #
151
+ # Registers the <code>IMediator</code> so that it can be retrieved by name,
152
+ # and further interrogates the <code>IMediator</code> for its
153
+ # <code>INotification</code> interests.
154
+ #
155
+ # If the <code>IMediator</code> returns any <code>INotification</code>
156
+ # names to be notified about, an <code>Observer</code> is created encapsulating
157
+ # the <code>IMediator</code> instance's <code>handleNotification</code> method
158
+ # and registering it as an <code>Observer</code> for all <code>INotifications</code> the
159
+ # <code>IMediator</code> is interested in.
160
+ #
161
+ # @param mediator [IMediator] a reference to the <code>IMediator</code> instance
162
+ def register_mediator(mediator)
163
+ @mediator_mutex.synchronize do
164
+ return if @mediator_map.key?(mediator.name)
165
+
166
+ @mediator_map[mediator.name] = mediator
167
+ end
168
+
169
+ mediator.initialize_notifier(@multiton_key)
170
+
171
+ # Create Observer referencing this mediator's handleNotification method
172
+ # @type observer [IObserver]
173
+ observer = Observer.new(mediator.method(:handle_notification), mediator)
174
+
175
+ # Get Notification interests, if any.
176
+ # @type interests [Array<String>]
177
+ interests = mediator.list_notification_interests
178
+ # Register Mediator as Observer for its list of Notification interests
179
+ interests.each { |interest| register_observer(interest, observer) }
180
+
181
+ # alert the mediator that it has been registered
182
+ mediator.on_register
183
+ end
184
+
185
+ # Retrieve an <code>IMediator</code> from the <code>View</code>.
186
+ #
187
+ # @param mediator_name [String] the name of the <code>IMediator</code> instance to retrieve.
188
+ # @return [IMediator, nil] <code>IMediator</code> previously registered with the given <code>mediatorName</code>.
189
+ def retrieve_mediator(mediator_name)
190
+ @mediator_mutex.synchronize do
191
+ @mediator_map[mediator_name]
192
+ end
193
+ end
194
+
195
+ # Check if a Mediator is registered or not.
196
+ #
197
+ # @param mediator_name [String] the name of the mediator to check.
198
+ # @return [Boolean] whether a Mediator is registered with the given <code>mediatorName</code>.
199
+ def has_mediator?(mediator_name)
200
+ @mediator_mutex.synchronize do
201
+ @mediator_map.key?(mediator_name)
202
+ end
203
+ end
204
+
205
+ # Remove an <code>IMediator</code> from the <code>View</code>.
206
+ #
207
+ # @param mediator_name [String] name of the <code>IMediator</code> instance to be removed.
208
+ # @return [IMediator, nil] <code>IMediator</code> that was removed from the <code>View</code>, or nil if none found.
209
+ def remove_mediator(mediator_name)
210
+ # @type var mediator: _IMediator?
211
+ mediator = nil
212
+ @mediator_mutex.synchronize do
213
+ # retrieve the named mediator and delete from the mediator map
214
+ mediator = @mediator_map.delete(mediator_name)
215
+ end
216
+ return unless mediator
217
+
218
+ # for every notification this mediator is interested in...
219
+ # @type var interests: Array[String]
220
+ interests = mediator.list_notification_interests
221
+ # remove the observer linking the mediator
222
+ # to the notification interest
223
+ interests.each { |interest| remove_observer(interest, mediator) }
224
+
225
+ # alert the mediator that it has been removed
226
+ mediator.on_remove
227
+ mediator
228
+ end
229
+ end
230
+ end
@@ -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