gloo 3.2.0 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
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