gloo 3.1.1 → 3.3.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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/gloo.gemspec +1 -0
  3. data/lib/VERSION +1 -1
  4. data/lib/VERSION_NOTES +13 -0
  5. data/lib/gloo/app/log.rb +46 -6
  6. data/lib/gloo/app/platform.rb +7 -1
  7. data/lib/gloo/app/running_app.rb +42 -1
  8. data/lib/gloo/convert/falseclass_to_integer.rb +20 -0
  9. data/lib/gloo/convert/nilclass_to_date.rb +21 -0
  10. data/lib/gloo/convert/nilclass_to_datetime.rb +21 -0
  11. data/lib/gloo/convert/nilclass_to_integer.rb +21 -0
  12. data/lib/gloo/convert/nilclass_to_string.rb +21 -0
  13. data/lib/gloo/convert/nilclass_to_time.rb +21 -0
  14. data/lib/gloo/convert/trueclass_to_integer.rb +20 -0
  15. data/lib/gloo/core/error.rb +7 -0
  16. data/lib/gloo/core/it.rb +7 -0
  17. data/lib/gloo/core/obj.rb +41 -1
  18. data/lib/gloo/core/parser.rb +6 -3
  19. data/lib/gloo/exec/exec_env.rb +4 -0
  20. data/lib/gloo/exec/script.rb +9 -1
  21. data/lib/gloo/objs/basic/container.rb +18 -2
  22. data/lib/gloo/objs/basic/integer.rb +1 -0
  23. data/lib/gloo/objs/cli/menu.rb +9 -3
  24. data/lib/gloo/objs/{basic → ctrl}/function.rb +12 -0
  25. data/lib/gloo/objs/data/markdown.rb +60 -2
  26. data/lib/gloo/objs/data/mysql.rb +35 -7
  27. data/lib/gloo/objs/data/pg.rb +8 -0
  28. data/lib/gloo/objs/data/query.rb +66 -11
  29. data/lib/gloo/objs/data/query_result.rb +22 -1
  30. data/lib/gloo/objs/data/sqlite.rb +17 -2
  31. data/lib/gloo/objs/data/table.rb +143 -4
  32. data/lib/gloo/objs/web/json.rb +2 -0
  33. data/lib/gloo/objs/web_svr/element.rb +13 -5
  34. data/lib/gloo/objs/web_svr/page.rb +44 -2
  35. data/lib/gloo/objs/web_svr/partial.rb +7 -2
  36. data/lib/gloo/objs/web_svr/svr.rb +71 -4
  37. data/lib/gloo/verbs/break.rb +44 -0
  38. data/lib/gloo/verbs/create.rb +6 -0
  39. data/lib/gloo/verbs/invoke.rb +80 -0
  40. data/lib/gloo/verbs/log.rb +26 -5
  41. data/lib/gloo/verbs/redirect.rb +54 -6
  42. data/lib/gloo/web_svr/asset.rb +51 -23
  43. data/lib/gloo/web_svr/handler.rb +4 -2
  44. data/lib/gloo/web_svr/request.rb +35 -2
  45. data/lib/gloo/web_svr/routing/resource_router.rb +47 -0
  46. data/lib/gloo/web_svr/routing/router.rb +218 -0
  47. data/lib/gloo/web_svr/routing/show_routes.rb +98 -0
  48. data/lib/gloo/web_svr/server.rb +10 -2
  49. data/lib/gloo/web_svr/table_renderer.rb +147 -0
  50. data/lib/gloo/web_svr/web_method.rb +54 -0
  51. metadata +31 -4
  52. data/lib/gloo/web_svr/router.rb +0 -179
@@ -3,6 +3,7 @@
3
3
  #
4
4
  # Markdown data.
5
5
  #
6
+ require 'redcarpet'
6
7
 
7
8
  module Gloo
8
9
  module Objs
@@ -55,7 +56,7 @@ module Gloo
55
56
  # Get a list of message names that this object receives.
56
57
  #
57
58
  def self.messages
58
- return super + %w[show page]
59
+ return super + %w[show page render]
59
60
  end
60
61
 
61
62
  #
