• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mobalazs / rotor-framework / 20104362640

10 Dec 2025 03:41PM UTC coverage: 84.955% (+15.7%) from 69.231%
20104362640

Pull #12

github

web-flow
Merge 92eaf0316 into 6da2480b7
Pull Request #12: Feat/tts support

168 of 262 new or added lines in 7 files covered. (64.12%)

3 existing lines in 2 files now uncovered.

1965 of 2313 relevant lines covered (84.95%)

1.17 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.89
/src/source/plugins/FieldsPlugin.bs
1
import "../base/BasePlugin.bs"
2

3
namespace Rotor
4

5
    ' =====================================================================
6
    ' FieldsPlugin - Handles dynamic field expressions and interpolation
7
    '
8
    ' Rotor Framework plugin for handling dynamic field expressions and interpolation.
9
    '
10
    ' Key Features:
11
    '   - Evaluates function-based field values
12
    '   - Interpolates @-prefixed expressions (e.g., @viewModelState.value)
13
    '   - Supports dynamic field resolution from widget context
14
    '   - Automatically updates fields on widget lifecycle changes
15
    '
16
    ' Expression Syntax:
17
    '   @key.path - Resolves from widget.viewModelState
18
    '
19
    ' =====================================================================
20
    class FieldsPlugin extends Rotor.BasePlugin
21

22
        pluginKey = "fields"
23

24
        ' =============================================================
25
        ' MEMBER VARIABLES
26
        ' =============================================================
27

28
        ' Regex pattern for matching @ and $ prefixed expressions
29
        ' @ = viewModelState, $ = widget properties
30
        ' Matches: @ or $ followed by any characters except space, @, $, or comma
31
        configRegex = /([\@\$])([^\s\@\$\,]*)/i
32

33
        ' =============================================================
34
        ' LIFECYCLE HOOKS
35
        ' =============================================================
36

37
        hooks = {
38
            ' ---------------------------------------------------------------------
39
            ' beforeMount - Sets custom fields when widget is mounted
40
            '
41
            ' Evaluates and applies all field expressions after widget creation.
42
            '
43
            ' @param {object} scope - Plugin instance (m)
44
            ' @param {object} widget - Widget being mounted
45
            '
46
            beforeMount: sub(scope as object, widget as object)
47
                value = widget[scope.pluginKey]
1✔
48
                scope.setCustomFields(widget, value)
1✔
49
            end sub,
50

51
            ' ---------------------------------------------------------------------
52
            ' beforeUpdate - Updates custom fields when widget config changes
53
            '
54
            ' Merges new field config with existing config and re-evaluates all fields.
55
            '
56
            ' @param {object} scope - Plugin instance (m)
57
            ' @param {object} widget - Widget being updated
58
            ' @param {object} newValue - New field configuration
59
            ' @param {object} oldValue - Previous field configuration
60
            '
61
            beforeUpdate: sub(scope as object, widget as object, newValue = {}, oldValue = {})
62
                ' Extend old config with new values
63
                Rotor.Utils.deepExtendAA(widget[scope.pluginKey], newValue)
1✔
64
                scope.setCustomFields(widget, newValue)
1✔
65
            end sub,
66

67
            ' ---------------------------------------------------------------------
68
            ' beforeDestroy - Clears field configuration on destruction
69
            '
70
            ' @param {object} scope - Plugin instance (m)
71
            ' @param {object} widget - Widget being destroyed
72
            '
73
            beforeDestroy: sub(scope as object, widget as object)
74
                widget[scope.pluginKey].Clear()
1✔
75
            end sub
76
        }
77

78
        ' =============================================================
79
        ' FIELD PROCESSING
80
        ' =============================================================
81

82
        ' ---------------------------------------------------------------------
83
        ' setCustomFields - Evaluates and applies fields to widget node
84
        '
85
        ' Parses field expressions and sets the resolved values on the widget's SceneGraph node.
86
        '
87
        ' @param {object} widget - Widget instance
88
        ' @param {object} fields - Field configuration object
89
        '
90
        sub setCustomFields(widget as object, fields)
91
            parsedFields = m.parseFields(widget, fields)
1✔
92
            node = widget.node
1✔
93
            Rotor.Utils.setCustomFields(node, parsedFields, true)
1✔
94
        end sub
95

96
        ' ---------------------------------------------------------------------
97
        ' parseFields - Parses and resolves field expressions
98
        '
99
        ' Processes field values through multiple resolution strategies:
100
        '   1. Function values - Executes function in widget scope
101
        '   2. String interpolation - Resolves @-prefixed expressions
102
        '   3. Direct values - Passes through unchanged
103
        '
104
        ' Expression Resolution:
105
        '   - @key.path resolves from widget.viewModelState
106
        '   - If result is string, performs string interpolation
107
        '   - If result is non-string, replaces entire value
108
        '
109
        ' @param {object} widget - Widget instance providing context
110
        ' @param {object} fields - Field configuration to parse
111
        ' @returns {object} Parsed fields with resolved values
112
        '
113
        function parseFields(widget as object, fields as object) as object
114
            parsedFields = {}
1✔
115

116
            for each fieldId in fields
1✔
117
                value = fields[fieldId]
1✔
118

119
                ' Step 1: Resolve function-based values
120
                if Rotor.Utils.isFunction(value)
3✔
121
                    parsedFields[fieldId] = Rotor.Utils.callbackScoped(value, widget)
1✔
122
                    value = parsedFields[fieldId]
1✔
123
                end if
124

125
                ' Step 2: Process string interpolation
126
                if Rotor.Utils.isString(value)
3✔
127
                    results = m.configRegex.MatchAll(value)
1✔
128

129
                    if results.Count() > 0
2✔
130
                        for each result in results
1✔
131
                            matchKey = result[2] ' The key path after @
1✔
132
                            sourceTypeOperator = result[1] ' The @ symbol
1✔
133

134
                            ' Determine source based on operator
135
                            source = invalid
1✔
136
                            if sourceTypeOperator = "@"
3✔
137
                                source = widget.viewModelState
1✔
NEW
138
                            else if sourceTypeOperator = "$"
×
139
                                source = widget
×
140
                            end if
141

142
                            ' Skip if unknown operator
143
                            if source = invalid then goto nextResult
2✔
144

145
                            ' Resolve value from key path
146
                            asset = Rotor.Utils.getValueByKeyPath(source, matchKey)
1✔
147

148
                            ' Handle string vs non-string results
149
                            if Rotor.Utils.isString(asset)
3✔
150
                                ' String interpolation - replace in original string
151
                                replaceRegex = CreateObject("roRegex", sourceTypeOperator + matchKey, "ig")
1✔
152
                                value = replaceRegex.ReplaceAll(value, asset)
1✔
153
                            else
154
                                ' Non-string value - replace entire field value
×
155
                                value = asset
×
156
                                exit for
157
                            end if
158

159
                            nextResult:
160
                        end for
161
                    end if
162

163
                    parsedFields[fieldId] = value
1✔
164
                else
165
                    ' Step 3: Direct value assignment
3✔
166
                    parsedFields[fieldId] = value
1✔
167
                end if
168
            end for
169

170
            return parsedFields
1✔
171
        end function
172

173
    end class
174

175
end namespace
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc