rotor-framework 0.3.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.
- package/LICENSE.md +21 -0
- package/README.md +120 -0
- package/package.json +59 -0
- package/src/source/RotorFramework.bs +654 -0
- package/src/source/RotorFrameworkTask.bs +278 -0
- package/src/source/base/BaseModel.bs +52 -0
- package/src/source/base/BasePlugin.bs +48 -0
- package/src/source/base/BaseReducer.bs +184 -0
- package/src/source/base/BaseStack.bs +92 -0
- package/src/source/base/BaseViewModel.bs +124 -0
- package/src/source/base/BaseWidget.bs +104 -0
- package/src/source/base/DispatcherCreator.bs +193 -0
- package/src/source/base/DispatcherExternal.bs +260 -0
- package/src/source/base/ListenerForDispatchers.bs +246 -0
- package/src/source/engine/Constants.bs +74 -0
- package/src/source/engine/animator/Animator.bs +334 -0
- package/src/source/engine/builder/Builder.bs +213 -0
- package/src/source/engine/builder/NodePool.bs +236 -0
- package/src/source/engine/builder/PluginAdapter.bs +139 -0
- package/src/source/engine/builder/PostProcessor.bs +331 -0
- package/src/source/engine/builder/Processor.bs +156 -0
- package/src/source/engine/builder/Tree.bs +278 -0
- package/src/source/engine/builder/TreeBase.bs +313 -0
- package/src/source/engine/builder/WidgetCreate.bs +322 -0
- package/src/source/engine/builder/WidgetRemove.bs +72 -0
- package/src/source/engine/builder/WidgetUpdate.bs +113 -0
- package/src/source/engine/providers/Dispatcher.bs +72 -0
- package/src/source/engine/providers/DispatcherProvider.bs +95 -0
- package/src/source/engine/services/I18n.bs +169 -0
- package/src/source/libs/animate/Animate.bs +753 -0
- package/src/source/libs/animate/LICENSE.txt +21 -0
- package/src/source/plugins/DispatcherProviderPlugin.bs +127 -0
- package/src/source/plugins/FieldsPlugin.bs +180 -0
- package/src/source/plugins/FocusPlugin.bs +1522 -0
- package/src/source/plugins/FontStylePlugin.bs +159 -0
- package/src/source/plugins/ObserverPlugin.bs +548 -0
- package/src/source/utils/ArrayUtils.bs +495 -0
- package/src/source/utils/GeneralUtils.bs +181 -0
- package/src/source/utils/NodeUtils.bs +180 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
'''''''''
|
|
2
|
+
' ▗▄▄▖ ▗▄▖▗▄▄▄▖▗▄▖ ▗▄▄▖ ▗▄▄▄▖▗▄▄▖ ▗▄▖ ▗▖ ▗▖▗▄▄▄▖▗▖ ▗▖ ▗▄▖ ▗▄▄▖ ▗▖ ▗▖
|
|
3
|
+
' ▐▌ ▐▌▐▌ ▐▌ █ ▐▌ ▐▌▐▌ ▐▌ ▐▌ ▐▌ ▐▌▐▌ ▐▌▐▛▚▞▜▌▐▌ ▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▌▗▞▘
|
|
4
|
+
' ▐▛▀▚▖▐▌ ▐▌ █ ▐▌ ▐▌▐▛▀▚▖ ▐▛▀▀▘▐▛▀▚▖▐▛▀▜▌▐▌ ▐▌▐▛▀▀▘▐▌ ▐▌▐▌ ▐▌▐▛▀▚▖▐▛▚▖
|
|
5
|
+
' ▐▌ ▐▌▝▚▄▞▘ █ ▝▚▄▞▘▐▌ ▐▌ ▐▌ ▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▙▄▄▖▐▙█▟▌▝▚▄▞▘▐▌ ▐▌▐▌ ▐▌
|
|
6
|
+
' Rotor Framework™ © 2025 Balázs Molnár. All rights reserved.
|
|
7
|
+
' Version 0.3.2
|
|
8
|
+
'''''''''
|
|
9
|
+
|
|
10
|
+
' constants
|
|
11
|
+
import "engine/Constants.bs"
|
|
12
|
+
|
|
13
|
+
' engine
|
|
14
|
+
import "engine/providers/DispatcherProvider.bs"
|
|
15
|
+
import "engine/providers/Dispatcher.bs"
|
|
16
|
+
|
|
17
|
+
' base classes
|
|
18
|
+
import "base/DispatcherCreator.bs"
|
|
19
|
+
import "base/DispatcherExternal.bs"
|
|
20
|
+
import "base/BaseReducer.bs"
|
|
21
|
+
import "base/BaseModel.bs"
|
|
22
|
+
import "base/BaseStack.bs"
|
|
23
|
+
|
|
24
|
+
' utils
|
|
25
|
+
import "utils/GeneralUtils.bs"
|
|
26
|
+
import "utils/NodeUtils.bs"
|
|
27
|
+
import "utils/ArrayUtils.bs"
|
|
28
|
+
|
|
29
|
+
namespace Rotor
|
|
30
|
+
' =====================================================================
|
|
31
|
+
' FrameworkTask - Task thread version of Rotor Framework for MVI
|
|
32
|
+
'
|
|
33
|
+
' Task thread version of the Rotor Framework that enables cross-thread MVI
|
|
34
|
+
' (Model-View-Intent) architecture. This class manages state and dispatchers
|
|
35
|
+
' on a separate task thread, allowing heavy computations and state management
|
|
36
|
+
' to run off the render thread for better performance.
|
|
37
|
+
'
|
|
38
|
+
' Configuration:
|
|
39
|
+
' - tasks (array, optional): List of additional task node names to synchronize with.
|
|
40
|
+
' Allows multiple task threads to communicate and share
|
|
41
|
+
' dispatchers across different threads.
|
|
42
|
+
'
|
|
43
|
+
' USAGE NOTES:
|
|
44
|
+
' The FrameworkTask must be instantiated in the task's init() function and the sync()
|
|
45
|
+
' method MUST be called at the end of your task function to establish the message loop.
|
|
46
|
+
'
|
|
47
|
+
' IMPORTANT: The sync() method creates an infinite loop that handles cross-thread
|
|
48
|
+
' communication and dispatcher synchronization. This call should be the LAST statement
|
|
49
|
+
' in your task function, after all dispatcher initialization.
|
|
50
|
+
'
|
|
51
|
+
' Example:
|
|
52
|
+
' File: MyTask.task.bs
|
|
53
|
+
' import "pkg:/source/RotorFrameworkTask.bs"
|
|
54
|
+
' import "pkg:/source/MyDispatcher.bs"
|
|
55
|
+
'
|
|
56
|
+
' sub init()
|
|
57
|
+
' m.top.functionName = "task"
|
|
58
|
+
' m.appFw = new Rotor.FrameworkTask({
|
|
59
|
+
' tasks: ["AnotherTask"]
|
|
60
|
+
' })
|
|
61
|
+
' end sub
|
|
62
|
+
'
|
|
63
|
+
' sub task()
|
|
64
|
+
' m.fooDispatcher = createFooDispatcher()
|
|
65
|
+
' m.barDispatcher = createBarDispatcher()
|
|
66
|
+
' m.appFw.sync()
|
|
67
|
+
' end sub
|
|
68
|
+
' =====================================================================
|
|
69
|
+
class FrameworkTask
|
|
70
|
+
|
|
71
|
+
name = "Rotor Framework"
|
|
72
|
+
version = "0.3.2"
|
|
73
|
+
|
|
74
|
+
config = {
|
|
75
|
+
tasks: invalid, ' optional
|
|
76
|
+
debug: {
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
threadType = Rotor.Const.ThreadType.TASK
|
|
81
|
+
|
|
82
|
+
keepAlive = true
|
|
83
|
+
|
|
84
|
+
' helper vars
|
|
85
|
+
taskNode as object
|
|
86
|
+
dispatcherProvider as object
|
|
87
|
+
port as object
|
|
88
|
+
|
|
89
|
+
' ---------------------------------------------------------------------
|
|
90
|
+
' new - Initializes the FrameworkTask instance
|
|
91
|
+
'
|
|
92
|
+
' Sets up the task thread dispatcher provider, message port, and
|
|
93
|
+
' global framework helper for cross-thread communication.
|
|
94
|
+
'
|
|
95
|
+
' @param {object} config - Configuration object (see class documentation)
|
|
96
|
+
'
|
|
97
|
+
sub new(config = {} as object)
|
|
98
|
+
|
|
99
|
+
Rotor.Utils.deepExtendAA(m.config, config)
|
|
100
|
+
|
|
101
|
+
globalScope = GetGlobalAA()
|
|
102
|
+
globalScope.rotor_framework_helper = { ' this give to dispatcher instance the possibility to self-register
|
|
103
|
+
threadType: m.threadType,
|
|
104
|
+
frameworkInstance: m
|
|
105
|
+
}
|
|
106
|
+
m.taskNode = globalScope.top
|
|
107
|
+
|
|
108
|
+
m.dispatcherProvider = new Rotor.DispatcherProvider(m.threadType)
|
|
109
|
+
|
|
110
|
+
m.taskNode.addField("rotorSync", "assocarray", true)
|
|
111
|
+
m.port = CreateObject("roMessagePort")
|
|
112
|
+
m.taskNode.observeFieldScoped("rotorSync", m.port)
|
|
113
|
+
|
|
114
|
+
end sub
|
|
115
|
+
|
|
116
|
+
' =====================================================================
|
|
117
|
+
' PUBLIC API
|
|
118
|
+
' =====================================================================
|
|
119
|
+
|
|
120
|
+
' ---------------------------------------------------------------------
|
|
121
|
+
' getDispatcher - Gets dispatcher facade by ID
|
|
122
|
+
'
|
|
123
|
+
' @param {string} dispatcherId - Dispatcher identifier
|
|
124
|
+
' @returns {object} Dispatcher facade instance
|
|
125
|
+
'
|
|
126
|
+
public function getDispatcher(dispatcherId as string) as object
|
|
127
|
+
return m.dispatcherProvider.getFacade(dispatcherId, GetGlobalAA())
|
|
128
|
+
end function
|
|
129
|
+
|
|
130
|
+
' ---------------------------------------------------------------------
|
|
131
|
+
' sync - Starts the message loop for cross-thread communication
|
|
132
|
+
'
|
|
133
|
+
' IMPORTANT: This method creates an infinite loop that handles:
|
|
134
|
+
' - Intent dispatching from render thread
|
|
135
|
+
' - External dispatcher registration
|
|
136
|
+
' - State change notifications
|
|
137
|
+
' - Async reducer callbacks
|
|
138
|
+
'
|
|
139
|
+
' This method MUST be the last call in your task function, as it
|
|
140
|
+
' blocks execution until the framework is destroyed.
|
|
141
|
+
'
|
|
142
|
+
sub sync()
|
|
143
|
+
m.notifySyncStatus(Rotor.Const.ThreadSyncType.TASK_SYNCING)
|
|
144
|
+
|
|
145
|
+
keepAlive = true
|
|
146
|
+
|
|
147
|
+
while true and keepAlive = true
|
|
148
|
+
msg = wait(0, m.port)
|
|
149
|
+
if msg <> invalid
|
|
150
|
+
msgType = type(msg)
|
|
151
|
+
if msgType = "roSGNodeEvent"
|
|
152
|
+
fieldId = msg.getField()
|
|
153
|
+
|
|
154
|
+
if fieldId = "rotorSync"
|
|
155
|
+
|
|
156
|
+
sync = msg.getData() ' @type:AA
|
|
157
|
+
|
|
158
|
+
if sync.type = Rotor.Const.ThreadSyncType.DISPATCH
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
dispatcherId = sync.payload.dispatcherId
|
|
162
|
+
intent = sync.payload.intent
|
|
163
|
+
dispatcherInstance = m.dispatcherProvider.stack.LookupCI(dispatcherId)
|
|
164
|
+
|
|
165
|
+
' taskIntent = Rotor.Utils.deepCopy(intent)
|
|
166
|
+
dispatcherInstance.dispatch(intent)
|
|
167
|
+
|
|
168
|
+
else if sync.type = Rotor.Const.ThreadSyncType.REGISTER_EXTERNAL_DISPATCHER
|
|
169
|
+
|
|
170
|
+
for each item in sync.externalDispatcherList
|
|
171
|
+
m.dispatcherProvider.registerExternalDispatchers(item.dispatcherId, item.externalTaskNode)
|
|
172
|
+
end for
|
|
173
|
+
|
|
174
|
+
m.notifySyncStatus(Rotor.Const.ThreadSyncType.TASK_SYNCED)
|
|
175
|
+
|
|
176
|
+
else if sync.type = Rotor.Const.ThreadSyncType.DESTROY
|
|
177
|
+
|
|
178
|
+
keepAlive = false
|
|
179
|
+
|
|
180
|
+
end if
|
|
181
|
+
else
|
|
182
|
+
|
|
183
|
+
data = msg.getData()
|
|
184
|
+
extraInfo = msg.GetInfo() ' Info AA passed during observeFieldScoped
|
|
185
|
+
|
|
186
|
+
if extraInfo?.asyncReducerCallbackId <> invalid and m.dispatcherProvider.get(extraInfo?.asyncReducerCallbackId) <> invalid
|
|
187
|
+
' Catch by dispatcherId
|
|
188
|
+
m.dispatcherProvider.get(extraInfo?.asyncReducerCallbackId).asyncReducerCallback(msg)
|
|
189
|
+
else
|
|
190
|
+
dispatcherId = fieldId
|
|
191
|
+
dispatcherInstance = m.dispatcherProvider.get(dispatcherId)
|
|
192
|
+
dispatcherInstance.notifyListeners(data)
|
|
193
|
+
end if
|
|
194
|
+
|
|
195
|
+
end if
|
|
196
|
+
end if
|
|
197
|
+
end if
|
|
198
|
+
end while
|
|
199
|
+
m.destroy()
|
|
200
|
+
end sub
|
|
201
|
+
|
|
202
|
+
' =====================================================================
|
|
203
|
+
' INTERNAL METHODS
|
|
204
|
+
' =====================================================================
|
|
205
|
+
|
|
206
|
+
' ---------------------------------------------------------------------
|
|
207
|
+
' notifySyncStatus - Notifies render thread of sync status
|
|
208
|
+
'
|
|
209
|
+
' Sends sync status message to render thread via rotorSync field.
|
|
210
|
+
'
|
|
211
|
+
' @param {string} status - Sync status type (TASK_SYNCING or TASK_SYNCED)
|
|
212
|
+
'
|
|
213
|
+
sub notifySyncStatus(status as string)
|
|
214
|
+
|
|
215
|
+
payload = {
|
|
216
|
+
type: status,
|
|
217
|
+
taskNode: m.taskNode
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if status = Rotor.Const.ThreadSyncType.TASK_SYNCING
|
|
221
|
+
payload.append({
|
|
222
|
+
dispatcherIds: m.dispatcherProvider.stack.Keys(),
|
|
223
|
+
tasks: m.config.tasks
|
|
224
|
+
})
|
|
225
|
+
end if
|
|
226
|
+
|
|
227
|
+
m.taskNode.rootNode.setField("rotorSync", payload)
|
|
228
|
+
|
|
229
|
+
end sub
|
|
230
|
+
|
|
231
|
+
' ---------------------------------------------------------------------
|
|
232
|
+
' addObserver - Adds field observer to task thread message port
|
|
233
|
+
'
|
|
234
|
+
' @param {string} fieldId - Field name to observe
|
|
235
|
+
' @param {object} node - SceneGraph node to observe
|
|
236
|
+
'
|
|
237
|
+
sub addObserver(fieldId as string, node)
|
|
238
|
+
node.observeFieldScoped(fieldId, m.port)
|
|
239
|
+
end sub
|
|
240
|
+
|
|
241
|
+
' ---------------------------------------------------------------------
|
|
242
|
+
' removeObserver - Removes field observer from node
|
|
243
|
+
'
|
|
244
|
+
' @param {string} fieldId - Field name to stop observing
|
|
245
|
+
' @param {object} node - SceneGraph node to unobserve
|
|
246
|
+
'
|
|
247
|
+
sub removeObserver(fieldId as string, node)
|
|
248
|
+
node.unobserveFieldScoped(fieldId)
|
|
249
|
+
end sub
|
|
250
|
+
|
|
251
|
+
' =====================================================================
|
|
252
|
+
' CLEANUP
|
|
253
|
+
' =====================================================================
|
|
254
|
+
|
|
255
|
+
' ---------------------------------------------------------------------
|
|
256
|
+
' destroy - Cleans up task thread resources
|
|
257
|
+
'
|
|
258
|
+
' Destroys dispatcher provider and clears global framework helper.
|
|
259
|
+
'
|
|
260
|
+
public sub destroy()
|
|
261
|
+
m.dispatcherProvider.destroy()
|
|
262
|
+
|
|
263
|
+
globalScope = GetGlobalAA()
|
|
264
|
+
globalScope.rotor_framework_helper = {
|
|
265
|
+
frameworkInstance: invalid
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
m.taskNode.rootNode = invalid
|
|
269
|
+
m.taskNode = invalid
|
|
270
|
+
end sub
|
|
271
|
+
|
|
272
|
+
end class
|
|
273
|
+
|
|
274
|
+
end namespace
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
namespace Rotor
|
|
2
|
+
|
|
3
|
+
' =====================================================================
|
|
4
|
+
' Model (BaseModel) - Base model class for MVI pattern state management
|
|
5
|
+
'
|
|
6
|
+
' Responsibilities:
|
|
7
|
+
' - Holds application state as immutable container
|
|
8
|
+
' - Works with Dispatchers and Reducers for state changes
|
|
9
|
+
' - Provides cleanup mechanism
|
|
10
|
+
'
|
|
11
|
+
' Usage in MVI:
|
|
12
|
+
' Model → Holds current state
|
|
13
|
+
' Reducer → Creates new state based on intents
|
|
14
|
+
' Dispatcher → Routes intents and state updates
|
|
15
|
+
' =====================================================================
|
|
16
|
+
class Model
|
|
17
|
+
|
|
18
|
+
' =============================================================
|
|
19
|
+
' MEMBER VARIABLES
|
|
20
|
+
' =============================================================
|
|
21
|
+
|
|
22
|
+
state as object ' Application state container
|
|
23
|
+
|
|
24
|
+
' =============================================================
|
|
25
|
+
' CONSTRUCTOR
|
|
26
|
+
' =============================================================
|
|
27
|
+
|
|
28
|
+
' ---------------------------------------------------------------------
|
|
29
|
+
' Constructor - Initializes model with optional initial state
|
|
30
|
+
'
|
|
31
|
+
' @param {object} state - Initial state object (optional)
|
|
32
|
+
'
|
|
33
|
+
sub new(state = invalid as object)
|
|
34
|
+
if state <> invalid
|
|
35
|
+
m.state = state
|
|
36
|
+
end if
|
|
37
|
+
end sub
|
|
38
|
+
|
|
39
|
+
' =============================================================
|
|
40
|
+
' CLEANUP
|
|
41
|
+
' =============================================================
|
|
42
|
+
|
|
43
|
+
' ---------------------------------------------------------------------
|
|
44
|
+
' destroy - Cleans up model by invalidating state reference
|
|
45
|
+
'
|
|
46
|
+
sub destroy()
|
|
47
|
+
m.state = invalid
|
|
48
|
+
end sub
|
|
49
|
+
|
|
50
|
+
end class
|
|
51
|
+
|
|
52
|
+
end namespace
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
namespace Rotor
|
|
2
|
+
|
|
3
|
+
' =====================================================================
|
|
4
|
+
' BasePlugin - Base class for all Rotor Framework plugins
|
|
5
|
+
'
|
|
6
|
+
' Provides:
|
|
7
|
+
' - Plugin identification and lifecycle management
|
|
8
|
+
' - Enable/disable functionality
|
|
9
|
+
' - Parameter storage
|
|
10
|
+
'
|
|
11
|
+
' Plugin Types:
|
|
12
|
+
' - Lifecycle plugins: Implement hooks {} for beforeMount, beforeUpdate, etc.
|
|
13
|
+
' - Widget method plugins: Implement widgetMethods {} to inject methods
|
|
14
|
+
' =====================================================================
|
|
15
|
+
class BasePlugin
|
|
16
|
+
|
|
17
|
+
' =============================================================
|
|
18
|
+
' MEMBER VARIABLES
|
|
19
|
+
' =============================================================
|
|
20
|
+
|
|
21
|
+
isEnabled = true ' Plugin enabled/disabled flag
|
|
22
|
+
key as string ' Plugin identifier (unique key)
|
|
23
|
+
scope as object ' Plugin scope (m context in plugin definitions)
|
|
24
|
+
params as object ' Plugin initialization parameters
|
|
25
|
+
|
|
26
|
+
' Optional plugin features (implemented by subclasses):
|
|
27
|
+
' hooks as object ' Widget lifecycle hooks (beforeMount, beforeUpdate, etc.)
|
|
28
|
+
' widgetMethods as object ' Methods to inject into widgets
|
|
29
|
+
|
|
30
|
+
' =============================================================
|
|
31
|
+
' CONSTRUCTOR
|
|
32
|
+
' =============================================================
|
|
33
|
+
|
|
34
|
+
' ---------------------------------------------------------------------
|
|
35
|
+
' Constructor - Initializes plugin with key and parameters
|
|
36
|
+
'
|
|
37
|
+
' @param {string} key - Plugin identifier (auto-generated if empty)
|
|
38
|
+
' @param {object} params - Plugin initialization parameters
|
|
39
|
+
'
|
|
40
|
+
sub new(key = "" as string, params = invalid as object)
|
|
41
|
+
if key = "" then key = Rotor.Utils.getUUIDHex(8)
|
|
42
|
+
m.params = params
|
|
43
|
+
m.key = key
|
|
44
|
+
end sub
|
|
45
|
+
|
|
46
|
+
end class
|
|
47
|
+
|
|
48
|
+
end namespace
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
namespace Rotor
|
|
2
|
+
|
|
3
|
+
' =====================================================================
|
|
4
|
+
' Reducer (BaseReducer) - Base class for state reducers in the MVI pattern
|
|
5
|
+
'
|
|
6
|
+
' Responsibilities:
|
|
7
|
+
' - Computes next state from current state + intent
|
|
8
|
+
' - Applies middleware pipeline for async operations, logging, validation
|
|
9
|
+
' - Provides pure state transformation logic
|
|
10
|
+
' - Integrates with Dispatcher for state updates
|
|
11
|
+
'
|
|
12
|
+
' MVI Flow:
|
|
13
|
+
' Intent → Middleware → Reducer → New State → Model → View Update
|
|
14
|
+
'
|
|
15
|
+
' Override Points:
|
|
16
|
+
' - reducer(state, intent): Define state transitions
|
|
17
|
+
' - applyMiddlewares(): Return array of middleware functions
|
|
18
|
+
' - middlewares: Set middleware array directly
|
|
19
|
+
' =====================================================================
|
|
20
|
+
class Reducer
|
|
21
|
+
|
|
22
|
+
' =============================================================
|
|
23
|
+
' MEMBER VARIABLES
|
|
24
|
+
' =============================================================
|
|
25
|
+
|
|
26
|
+
model as object ' Reference to Model holding state
|
|
27
|
+
middlewares = [] as function[] ' Middleware function array
|
|
28
|
+
port as object ' Framework port for async operations
|
|
29
|
+
|
|
30
|
+
' Dispatcher integration
|
|
31
|
+
getDispatcher as function ' Get dispatcher facade by ID
|
|
32
|
+
dispatch as function ' Dispatch intent to owner dispatcher
|
|
33
|
+
ownerDispatcher as object ' Owning dispatcher instance
|
|
34
|
+
ownerDispatcherId as string ' Owning dispatcher ID
|
|
35
|
+
|
|
36
|
+
' Internal
|
|
37
|
+
middlewareFnScoped as dynamic ' Currently executing middleware
|
|
38
|
+
|
|
39
|
+
' =============================================================
|
|
40
|
+
' CONSTRUCTOR
|
|
41
|
+
' =============================================================
|
|
42
|
+
|
|
43
|
+
' ---------------------------------------------------------------------
|
|
44
|
+
' Constructor - Initializes reducer with framework integration
|
|
45
|
+
'
|
|
46
|
+
sub new()
|
|
47
|
+
frameworkInstance = GetGlobalAA().rotor_framework_helper.frameworkInstance
|
|
48
|
+
m.port = frameworkInstance.port
|
|
49
|
+
|
|
50
|
+
' Inject getDispatcher method
|
|
51
|
+
m.getDispatcher = function(dispatcherId as string) as object
|
|
52
|
+
return GetGlobalAA().rotor_framework_helper.frameworkInstance.dispatcherProvider.getFacade(dispatcherId, m)
|
|
53
|
+
end function
|
|
54
|
+
|
|
55
|
+
' Inject dispatch method for self-dispatching
|
|
56
|
+
m.dispatch = sub(intent as object)
|
|
57
|
+
m.ownerDispatcher.dispatch(intent)
|
|
58
|
+
end sub
|
|
59
|
+
end sub
|
|
60
|
+
|
|
61
|
+
' =============================================================
|
|
62
|
+
' REDUCER LOGIC
|
|
63
|
+
' =============================================================
|
|
64
|
+
|
|
65
|
+
' ---------------------------------------------------------------------
|
|
66
|
+
' reducer - Pure function that computes next state
|
|
67
|
+
'
|
|
68
|
+
' Override this method to implement custom state transitions based on intent type.
|
|
69
|
+
' Should return a new state object (immutable pattern).
|
|
70
|
+
'
|
|
71
|
+
' @param {object} state - Current state
|
|
72
|
+
' @param {Intent} intent - Intent object { type, payload, ... }
|
|
73
|
+
' @return {object} New state
|
|
74
|
+
'
|
|
75
|
+
' Example:
|
|
76
|
+
' function reducer(state as object, intent as Intent)
|
|
77
|
+
' if intent.type = "INCREMENT"
|
|
78
|
+
' newState = Rotor.Utils.deepCopy(state)
|
|
79
|
+
' newState.count = state.count + 1
|
|
80
|
+
' return newState
|
|
81
|
+
' end if
|
|
82
|
+
' return state
|
|
83
|
+
' end function
|
|
84
|
+
'
|
|
85
|
+
public function reducer(state as object, intent as Intent)
|
|
86
|
+
return state
|
|
87
|
+
end function
|
|
88
|
+
|
|
89
|
+
' =============================================================
|
|
90
|
+
' MIDDLEWARE
|
|
91
|
+
' =============================================================
|
|
92
|
+
|
|
93
|
+
' ---------------------------------------------------------------------
|
|
94
|
+
' applyMiddlewares - Returns middleware function array
|
|
95
|
+
'
|
|
96
|
+
' Override to implement middleware logic for:
|
|
97
|
+
' - Async operations (API calls, timers)
|
|
98
|
+
' - Logging and debugging
|
|
99
|
+
' - Intent validation
|
|
100
|
+
' - Intent transformation
|
|
101
|
+
'
|
|
102
|
+
' @return {function[]} Array of middleware functions
|
|
103
|
+
'
|
|
104
|
+
' Middleware signature:
|
|
105
|
+
' function(intent as Intent, state as object) as Dynamic
|
|
106
|
+
' - Return intent to continue pipeline
|
|
107
|
+
' - Return modified intent to transform
|
|
108
|
+
' - Return invalid to cancel reducer execution
|
|
109
|
+
'
|
|
110
|
+
public function applyMiddlewares() as function[]
|
|
111
|
+
return [] as function[]
|
|
112
|
+
end function
|
|
113
|
+
|
|
114
|
+
' =============================================================
|
|
115
|
+
' REDUCE PIPELINE
|
|
116
|
+
' =============================================================
|
|
117
|
+
|
|
118
|
+
' ---------------------------------------------------------------------
|
|
119
|
+
' reduce - Executes middleware pipeline and reducer
|
|
120
|
+
'
|
|
121
|
+
' Process flow:
|
|
122
|
+
' 1. Validate intent payload
|
|
123
|
+
' 2. Execute middleware pipeline
|
|
124
|
+
' 3. If intent survives, execute reducer
|
|
125
|
+
' 4. Return new state or invalid
|
|
126
|
+
'
|
|
127
|
+
' @param {object} state - Current state
|
|
128
|
+
' @param {Intent} intent - Intent to process
|
|
129
|
+
' @return {object} New state, or invalid if cancelled
|
|
130
|
+
'
|
|
131
|
+
function reduce(state as object, intent as Intent) as object
|
|
132
|
+
if intent?.payload <> invalid and intent.payload.Count() > 1 and intent.payload = invalid
|
|
133
|
+
throw "[WARNING] Intent payload is invalid."
|
|
134
|
+
end if
|
|
135
|
+
|
|
136
|
+
' Execute middleware pipeline
|
|
137
|
+
middlewares = m.applyMiddlewares()
|
|
138
|
+
mwIndex = 0
|
|
139
|
+
mwCount = middlewares.Count()
|
|
140
|
+
while intent <> invalid and mwIndex < mwCount
|
|
141
|
+
middlewareFnScoped = middlewares[mwIndex]
|
|
142
|
+
m.middlewareFnScoped = middlewareFnScoped
|
|
143
|
+
intent = m.middlewareFnScoped(intent, state)
|
|
144
|
+
mwIndex++
|
|
145
|
+
end while
|
|
146
|
+
m.middlewareFnScoped = invalid
|
|
147
|
+
|
|
148
|
+
' Middleware cancelled intent
|
|
149
|
+
if intent = invalid then return invalid
|
|
150
|
+
|
|
151
|
+
' Execute reducer
|
|
152
|
+
newState = m.reducer(state, intent)
|
|
153
|
+
|
|
154
|
+
return newState
|
|
155
|
+
end function
|
|
156
|
+
|
|
157
|
+
' =============================================================
|
|
158
|
+
' ASYNC CALLBACK
|
|
159
|
+
' =============================================================
|
|
160
|
+
|
|
161
|
+
' ---------------------------------------------------------------------
|
|
162
|
+
' asyncReducerCallback - Callback for async middleware operations
|
|
163
|
+
'
|
|
164
|
+
' @param {object} msg - Message from async operation
|
|
165
|
+
'
|
|
166
|
+
sub asyncReducerCallback(msg)
|
|
167
|
+
' Override in subclass to handle async responses
|
|
168
|
+
end sub
|
|
169
|
+
|
|
170
|
+
' =============================================================
|
|
171
|
+
' CLEANUP
|
|
172
|
+
' =============================================================
|
|
173
|
+
|
|
174
|
+
' ---------------------------------------------------------------------
|
|
175
|
+
' destroy - Cleans up reducer references
|
|
176
|
+
'
|
|
177
|
+
sub destroy()
|
|
178
|
+
m.ownerDispatcher = invalid
|
|
179
|
+
m.port = invalid
|
|
180
|
+
end sub
|
|
181
|
+
|
|
182
|
+
end class
|
|
183
|
+
|
|
184
|
+
end namespace
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
namespace Rotor
|
|
2
|
+
|
|
3
|
+
' =====================================================================
|
|
4
|
+
' BaseStack - Generic stack/collection base class for managing keyed items
|
|
5
|
+
'
|
|
6
|
+
' Provides:
|
|
7
|
+
' - Add/set items by ID
|
|
8
|
+
' - Get items by ID (case-insensitive)
|
|
9
|
+
' - Remove items by ID
|
|
10
|
+
' - Clear all items
|
|
11
|
+
' - Check item existence
|
|
12
|
+
'
|
|
13
|
+
' Used by:
|
|
14
|
+
' - ObserverStack (observer management)
|
|
15
|
+
' - FocusStack (focus state management)
|
|
16
|
+
' - Other framework collection needs
|
|
17
|
+
' =====================================================================
|
|
18
|
+
class BaseStack
|
|
19
|
+
|
|
20
|
+
' =============================================================
|
|
21
|
+
' MEMBER VARIABLES
|
|
22
|
+
' =============================================================
|
|
23
|
+
|
|
24
|
+
stack = {} ' Internal associative array storage
|
|
25
|
+
|
|
26
|
+
' =============================================================
|
|
27
|
+
' STACK OPERATIONS
|
|
28
|
+
' =============================================================
|
|
29
|
+
|
|
30
|
+
' ---------------------------------------------------------------------
|
|
31
|
+
' set - Adds or replaces item in stack
|
|
32
|
+
'
|
|
33
|
+
' If item exists, removes old item first.
|
|
34
|
+
'
|
|
35
|
+
' @param {string} id - Item identifier
|
|
36
|
+
' @param {object} newItem - Item to store
|
|
37
|
+
'
|
|
38
|
+
sub set(id as string, newItem as object)
|
|
39
|
+
if m.has(id) = true
|
|
40
|
+
m.remove(id)
|
|
41
|
+
end if
|
|
42
|
+
m.stack[id] = newItem
|
|
43
|
+
end sub
|
|
44
|
+
|
|
45
|
+
' ---------------------------------------------------------------------
|
|
46
|
+
' get - Retrieves item by ID (case-insensitive)
|
|
47
|
+
'
|
|
48
|
+
' @param {string} id - Item identifier
|
|
49
|
+
' @returns {object} Item if found, or invalid
|
|
50
|
+
'
|
|
51
|
+
function get(id as string) as object
|
|
52
|
+
return m.stack.LookupCI(id)
|
|
53
|
+
end function
|
|
54
|
+
|
|
55
|
+
' ---------------------------------------------------------------------
|
|
56
|
+
' getAll - Returns all items in stack
|
|
57
|
+
'
|
|
58
|
+
' @returns {object} Associative array of all items
|
|
59
|
+
'
|
|
60
|
+
function getAll() as object
|
|
61
|
+
return m.stack
|
|
62
|
+
end function
|
|
63
|
+
|
|
64
|
+
' ---------------------------------------------------------------------
|
|
65
|
+
' remove - Removes item from stack
|
|
66
|
+
'
|
|
67
|
+
' @param {string} id - Item identifier to remove
|
|
68
|
+
'
|
|
69
|
+
sub remove(id as string)
|
|
70
|
+
m.stack.delete(id)
|
|
71
|
+
end sub
|
|
72
|
+
|
|
73
|
+
' ---------------------------------------------------------------------
|
|
74
|
+
' clear - Removes all items from stack
|
|
75
|
+
'
|
|
76
|
+
sub clear()
|
|
77
|
+
m.stack.Clear()
|
|
78
|
+
end sub
|
|
79
|
+
|
|
80
|
+
' ---------------------------------------------------------------------
|
|
81
|
+
' has - Checks if item exists in stack
|
|
82
|
+
'
|
|
83
|
+
' @param {string} id - Item identifier to check
|
|
84
|
+
' @returns {boolean} True if item exists
|
|
85
|
+
'
|
|
86
|
+
function has(id as string) as boolean
|
|
87
|
+
return m.stack.doesExist(id)
|
|
88
|
+
end function
|
|
89
|
+
|
|
90
|
+
end class
|
|
91
|
+
|
|
92
|
+
end namespace
|