@@ -71,9 +72,66 @@ module Gloo
71
72
  def msg_page
72
73
  return unless self.value
73
74
 
74
- @engine.platform.show( md, true, true )
75
+ @engine.platform.show( self.value, true, true )
75
76
  end
76
77
 
78
+ #
79
+ # Render the markdown as HTML.
80
+ # Needs an optional parameter of where to put the rendered html.
81
+ # The html will be in 'it' as well.
82
+ #
83
+ def msg_render
84
+ html = Gloo::Objs::Markdown.md_2_html( value )
85
+
86
+ # Put the HTML in the optional parameter if one is given.
87
+ if @params&.token_count&.positive?
88
+ pn = Gloo::Core::Pn.new( @engine, @params.first )
89
+ o = pn.resolve
90
+ o.set_value html
91
+ end
92
+
93
+ # Put the HTML in it, in any case.
94
+ @engine.heap.it.set_to html
95
+ end
96
+
97
+ # ---------------------------------------------------------------------
98
+ # Static Helpers
99
+ # ---------------------------------------------------------------------
100
+
101
+ #
102
+ # Convert markdown to HTML using the
103
+ # Redcarpet markdown processor.
104
+ #
105
+ def self.md_2_html( md )
106
+ markdown = Redcarpet::Markdown.new(
107
+ Redcarpet::Render::HTML,
108
+ autolink: true,
109
+ fenced_code_blocks: true,
110
+ tables: true,
111
+ strikethrough: true )
112
+
113
+ return markdown.render( md )
114
+ end
115
+
116
+ #
117
+ # Does not work.
118
+ # See note in the platform.rb file.
119
+ #
120
+ # #
121
+ # # Convert markdown to manpage using the
122
+ # # Redcarpet markdown processor.
123
+ # #
124
+ # def self.md_2_manpage( md )
125
+ # markdown = Redcarpet::Markdown.new(
126
+ # Redcarpet::Render::ManPage,
127
+ # autolink: true,
128
+ # fenced_code_blocks: true,
129
+ # tables: true,
130
+ # strikethrough: true )
131
+
132
+ # return markdown.render( md )
133
+ # end
134
+
77
135
  end
78
136
  end
79
137
  end
@@ -90,22 +90,42 @@ module Gloo
90
90
  @engine.heap.it.set_to true
91
91
  end
92
92
 
93
+
93
94
  # ---------------------------------------------------------------------
94
95
  # DB functions (all database connections)
95
96
  # ---------------------------------------------------------------------
96
97
 
98
+ #
99
+ # Get the client object.
100
+ # It might be cached, so check first.
101
+ # If it is not cached, create a new one.
102
+ #
103
+ def get_client
104
+ app = @engine.running_app
105
+
106
+ client = app.db_client_for_obj( self ) if app
107
+
108
+ unless client
109
+ h = {
110
+ host: host_value,
111
+ database: db_value,
112
+ username: user_value,
113
+ password: passwd_value
114
+ }
115
+ client = Mysql2::Client.new( h )
116
+
117
+ app.cache_db_client( self, client ) if app
118
+ end
119
+
120
+ return client
121
+ end
122
+
97
123
  #
98
124
  # Open a connection and execute the SQL statement.
99
125
  # Return the resulting data.
100
126
  #
101
127
  def query( sql, params = nil )
102
- h = {
103
- host: host_value,
104
- database: db_value,
105
- username: user_value,
106
- password: passwd_value
107
- }
108
- client = Mysql2::Client.new( h )
128
+ client = get_client
109
129
 
110
130
  heads = []
111
131
  data = []
@@ -134,6 +154,14 @@ module Gloo
134
154
  return [ heads, data ]
135
155
  end
136
156
 
157
+ #
158
+ # Based on the result set, build a QueryResult object.
159
+ #
160
+ def get_query_result( result )
161
+ return QueryResult.new result[0], result[1]
162
+ end
163
+
164
+
137
165
  # ---------------------------------------------------------------------
138
166
  # Private functions
139
167
  # ---------------------------------------------------------------------
@@ -119,6 +119,14 @@ module Gloo
119
119
  return [ heads, data ]
