gloo 3.2.0 → 3.4.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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/gloo.gemspec +9 -3
  4. data/lib/VERSION +1 -1
  5. data/lib/VERSION_NOTES +14 -0
  6. data/lib/gloo/app/engine.rb +1 -1
  7. data/lib/gloo/app/log.rb +15 -16
  8. data/lib/gloo/app/platform.rb +11 -84
  9. data/lib/gloo/app/prompt.rb +90 -0
  10. data/lib/gloo/app/table.rb +51 -0
  11. data/lib/gloo/convert/falseclass_to_integer.rb +20 -0
  12. data/lib/gloo/convert/nilclass_to_date.rb +21 -0
  13. data/lib/gloo/convert/nilclass_to_datetime.rb +21 -0
  14. data/lib/gloo/convert/nilclass_to_integer.rb +21 -0
  15. data/lib/gloo/convert/nilclass_to_string.rb +21 -0
  16. data/lib/gloo/convert/nilclass_to_time.rb +21 -0
  17. data/lib/gloo/convert/trueclass_to_integer.rb +20 -0
  18. data/lib/gloo/core/error.rb +7 -0
  19. data/lib/gloo/core/gloo_system.rb +7 -14
  20. data/lib/gloo/core/it.rb +7 -0
  21. data/lib/gloo/core/obj.rb +7 -0
  22. data/lib/gloo/core/parser.rb +6 -3
  23. data/lib/gloo/objs/basic/container.rb +1 -2
  24. data/lib/gloo/objs/basic/integer.rb +24 -1
  25. data/lib/gloo/objs/basic/string.rb +116 -1
  26. data/lib/gloo/objs/basic/string_generator.rb +49 -0
  27. data/lib/gloo/objs/basic/text.rb +1 -17
  28. data/lib/gloo/objs/cli/menu.rb +5 -4
  29. data/lib/gloo/objs/cli/select.rb +3 -2
  30. data/lib/gloo/objs/{basic → ctrl}/function.rb +12 -0
  31. data/lib/gloo/objs/data/markdown.rb +59 -6
  32. data/lib/gloo/objs/data/mysql.rb +39 -27
  33. data/lib/gloo/objs/data/pg.rb +1 -1
  34. data/lib/gloo/objs/data/query_result.rb +4 -9
  35. data/lib/gloo/objs/data/table.rb +1 -1
  36. data/lib/gloo/objs/security/cipher.rb +193 -0
  37. data/lib/gloo/objs/security/password.rb +167 -0
  38. data/lib/gloo/objs/system/file_handle.rb +1 -3
  39. data/lib/gloo/objs/web/json.rb +3 -0
  40. data/lib/gloo/objs/web_svr/page.rb +26 -6
  41. data/lib/gloo/objs/web_svr/partial.rb +7 -6
  42. data/lib/gloo/objs/web_svr/svr.rb +267 -14
  43. data/lib/gloo/verbs/invoke.rb +80 -0
  44. data/lib/gloo/verbs/version.rb +1 -1
  45. data/lib/gloo/web_svr/asset.rb +54 -33
  46. data/lib/gloo/web_svr/config.rb +1 -1
  47. data/lib/gloo/web_svr/embedded_renderer.rb +1 -1
  48. data/lib/gloo/web_svr/handler.rb +6 -4
  49. data/lib/gloo/web_svr/request.rb +34 -8
  50. data/lib/gloo/web_svr/response.rb +14 -2
  51. data/lib/gloo/web_svr/response_code.rb +1 -1
  52. data/lib/gloo/web_svr/routing/router.rb +1 -2
  53. data/lib/gloo/web_svr/routing/show_routes.rb +4 -7
  54. data/lib/gloo/web_svr/server.rb +1 -1
  55. data/lib/gloo/web_svr/session.rb +161 -0
  56. data/lib/gloo/web_svr/table_renderer.rb +1 -1
  57. metadata +81 -26
  58. data/lib/gloo/objs/cli/banner.rb +0 -118
  59. data/lib/gloo/objs/cli/bar.rb +0 -133
  60. data/lib/gloo/objs/cli/pastel.rb +0 -104
