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
@@ -0,0 +1,56 @@
1
+ module PureMVC
2
+ # The interface definition for a PureMVC Observer.
3
+ #
4
+ # In PureMVC, <code>_IObserver</code> implementors assume these responsibilities:
5
+ # - Encapsulate the notification (callback) method of the interested object.
6
+ # - Encapsulate the notification context (this) of the interested object.
7
+ # - Provide methods for setting the interested object's notification method and context.
8
+ # - Provide a method for notifying the interested object.
9
+ #
10
+ # PureMVC does not rely upon underlying event
11
+ # models such as the one provided with Flash,
12
+ # and ActionScript 3 does not have an inherent
13
+ # event model.
14
+ #
15
+ # The Observer Pattern as implemented within
16
+ # PureMVC exists to support event-driven communication
17
+ # between the application and the actors of the
18
+ # MVC triad.
19
+ #
20
+ # An Observer is an object that encapsulates information
21
+ # about an interested object with a notification method that
22
+ # should be called when an <code>_INotification</code> is broadcast. The Observer then
23
+ # acts as a proxy for notifying the interested object.
24
+ #
25
+ # Observers can receive <code>Notification</code>s by having their
26
+ # <code>notifyObserver</code> method invoked, passing
27
+ # in an object implementing the <code>_INotification</code> interface, such
28
+ # as a subclass of <code>Notification</code>.
29
+ #
30
+ # @see IView
31
+ # @see INotification
32
+ interface _IObserver
33
+
34
+ # @return [Method, nil] The callback method to be called on notification.
35
+ def notify: () -> Method?
36
+ def notify=: (Method?) -> void
37
+
38
+ # @return [Object, nil] The object context for the callback.
39
+ def context: () -> Object?
40
+ def context=: (Object?) -> void
41
+
42
+ # Notify the interested object.
43
+ #
44
+ # @param notification [_INotification] the <code>_INotification</code> to pass to the interested object's notification method
45
+ def notify_observer: (_INotification notification) -> void
46
+
47
+ # Compare the given object to the notification context object.
48
+ #
49
+ # @param object [Object] the object to compare.
50
+ # @return [Boolean] indicating if the notification context and the object are the same.
51
+ def compare_notify_context?: (Object object) -> bool
52
+ end
53
+
54
+ # Assert that the given `Observer` type parameter is a subtype of `_IObserver`
55
+ type validate_observer[Observer < _IObserver] = top
56
+ end
@@ -0,0 +1,36 @@
1
+ module PureMVC
2
+ # The interface definition for a PureMVC Proxy.
3
+ #
4
+ # In PureMVC, <code>_IProxy</code> implementors assume these responsibilities:
5
+ # - Implement a common method which returns the name of the Proxy.
6
+ # - Provide methods for setting and getting the data object.
7
+ #
8
+ # Additionally, <code>_IProxy</code>s typically:
9
+ # - Maintain references to one or more pieces of model data.
10
+ # - Provide methods for manipulating that data.
11
+ # - Generate <code>_INotifications</code> when their model data changes.
12
+ # - Expose their name called <code>NAME</code>, if they are not instantiated multiple times.
13
+ # - Encapsulate interaction with local or remote services used to fetch and persist model data.
14
+ #
15
+ # @see INotifier
16
+ interface _IProxy
17
+
18
+ include _INotifier
19
+
20
+ # @return [String] The proxy name
21
+ def name: () -> String
22
+
23
+ # @return [Object | nil] The data managed by the proxy
24
+ def data: () -> Object?
25
+ def data=: (Object?) -> void
26
+
27
+ # Called by the Model when the Proxy is registered
28
+ def on_register: () -> void
29
+
30
+ # Called by the Model when the Proxy is removed
31
+ def on_remove: () -> void
32
+ end
33
+
34
+ # Assert that the given `Proxy` type parameter is a subtype of `_IProxy`
35
+ type validate_proxy[Proxy < _IProxy] = top
36
+ end
@@ -0,0 +1,71 @@
1
+ module PureMVC
2
+ # The interface definition for a PureMVC View.
3
+ #
4
+ # In PureMVC, <code>_IView</code> implementors assume these responsibilities:
5
+ #
6
+ # In PureMVC, the <code>View</code> class assumes these responsibilities:
7
+ # - Maintain a cache of <code>_IMediator</code> instances.
8
+ # - Provide methods for registering, retrieving, and removing <code>_IMediators</code>.
9
+ # - Managing the observer lists for each <code>_INotification</code> in the application.
10
+ # - Providing a method for attaching <code>_IObservers</code> to an <code>_INotification</code>'s observer list.
11
+ # - Providing a method for broadcasting an <code>_INotification</code>.
12
+ # - Notifying the <code>_IObservers</code> of a given <code>_INotification</code> when it is broadcast.
13
+ interface _IView
14
+ # Register an <code>_IObserver</code> to be notified of <code>_INotifications</code> with a given name.
15
+ #
16
+ # @param notification_name [String] the name of the <code>_INotifications</code> to notify this <code>_IObserver</code> of
17
+ # @param observer [_IObserver] the <code>_IObserver</code> to register
18
+ def register_observer: (String notification_name, _IObserver observer) -> void
19
+
20
+ # Notify the <code>_IObservers</code> for a particular <code>_INotification</code>.
21
+ #
22
+ # All previously attached <code>_IObservers</code> for this <code>_INotification</code>'s
23
+ # list are notified and are passed a reference to the <code>_INotification</code> in
24
+ # the order in which they were registered.
25
+ #
26
+ # @param notification [_INotification] the <code>_INotification</code> to notify <code>_IObservers</code> of.
27
+ def notify_observers: (_INotification notification) -> void
28
+
29
+ # Remove a group of observers from the observer list for a given <code>Notification</code> name.
30
+ #
31
+ # @param notification_name [String] which observer list to remove from
32
+ # @param notify_context [untyped] remove the observers with this object as their notifyContext
33
+ def remove_observer: (String notification_name, untyped notify_context) -> void
34
+
35
+ # Register an <code>_IMediator</code> instance with the <code>View</code>.
36
+ #
37
+ # Registers the <code>_IMediator</code> so that it can be retrieved by name,
38
+ # and further interrogates the <code>_IMediator</code> for its
39
+ # <code>_INotification</code> interests.
40
+ #
41
+ # If the <code>_IMediator</code> returns any <code>_INotification</code>
42
+ # names to be notified about, an <code>Observer</code> is created encapsulating
43
+ # the <code>_IMediator</code> instance's <code>handleNotification</code> method
44
+ # and registering it as an <code>Observer</code> for all <code>_INotifications</code> the
45
+ # <code>_IMediator</code> is interested in.
46
+ #
47
+ # @param mediator [_IMediator] a reference to the <code>_IMediator</code> instance
48
+ def register_mediator: (_IMediator mediator) -> void
49
+
50
+ # Retrieve an <code>_IMediator</code> from the <code>View</code>.
51
+ #
52
+ # @param mediator_name [String] the name of the <code>_IMediator</code> instance to retrieve.
53
+ # @return [_IMediator | nil] the <code>_IMediator</code> instance previously registered with the given <code>mediatorName</code>.
54
+ def retrieve_mediator: (String mediator_name) -> _IMediator?
55
+
56
+ # Check if a <code>Mediator</code> is registered or not.
57
+ #
58
+ # @param mediator_name [String]
59
+ # @return [Boolean] whether a <code>Mediator</code> is registered with the given <code>mediatorName</code>.
60
+ def has_mediator?: (String mediator_name) -> bool
61
+
62
+ # Remove an <code>_IMediator</code> from the <code>View</code>.
63
+ #
64
+ # @param mediator_name [String] name of the <code>_IMediator</code> instance to be removed.
65
+ # @return [_IMediator] the <code>_IMediator</code> that was removed from the <code>View</code>
66
+ def remove_mediator: (String mediator_name) -> _IMediator?
67
+ end
68
+
69
+ # Assert that the given `View` type parameter is a subtype of `_IView`
70
+ type validate_view[View < _IView] = top
71
+ end
@@ -0,0 +1,18 @@
1
+ module PureMVC
2
+
3
+ class MacroCommand < Notifier
4
+
5
+ include _ICommand
6
+
7
+ @sub_commands: Array[^() -> _ICommand]
8
+
9
+ def initialize: () -> void
10
+
11
+ def initialize_macro_command: () -> void
12
+
13
+ def add_sub_command: () { () -> _ICommand } -> void
14
+ end
15
+
16
+ # Type-level assertion that MacroCommand conforms to _ICommand
17
+ type macro_command_validation = validate_command[MacroCommand]
18
+ end
@@ -0,0 +1,11 @@
1
+ module PureMVC
2
+
3
+ class SimpleCommand < Notifier
4
+
5
+ include _ICommand
6
+
7
+ end
8
+
9
+ # Type-level assertion that SimpleCommand conforms to _ICommand
10
+ type simple_command_validation = validate_command[SimpleCommand]
11
+ end
@@ -0,0 +1,48 @@
1
+ module PureMVC
2
+
3
+ class Facade
4
+
5
+ include _IFacade
6
+
7
+ self.@instance_map: Hash[String, _IFacade]
8
+
9
+ self.@mutex: Mutex
10
+
11
+ @model: _IModel?
12
+
13
+ @controller: _IController?
14
+
15
+ @view: _IView?
16
+
17
+ @multiton_key: String
18
+
19
+ MULTITON_MSG: String
20
+
21
+ def self.instance_map: () -> Hash[String, _IFacade]
22
+
23
+ private
24
+
25
+ def self.mutex: () -> Mutex
26
+
27
+ public
28
+
29
+ def self.get_instance: (String key) { (String k) -> _IFacade } -> _IFacade
30
+
31
+ def self.has_core?: (String key) -> bool
32
+
33
+ def self.remove_core: (String key) -> void
34
+
35
+ def initialize: (String key) -> void
36
+
37
+ def initialize_facade: () -> void
38
+
39
+ def initialize_controller: () -> void
40
+
41
+ def initialize_model: () -> void
42
+
43
+ def initialize_view: () -> void
44
+ end
45
+
46
+ # Type-level assertion that Facade conforms to _IFacade
47
+ type facade_validation = validate_facade[Facade]
48
+ end
@@ -0,0 +1,18 @@
1
+ module PureMVC
2
+
3
+ class Mediator < Notifier
4
+
5
+ include _IMediator
6
+
7
+ NAME: String
8
+
9
+ attr_reader name: String
10
+
11
+ attr_accessor component: Object?
12
+
13
+ def initialize: (?String? name, ?Object? component) -> void
14
+ end
15
+
16
+ # Type-level assertion that Mediator conforms to _IMediator
17
+ type mediator_validation = validate_mediator[Mediator]
18
+ end
@@ -0,0 +1,18 @@
1
+ module PureMVC
2
+
3
+ class Notification
4
+
5
+ include _INotification
6
+
7
+ attr_reader name: String
8
+
9
+ attr_accessor body: Object?
10
+
11
+ attr_accessor type: String?
12
+
13
+ def initialize: (String name, ?Object? body, ?String? type) -> void
14
+ end
15
+
16
+ # Type-level assertion that Notification conforms to _INotification
17
+ type notification_validation = validate_notification[Notification]
18
+ end
@@ -0,0 +1,17 @@
1
+ module PureMVC
2
+
3
+ class Notifier
4
+
5
+ include _INotifier
6
+
7
+ @multiton_key: String
8
+
9
+ MULTITON_MSG: String
10
+
11
+ def multiton_key: () -> String
12
+
13
+ end
14
+
15
+ # Type-level assertion that Notifier conforms to _INotifier
16
+ type notifier_validation = validate_notifier[Notifier]
17
+ end
@@ -0,0 +1,20 @@
1
+ module PureMVC
2
+
3
+ class Observer
4
+
5
+ include _IObserver
6
+
7
+ attr_accessor notify: Method?
8
+
9
+ attr_accessor context: Object?
10
+
11
+ def initialize: (?Method? notify, ?Object? context) -> void
12
+
13
+ def notify_observer: (_INotification) -> void
14
+
15
+ def compare_notify_context?: (Object) -> bool
16
+ end
17
+
18
+ # Type-level assertion that Observer conforms to _IObserver
19
+ type observer_validation = validate_observer[Observer]
20
+ end
@@ -0,0 +1,19 @@
1
+ module PureMVC
2
+
3
+ class Proxy < Notifier
4
+
5
+ include _IProxy
6
+
7
+ NAME: String
8
+
9
+ attr_reader name: String
10
+
11
+ attr_accessor data: Object?
12
+
13
+ def initialize: (?String? name, ?Object? data) -> void
14
+ end
15
+
16
+ # Type-level assertion that Proxy conforms to _IProxy
17
+ type proxy_validation = validate_proxy[Proxy]
18
+ end
19
+
@@ -0,0 +1,177 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ # controller.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>IController</code> implementation.
12
+ #
13
+ # In PureMVC, the <code>Controller</code> class follows the
14
+ # 'Command and Controller' strategy and assumes these responsibilities:
15
+ #
16
+ # * Remembering which <code>ICommand</code>s are intended to handle which <code>INotifications</code>.
17
+ # * Registering itself as an <code>IObserver</code> with the <code>View</code> for each <code>INotification</code>
18
+ # that has an <code>ICommand</code> mapping.
19
+ # * Creating a new instance of the proper <code>ICommand</code> to handle a given <code>INotification</code>
20
+ # when notified by the <code>View</code>.
21
+ # * Calling the <code>ICommand</code>'s <code>execute</code> method, passing in the <code>INotification</code>.
22
+ #
23
+ # Your application must register <code>ICommands</code> with the <code>Controller</code>.
24
+ #
25
+ # The simplest way is to subclass <code>Facade</code>, and use its <code>initializeController</code> method.
26
+ # to add your registrations.
27
+ #
28
+ # @see View
29
+ # @see Observer
30
+ # @see Notification
31
+ # @see SimpleCommand
32
+ # @see MacroCommand
33
+ class Controller
34
+ # Message Constants
35
+ MULTITON_MSG = 'Controller instance for this Multiton key already constructed!'
36
+ private_constant :MULTITON_MSG
37
+
38
+ class << self
39
+ # The Multiton IController instanceMap.
40
+ # @return [Hash{String => IController}]
41
+ def instance_map = (@instance_map ||= {})
42
+
43
+ # Mutex used to synchronize access to the instance map for thread safety.
44
+ # @return [Mutex]
45
+ def mutex = (@mutex ||= Mutex.new)
46
+
47
+ # Gets an instance using the provided factory block
48
+ #
49
+ # @param key [String] the unique key identifying the Multiton instance
50
+ # @param factory [^(String) -> _IController] the unique key passed to the factory block
51
+ # @return [IController] The controller instance created by the factory
52
+ def get_instance(key, &factory)
53
+ mutex.synchronize do
54
+ instance_map[key] ||= factory.call(key)
55
+ end
56
+ end
57
+
58
+ # Remove an IController instance
59
+ #
60
+ # @param key [String] the multiton key of the IController instance to remove
61
+ def remove_controller(key)
62
+ mutex.synchronize do
63
+ instance_map.delete(key)
64
+ end
65
+ end
66
+ end
67
+
68
+ # Constructor.
69
+ #
70
+ # This IController implementation is a Multiton, so you should not call the constructor
71
+ # directly. Instead, call the static factory method, passing the unique key for this instance:
72
+ # <code>PureMVC::Controller.getInstance(key) { |key| PureMVC::Controller.new(key) }</code>
73
+ #
74
+ # @param key [String]
75
+ # @raise [RuntimeError] if an instance for this Multiton key has already been constructed
76
+ def initialize(key)
77
+ raise MULTITON_MSG if self.class.instance_map[key]
78
+
79
+ self.class.instance_map[key] = self
80
+ # The Multiton Key for this Core
81
+ # @type var multiton_key: String
82
+ @multiton_key = key
83
+ # Local reference to View
84
+ # @type var component: _IView?
85
+ @view = nil
86
+ # Mapping of Notification names to Command factories
87
+ # @type var command_map: Hash[String, ^() -> _ICommand]
88
+ @command_map = {}
89
+ # Mutex used to synchronize access to the @command_map
90
+ # @type var command_mutex: Mutex
91
+ @command_mutex = Mutex.new
92
+ initialize_controller
93
+ end
94
+
95
+ # Initialize the Multiton Controller instance.
96
+ #
97
+ # Called automatically by the constructor.
98
+ #
99
+ # Note that if you are using a subclass of View
100
+ # in your application, you should also subclass Controller
101
+ # and override the initialize_controller method in the
102
+ # following way:
103
+ #
104
+ # @example
105
+ # # ensure that the Controller is talking to my IView implementation
106
+ # def initialize_controller
107
+ # @view = MyView::get_instance(key) { |key| MyView.new(key) }
108
+ # end
109
+ #
110
+ def initialize_controller
111
+ @view = View.get_instance(@multiton_key) { |key| View.new(key) }
112
+ end
113
+
114
+ # Register a particular ICommand class as the handler for a particular INotification.
115
+ #
116
+ # If an ICommand has already been registered to handle INotifications with this name,
117
+ # it is replaced by the new ICommand.
118
+ #
119
+ # The Observer for the new ICommand is only created if this is the first time
120
+ # an ICommand has been registered for this notification name.
121
+ #
122
+ # @param notification_name [String] the name of the INotification
123
+ # @param factory [^<() -> ICommand>] the factory to produce an instance of the ICommand
124
+ def register_command(notification_name, &factory)
125
+ @command_mutex.synchronize do
126
+ if @command_map[notification_name].nil?
127
+ @view&.register_observer(notification_name, Observer.new(method(:execute_command), self))
128
+ end
129
+ @command_map[notification_name] = factory
130
+ end
131
+ end
132
+
133
+ # If an ICommand has previously been registered to handle the given INotification, then it is executed.
134
+ #
135
+ # @param notification [INotification] the notification to handle
136
+ def execute_command(notification)
137
+ @command_mutex.synchronize do
138
+ # @type factory: [^() -> ICommand]?
139
+ factory = @command_map[notification.name]
140
+ return if factory.nil?
141
+
142
+ # @type var command: _ICommand
143
+ command = factory.call
144
+ command.initialize_notifier(@multiton_key)
145
+ command.execute(notification)
146
+ end
147
+ end
148
+
149
+ # Check if a Command is registered for a given Notification.
150
+ #
151
+ # @param notification_name [String] the name of the Notification
152
+ # @return [Boolean] whether a Command is currently registered for the given notification_name
153
+ def has_command?(notification_name)
154
+ @command_mutex.synchronize do
155
+ @command_map.key?(notification_name)
156
+ end
157
+ end
158
+
159
+ # Remove a previously registered ICommand to INotification mapping.
160
+ #
161
+ # @param notification_name [String] the name of the INotification to remove the ICommand mapping for
162
+ def remove_command(notification_name)
163
+ @command_mutex.synchronize do
164
+ # @type var command: [^() -> ICommand]?
165
+ command = @command_map[notification_name]
166
+
167
+ # if the Command is registered...
168
+ if command
169
+ # remove the observer
170
+ @view&.remove_observer(notification_name, self)
171
+ # remove the command
172
+ @command_map.delete(notification_name)
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
data/src/core/model.rb ADDED
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ # model.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>IModel</code> implementation.
12
+ #
13
+ # In PureMVC, the <code>Model</code> class provides access to model objects (Proxies) by named lookup.
14
+ #
15
+ # The <code>Model</code> assumes these responsibilities:
16
+ #
17
+ # - Maintains a cache of <code>IProxy</code> instances.
18
+ # - Provides methods for registering, retrieving, and removing <code>IProxy</code> instances.
19
+ #
20
+ # Your application must register <code>IProxy</code> instances with the <code>Model</code>. Typically,
21
+ # an <code>ICommand</code> is used to create and register <code>IProxy</code> instances after the <code>Facade<code>
22
+ # has initialized the Core actors.
23
+ #
24
+ # @see Proxy
25
+ # @see IProxy
26
+ class Model
27
+ # Message Constants
28
+ MULTITON_MSG = 'Model instance for this Multiton key already constructed!'
29
+ private_constant :MULTITON_MSG
30
+
31
+ class << self
32
+ # The Multiton IModel instanceMap.
33
+ # @return [Hash{String => IModel}]
34
+ def instance_map = (@instance_map ||= {})
35
+
36
+ # Mutex used to synchronize access to the instance map for thread safety.
37
+ # @return [Mutex]
38
+ def mutex = (@mutex ||= Mutex.new)
39
+
40
+ # <code>Model</code> Multiton Factory method.
41
+ #
42
+ # @param key [String] the unique key identifying the Multiton instance
43
+ # @param factory [^(String) -> IModel] the unique key passed to the factory block
44
+ # @return [IModel] the instance for this Multiton key
45
+ def get_instance(key, &factory)
46
+ mutex.synchronize do
47
+ instance_map[key] ||= factory.call(key)
48
+ end
49
+ end
50
+
51
+ # Remove an IModel instance
52
+ #
53
+ # @param key [String] the multiton key of the IModel instance to remove
54
+ def remove_model(key)
55
+ mutex.synchronize do
56
+ instance_map.delete(key)
57
+ end
58
+ end
59
+ end
60
+
61
+ # Constructor.
62
+ #
63
+ # This <code>IModel</code> implementation is a Multiton,
64
+ # so you should not call the constructor directly, but instead call the static
65
+ # Multiton Factory method <code>PureMVC::Model.get_instance(key) { |key| PureMVC::Model.new(key) }</code>.
66
+ #
67
+ # @param key [String]
68
+ # @raise [RuntimeError] Error if an instance for this Multiton key has already been constructed.
69
+ def initialize(key)
70
+ raise MULTITON_MSG if self.class.instance_map[key]
71
+
72
+ self.class.instance_map[key] = self
73
+ @multiton_key = key
74
+ @proxy_map = {}
75
+ @proxy_mutex = Mutex.new
76
+ initialize_model
77
+ end
78
+
79
+ # Initialize the <code>Model</code> instance.
80
+ #
81
+ # Called automatically by the constructor, this
82
+ # is your opportunity to initialize the Multiton
83
+ # instance in your subclass without overriding the
84
+ # constructor.
85
+ def initialize_model; end
86
+
87
+ # Register an <code>IProxy</code> with the <code>Model</code>.
88
+ #
89
+ # @param proxy [_IProxy] an <code>IProxy</code> to be held by the <code>Model</code>.
90
+ def register_proxy(proxy)
91
+ proxy.initialize_notifier(@multiton_key)
92
+ @proxy_mutex.synchronize do
93
+ @proxy_map[proxy.name] = proxy
94
+ end
95
+ proxy.on_register
96
+ end
97
+
98
+ # Retrieve an <code>IProxy</code> from the <code>Model</code>.
99
+ #
100
+ # @param proxy_name [String] the name of the proxy to retrieve.
101
+ # @return [_IProxy, nil] the <code>IProxy</code> instance previously registered with the <code>proxy_name</code>,
102
+ # or nil if none found.
103
+ def retrieve_proxy(proxy_name)
104
+ @proxy_mutex.synchronize do
105
+ @proxy_map[proxy_name]
106
+ end
107
+ end
108
+
109
+ # Check if a Proxy is registered.
110
+ #
111
+ # @param proxy_name [String] the name of the proxy to check.
112
+ # @return [Boolean] whether a Proxy is currently registered with the given <code>proxy_name</code>.
113
+ def has_proxy?(proxy_name)
114
+ @proxy_mutex.synchronize do
115
+ @proxy_map.key?(proxy_name)
116
+ end
117
+ end
118
+
119
+ # Remove an <code>IProxy</code> from the <code>Model</code>.
120
+ #
121
+ # @param proxy_name [String] name of the <code>IProxy</code> instance to be removed.
122
+ # @return [_IProxy, nil] the <code>IProxy</code> that was removed from the <code>Model</code>, or nil if none found.
123
+ def remove_proxy(proxy_name)
124
+ # @type var proxy: _IProxy?
125
+ proxy = nil
126
+ @proxy_mutex.synchronize do
127
+ proxy = @proxy_map.delete(proxy_name)
128
+ end
129
+ proxy&.on_remove
130
+ proxy
131
+ end
132
+ end
133
+ end