120
120
  end
121
121
 
122
+ #
123
+ # Based on the result set, build a QueryResult object.
124
+ #
125
+ def get_query_result( result )
126
+ return QueryResult.new result[0], result[1]
127
+ end
128
+
129
+
122
130
  # ---------------------------------------------------------------------
123
131
  # Private functions
124
132
  # ---------------------------------------------------------------------
@@ -16,6 +16,7 @@ module Gloo
16
16
  SQL = 'sql'.freeze
17
17
  RESULT = 'result'.freeze
18
18
  PARAMS = 'params'.freeze
19
+ SIMPLE_LIST = 'simple_list'.freeze
19
20
 
20
21
  DB_MISSING_ERR = 'The database connection is missing!'.freeze
21
22
 
@@ -70,24 +71,69 @@ module Gloo
70
71
  end
71
72
 
72
73
  #
73
- # SSH to the host and execute the command, then update result.
74
+ # Run the query and process the results.
74
75
  #
75
76
  def msg_run
76
77
  db = db_obj
77
- unless db
78
- @engine.err DB_MISSING_ERR
78
+ return unless db
79
+
80
+ begin
81
+ result = db.query( sql_value, param_array )
82
+ process_result( result, db )
83
+ rescue => e
84
+ @engine.err e.message
79
85
  return
80
86
  end
87
+ end
88
+
89
+ #
90
+ # Run the query and return the results.
91
+ #
92
+ def run_query
93
+ db = db_obj
94
+ return unless db
81
95
 
82
96
  begin
97
+ log_query sql_value, param_array
98
+
99
+ db_start = ::Time.now
83
100
  result = db.query( sql_value, param_array )
84
- process_result result
101
+ db_done = ::Time.now
102
+ elapsed = ( ( db_done - db_start ) * 1000.0 ).round(2)
103
+
104
+ app = @engine.running_app
105
+ app.add_db_time elapsed if app
106
+ return result
85
107
  rescue => e
86
108
  @engine.err e.message
87
109
  return
88
110
  end
89
111
  end
90
112
 
113
+ #
114
+ # Write the query to the log.
115
+ #
116
+ def log_query sql, params
117
+ @engine.log.info "QUERY PARAMS: #{params}" if params
118
+ @engine.log.info "QUERY: #{sql}"
119
+ end
120
+
121
+
122
+ # ---------------------------------------------------------------------
123
+ # Output as simple list
124
+ # ---------------------------------------------------------------------
125
+
126
+ #
127
+ # Should the output be put in a simple list?
128
+ #
129
+ def simple_list?
130
+ o = find_child SIMPLE_LIST
131
+ return false unless o
132
+
133
+ return o.value
134
+ end
135
+
136
+
91
137
  # ---------------------------------------------------------------------
92
138
  # Private functions
93
139
  # ---------------------------------------------------------------------
@@ -99,6 +145,12 @@ module Gloo
99
145
  #
100
146
  def db_obj
101
147
  o = find_child DB
148
+
149
+ unless o
150
+ @engine.err DB_MISSING_ERR
151
+ return nil
152
+ end
153
+
102
154
  return Gloo::Objs::Alias.resolve_alias( @engine, o )
103
155
  end
104
156
 
@@ -118,21 +170,24 @@ module Gloo
118
170
  # If there's a result container, we'll create objects in it.
119
171
  # If not, we'll just show the output in the console.
120
172
  #
121
- def process_result( result )
173
+ def process_result( result, db )
122
174
  return if result.nil?
123
175
 
124
- heads = result[0]
125
- data = result[1]
126
- qr = QueryResult.new heads, data
127
- return unless qr.has_data_to_show?
176
+ query_result = db.get_query_result( result )
177
+ return unless query_result
178
+ return unless query_result.has_data_to_show?
128
179
 
129
180
  result_can = find_child RESULT
130
181
  result_can = Gloo::Objs::Alias.resolve_alias( @engine, result_can )
131
182
 
132
183
  if result_can