@@ -6,7 +6,6 @@
6
6
  # system level variables and functions. But it is not
7
7
  # actually an object in the normal sense of the word.
8
8
  #
9
- require 'tty-platform'
10
9
  require 'os'
11
10
 
12
11
  module Gloo
@@ -217,20 +216,17 @@ module Gloo
217
216
 
218
217
  # Get the platform CPU
219
218
  def msg_platform_cpu
220
- platform = TTY::Platform.new
221
- return platform.cpu
219
+ return OS.host_cpu
222
220
  end
223
221
 
224
222
  # Get the platform Operating System
225
223
  def msg_platform_os
226
- platform = TTY::Platform.new
227
- return platform.os
224
+ return RUBY_PLATFORM
228
225
  end
229
226
 
230
227
  # Get the platform version
231
228
  def msg_platform_version
232
- platform = TTY::Platform.new
233
- return platform.version
229
+ return 'n/a'
234
230
  end
235
231
 
236
232
  # Is the platform Windows?
@@ -240,14 +236,12 @@ module Gloo
240
236
 
241
237
  # Is the platform Unix?
242
238
  def msg_platform_unix?
243
- platform = TTY::Platform.new
244
- return platform.unix?
239
+ return OS.posix?
245
240
  end
246
241
 
247
242
  # Is the platform Linux?
248
243
  def msg_platform_linux?
249
- platform = TTY::Platform.new
250
- return platform.linux?
244
+ return OS.posix?
251
245
  end
252
246
 
253
247
  # Is the platform Mac?
@@ -259,9 +253,8 @@ module Gloo
259
253
  # Get the command to open a file on this platform.
260
254
  #
261
255
  def self.open_for_platform
262
- platform = TTY::Platform.new
263
- return 'open' if platform.mac?
264
- return 'xdg-open' if platform.linux?
256
+ return 'open' if OS.mac?
257
+ return 'xdg-open' if OS.posix?
265
258
 
266
259
  return 'Start-Process' if OS.windows?
267
260
 
data/lib/gloo/core/it.rb CHANGED
@@ -31,6 +31,13 @@ module Gloo
31
31
  return @value.to_s
32
32
  end
33
33
 
34
+ #
35
+ # Is this a function object?
36
+ #
37
+ def is_function?
38
+ return false
39
+ end
40
+
34
41
  end
35
42
  end
36
43
  end
data/lib/gloo/core/obj.rb CHANGED
@@ -144,6 +144,13 @@ module Gloo
144
144
  return self.type_display == Gloo::Objs::Alias.typename
145
145
  end
146
146
 
147
+ #
148
+ # Is this a function object?
149
+ #
150
+ def is_function?
151
+ return self.type_display == Gloo::Objs::Function.typename
152
+ end
153
+
147
154
 
148
155
  # ---------------------------------------------------------------------
149
156
  # Children
@@ -20,8 +20,11 @@ module Gloo
20
20
  #
21
21
  # Parse a command from the immediate execution context.
22
22
  #
23
- def parse_immediate( cmd )
24
- cmd, params = split_params cmd
23
+ def parse_immediate( full_cmd )
24
+ # Break the full command into verb and params
25
+ cmd, params = split_params full_cmd
26
+
27
+ # Params are the parenthetical part of the command at the end
25
28
  params = Gloo::Core::Tokens.new( params ) if params
26
29
  tokens = Gloo::Core::Tokens.new( cmd )
27
30
  dic = Gloo::Core::Dictionary.instance
@@ -42,7 +45,7 @@ module Gloo
42
45
  if i && cmd.strip.end_with?( ')' )
43
46
  pstr = cmd[ i + 1..-1 ]
44
47
  params = pstr.strip[ 0..-2 ] if pstr
45
- cmd = cmd[ 0, i - 1 ]
48
+ cmd = cmd[ 0, i].strip
46
49
  end
47
50
  return cmd, params
48
51
  end
@@ -58,8 +58,7 @@ module Gloo
58
58
  def msg_show_key_value_table
59
59
  data = self.children.map { |o| [ o.name, o.value ] }
