gloo 4.3.0 → 4.5.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f68137523fd4bda7f48c75dc97b99da12dd24b4bc12dbe6ec1a5d608b05e7ab3
4
- data.tar.gz: 611305b9634cf5d1742ab3241c74c57b14edb19d233606ec1fe0d35401c6dc2f
3
+ metadata.gz: c0cb1d5428d6f7d2de892b588de73cef44bded6418fd69929d966e25c14a32d9
4
+ data.tar.gz: bff713e6cc80fab161f0d0070ecfd04c8a1481faa5086d399982e1bfe91bfb03
5
5
  SHA512:
6
- metadata.gz: ecf31d454b8bcfcaac603c73ab3bcb1b8b0d5e551a919f38e7e80f52add50000284bf5426f1f8c50050f4476bfed0528d7ff74ad1aee02fd0951fe09db1103aa
7
- data.tar.gz: 13f51ba5152a76aa032b28f4787ec7bfc67df616339d9ab079fc253fdc37f096c177e4ef81bd3d4e34dd85a5a338d493ada871a0e29e5045db9d2cfedf91dea5
6
+ metadata.gz: b20fdac6c4dee94e91075e2e72caefc2e448ddeb6198a2e7470b3af8edb0069211ca9d5f425d1cf2fc1149a9c84778b7008d6148528fd2a91c28e4cb6ced48d6
7
+ data.tar.gz: ca7be6a02372200e9773279b769aaa46aef2df6a8e577cc5446fc7e2af48571fd5a721f68f6c27eeffe37d0c057635544de181502feea9b371400c35e60e7d4c
data/lib/VERSION CHANGED
@@ -1 +1 @@
1
- 4.3.0
1
+ 4.5.0
data/lib/VERSION_NOTES CHANGED
@@ -1,3 +1,12 @@
1
+ 4.5.0 - 2025.08.05
2
+ - Adds option to use assets in the user lib.
3
+
4
+
5
+ 4.4.0 - 2025.07.30
6
+ - Adds form and fields
7
+ - Adds option to include image_tag and other asset tags in helper rendering.
8
+
9
+
1
10
  4.3.0 - 2025.07.02
2
11
  - Removes postgres dependency (for now)
3
12
 
@@ -71,6 +71,22 @@ module Gloo
71
71
  return return_any ? return_any.value : nil
72
72
  end
73
73
 
74
+ #
75
+ # Get the params hash from the child object.
76
+ # Returns nil if there is none.
77
+ #
78
+ def params_hash
79
+ params_can = find_child PARAMS
80
+ return nil unless params_can
81
+
82
+ h = {}
83
+ params_can.children.each do |o|
84
+ h[ o.name ] = o.value
85
+ end
86
+
87
+ return h
88
+ end
89
+
74
90
 
75
91
  # ---------------------------------------------------------------------
76
92
  # Children
@@ -156,7 +172,12 @@ module Gloo
156
172
 
157
173
  set_params args if args
158
174
  run_on_invoke
159
- return_value = result
175
+
176
+ if @engine.running_app&.obj&.embedded_renderer
177
+ return_value = @engine.running_app.obj.embedded_renderer.render result, params_hash
178
+ else
179
+ return_value = result
180
+ end
160
181
  @engine.heap.it.set_to return_value
161
182
  run_after_invoke
162
183
 
@@ -226,6 +226,8 @@ module Gloo
226
226
  e = Gloo::Objs::Alias.resolve_alias( engine, e )
227
227
  if e.class == Element
228
228
  rendered_obj_content << e.send( render_ƒ )
229
+ elsif e.class == Form
230
+ rendered_obj_content << e.render
229
231
  elsif e
230
232
  data = render_thing e, render_ƒ, engine
231
233
  ( rendered_obj_content << data ) if data # e.render( render_ƒ )
