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.
@@ -0,0 +1,405 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2024 Eric Crane. All rights reserved.
3
+ #
4
+ # A helper class for static assets.
5
+ #
6
+
7
+ module WebSvr
8
+ class Asset
9
+
10
+ LIB_FOLDER = 'lib'.freeze
11
+ ASSET_FOLDER = 'asset'.freeze
12
+ IMAGE_FOLDER = 'image'.freeze
13
+ STYLESHEET_FOLDER = 'stylesheet'.freeze
14
+ JAVASCRIPT_FOLDER = 'javascript'.freeze
15
+
16
+ CSS_TYPE = 'text/css'.freeze
17
+ JS_TYPE = 'text/javascript'.freeze
18
+
19
+ IMAGE_TYPE = 'image/'.freeze
20
+ FAVICON_TYPE = 'image/x-icon'.freeze
21
+
22
+
23
+ # ---------------------------------------------------------------------
24
+ # Initialization
25
+ # ---------------------------------------------------------------------
26
+
27
+ #
28
+ # Set up the web server.
29
+ #
30
+ def initialize( engine, web_svr_obj )
31
+ @engine = engine
32
+ @log = @engine.log
33
+
34
+ @web_svr_obj = web_svr_obj
35
+ end
36
+
37
+
38
+ # ---------------------------------------------------------------------
39
+ # lib asset Helpers
40
+ # ---------------------------------------------------------------------
41
+
42
+ #
43
+ # Get the asset folder in the User's lib.
44
+ # Returns nil if it does not exist.
45
+ #
46
+ def lib_asset_folder
47
+ dir = File.join( @engine.settings.user_root, LIB_FOLDER, ASSET_FOLDER )
48
+ return dir if Dir.exist?( dir )
49
+
50
+ return nil
51
+ end
52
+
53
+ #
54
+ # Get the stylesheets folder in the User's lib.
55
+ # Returns nil if it does not exist.
56
+ #
57
+ def lib_stylesheet_folder
58
+ dir = File.join( lib_asset_folder, STYLESHEET_FOLDER )
59
+ return dir if Dir.exist?( dir )
60
+
61
+ return nil
62
+ end
63
+
64
+ #
65
+ # Get the javascript folder in the User's lib.
66
+ # Returns nil if it does not exist.
67
+ #
68
+ def lib_javascript_folder
69
+ dir = File.join( lib_asset_folder, JAVASCRIPT_FOLDER )
70
+ return dir if Dir.exist?( dir )
71
+
72
+ return nil
73
+ end
74
+
75
+ #
76
+ # Get the images folder in the User's lib.
77
+ # Returns nil if it does not exist.
78
+ #
79
+ def lib_image_folder
80
+ dir = File.join( lib_asset_folder, IMAGE_FOLDER )
81
+ return dir if Dir.exist?( dir )
82
+
83
+ return nil
84
+ end
85
+
86
+
87
+ # ---------------------------------------------------------------------
88
+ # Asset Helpers
89
+ # ---------------------------------------------------------------------
90
+
91
+ #
92
+ # Get the asset folder in the project.
93
+ #
94
+ def asset_folder
95
+ return File.join( @engine.settings.project_path, ASSET_FOLDER )
96
+ end
97
+
98
+ #
99
+ # Get the images folder in the project.
100
+ #
101
+ def image_folder
102
+ return File.join( asset_folder, IMAGE_FOLDER )
103
+ end
104
+
105
+ #
106
+ # Get the stylesheets folder in the project.
107
+ #
108
+ def stylesheet_folder
109
+ return File.join( asset_folder, STYLESHEET_FOLDER )
110
+ end
111
+
112
+ #
113
+ # Get the stylesheets folder in the project.
114
+ #
115
+ def javascript_folder
116
+ return File.join( asset_folder, JAVASCRIPT_FOLDER )
117
+ end
118
+
119
+ #
120
+ # Find and return the page for the given route.
121
+ #
122
+ def path_for_file file
123
+ pn = file.value
124
+
125
+ # Is the file's value a recognizable file?
126
+ return pn if File.exist? pn
127
+
128
+ # Look in the web server's asset folder.
129
+ pn = File.join( asset_folder, pn )
130
+
131
+ # Try the lib assets if not found
132
+ unless File.exist? pn
133
+ lib = lib_asset_folder
134
+ pn = File.join( lib, file.value ) if lib
135
+ end
136
+
137
+ return pn
138
+ end
139
+
140
+ #
141
+ # Get the return type for the given file.
142
+ #
143
+ def type_for_file file
144
+ ext = File.extname( file ).downcase
145
+ ext = ext[1..-1] if ext[0] == '.'
146
+
147
+ if ext == 'css'
148
+ return CSS_TYPE
149
+ elsif ext == 'js'
150
+ return JS_TYPE
151
+ elsif ext == 'ico'
152
+ return FAVICON_TYPE
153
+ else
154
+ return "#{IMAGE_TYPE}#{ext}"
155
+ end
156
+ end
157
+
158
+
159
+ # ---------------------------------------------------------------------
160
+ # Render Asset
161
+ # ---------------------------------------------------------------------
162
+
163
+ #
164
+ # Helper to create a successful image response with the given data.
165
+ #
166
+ def render_file( file )
167
+ type = type_for_file file
168
+ data = File.binread file
169
+ code = WebSvr::ResponseCode::SUCCESS
170
+
171
+ return WebSvr::Response.new( @engine, code, type, data, true )
172
+ end
173
+
174
+ #
175
+ # Check if the given name is an asset.
176
+ #
177
+ def is_asset? name
178
+ return name == ASSET_FOLDER
179
+ end
180
+
181
+
182
+ # ---------------------------------------------------------------------
183
+ # Asset with Fingerprints
184
+ # ---------------------------------------------------------------------
185
+
186
+ #
187
+ # Register an asset with the web server.
188
+ # Adds fingerprint to the file names for later access.
189
+ #
190
+ # full_path is the FILE from which we build the SHA256 hash
191
+ # pn is the path and name within the assets directory
192
+ # name is the simple file name (icon.png)
193
+ #
194
+ def register_asset name, pn, full_path
195
+ asset_pn = "/asset/#{pn}"
196
+ return AssetInfo.new( @engine, full_path, name, asset_pn ).register
197
+ end
198
+
199
+ #
200
+ # Get the published name for the given asset name.
201
+ #
202
+ def published_name asset_name
203
+ return AssetInfo.find_published_name_for( asset_name )
204
+ end
205
+
206
+
207
+ # ---------------------------------------------------------------------
208
+ # Dynamic Add Assets
209
+ # ---------------------------------------------------------------------
210
+
211
+ #
212
+ # Add all asssets to the web server pages (routes).
213
+ #
214
+ def add_asset_routes
215
+ return unless File.exist? asset_folder
216
+
217
+ @log.debug 'Adding asset routes to web server…'
218
+ @factory = @engine.factory
219
+
220
+ add_containers
221
+ add_images
222
+ add_stylesheets
223
+ add_javascript
224
+ end
225
+
226
+ #
227
+ # Create the containers for the assets if they do not exist.
228
+ #
229
+ def add_containers
230
+ pages = @web_svr_obj.pages_container
231
+
232
+ @assets = pages.find_child( ASSET_FOLDER ) ||
233
+ @factory.create_can( ASSET_FOLDER, pages )
234
+
235
+ @images = @assets.find_child( IMAGE_FOLDER ) ||
236
+ @factory.create_can( IMAGE_FOLDER, @assets )
237
+
238
+ @stylesheets = @assets.find_child( STYLESHEET_FOLDER ) ||
239
+ @factory.create_can( STYLESHEET_FOLDER, @assets )
240
+
241
+ @javascript = @assets.find_child( JAVASCRIPT_FOLDER ) ||
242
+ @factory.create_can( JAVASCRIPT_FOLDER, @assets )
243
+ end
244
+
245
+ #
246
+ # Traverse the given folder and add all files to the container.
247
+ # This is a recursive method and look look for files in subfolders.
248
+ #
249
+ def add_files_in_folder( folder, container, path )
250
+ Dir.each_child( folder ) do |name|
251
+ pn = File.join( path, name )
252
+ full_path = File.join( folder, name )
253
+
254
+ if File.directory? full_path
255
+ child = container.find_child( name )
256
+ child = @factory.create_can( name, container ) if child.nil?
257
+
258
+ add_files_in_folder( full_path, child, pn )
259
+ else
260
+ info = register_asset( name, pn, full_path )
261
+ add_file_obj( container, name, pn, info )
262
+ end
263
+ end
264
+ end
265
+
266
+ #
267
+ # Add the images to the web server pages.
268
+ #
269
+ def add_images
270
+ @log.debug 'Adding image asset routes to web server…'
271
+
272
+ lib = lib_image_folder
273
+ if lib
274
+ add_files_in_folder( lib, @images, IMAGE_FOLDER )
275
+ end
276
+
277
+ return unless File.exist? image_folder
278
+
279
+ # for each file in the images folder
280
+ # create a file object and add it to the images container
281
+ add_files_in_folder( image_folder, @images, IMAGE_FOLDER )
282
+ end
283
+
284
+ #
285
+ # Add the stylesheets to the web server pages.
286
+ #
287
+ def add_stylesheets
288
+ @log.debug 'Adding stylesheet asset routes to web server…'
289
+
290
+ lib = lib_stylesheet_folder
291
+ if lib
292
+ add_files_in_folder( lib, @stylesheets, STYLESHEET_FOLDER )
293
+ end
294
+
295
+ return unless File.exist? stylesheet_folder
296
+
297
+ # for each file in the stylesheets folder
298
+ # create a file object and add it to the stylesheets container
299
+ add_files_in_folder( stylesheet_folder, @stylesheets, STYLESHEET_FOLDER )
300
+
301
+ # Dir.each_child( stylesheet_folder ) do |name|
302
+ # pn = File.join( STYLESHEET_FOLDER, name )
303
+ # add_file_obj( @stylesheets, name, pn )
304
+ # end
305
+ end
306
+
307
+ #
308
+ # Add the Javascript files to the web server pages.
309
+ #
310
+ def add_javascript
311
+ @log.debug 'Adding javascript asset routes to web server…'
312
+
313
+ lib = lib_javascript_folder
314
+ if lib
315
+ add_files_in_folder( lib, @javascript, JAVASCRIPT_FOLDER )
316
+ end
317
+
318
+ return unless File.exist? javascript_folder
319
+
320
+ # for each file in the javascript folder
321
+ # create a file object and add it to the javascript container
322
+ add_files_in_folder( javascript_folder, @javascript, JAVASCRIPT_FOLDER )
323
+
324
+ # Dir.each_child( javascript_folder ) do |name|
325
+ # pn = File.join( JAVASCRIPT_FOLDER, name )
326
+ # add_file_obj( @javascript, name, pn )
327
+ # end
328
+ end
329
+
330
+ #
331
+ # Add a file object (page route) to the given container.
332
+ #
333
+ def add_file_obj( can, name, pn, info )
334
+ name = name.gsub( '.', '_' )
335
+ @log.debug "Adding route for file: #{name}"
336
+
337
+ # First make sure the child doesn't already exist.
338
+ child = can.find_child( name )
339
+ return if child
340
+
341
+ @factory.create_file( name, pn, can )
342
+ # @factory.create_file( info.published_name, pn, can )
343
+ end
344
+
345
+
346
+ # ---------------------------------------------------------------------
347
+ # List Asset Helpers
348
+ # ---------------------------------------------------------------------
349
+
350
+ #
351
+ # List all image assets.
352
+ # This looks in the image container and lists the images found earlier.
353
+ # A Debugging tool.
354
+ #
355
+ def list_image_assets
356
+ data = []
357
+ @images.children.each do |o|
358
+ data << [ o.name, o.pn, o.value ]
359
+ end
360
+ headers = [ "Name", "PN", "Value" ]
361
+
362
+ puts Gloo::App::Platform::RETURN
363
+ title = "Image Assets with Routes"
364
+ @engine.platform.table.show headers, data, title
365
+ puts Gloo::App::Platform::RETURN
366
+ end
367
+
368
+ #
369
+ # List all js assets.
370
+ # This looks in the js container and lists the js files found earlier.
371
+ # A Debugging tool.
372
+ #
373
+ def list_js_assets
374
+ data = []
375
+ @javascript.children.each do |o|
376
+ data << [ o.name, o.pn, o.value ]
377
+ end
378
+ headers = [ "Name", "PN", "Value" ]
379
+
380
+ puts Gloo::App::Platform::RETURN
381
+ title = "JavaScript Assets with Routes"
382
+ @engine.platform.table.show headers, data, title
383
+ puts Gloo::App::Platform::RETURN
384
+ end
385
+
386
+ #
387
+ # List all css assets.
388
+ # This looks in the css container and lists the css files found earlier.
389
+ # A Debugging tool.
390
+ #
391
+ def list_css_assets
392
+ data = []
393
+ @stylesheets.children.each do |o|
394
+ data << [ o.name, o.pn, o.value ]
395
+ end
396
+ headers = [ "Name", "PN", "Value" ]
397
+
398
+ puts Gloo::App::Platform::RETURN
399
+ title = "Stylesheet Assets with Routes"
400
+ @engine.platform.table.show headers, data, title
401
+ puts Gloo::App::Platform::RETURN
402
+ end
403
+
404
+ end
405
+ end
@@ -0,0 +1,114 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2025 Eric Crane. All rights reserved.
3
+ #
4
+ # Information about a single asset.
5
+ #
6
+ # Full Path is the full path to the file in the file system.
7
+ # Name is the name of the file with extension.
8
+ # PN is the path within assets and the name.
9
+ # ie: /asset/stylesheet/stylesheet.css
10
+ # Hash is the SHA256 hash of the file.
11
+ # Published Name is the name of the file that is published
12
+ # to the web server, and includes the hash.
13
+ #
14
+
15
+ module WebSvr
16
+ class AssetInfo
17
+
18
+ # Class Variables
19
+ @@index_by_published = {}
20
+ @@index_by_pn = {}
21
+
22
+ attr_reader :name, :pn, :published_name, :published_pn, :hash
23
+
24
+
25
+ # ---------------------------------------------------------------------
26
+ # Initialization
27
+ # ---------------------------------------------------------------------
28
+
29
+ #
30
+ # Set up an asset information object.
31
+ #
32
+ def initialize( engine, full_path, name, pn )
33
+ @engine = engine
34
+ @log = @engine.log
35
+
36
+ @full_path = full_path
37
+ @name = name
38
+ @pn = pn
39
+ end
40
+
41
+
42
+ # ---------------------------------------------------------------------
43
+ # Functions
44
+ # ---------------------------------------------------------------------
45
+
46
+ #
47
+ # Register the asset with indexes, inflating all needed data elements.
48
+ #
49
+ def register
50
+ @log.debug "*** REGISTERING ASSET: #{@name}"
51
+ @log.debug "*** #{@full_path} "
52
+ @log.debug "*** #PN: #{@pn} name: #{@name}"
53
+
54
+ @hash = Gloo::Objs::FileHandle.hash_for_file( @full_path )
55
+
56
+ # Build published name
57
+ ext = File.extname( @pn ) # Gets just the extension
58
+ n = @name[ 0..-ext.length - 1 ]
59
+ pn = @pn[ 0..-ext.length - 1 ]
60
+
61
+ @published_name = "#{n}-#{@hash}#{ext}"
62
+ @published_pn = "#{pn}-#{@hash}#{ext}"
63
+
64
+ @log.debug "*** Published Name: #{@published_name}"
65
+ @log.debug "*** Published Path: #{@published_pn}"
66
+
67
+ # Add to indexes
68
+ AssetInfo.index self
69
+ end
70
+
71
+ #
72
+ # Index the the given asset info record.
73
+ #
74
+ def self.index info
75
+ return unless info
76
+
77
+ @@index_by_pn[ info.pn ] = info
78
+ @@index_by_published[ info.published_pn ] = info
79
+ end
80
+
81
+ #
82
+ # Find the asset info for the given published name.
83
+ #
84
+ def self.find_published_name_for pn
85
+ return nil unless pn
86
+
87
+ return @@index_by_pn[ pn ]&.published_pn
88
+ end
89
+
90
+ #
91
+ # Find the asset info for the given published name.
92
+ #
93
+ def self.find_info_for pn
94
+ return @@index_by_published[ pn ]
95
+ end
96
+
97
+ #
98
+ # List All assets.
99
+ #
100
+ def self.list_all engine
101
+ data = []
102
+ @@index_by_pn.each do |pn, info|
103
+ data << [ info.name, info.pn, info.published_pn ]
104
+ end
105
+ headers = [ "Name", "Asset Path", "Published" ]
106
+
107
+ puts Gloo::App::Platform::RETURN
108
+ title = "Assets in Running Web App"
109
+ engine.platform.table.show headers, data, title
110
+ puts Gloo::App::Platform::RETURN
111
+ end
112
+
113
+ end
114
+ end
@@ -0,0 +1,55 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2024 Eric Crane. All rights reserved.
3
+ #
4
+ # Configuration for a gloo web server.
5
+ #
6
+
7
+
8
+ module WebSvr
9
+ class Config
10
+
11
+ SCHEME_SEPARATOR = '://'
12
+ HTTP = 'http'
13
+ HTTPS = 'https'
14
+ LOCALHOST = 'localhost'
15
+ PORT_DEFAULT = '8080'
16
+
17
+ attr_reader :scheme, :host, :port
18
+
19
+
20
+ # ---------------------------------------------------------------------
21
+ # Initialization
22
+ # ---------------------------------------------------------------------
23
+
24
+ #
25
+ # Set up the web server.
26
+ #
27
+ def initialize( scheme = HTTP, host = LOCALHOST, port = PORT_DEFAULT )
28
+ @scheme = scheme
29
+ @host = host
30
+ @port = port
31
+ end
32
+
33
+
34
+ # ---------------------------------------------------------------------
35
+ # Static Helper Functions
36
+ # ---------------------------------------------------------------------
37
+
38
+
39
+ # ---------------------------------------------------------------------
40
+ # Helper Functions
41
+ # ---------------------------------------------------------------------
42
+
43
+ #
44
+ # The base url, including scheme, host and port.
45
+ #
46
+ def base_url
47
+ url = "#{self.scheme}#{SCHEME_SEPARATOR}#{self.host}"
48
+ unless self.port.blank?
49
+ url << ":#{self.port}"
50
+ end
51
+ return url
52
+ end
53
+
54
+ end
55
+ end