60
60
 
61
- # TODO: this doesn't work:
62
- # @engine.platform.show_table nil, data, title
61
+ @engine.platform.table.show [], data
63
62
  end
64
63
 
65
64
  #
@@ -10,6 +10,7 @@ module Gloo
10
10
 
11
11
  KEYWORD = 'integer'.freeze
12
12
  KEYWORD_SHORT = 'int'.freeze
13
+ DEFAULT_RANDOM_RANGE = 100
13
14
 
14
15
  #
15
16
  # The name of the object type.
@@ -37,6 +38,7 @@ module Gloo
37
38
  self.value = new_value.to_i
38
39
  end
39
40
 
41
+
40
42
  # ---------------------------------------------------------------------
41
43
  # Messages
42
44
  # ---------------------------------------------------------------------
@@ -45,7 +47,7 @@ module Gloo
45
47
  # Get a list of message names that this object receives.
46
48
  #
47
49
  def self.messages
48
- return super + %w[inc dec]
50
+ return super + %w[inc dec randomize]
49
51
  end
50
52
 
51
53
  #
@@ -68,6 +70,27 @@ module Gloo
68
70
  return i
69
71
  end
70
72
 
73
+ #
74
+ # Set the value to a random number.
75
+ # The range is 0 to DEFAULT_RANDOM_RANGE (not including the range).
76
+ # To model a 6-sided die,
77
+ # set range to 6 and add 1 to the result.
78
+ #
79
+ def msg_randomize
80
+ range = DEFAULT_RANDOM_RANGE
81
+
82
+ # Check for a range.
83
+ if @params&.token_count&.positive?
84
+ expr = Gloo::Expr::Expression.new( @engine, @params.tokens )
85
+ range = expr.evaluate
86
+ end
87
+
88
+ rand_value = rand( range )
89
+ set_value rand_value
90
+ @engine.heap.it.set_to rand_value
91
+ return rand_value
92
+ end
93
+
71
94
  end
72
95
  end
73
96
  end
@@ -3,6 +3,8 @@
3
3
  #
4
4
  # A String.
5
5
  #
6
+ require 'base64'
7
+ require 'uri'
6
8
 
7
9
  module Gloo
8
10
  module Objs
@@ -40,7 +42,8 @@ module Gloo
40
42
  # Get a list of message names that this object receives.
41
43
  #
42
44
  def self.messages
43
- return super + %w[up down size]
45
+ return super + %w[up down size encode64 decode64 escape unescape
46
+ gen_alphanumeric gen_uuid gen_hex gen_base64]
44
47
  end
45
48
 
46
49
  #
@@ -52,6 +55,118 @@ module Gloo
52
55
  return s
53
56
  end
54
57
 
