gloo-web 1.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.
data/lib/objs/page.rb ADDED
@@ -0,0 +1,560 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2024 Eric Crane. All rights reserved.
3
+ #
4
+ # A web page hosted in a gloo web server.
5
+ #
6
+
7
+ module Objs
8
+ class Page < Gloo::Core::Obj
9
+
10
+ KEYWORD = 'page'.freeze
11
+ KEYWORD_SHORT = 'page'.freeze
12
+
13
+ # Events
14
+ ON_RENDER = 'on_render'.freeze
15
+ ON_PRERENDER = 'on_prerender'.freeze
16
+ AFTER_RENDER = 'after_render'.freeze
17
+
18
+ # Parameters used during render.
19
+ PARAMS = 'params'.freeze
20
+ ID = 'id'.freeze
21
+
22
+ # Content
23
+ HEAD = 'head'.freeze
24
+ BODY = 'body'.freeze
25
+ CONTENT = 'content'.freeze
26
+ TITLE = 'title'.freeze
27
+
28
+ # Layout for this page.
29
+ # If not specified, use the layout for the app.
30
+ LAYOUT = 'layout'.freeze
31
+
32
+ # Return Content type and HTML Code
33
+ CONTENT_TYPE = 'content_type'.freeze
34
+ HTML_CONTENT = 'html'.freeze
35
+ TEXT_CONTENT = 'text'.freeze
36
+ JSON_CONTENT = 'json'.freeze
37
+ FILE_CONTENT = 'file'.freeze
38
+ RETURN_CODE = 'return_code'.freeze
39
+
40
+ # Children for FILE pages.
41
+ FILE_TYPE = 'file_type'.freeze
42
+ FILE_PATH = 'file_path'.freeze
43
+ FILE_NAME = 'file_name'.freeze
44
+ FILE_DATA = 'file_data'.freeze
45
+ DOWNLOAD_FILE = 'download_file'.freeze
46
+
47
+ #
48
+ # The name of the object type.
49
+ #
50
+ def self.typename
51
+ return KEYWORD
52
+ end
53
+
54
+ #
55
+ # The short name of the object type.
56
+ #
57
+ def self.short_typename
58
+ return KEYWORD_SHORT
59
+ end
60
+
61
+ #
62
+ # Set the value with any necessary type conversions.
63
+ #
64
+ def set_value( new_value )
65
+ self.value = new_value.to_s
66
+ end
67
+
68
+ #
69
+ # Does this object support multi-line values?
70
+ # Initially only true for scripts.
71
+ #
72
+ def multiline_value?
73
+ return false
74
+ end
75
+
76
+ #
77
+ # Get the head element.
78
+ #
79
+ def head
80
+ return find_child HEAD
81
+ end
82
+
83
+ #
84
+ # Get the header content.
85
+ # This might be in a content child, or it might be
86
+ # the head object itself.
87
+ #
88
+ def head_content
89
+ head_obj = head
90
+ return nil unless head_obj
91
+
92
+ content_obj = head_obj.find_child CONTENT
93
+ return content_obj ? content_obj : head_obj
94
+ end
95
+
96
+ #
97
+ # Get the body element.
98
+ #
99
+ def body
100
+ return find_child BODY
101
+ end
102
+
103
+ #
104
+ # Get the body content.
105
+ # This might be in a content child, or it might be
106
+ # the body object itself.
107
+ #
108
+ def body_content
109
+ body_obj = body
110
+ return nil unless body_obj
111
+
112
+ content_obj = body_obj.find_child CONTENT
113
+ return content_obj ? content_obj : body_obj
114
+ end
115
+
116
+ #
117
+ # Init params container with request values.
118
+ #
119
+ def init_params
120
+ params_can = find_child PARAMS
121
+ return nil unless params_can
122
+
123
+ # First set URL route params if there are any.
124
+ if @request&.request_params&.route_params
125
+ @request.request_params.route_params.each_with_index do |route_p,i|
126
+ o = params_can.children[i]
127
+ o.set_value( route_p ) if o && o.name != ID
128
+ end
129
+ end
130
+
131
+ if @request
132
+ url_params = @request.request_params.query_params
133
+ url_params.each do |k,v|
134
+ o = params_can.find_child k
135
+ o.set_value( v ) if o
136
+ end
137
+
138
+ @request.request_params.body_params.each do |k,v|
139
+ o = params_can.find_child k
140
+ o.set_value( v ) if o
141
+ end
142
+ end
143
+ end
144
+
145
+ #
146
+ # Get the params hash from the child object.
147
+ # Returns nil if there is none.
148
+ #
149
+ def params_hash
150
+ params_can = find_child PARAMS
151
+ return nil unless params_can
152
+
153
+ h = {}
154
+ params_can.children.each do |o|
155
+ # resolve aliases
156
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
157
+ h[ o.name ] = o.value
158
+ end
159
+
160
+ return h
161
+ end
162
+
163
+ #
164
+ # Get the return code.
165
+ # SUCCESS is the default if none is set.
166
+ #
167
+ def return_code
168
+ code = find_child RETURN_CODE
169
+ return code ? code.value : WebSvr::ResponseCode::SUCCESS
170
+ end
171
+
172
+ #
173
+ # Get the content type.
174
+ #
175
+ def content_type
176
+ type = find_child CONTENT_TYPE
177
+ return type ? type.value : nil
178
+ end
179
+
180
+ #
181
+ # Get the layout for this page.
182
+ #
183
+ def page_layout
184
+ o = find_child LAYOUT
185
+ return nil unless o
186
+
187
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
188
+ return o
189
+ end
190
+
191
+ #
192
+ # Is the return type HTML?
193
+ #
194
+ def is_html?
195
+ return true if content_type.nil?
196
+ return content_type == HTML_CONTENT
197
+ end
198
+
199
+ #
200
+ # Is the return type TEXT?
201
+ #
202
+ def is_text?
203
+ return content_type == TEXT_CONTENT
204
+ end
205
+
206
+ #
207
+ # Is the return type JSON?
208
+ #
209
+ def is_json?
210
+ return content_type == JSON_CONTENT
211
+ end
212
+
213
+ def is_file?
214
+ return content_type == FILE_CONTENT
215
+ end
216
+
217
+
218
+ # ---------------------------------------------------------------------
219
+ # Events
220
+ # ---------------------------------------------------------------------
221
+
222
+ #
223
+ # Run the on prerender script if there is one.
224
+ #
225
+ def run_on_prerender
226
+ o = find_child ON_PRERENDER
227
+ return unless o
228
+
229
+ @engine.log.debug "running on_prerender for page"
230
+
231
+ Gloo::Exec::Dispatch.message( @engine, 'run', o )
232
+ end
233
+
234
+ #
235
+ # Run the on render script if there is one.
236
+ #
237
+ def run_on_render
238
+ o = find_child ON_RENDER
239
+ return unless o
240
+
241
+ @engine.log.debug "running on_render for page"
242
+
243
+ Gloo::Exec::Dispatch.message( @engine, 'run', o )
244
+ end
245
+
246
+ #
247
+ # Run the on rendered script if there is one.
248
+ #
249
+ def run_after_render
250
+ o = find_child AFTER_RENDER
251
+ return unless o
252
+
253
+ @engine.log.debug "running after_render for page"
254
+
255
+ Gloo::Exec::Dispatch.message( @engine, 'run', o )
256
+ end
257
+
258
+
259
+ # ---------------------------------------------------------------------
260
+ # Children
261
+ # ---------------------------------------------------------------------
262
+
263
+ #
264
+ # Does this object have children to add when an object
265
+ # is created in interactive mode?
266
+ # This does not apply during obj load, etc.
267
+ #
268
+ def add_children_on_create?
269
+ return true
270
+ end
271
+
272
+ #
273
+ # Add children to this object.
274
+ # This is used by containers to add children needed
275
+ # for default configurations.
276
+ #
277
+ def add_default_children
278
+ fac = @engine.factory
279
+
280
+ fac.create_script ON_RENDER, '', self
281
+ fac.create_script AFTER_RENDER, '', self
282
+ fac.create_can PARAMS, self
283
+
284
+ params = { :name => HEAD,
285
+ :type => Objs::Element.typename,
286
+ :value => nil,
287
+ :parent => self }
288
+ head = fac.create params
289
+ content = fac.create_can CONTENT, head
290
+ params = { :name => TITLE,
291
+ :type => Objs::Element.typename,
292
+ :value => nil,
293
+ :parent => content }
294
+ title = fac.create params
295
+
296
+ params = { :name => BODY,
297
+ :type => Objs::Element.typename,
298
+ :value => nil,
299
+ :parent => self }
300
+ body = fac.create params
301
+ content = fac.create_can CONTENT, body
302
+ end
303
+
304
+
305
+ # ---------------------------------------------------------------------
306
+ # Messages
307
+ # ---------------------------------------------------------------------
308
+
309
+ #
310
+ # Get a list of message names that this object receives.
311
+ #
312
+ def self.messages
313
+ return super + [ 'render' ]
314
+ end
315
+
316
+ #
317
+ # Get the expiration date for the certificate.
318
+ #
319
+ def msg_render
320
+ content = self.render
321
+ @engine.heap.it.set_to content
322
+ return content
323
+ end
324
+
325
+
326
+ # ---------------------------------------------------------------------
327
+ # Render
328
+ # ---------------------------------------------------------------------
329
+
330
+ #
331
+ # Wrap the content in the tag with id and class.
332
+ #
333
+ def wrap( tag, content, id=nil, classes=nil )
334
+ return "<#{tag}>#{content}</#{tag}>"
335
+ end
336
+
337
+ #
338
+ # Is there a redirect page set in the running app?
339
+ #
340
+ def redirect_set?
341
+ return false unless @engine.app_running?
342
+ return @engine.running_app.obj.redirect
343
+ end
344
+
345
+ #
346
+ # Set the ID parameter if there is one.
347
+ #
348
+ def set_id
349
+ return unless @request.request_params.id
350
+ @engine.log.info "Setting ID: #{@request.request_params.id}"
351
+
352
+ params_can = find_child PARAMS
353
+ return nil unless params_can
354
+
355
+ id_obj = params_can.find_child( ID )
356
+ return unless id_obj
357
+
358
+ id_obj.set_value( @request.request_params.id )
359
+ end
360
+
361
+ #
362
+ # Render the page.
363
+ # If this is being called from the web server,
364
+ # the request will be passed in and will include
365
+ # request context such as params.
366
+ #
367
+ def render request=nil
368
+ @request = request
369
+
370
+ # TODO : refactor this
371
+ set_id if @request
372
+
373
+ # Put params from request into params container.
374
+ init_params
375
+
376
+ # Run the on prerender script
377
+ run_on_prerender
378
+
379
+ # Get Params hash before running on render
380
+ params = params_hash
381
+
382
+ run_on_render
383
+ return nil if redirect_set?
384
+
385
+ if is_html?
386
+ contents = render_html params
387
+ elsif is_json?
388
+ contents = render_json
389
+ elsif is_text?
390
+ contents = render_text params
391
+ elsif is_file?
392
+ contents = render_file params
393
+ else
394
+ @engine.err "Unknown content type: #{content_type}"
395
+ return nil
396
+ end
397
+
398
+ run_after_render
399
+ @request = nil
400
+ return nil if redirect_set?
401
+
402
+ return contents
403
+ end
404
+
405
+ #
406
+ # Render the page as HTML.
407
+ #
408
+ def render_html params
409
+ head_obj = render_with_params head_content, :render_html, params
410
+ body_obj = render_with_params body_content, :render_html, params
411
+
412
+ layout = page_layout_or_app_layout
413
+ if layout
414
+ @engine.log.debug "Using Page Layout: #{layout.pn}"
415
+ contents = layout.render_layout( head_obj, body_obj )
416
+ else
417
+ @engine.log.debug "No layout for page."
418
+ contents = wrap( 'html', head_obj + body_obj )
419
+ end
420
+
421
+ return WebSvr::Response.html_response(
422
+ @engine, contents, return_code )
423
+ end
424
+
425
+ #
426
+ # Get the layout for this page or if none for the app.
427
+ #
428
+ def page_layout_or_app_layout
429
+ layout = page_layout
430
+ return layout if layout
431
+
432
+ return nil unless @engine.app_running?
433
+ return @engine.running_app.obj.default_page_layout
434
+ end
435
+
436
+ #
437
+ # Render the page as JSON.
438
+ #
439
+ def render_json
440
+ json_content = Gloo::Objs::Json.convert_obj_to_json( body )
441
+
442
+ return WebSvr::Response.json_response( @engine, json_content, return_code )
443
+ end
444
+
445
+ #
446
+ # Render the page as TEXT.
447
+ #
448
+ def render_text params
449
+ text_content = render_with_params body_content, :render_text, params
450
+ return WebSvr::Response.text_response( @engine, text_content, return_code )
451
+ end
452
+
453
+ #
454
+ # Given an object and a render message, render the object.
455
+ # If the obj is nil, return an empty string.
456
+ # If the params are nil, no param rendering is done.
457
+ #
458
+ def render_with_params obj, render_ƒ, params
459
+ return '' unless obj
460
+
461
+ content = Element.render_obj( obj, render_ƒ, @engine )
462
+ # content = Page.render_params( content, params ) if params
463
+ content = @engine.running_app.obj.embedded_renderer.render content, params
464
+
465
+ return content
466
+ end
467
+
468
+ #
469
+ # Render content with the given params.
470
+ # Params might be nil, in which case the content
471
+ # is returned with no changes.
472
+ #
473
+ def self.render_params content, params
474
+ return content unless params
475
+
476
+ renderer = ERB.new( content )
477
+ content = renderer.result_with_hash( params )
478
+
479
+ return content
480
+ end
481
+
482
+
483
+ # ---------------------------------------------------------------------
484
+ # File Renderer
485
+ # ---------------------------------------------------------------------
486
+
487
+ #
488
+ # Get the type of the file.
489
+ #
490
+ def file_type
491
+ o = find_child FILE_TYPE
492
+ return nil unless o
493
+
494
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
495
+ return o&.value
496
+ end
497
+
498
+ #
499
+ # Get the path to the file.
500
+ #
501
+ def file_path
502
+ o = find_child FILE_PATH
503
+ return nil unless o
504
+
505
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
506
+ return o&.value
507
+ end
508
+
509
+ #
510
+ # Get the File content
511
+ #
512
+ def file_data
513
+ o = find_child FILE_DATA
514
+ return nil unless o
515
+
516
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
517
+ return o&.value
518
+ end
519
+
520
+ #
521
+ # Get the name of the file.
522
+ #
523
+ def file_name
524
+ o = find_child FILE_NAME
525
+ return nil unless o
526
+
527
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
528
+ return o&.value
529
+ end
530
+
531
+ def download_file
532
+ o = find_child DOWNLOAD_FILE
533
+ return false unless o
534
+
535
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
536
+ return o&.value
537
+ end
538
+
539
+ #
540
+ # Render a file.
541
+ #
542
+ def render_file params
543
+ type = file_type
544
+ inline_content = file_data
545
+ if inline_content.nil?
546
+ data = File.binread( file_path )
547
+ else
548
+ data = inline_content
549
+ end
550
+ code = WebSvr::ResponseCode::SUCCESS
551
+ fname = file_name
552
+ download = download_file
553
+
554
+ return WebSvr::Response.new(
555
+ @engine, code, type, data, true, fname, download )
556
+ end
557
+
558
+
559
+ end
560
+ end