@@ -0,0 +1,429 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2025 Eric Crane. All rights reserved.
3
+ #
4
+ # An HTML Form Field.
5
+ #
6
+ # A Form Field is the definition of a form field, including label, type, etc.
7
+ #
8
+
9
+ module Gloo
10
+ module Objs
11
+ class Field < Gloo::Core::Obj
12
+
13
+ KEYWORD = 'field'.freeze
14
+ KEYWORD_SHORT = 'field'.freeze
15
+
16
+ # Form
17
+ NAME = 'name'.freeze
18
+ ID = 'id'.freeze
19
+ TYPE = 'type'.freeze
20
+ VALUE = 'value'.freeze
21
+ LABEL = 'label'.freeze
22
+ PLACEHOLDER = 'placeholder'.freeze
23
+ AUTOFOCUS = 'autofocus'.freeze
24
+ COLS = 'cols'.freeze
25
+ ROWS = 'rows'.freeze
26
+ DESCRIPTION = 'description'.freeze
27
+ CHECKED = 'checked'.freeze
28
+ OPTIONS = 'options'.freeze
29
+
30
+ # Style attributes
31
+ FIELD_GROUP = 'field_group'.freeze
32
+ FIELD_LABEL = 'field_label'.freeze
33
+ FIELD_CONTROL = 'field_control'.freeze
34
+
35
+ #
36
+ # The name of the object type.
37
+ #
38
+ def self.typename
39
+ return KEYWORD
40
+ end
41
+
42
+ #
43
+ # The short name of the object type.
44
+ #
45
+ def self.short_typename
46
+ return KEYWORD_SHORT
47
+ end
48
+
49
+ #
50
+ # Get the name for the form field.
51
+ #
52
+ def name_value
53
+ o = find_child NAME
54
+
55
+ # If there is no child, use the obj's name
56
+ return self.name unless o
57
+
58
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
59
+ return o ? o.value : nil
60
+ end
61
+
62
+ #
63
+ # Get the type for the form field.
64
+ # For example, 'text', 'password', 'checkbox', etc.
65
+ #
66
+ def type_value
67
+ o = find_child TYPE
68
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
69
+ return o ? o.value : nil
70
+ end
71
+
72
+ #
73
+ # Get the value for the form field.
74
+ #
75
+ def field_value
76
+ o = find_child VALUE
77
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
78
+ return o ? o.value : nil
79
+ end
80
+
81
+ #
82
+ # Get the value tag for the form field.
83
+ #
84
+ def value_tag
85
+ value = field_value
86
+ return "value='#{value}'" if value
87
+ return ''
88
+ end
89
+
90
+ #
91
+ # Get the label for the form field.
92
+ #
93
+ def label_value
94
+ o = find_child LABEL
95
+
96
+ # If there is no child, use the obj's name
97
+ return self.name_value.capitalize unless o
98
+
99
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
100
+ return o ? o.value : nil
101
+ end
102
+
103
+ #
104
+ # Get the label tag for the form field.
105
+ #
106
+ def label_tag
107
+ label = label_value
108
+ label_data = ''
109
+ if label
110
+ label_data = <<~HTML
111
+ <label class="#{field_label_styles}" for="#{name_value}">
112
+ #{label}
113
+ </label>
114
+ HTML
115
+ end
116
+ return label_data
117
+ end
118
+
119
+ #
120
+ # Get the placeholder for the form field.
121
+ #
122
+ def placeholder_value
123
+ o = find_child PLACEHOLDER
124
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
125
+ return o ? o.value : nil
126
+ end
127
+
128
+ #
129
+ # Get the placeholder tag for the form field.
130
+ #
131
+ def placeholder_tag
132
+ placeholder = placeholder_value
133
+ return "placeholder='#{placeholder}'" if placeholder
134
+ return ''
135
+ end
136
+
137
+ #
138
+ # Should this field autofocus?
139
+ #
140
+ def autofocus?
141
+ o = find_child AUTOFOCUS
142
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
143
+ return nil unless o
144
+ return o.value
145
+ end
146
+
147
+ #
148
+ # Get the autofocus tag for the form field.
149
+ #
150
+ def autofocus_tag
151
+ return "autofocus='autofocus'" if autofocus?
152
+ return ''
153
+ end
154
+
155
+ #
156
+ # Get the cols for the form field.
157
+ #
158
+ def cols_value
159
+ o = find_child COLS
160
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
161
+ return o ? o.value : nil
162
+ end
163
+
164
+ #
165
+ # Get the cols tag for the form field.
166
+ #
167
+ def cols_tag
168
+ cols = cols_value
169
+ return "col-#{cols}" if cols
170
+ return ''
171
+ end
172
+
173
+ #
174
+ # Get the rows for the form field.
175
+ # Only applies to textarea fields.
176
+ #
177
+ def rows_value
178
+ o = find_child ROWS
179
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
180
+ return o ? o.value : nil
181
+ end
182
+
183
+ #
184
+ # Get the rows tag for the form field.
185
+ # Only applies to textarea fields.
186
+ #
187
+ def rows_tag
188
+ rows = rows_value
189
+ return "rows='#{rows}'" if rows
190
+ return ''
191
+ end
192
+
193
+ #
194
+ # Get the description for the form field.
195
+ #
196
+ def description_value
197
+ o = find_child DESCRIPTION
198
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
199
+ return o ? o.value : nil
200
+ end
201
+
202
+ #
203
+ # Should this field be checked?
204
+ #
205
+ def checked?
206
+ o = find_child CHECKED
207
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
208
+ return nil unless o
209
+ return o.value
210
+ end
211
+
212
+ #
213
+ # Get the checked tag for the form field.
214
+ #
215
+ def checked_tag
216
+ return "checked='checked'" if checked?
217
+ return ''
218
+ end
219
+
220
+ #
221
+ # Get options for the select list.
222
+ #
223
+ def select_options
224
+ o = find_child OPTIONS
225
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
226
+ return nil unless o
227
+
228
+ selected_value = field_value
229
+
230
+ options = ''
231
+ o.children.each do |child|
232
+ if selected_value == child.name || selected_value == child.value
233
+ selected = 'selected="selected"'
234
+ else
235
+ selected = ''
236
+ end
237
+ options += <<~HTML
238
+ <option value="#{child.name}" #{selected}>#{child.value}</option>
239
+ HTML
240
+ end
241
+ return options
242
+ end
243
+
244
+ #
245
+ # Get the field group styles.
246
+ #
247
+ def field_group_styles
248
+ o = find_child FIELD_GROUP
249
+ if o
250
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
251
+ return o ? o.value : ''
252
+ end
253
+
254
+ return @styles[FIELD_GROUP] || ''
255
+ end
256
+
257
+ #
258
+ # Get the field label styles.
259
+ #
260
+ def field_label_styles
261
+ o = find_child FIELD_LABEL
262
+ if o
263
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
264
+ return o ? o.value : ''
265
+ end
266
+
267
+ return @styles[FIELD_LABEL] || ''
268
+ end
269
+
270
+ #
271
+ # Get the field control styles.
272
+ #
273
+ def field_control_styles
274
+ o = find_child FIELD_CONTROL
275
+ if o
276
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
277
+ return o ? o.value : ''
278
+ end
279
+
280
+ return @styles[FIELD_CONTROL] || ''
281
+ end
282
+
283
+
284
+ # ---------------------------------------------------------------------
285
+ # Children
286
+ # ---------------------------------------------------------------------
287
+
288
+ #
289
+ # Does this object have children to add when an object
290
+ # is created in interactive mode?
291
+ # This does not apply during obj load, etc.
292
+ #
293
+ def add_children_on_create?
294
+ return true
295
+ end
296
+
297
+ #
298
+ # Add children to this object.
299
+ # This is used by containers to add children needed
300
+ # for default configurations.
301
+ #
302
+ def add_default_children
303
+ fac = @engine.factory
304
+
305
+ # Create attributes with ID and Classes
306
+ fac.create_string NAME, '', self
307
+ fac.create_string TYPE, 'text', self
308
+ end
309
+
310
+
311
+ # ---------------------------------------------------------------------
312
+ # Messages
313
+ # ---------------------------------------------------------------------
314
+
315
+ #
316
+ # Get a list of message names that this object receives.
317
+ #
318
+ def self.messages
319
+ return super + [ 'render' ]
320
+ end
321
+
322
+ #
323
+ # Render the form field.
324
+ #
325
+ def msg_render
326
+ content = self.render
327
+ @engine.heap.it.set_to content
328
+ return content
329
+ end
330
+
331
+
332
+ # ---------------------------------------------------------------------
333
+ # Render
334
+ # ---------------------------------------------------------------------
335
+
336
+ #
337
+ # Render the field, switch on type.
338
+ def render styles = {}
339
+ @styles = styles
340
+
341
+ case type_value
342
+ when 'text'
343
+ return render_text
344
+ when 'hidden'
345
+ return render_hidden
346
+ when 'textarea'
347
+ return render_textarea
348
+ when 'checkbox'
349
+ return render_checkbox
350
+ when 'search'
351
+ return render_text
352
+ when 'select'
353
+ return render_select
354
+ end
355
+ end
356
+
357
+ #
358
+ # Render the hidden field as HTML.
359
+ #
360
+ def render_hidden
361
+ return <<~HTML
362
+ <input type="hidden" #{value_tag} name="#{name_value}" id="#{name_value}" />
363
+ HTML
364
+ end
365
+
366
+ #
367
+ # Render the text field as HTML.
368
+ #
369
+ def render_text
370
+ return <<~HTML
371
+ <div class="#{field_group_styles} #{cols_tag}">
372
+ #{label_tag}
373
+ <input #{placeholder_tag} #{autofocus_tag} #{rows_tag}
374
+ class="#{field_control_styles}"
375
+ type="#{type_value}" #{value_tag}
376
+ name="#{name_value}" id="#{name_value}" />
377
+ </div>
378
+ HTML
379
+ end
380
+
381
+ #
382
+ # Render the textarea field as HTML.
383
+ #
384
+ def render_textarea
385
+ return <<~HTML
386
+ <div class="#{field_group_styles} #{cols_tag}">
387
+ #{label_tag}
388
+ <textarea #{placeholder_tag} #{autofocus_tag} #{rows_tag}
389
+ class="#{field_control_styles}"
390
+ name="#{name_value}" id="#{name_value}">#{field_value}</textarea>
391
+ </div>
392
+ HTML
393
+ end
394
+
395
+ #
396
+ # Render the checkbox field as HTML.
397
+ #
398
+ def render_checkbox
399
+ return <<~HTML
400
+ <div class="#{field_group_styles} #{cols_tag}">
401
+ #{label_tag}
402
+ <label class="checkbox #{field_control_styles}">
403
+ <input type="checkbox"
404
+ #{checked_tag} value="true"
405
+ name="#{name_value}" id="#{name_value}" />
406
+ #{description_value}
407
+ </label>
408
+ </div>
409
+ HTML
410
+ end
411
+
412
+ #
413
+ # Render the select field as HTML.
414
+ #
415
+ def render_select
416
+ return <<~HTML
417
+ <div class="#{field_group_styles} #{cols_tag}">
418
+ #{label_tag}
419
+ <select class="#{field_control_styles}"
420
+ name="#{name_value}" id="#{name_value}">
421
+ #{select_options}
422
+ </select>
423
+ </div>
424
+ HTML
425
+ end
426
+
427
+ end
428
+ end
429
+ end
@@ -0,0 +1,271 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2025 Eric Crane. All rights reserved.
3
+ #
4
+ # An HTML Form.
5
+ #
6
+ # A Form is the definition of a form, with a collection of form fields
7
+ #
8
+
9
+ module Gloo
10
+ module Objs
11
+ class Form < Gloo::Core::Obj
12
+
13
+ KEYWORD = 'form'.freeze
14
+ KEYWORD_SHORT = 'form'.freeze
15
+
16
+ # Form
17
+ NAME = 'name'.freeze
18
+ ID = 'id'.freeze
19
+ METHOD = 'method'.freeze
20
+ METHOD_DEFAULT = 'post'.freeze
21
+ ACTION = 'action'.freeze
22
+ CANCEL_PATH = 'cancel_path'.freeze
23
+ CONTENT = 'content'.freeze
24
+ STYLES = 'styles'.freeze
25
+
26
+
27
+ #
28
+ # The name of the object type.
29
+ #
30
+ def self.typename
31
+ return KEYWORD
32
+ end
33
+
34
+ #
35
+ # The short name of the object type.
36
+ #
37
+ def self.short_typename
38
+ return KEYWORD_SHORT
39
+ end
40
+
41
+ #
42
+ # Get the name for the form.
43
+ #
44
+ def name_value
45
+ o = find_child NAME
46
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
47
+ return o ? o.value : nil
48
+ end
49
+
50
+ #
51
+ # Get the method for the form.
52
+ # 'post' is the default.
53
+ #
54
+ def method_value
55
+ o = find_child METHOD
56
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
57
+ return o.value || METHOD_DEFAULT
58
+ end
59
+
60
+ #
61
+ # Get the action for the form.
62
+ # This is the path to POST to, for example.
63
+ #
64
+ def action_value
65
+ o = find_child ACTION
66
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
67
+ return o ? o.value : nil
68
+ end
69
+
70
+ #
71
+ # Get the cancel path for the form.
72
+ #
73
+ def cancel_path_value
74
+ o = find_child CANCEL_PATH
75
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
76
+ return o ? o.value : nil
77
+ end
78
+
79
+ #
80
+ # Get all the form content, the collection of form fields.
81
+ #
82
+ def form_content
83
+ o = find_child CONTENT
84
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
85
+ return o
86
+ end
87
+
88
+ #
89
+ # Get the styles for the form.
90
+ # Retuns styles in the form of a hash:
91
+ # { 'field_group' => 'form-group mt-3', … }
92
+ #
93
+ def styles
94
+ style_h = {}
95
+ o = find_child STYLES
96
+ return style_h unless o
97
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
98
+
99
+ o.children.each do |c|
100
+ style_h[ c.name ] = c.value
101
+ end
102
+
103
+ # puts "styles: #{style_h}"
104
+ return style_h
105
+ end
106
+
107
+ #
108
+ # Get the form styles.
109
+ # Use the name if none is provided.
110
+ #
111
+ def form_styles
112
+ return @styles['form'] || name_value
113
+ end
114
+
115
+ #
116
+ # Get the submit button styles.
117
+ #
118
+ def submit_button_styles
119
+ return @styles['submit'] || ''
120
+ end
121
+
122
+ #
123
+ # Get the cancel button styles.
124
+ #
125
+ def cancel_button_styles
126
+ return @styles['cancel'] || ''
127
+ end
128
+
129
+
130
+ # ---------------------------------------------------------------------
131
+ # Children
132
+ # ---------------------------------------------------------------------
133
+
134
+ #
135
+ # Does this object have children to add when an object
136
+ # is created in interactive mode?
137
+ # This does not apply during obj load, etc.
138
+ #
139
+ def add_children_on_create?
140
+ return true
141
+ end
142
+
143
+ #
144
+ # Add children to this object.
145
+ # This is used by containers to add children needed
146
+ # for default configurations.
147
+ #
148
+ def add_default_children
149
+ fac = @engine.factory
150
+
151
+ # Create attributes with ID and Classes
152
+ fac.create_string NAME, '', self
153
+ fac.create_string METHOD, 'post', self
154
+ fac.create_string ACTION, '', self
155
+ fac.create_string CANCEL_PATH, '', self
156
+
157
+ fac.create_can CONTENT, self
158
+ end
159
+
160
+
161
+ # ---------------------------------------------------------------------
162
+ # Messages
163
+ # ---------------------------------------------------------------------
164
+
165
+ #
166
+ # Get a list of message names that this object receives.
167
+ #
168
+ def self.messages
169
+ return super + [ 'render' ]
170
+ end
171
+
172
+ #
173
+ # Render the form and all contained fields.
174
+ #
175
+ def msg_render
176
+ content = self.render
177
+ @engine.heap.it.set_to content
178
+ return content
179
+ end
180
+
181
+
182
+ # ---------------------------------------------------------------------
183
+ # Render
184
+ # ---------------------------------------------------------------------
185
+
186
+ #
187
+ # Open the form.
188
+ #
189
+ def open_form
190
+ name = name_value
191
+
192
+ cancel_button = ""
193
+ if cancel_path_value
194
+ cancel_button = <<~HTML
195
+ <a class="#{cancel_button_styles}"
196
+ href="#{cancel_path_value}">
197
+ Cancel</a>
198
+ HTML
199
+ end
200
+ return <<~HTML
201
+ <form class='#{form_styles}'
202
+ id='#{name}'
203
+ method='#{method_value}'
204
+ action='#{action_value}'
205
+ accept-charset='UTF-8'>
206
+ <div class="actions">
207
+ <input type="submit"
208
+ name="commit"
209
+ value="Save"
210
+ class="#{submit_button_styles}"
211
+ data-disable-with="Saving..." />
212
+
213
+ #{cancel_button}
214
+ </div>
215
+ HTML
216
+ end
217
+
218
+ #
219
+ # Close the form.
220
+ #
221
+ def close_form
222
+ return "</form>"
223
+ end
224
+
225
+ #
226
+ # Render the Form as HTML.
227
+ #
228
+ def render
229
+ @styles = styles
230
+ return open_form + render_content + close_form
231
+ end
232
+
233
+ #
234
+ # Render the element content using the specified render function.
235
+ # This is a recursive function (through one of the other render functions).
236
+ #
237
+ def render_content
238
+ fields = ""
239
+ field_can = form_content
240
+ return "" if field_can.nil?
241
+
242
+ field_can.children.each do |o|
243
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
244
+ if o.class == Field
245
+ fields << o.render( @styles )
246
+ elsif o.class == Element
247
+ fields << o.render_html
248
+ elsif o
249
+ data = render_thing o
250
+ ( fields << data ) if data
251
+ end
252
+ end
253
+
254
+ return fields
255
+ end
256
+
257
+ #
258
+ # Render a string or other object.
259
+ #
260
+ def render_thing e
261
+ begin
262
+ return e.render( 'render_html' )
263
+ rescue => e
264
+ @engine.log_exception e
265
+ return ''
266
+ end
267
+ end
268
+
269
+ end
270
+ end
271
+ end
@@ -8,6 +8,7 @@ module Gloo
8
8
  module WebSvr