58
+ #
59
+ # Escape the string.
60
+ # Make it URL safe.
61
+ # The value of the string is changed.
62
+ #
63
+ def msg_escape
64
+ s = URI::DEFAULT_PARSER.escape( value )
65
+ set_value s
66
+ @engine.heap.it.set_to s
67
+ return s
68
+ end
69
+
70
+ #
71
+ # Unescape the string.
72
+ # The value of the string is changed.
73
+ #
74
+ def msg_unescape
75
+ s = URI::DEFAULT_PARSER.unescape( value )
76
+ set_value s
77
+ @engine.heap.it.set_to s
78
+ return s
79
+ end
80
+
81
+ #
82
+ # Encode the string as base64.
83
+ # Changes the value of the string.
84
+ #
85
+ def msg_encode64
86
+ s = Base64.encode64( value )
87
+ set_value s
88
+ @engine.heap.it.set_to s
89
+ return s
90
+ end
91
+
92
+ #
93
+ # Decode the string from base64.
94
+ # Changes the value of the string.
95
+ #
96
+ def msg_decode64
97
+ s = Base64.decode64( value )
98
+ set_value s
99
+ @engine.heap.it.set_to s
100
+ return s
101
+ end
102
+
103
+ #
104
+ # Generate a new UUID in the string.
105
+ #
106
+ def msg_gen_uuid
107
+ s = StringGenerator.uuid
108
+ set_value s
109
+ @engine.heap.it.set_to s
110
+ return s
111
+ end
112
+
113
+ #
114
+ # Generate a random alphanumeric string.
115
+ # By default the length is 10 characters.
116
+ # Set the length with an optional parameter.
117
+ #
118
+ def msg_gen_alphanumeric
119
+ len = 10
120
+ if @params&.token_count&.positive?
121
+ expr = Gloo::Expr::Expression.new( @engine, @params.tokens )
122
+ data = expr.evaluate
123
+ len = data.to_i
124
+ end
125
+
126
+ s = StringGenerator.alphanumeric( len )
127
+ set_value s
128
+ @engine.heap.it.set_to s
129
+ return s
130
+ end
131
+
132
+ #
133
+ # Generate a random hex string.
134
+ # By default the length is 10 hex characters.
135
+ # Set the length with an optional parameter.
136
+ #
137
+ def msg_gen_hex
138
+ len = 10
139
+ if @params&.token_count&.positive?
140
+ expr = Gloo::Expr::Expression.new( @engine, @params.tokens )
141
+ data = expr.evaluate
142
+ len = data.to_i
143
+ end
144
+
145
+ s = StringGenerator.hex( len )
146
+ set_value s
147
+ @engine.heap.it.set_to s
148
+ return s
149
+ end
150
+
151
+ #
152
+ # Generate a random base64 string.
153
+ # By default the length is 12 characters.
154
+ # Set the length with an optional parameter.
155
+ #
156
+ def msg_gen_base64
157
+ len = 12
158
+ if @params&.token_count&.positive?
159
+ expr = Gloo::Expr::Expression.new( @engine, @params.tokens )
160
+ data = expr.evaluate
161
+ len = data.to_i
162
+ end
163
+
164
+ s = StringGenerator.base64( len )
165
+ set_value s
166
+ @engine.heap.it.set_to s
167
+ return s
168
+ end
169
+
55
170
  #
56
171
  # Convert string to upper case
57
172
  #
@@ -0,0 +1,49 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2024 Eric Crane. All rights reserved.
3
+ #
4
+ # String generation utilities.
5
+ # This is a static class.
6
+ #
7
+
8
+ module Gloo
9
+ module Objs
10
+ class StringGenerator
11
+
12
+ # TO DO: Consider adding in Faker generators as well.
13
+
14
+
15
+ # ---------------------------------------------------------------------
16
+ # Generators
17
+ # ---------------------------------------------------------------------
18
+
19
+ #
20
+ # Generate a new UUID.
21
+ #
22
+ def self.uuid
23
+ return SecureRandom.uuid
24
+ end
25
+
26
+ #
27
+ # Generate a random alphanumeric string.
28
+ #
29
+ def self.alphanumeric len=10
30
+ return SecureRandom.alphanumeric( len )
31
+ end
32
+
33
+ #
34
+ # Generate a random hex string.
35
+ #
36
+ def self.hex len=10
37
+ s = SecureRandom.hex( len )
38
+ end
39
+
40
+ #
41
+ # Generate a random base64 string.
42
+ #
43
+ def self.base64 len=12
44
+ return SecureRandom.base64( len )
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -55,23 +55,7 @@ module Gloo
55
55
  # Get a list of message names that this object receives.
56
56
  #
57
57
  def self.messages
58
- return super + %w[edit page]
59
- end
60
-
61
- #
62
- # Show the contents of the file, paginated.
63
- #
64
- def msg_page
65
- return unless value
66
-
67
- @engine.platform.show( value, false, true )
68
- end
69
-
70
- #
71
- # Edit the text in the default editor.
72
- #
73
- def msg_edit
74
- self.value = @engine.platform.edit( self.value )
58
+ return super
75
59
  end
76
60
 
77
61
  end
@@ -19,8 +19,7 @@ module Gloo
19
19
  BEFORE_MENU = 'before_menu'.freeze
20
20
  DEFAULT = 'default'.freeze
21
21
  TITLE = 'title'.freeze