133
- qr.update_result_container result_can
184
+ if simple_list?
185
+ query_result.update_result_container_simple result_can
186
+ else
187
+ query_result.update_result_container result_can
188
+ end
134
189
  else
135
- qr.show
190
+ query_result.show
136
191
  end
137
192
  end
138
193
 
@@ -98,6 +98,14 @@ module Gloo
98
98
  single_row_result? ? update_single_row : update_rows
99
99
  end
100
100
 
101
+ #
102
+ # Update the result container with the data from the query.
103
+ #
104
+ def update_result_container_simple( in_can )
105
+ @result_can = in_can
106
+ single_row_result? ? update_single_row : update_rows_simple
107
+ end
108
+
101
109
  #
102
110
  # The result has a single row.
103
111
  # Map values from the result set to objects that are present.
@@ -122,7 +130,20 @@ module Gloo
122
130
  end
123
131
  end
124
132
  end
125
-
133
+
134
+ #
135
+ # Put all rows in the result object.
136
+ #
137
+ def update_rows_simple
138
+ @data.each do |row|
139
+ row.each do |val|
140
+ o = @result_can.find_add_child( val, 'untyped' )
141
+ o.set_value val
142
+ end
143
+ end
144
+ end
145
+
146
+
126
147
  # ---------------------------------------------------------------------
127
148
  # Private functions
128
149
  # ---------------------------------------------------------------------
@@ -116,10 +116,25 @@ module Gloo
116
116
  end
117
117
 
118
118
  db = SQLite3::Database.open name
119
- db.results_as_hash = true
120
- return db.query( sql, params )
119
+ # db.results_as_hash = true
120
+ results = db.query( sql, params )
121
+
122
+ return results
123
+ end
124
+
125
+ #
126
+ # Based on the result set, build a QueryResult object.
127
+ #
128
+ def get_query_result( result )
129
+ rows = []
130
+ while ( row = result.next ) do
131
+ rows << row
132
+ end
133
+
134
+ return QueryResult.new( result.columns, rows )
121
135
  end
122
136
 
137
+
123
138
  # ---------------------------------------------------------------------
124
139
  # Private functions
125
140
  # ---------------------------------------------------------------------
@@ -13,6 +13,8 @@ module Gloo
13
13
  KEYWORD_SHORT = 'tbl'.freeze
14
14
  HEADERS = 'headers'.freeze
15
15
  DATA = 'data'.freeze
16
+ CELLS = 'cells'.freeze
17
+ STYLES = 'styles'.freeze
16
18
 
17
19
  #
18
20
  # The name of the object type.
@@ -58,12 +60,64 @@ module Gloo
58
60
  return [] unless o
59
61
 
60
62
  o = Gloo::Objs::Alias.resolve_alias( @engine, o )
61
- cols = self.columns
62
- return o.children.map do |e|
63
- cols.map { |h| e.find_child( h ).value }
63
+
64
+ if o.is_a? Gloo::Objs::Query
65
+ @engine.log.debug "Table getting data from query."
66
+ begin
67
+ result = o.run_query
68
+ return result
69
+ rescue => e
70
+ @engine.err e.message
71
+ return nil
72
+ end
73
+ else
74
+ cols = self.columns
75
+
76
+ if o.children&.first.children.empty?
77
+ # It is a simgle row table.
78
+ rows = [ cols.map { |h| o.find_child( h )&.value } ]
79
+ else
80
+ rows = o.children.map do |e|
81
+ cols.map { |h| e.find_child( h )&.value }
82
+ end
83
+ end
84
+
85
+ return [ cols, rows ]
64
86
  end
65
87
  end
66
88
 
89
+ #
90
+ # Get the styles for the table, if any.
91
+ #
92
+ def styles
93
+ style_h = {}
94
+ o = find_child STYLES
95
+ return style_h unless o
96
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
97
+
98
+ o.children.each do |c|
99
+ style_h[ c.name ] = c.value
100
+ end
101
+
102
+ return style_h
103
+ end
104
+
105
+ #
106
+ # Get cell renderer hash keyed by column name.
107
+ #
108
+ def cell_renderers
109
+ h = {}
110
+ o = find_child CELLS
111
+ return h unless o
112
+
113
+ o.children.each do |c|
114
+ h[ c.name ] = c.value
115
+ end
116
+
117
+ return h
118
+ end
119
+
120
+
67
121
  # ---------------------------------------------------------------------