9
9
  class Asset
10
10
 
11
+ LIB_FOLDER = 'lib'.freeze
11
12
  ASSET_FOLDER = 'asset'.freeze
12
13
  IMAGE_FOLDER = 'image'.freeze
13
14
  STYLESHEET_FOLDER = 'stylesheet'.freeze
@@ -35,6 +36,55 @@ module Gloo
35
36
  end
36
37
 
37
38
 
39
+ # ---------------------------------------------------------------------
40
+ # lib asset Helpers
41
+ # ---------------------------------------------------------------------
42
+
43
+ #
44
+ # Get the asset folder in the User's lib.
45
+ # Returns nil if it does not exist.
46
+ #
47
+ def lib_asset_folder
48
+ dir = File.join( @engine.settings.user_root, LIB_FOLDER, ASSET_FOLDER )
49
+ return dir if Dir.exist?( dir )
50
+
51
+ return nil
52
+ end
53
+
54
+ #
55
+ # Get the stylesheets folder in the User's lib.
56
+ # Returns nil if it does not exist.
57
+ #
58
+ def lib_stylesheet_folder
59
+ dir = File.join( lib_asset_folder, STYLESHEET_FOLDER )
60
+ return dir if Dir.exist?( dir )
61
+
62
+ return nil
63
+ end
64
+
65
+ #
66
+ # Get the javascript folder in the User's lib.
67
+ # Returns nil if it does not exist.
68
+ #
69
+ def lib_javascript_folder
70
+ dir = File.join( lib_asset_folder, JAVASCRIPT_FOLDER )
71
+ return dir if Dir.exist?( dir )
72
+
73
+ return nil
74
+ end
75
+
76
+ #
77
+ # Get the images folder in the User's lib.
78
+ # Returns nil if it does not exist.
79
+ #
80
+ def lib_image_folder
81
+ dir = File.join( lib_asset_folder, IMAGE_FOLDER )
82
+ return dir if Dir.exist?( dir )
83
+
84
+ return nil
85
+ end
86
+
87
+
38
88
  # ---------------------------------------------------------------------