22
- TITLE_STYLE = 'straight'.freeze
23
- TITLE_COLOR = 'bright_cyan'.freeze
22
+ TITLE_COLOR = 'green'.freeze
24
23
  QUIT_ITEM_NAME = 'q'.freeze
25
24
 
26
25
  @@menu_stack = []
@@ -234,7 +233,7 @@ module Gloo
234
233
  dt = DateTime.now
235
234
  d = dt.strftime( '%Y.%m.%d' )
236
235
  t = dt.strftime( '%I:%M:%S' )
237
- cmd = @engine.platform.prompt.ask( "\n#{d.yellow} #{t.white} >" )
236
+ cmd = @engine.platform.prompt.ask( "#{d.yellow} #{t.white} >" )
238
237
  else
239
238
  cmd = @engine.platform.prompt.ask( prompt_value )
240
239
  end
@@ -320,7 +319,9 @@ module Gloo
320
319
  def run_default_title
321
320
  @engine.platform&.clear_screen
322
321
  show_menu_stack
323
- Banner.show_banner( title, TITLE_STYLE, TITLE_COLOR )
322
+
323
+ title_text = @engine.platform.table.box( title )
324
+ puts title_text.colorize( :color => :white, :background => :black )
324
325
  end
325
326
 
326
327
  #
@@ -116,8 +116,9 @@ module Gloo
116
116
  prompt = prompt_value
117
117
  return unless prompt
118
118
 
119
- per = Gloo::App::Settings.page_size( @engine )
120
- result = @engine.platform.prompt.select( prompt, options, :per_page => per )
119
+ # Page size was part of the tty-prompt but not used now.
120
+ # per = Gloo::App::Settings.page_size( @engine )
121
+ result = @engine.platform.prompt.select( prompt, options )
121
122
  set_result self.key_for_option( result )
122
123
  end
123
124
 
@@ -14,6 +14,7 @@ module Gloo
14
14
 
15
15
  # Events
16
16
  ON_INVOKE = 'on_invoke'.freeze
17
+ AFTER_INVOKE = 'after_invoke'.freeze
17
18
 
18
19
  # Parameters to the function invocation.
19
20
  PARAMS = 'params'.freeze
@@ -132,6 +133,16 @@ module Gloo
132
133
  Gloo::Exec::Dispatch.message( @engine, 'run', o )
133
134
  end
134
135
 
136
+ #
137
+ # Run the after invoke script if there is one.
138
+ #
139
+ def run_after_invoke
140
+ o = find_child AFTER_INVOKE
141
+ return unless o
142
+
143
+ Gloo::Exec::Dispatch.message( @engine, 'run', o )
144
+ end
145
+
135
146
 
136
147
  # ---------------------------------------------------------------------
137
148
  # Messages
@@ -147,6 +158,7 @@ module Gloo
147
158
  run_on_invoke
148
159
  return_value = result
149
160
  @engine.heap.it.set_to return_value
161
+ run_after_invoke
150
162
 
151
163
  return return_value
152
164
  end
@@ -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,23 +56,75 @@ 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 render update_asset_path]
59
60
  end
60
61
 
61
62
  #
62
63
  # Show the markdown data in the terminal.
63
64
  #
64
65
  def msg_show
65
- @engine.platform.show( self.value, true, false )
66
+ @engine.platform.show self.value
66
67
  end
67
68
 
68
69
  #
69
- # Show the markdown data in the terminal, paginated.
70
+ # Render the markdown as HTML.
71
+ # Needs an optional parameter of where to put the rendered html.
72
+ # The html will be in 'it' as well.
70
73
  #
71
- def msg_page
72
- return unless self.value
74
+ def msg_render
75
+ html = Gloo::Objs::Markdown.md_2_html( value )
73
76
 
