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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +15 -0
- data/LICENSE +28 -0
- data/README.md +88 -0
- data/sig/core/controller.rbs +40 -0
- data/sig/core/model.rbs +38 -0
- data/sig/core/view.rbs +42 -0
- data/sig/interfaces/i_command.rbs +18 -0
- data/sig/interfaces/i_controller.rbs +52 -0
- data/sig/interfaces/i_facade.rbs +116 -0
- data/sig/interfaces/i_mediator.rbs +60 -0
- data/sig/interfaces/i_model.rbs +38 -0
- data/sig/interfaces/i_notification.rbs +52 -0
- data/sig/interfaces/i_notifier.rbs +48 -0
- data/sig/interfaces/i_observer.rbs +56 -0
- data/sig/interfaces/i_proxy.rbs +36 -0
- data/sig/interfaces/i_view.rbs +71 -0
- data/sig/patterns/command/macro_command.rbs +18 -0
- data/sig/patterns/command/simple_command.rbs +11 -0
- data/sig/patterns/facade/facade.rbs +48 -0
- data/sig/patterns/mediator/mediator.rbs +18 -0
- data/sig/patterns/observer/notification.rbs +18 -0
- data/sig/patterns/observer/notifier.rbs +17 -0
- data/sig/patterns/observer/observer.rbs +20 -0
- data/sig/patterns/proxy/proxy.rbs +19 -0
- data/src/core/controller.rb +177 -0
- data/src/core/model.rb +133 -0
- data/src/core/view.rb +230 -0
- data/src/patterns/command/macro_command.rb +81 -0
- data/src/patterns/command/simple_command.rb +28 -0
- data/src/patterns/facade/facade.rb +267 -0
- data/src/patterns/mediator/mediator.rb +56 -0
- data/src/patterns/observer/notification.rb +68 -0
- data/src/patterns/observer/notifier.rb +87 -0
- data/src/patterns/observer/observer.rb +55 -0
- data/src/patterns/proxy/proxy.rb +50 -0
- data/src/puremvc.rb +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
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
|