68
122
  # Children
69
123
  # ---------------------------------------------------------------------
@@ -88,6 +142,7 @@ module Gloo
88
142
  fac.create_can DATA, self
89
143
  end
90
144
 
145
+
91
146
  # ---------------------------------------------------------------------
92
147
  # Messages
93
148
  # ---------------------------------------------------------------------
@@ -96,7 +151,7 @@ module Gloo
96
151
  # Get a list of message names that this object receives.
97
152
  #
98
153
  def self.messages
99
- return super + %w[show]
154
+ return super + %w[show render]
100
155
  end
101
156
 
102
157
  #
@@ -107,6 +162,90 @@ module Gloo
107
162
  @engine.platform.show_table headers, data, title
108
163
  end
109
164
 
165
+ def msg_render
166
+ return render
167
+ end
168
+
169
+
170
+ # ---------------------------------------------------------------------
171
+ # Render
172
+ # ---------------------------------------------------------------------
173
+
174
+ #
175
+ # Render the table.
176
+ # The render_ƒ is 'render_html', 'render_text', 'render_json', etc.
177
+ #
178
+ def render render_ƒ
179
+ begin
180
+ result = self.data
181
+ head = self.headers
182
+ head = result[0] if head.empty?
183
+ rows = result[1]
184
+
185
+ columns = build_columns result[0]
186
+
187
+ params = {
188
+ head: head,
189
+ cols: result[0],
190
+ columns: columns,
191
+ rows: rows,
192
+ styles: self.styles,
193
+ cell_renderers: self.cell_renderers
194
+ }
195
+
196
+ helper = Gloo::WebSvr::TableRenderer.new( @engine )
197
+ return helper.data_to_table params
198
+ rescue => e
199
+ @engine.err e.message
200
+ return nil
201
+ end
202
+ end
203
+
204
+ #
205
+ # Build the column list based on the result data and
206
+ # the headers defined in the table object.
207
+ #
208
+ def build_columns result_data
209
+ head_children = find_child HEADERS
210
+ cell_renderers = find_child CELLS
211
+
212
+ columns = []
213
+ return columns unless result_data
214
+
215
+ result_data.each_with_index do |c,index|
216
+ visible = true
217
+ name = c
218
+ title = c
219
+ display_index = index
220
+
221
+ if head_children
222
+ child = head_children.find_child c
223
+ if child
224
+ title = child.value
225
+ display_index = head_children.child_index( c )
226
+ else
227
+ visible = false
228
+ end
229
+ end
230
+
231
+ cell_renderer = nil
232
+ if cell_renderers
233
+ this_cr = cell_renderers.find_child( c )
234
+ cell_renderer = this_cr.value if this_cr
235
+ end
236
+
237
+ columns << {
238
+ name: name,
239
+ title: title,
240
+ visible: visible,
241
+ data_index: index,
242
+ display_index: display_index,
243
+ cell_renderer: cell_renderer
244
+ }
245
+ end
246
+ return columns.sort_by { |hsh| hsh[ :display_index ] }
247
+ end
248
+
110
249
  end
111
250
  end
112
251
  end
@@ -63,6 +63,8 @@ module Gloo
63
63
  # Make the JSON pretty.
64
64
  #
65
65
  def msg_pretty
66
+ return unless self.value
67
+
66
68
  json = JSON.parse( self.value )
67
69
  pretty = JSON.pretty_generate( json )
68
70
  set_value pretty
@@ -173,7 +173,7 @@ module Gloo
173
173
  # Get the expiration date for the certificate.
174
174
  #
175
175
  def msg_render
176
- content = self.render
176
+ content = self.render_html
177
177
  @engine.heap.it.set_to content
178
178
  return content
179
179
  end
@@ -226,10 +226,9 @@ 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 == Partial
230
- rendered_obj_content << e.render( render_ƒ )
231
229
  else