74
- @engine.platform.show( md, true, true )
77
+ # Put the HTML in the optional parameter if one is given.
78
+ if @params&.token_count&.positive?
79
+ pn = Gloo::Core::Pn.new( @engine, @params.first )
80
+ o = pn.resolve
81
+ o.set_value html
82
+ end
83
+
84
+ # Put the HTML in it, in any case.
85
+ @engine.heap.it.set_to html
86
+ end
87
+
88
+ #
89
+ # Update the asset path in the markdown.
90
+ # Take out leading relative path so that path starts
91
+ # at the asset root.
92
+ #
93
+ def msg_update_asset_path
94
+ data = self.value
95
+ out_data = ""
96
+
97
+ data.lines.each do |line|
98
+ if line.include?( '![' ) && line.include?( '](') && line.include?( '/asset/')
99
+ prefix = line[ 0, ( line.index( '](' ) + 2 ) ]
100
+ suffix = line[ (line.index( '/asset/' )) .. -1 ]
101
+ out_data << "#{prefix}#{suffix}"
102
+ else
103
+ out_data << line
104
+ end
105
+ end
106
+
107
+ self.value = out_data
108
+ end
109
+
110
+
111
+ # ---------------------------------------------------------------------
112
+ # Static Helpers
113
+ # ---------------------------------------------------------------------
114
+
115
+ #
116
+ # Convert markdown to HTML using the
117
+ # Redcarpet markdown processor.
118
+ #
119
+ def self.md_2_html( md )
120
+ markdown = Redcarpet::Markdown.new(
121
+ Redcarpet::Render::HTML,
122
+ autolink: true,
123
+ fenced_code_blocks: true,
124
+ tables: true,
125
+ strikethrough: true )
126
+
127
+ return markdown.render( md )
75
128
  end
76
129
 
77
130
  end
@@ -105,17 +105,24 @@ module Gloo
105
105
 
106
106
  client = app.db_client_for_obj( self ) if app
107
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
108
+ if client && client.ping
109
+ @engine.log.debug "Connection is established and active."
110
+ return client
111
+ elsif client
112
+ @engine.log.debug "Connection is established but NOT active. Reconnecting."
113
+ else
114
+ @engine.log.debug "Opening a new Connection."
118
115
  end
116
+
117
+ h = {
118
+ host: host_value,
119
+ database: db_value,
120
+ username: user_value,
121
+ password: passwd_value
122
+ }
123
+ client = Mysql2::Client.new( h )
124
+
125
+ app.cache_db_client( self, client ) if app
119
126
 
120
127
  return client
121
128
  end
@@ -129,28 +136,33 @@ module Gloo
129
136
 
130
137
  heads = []
131
138
  data = []
132
- if params
133
- pst = client.prepare( sql )
134
- rs = pst.execute( *params, :as => :array )
135
- if rs
136
- rs.each do |row|
137
- arr = []
138
- row.each do |o|
139
- arr << o
139
+ begin
140
+ if params
141
+ pst = client.prepare( sql )
142
+ rs = pst.execute( *params, :as => :array )
143
+ if rs
144
+ rs.each do |row|
145
+ arr = []
146
+ row.each do |o|
147
+ arr << o
148
+ end
149
+ data << arr
140
150
  end
141
- data << arr
142
151
  end
143
- end
144
- else
145
- rs = client.query( sql, :as => :array )
146
- if rs
147
- rs.each do |row|
148
- data << row
152
+ else
153
+ rs = client.query( sql, :as => :array )
154
+ if rs
155
+ rs.each do |row|
156
+ data << row
157
+ end
149
158
  end
150
159
  end
160
+
161
+ heads = rs.fields if rs
162
+ rescue => e
163
+ @engine.err e.message
151
164
  end
152
165
 
153
- heads = rs.fields if rs
154
166
  return [ heads, data ]
155
167
  end
156
168
 
@@ -158,7 +170,7 @@ module Gloo
158
170
  # Based on the result set, build a QueryResult object.
159
171
  #
160
172
  def get_query_result( result )
161
- return QueryResult.new result[0], result[1]
173
+ return QueryResult.new result[0], result[1], @engine
162
174
  end
163
175
 
164
176
 
@@ -123,7 +123,7 @@ module Gloo
123
123
  # Based on the result set, build a QueryResult object.
124
124
  #
125
125
  def get_query_result( result )
126
- return QueryResult.new result[0], result[1]
126
+ return QueryResult.new result[0], result[1], @engine
127
127
  end
128
128
 
129
129