39
89
  # Asset Helpers
40
90
  # ---------------------------------------------------------------------
@@ -79,6 +129,12 @@ module Gloo
79
129
  # Look in the web server's asset folder.
80
130
  pn = File.join( asset_folder, pn )
81
131
 
132
+ # Try the lib assets if not found
133
+ unless File.exist? pn
134
+ lib = lib_asset_folder
135
+ pn = File.join( lib, file.value ) if lib
136
+ end
137
+
82
138
  return pn
83
139
  end
84
140
 
@@ -214,6 +270,11 @@ module Gloo
214
270
  def add_images
215
271
  @log.debug 'Adding image asset routes to web server…'
216
272
 
273
+ lib = lib_image_folder
274
+ if lib
275
+ add_files_in_folder( lib, @images, IMAGE_FOLDER )
276
+ end
277
+
217
278
  return unless File.exist? image_folder
218
279
 
219
280
  # for each file in the images folder
@@ -227,6 +288,11 @@ module Gloo
227
288
  def add_stylesheets
228
289
  @log.debug 'Adding stylesheet asset routes to web server…'
229
290
 
291
+ lib = lib_stylesheet_folder
292
+ if lib
293
+ add_files_in_folder( lib, @stylesheets, STYLESHEET_FOLDER )
294
+ end
295
+
230
296
  return unless File.exist? stylesheet_folder
231
297
 
232
298
  # for each file in the stylesheets folder
@@ -245,6 +311,11 @@ module Gloo
245
311
  def add_javascript
246
312
  @log.debug 'Adding javascript asset routes to web server…'
247
313
 
314
+ lib = lib_javascript_folder
315
+ if lib
316
+ add_files_in_folder( lib, @javascript, JAVASCRIPT_FOLDER )
317
+ end
318
+
248
319
  return unless File.exist? javascript_folder
249
320
 
250
321
  # for each file in the javascript folder
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gloo
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.3.0
4
+ version: 4.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Crane
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-07-02 00:00:00.000000000 Z
11
+ date: 2025-08-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -482,6 +482,8 @@ files:
482
482
  - lib/gloo/objs/web/teams.rb
483
483
  - lib/gloo/objs/web/uri.rb
484
484
  - lib/gloo/objs/web_svr/element.rb
485
+ - lib/gloo/objs/web_svr/field.rb
486
+ - lib/gloo/objs/web_svr/form.rb
485
487
  - lib/gloo/objs/web_svr/page.rb
486
488
  - lib/gloo/objs/web_svr/partial.rb
487
489
  - lib/gloo/objs/web_svr/svr.rb