232
- rendered_obj_content << e.value.to_s
230
+ data = render_thing e, render_ƒ, engine
231
+ ( rendered_obj_content << data ) if data # e.render( render_ƒ )
233
232
  end
234
233
  end
235
234
  else
@@ -238,7 +237,16 @@ module Gloo
238
237
 
239
238
  return rendered_obj_content
240
239
  end
241
-
240
+
241
+ def self.render_thing e, render_ƒ, engine
242
+ begin
243
+ return e.render( render_ƒ )
244
+ rescue => e
245
+ engine.err e.message
246
+ return ''
247
+ end
248
+ end
249
+
242
250
  end
243
251
  end
244
252
  end
@@ -17,6 +17,7 @@ module Gloo
17
17
 
18
18
  # Parameters used during render.
19
19
  PARAMS = 'params'.freeze
20
+ ID = 'id'.freeze
20
21
 
21
22
  # Content
22
23
  HEAD = 'head'.freeze
@@ -112,6 +113,19 @@ module Gloo
112
113
  params_can = find_child PARAMS
113
114
  return nil unless params_can
114
115
 
116
+ if @request
117
+ url_params = @request.query_params
118
+ url_params.each do |k,v|
119
+ o = params_can.find_child k
120
+ o.set_value( v ) if o
121
+ end
122
+
123
+ @request.body_params.each do |k,v|
124
+ o = params_can.find_child k
125
+ o.set_value( v ) if o
126
+ end
127
+ end
128
+
115
129
  h = {}
116
130
  params_can.children.each do |o|
117
131
  h[ o.name ] = o.value
@@ -182,6 +196,8 @@ module Gloo
182
196
  o = find_child ON_RENDER
183
197
  return unless o
184
198
 
199
+ @engine.log.debug "running on_render for page"
200
+
185
201
  Gloo::Exec::Dispatch.message( @engine, 'run', o )
186
202
  end
187
203
 
@@ -192,6 +208,8 @@ module Gloo
192
208
  o = find_child ON_RENDERED
193
209
  return unless o
194
210
 
211
+ @engine.log.debug "running on_rendered for page"
212
+
195
213
  Gloo::Exec::Dispatch.message( @engine, 'run', o )
196
214
  end
197
215
 
@@ -281,13 +299,36 @@ module Gloo
281
299
  return @engine.running_app.obj.redirect
282
300
  end
283
301
 
302
+ #
303
+ # Set the ID parameter if there is one.
304
+ #
305
+ def set_id
306
+ return unless @request.id
307
+ @engine.log.info "Setting ID: #{@request.id}"
308
+
309
+ params_can = find_child PARAMS
310
+ return nil unless params_can
311
+
312
+ id_obj = params_can.find_child( ID )
313
+ return unless id_obj
314
+
315
+ id_obj.set_value( @request.id )
316
+ end
317
+
284
318
  #
285
319
  # Render the page.
320
+ # If this is being called from the web server,
321
+ # the request will be passed in and will include
322
+ # request context such as params.
286
323
  #
287
- def render
324
+ def render request=nil
325
+ @request = request
326
+ set_id if @request
327
+
288
328
  run_on_render
289
329
  return nil if redirect_set?
290
330
 
331
+ # Set Params before running on render
291
332
  params = params_hash
292
333
 
293
334
  if is_html?
@@ -300,8 +341,9 @@ module Gloo
300
341
  @engine.log.error "Unknown content type: #{content_type}"
301
342
  return nil
302
343
  end
303
-
344
+
304
345
  run_on_rendered
346
+ @request = nil
305
347
  return nil if redirect_set?
306
348
 
307
349
  return contents
@@ -161,8 +161,13 @@ module Gloo
161
161
  run_on_render
162
162
 
163
163
  part_content = ''
164
- content.children.each do |e|
165
- part_content << e.send( render_ƒ )
164
+ data = content
165
+ if data.children.empty?
166
+ part_content = data.value
167
+ else
168
+ data.children.each do |e|
169
+ part_content << e.send( render_ƒ )
170
+ end
166
171
  end
167
172
 
168
173
  # part_content = Page.render_params part_content, params_hash