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
@@ -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,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
|