gloo 3.3.0 → 3.4.1

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/gloo.gemspec +8 -3
  4. data/lib/VERSION +1 -1
  5. data/lib/VERSION_NOTES +12 -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 -90
  9. data/lib/gloo/app/prompt.rb +90 -0
  10. data/lib/gloo/app/table.rb +51 -0
  11. data/lib/gloo/core/gloo_system.rb +7 -14
  12. data/lib/gloo/objs/basic/container.rb +1 -2
  13. data/lib/gloo/objs/basic/integer.rb +23 -1
  14. data/lib/gloo/objs/basic/string.rb +116 -1
  15. data/lib/gloo/objs/basic/string_generator.rb +49 -0
  16. data/lib/gloo/objs/basic/text.rb +1 -17
  17. data/lib/gloo/objs/cli/menu.rb +5 -4
  18. data/lib/gloo/objs/cli/select.rb +3 -2
  19. data/lib/gloo/objs/data/markdown.rb +25 -30
  20. data/lib/gloo/objs/data/mysql.rb +39 -27
  21. data/lib/gloo/objs/data/pg.rb +1 -1
  22. data/lib/gloo/objs/data/query_result.rb +4 -9
  23. data/lib/gloo/objs/data/table.rb +1 -1
  24. data/lib/gloo/objs/security/cipher.rb +193 -0
  25. data/lib/gloo/objs/security/password.rb +167 -0
  26. data/lib/gloo/objs/system/file_handle.rb +1 -3
  27. data/lib/gloo/objs/web/json.rb +3 -0
  28. data/lib/gloo/objs/web_svr/page.rb +24 -8
  29. data/lib/gloo/objs/web_svr/partial.rb +7 -6
  30. data/lib/gloo/objs/web_svr/svr.rb +267 -14
  31. data/lib/gloo/persist/disc_mech.rb +8 -0
  32. data/lib/gloo/verbs/version.rb +1 -1
  33. data/lib/gloo/web_svr/asset.rb +34 -13
  34. data/lib/gloo/web_svr/config.rb +1 -1
  35. data/lib/gloo/web_svr/embedded_renderer.rb +1 -1
  36. data/lib/gloo/web_svr/handler.rb +6 -4
  37. data/lib/gloo/web_svr/request.rb +34 -8
  38. data/lib/gloo/web_svr/response.rb +14 -2
  39. data/lib/gloo/web_svr/response_code.rb +1 -1
  40. data/lib/gloo/web_svr/routing/router.rb +1 -2
  41. data/lib/gloo/web_svr/routing/show_routes.rb +4 -7
  42. data/lib/gloo/web_svr/server.rb +1 -1
  43. data/lib/gloo/web_svr/session.rb +161 -0
  44. data/lib/gloo/web_svr/table_renderer.rb +1 -1
  45. metadata +58 -27
  46. data/lib/gloo/objs/cli/banner.rb +0 -118
  47. data/lib/gloo/objs/cli/bar.rb +0 -133
  48. data/lib/gloo/objs/cli/pastel.rb +0 -104
  49. data/lib/gloo/objs/snd/play.rb +0 -48
  50. data/lib/gloo/objs/snd/say.rb +0 -98
@@ -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
 
@@ -56,23 +56,14 @@ module Gloo
56
56
  # Get a list of message names that this object receives.
57
57
  #
58
58
  def self.messages
59
- return super + %w[show page render]
59
+ return super + %w[show render update_asset_path]
60
60
  end
61
61
 
62
62
  #
63
63
  # Show the markdown data in the terminal.
64
64
  #
65
65
  def msg_show
66
- @engine.platform.show( self.value, true, false )
67
- end
68
-
69
- #
70
- # Show the markdown data in the terminal, paginated.
71
- #
72
- def msg_page
73
- return unless self.value
74
-
75
- @engine.platform.show( self.value, true, true )
66
+ @engine.platform.show self.value
76
67
  end
77
68
 
78
69
  #
@@ -94,6 +85,29 @@ module Gloo
94
85
  @engine.heap.it.set_to html
95
86
  end
96
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
+
97
111
  # ---------------------------------------------------------------------
98
112
  # Static Helpers
99
113
  # ---------------------------------------------------------------------
@@ -113,25 +127,6 @@ module Gloo
113
127
  return markdown.render( md )
114
128
  end
115
129
 
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
-
135
130
  end
136
131
  end
137
132
  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
 
@@ -3,7 +3,6 @@
3
3
  #
4
4
  # The result of a SQL database query.
5
5
  #
6
- require 'tty-table'
7
6
 
8
7
  module Gloo
9
8
  module Objs
@@ -15,16 +14,16 @@ module Gloo
15
14
  PARAMS = 'params'.freeze
16
15
 
17
16
 
18
-
19
17
  # ---------------------------------------------------------------------
20
18
  # Set up the Result
21
19
  # ---------------------------------------------------------------------
22
20
 
23
21
  #
24
22
  # Create the Result object
25
- def initialize( heads, data )
23
+ def initialize( heads, data, engine=nil )
26
24
  @heads = heads
27
25
  @data = data
26
+ @engine = engine
28
27
  end
29
28
 
30
29
 
@@ -72,18 +71,14 @@ module Gloo
72
71
  @heads.each_with_index do |h, i|
73
72
  arr << [ h, row[i] ]
74
73
  end
75
- table = TTY::Table.new( [ 'Field', 'Value' ], arr )
76
- renderer = TTY::Table::Renderer::Unicode.new( table, padding: [0,1] )
77
- puts renderer.render
74
+ @engine.platform.table.show [ 'Field', 'Value' ], arr
78
75
  end
79
76
 
80
77
  #
81
78
  # Show multiple rows in a table view.
82
79
  #
83
80
  def show_rows
84
- table = TTY::Table.new( @heads, @data )
85
- renderer = TTY::Table::Renderer::Unicode.new( table, padding: [0,1] )
86
- puts renderer.render
81
+ @engine.platform.table.show @heads, @data
87
82
  end
88
83
 
89
84
  # ---------------------------------------------------------------------
@@ -159,7 +159,7 @@ module Gloo
159
159
  #
160
160
  def msg_show
161
161
  title = self.value
162
- @engine.platform.show_table headers, data, title
162
+ @engine.platform.table.show headers, data[1], title
163
163
  end
164
164
 
165
165
  def msg_render
@@ -0,0 +1,193 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2024 Eric Crane. All rights reserved.
3
+ #
4
+ # A object to encrypt and decrypt text.
5
+ #
6
+ require 'openssl'
7
+ require 'base64'
8
+
9
+ module Gloo
10
+ module Objs
11
+ class Cipher < Gloo::Core::Obj
12
+
13
+ KEYWORD = 'cipher'.freeze
14
+ KEYWORD_SHORT = 'crypt'.freeze
15
+
16
+ CIPHER_TYPE = 'AES-256-CBC'.freeze
17
+ KEY = 'key'.freeze
18
+ INIT_VECTOR = 'init_vector'.freeze
19
+ DATA = 'data'.freeze
20
+
21
+ #
22
+ # The name of the object type.
23
+ #
24
+ def self.typename
25
+ return KEYWORD
26
+ end
27
+
28
+ #
29
+ # The short name of the object type.
30
+ #
31
+ def self.short_typename
32
+ return KEYWORD_SHORT
33
+ end
34
+
35
+ #
36
+ # Get the Cipher Key.
37
+ # Returns nil if there is none.
38
+ #
39
+ def key
40
+ o = find_child KEY
41
+ return o&.value
42
+ end
43
+
44
+ #
45
+ # Get the Initialization Vector.
46
+ # Returns nil if there is none.
47
+ #
48
+ def init_vector
49
+ o = find_child INIT_VECTOR
50
+ return o&.value
51
+ end
52
+
53
+ #
54
+ # Get the data value of the object.
55
+ # This might be encrypted or decrypted based on
56
+ # what action was last taken.
57
+ #
58
+ def data
59
+ o = find_child DATA
60
+ return o&.value
61
+ end
62
+
63
+ #
64
+ # Update the key value.
65
+ #
66
+ def update_key( new_val )
67
+ o = find_child KEY
68
+ return unless o
69
+
70
+ o.set_value new_val
71
+ end
72
+
73
+ #
74
+ # Update the initialization vector value.
75
+ #
76
+ def update_init_vector( new_val )
77
+ o = find_child INIT_VECTOR
78
+ return unless o
79
+
80
+ o.set_value new_val
81
+ end
82
+
83
+ #
84
+ # Update the data value of the object.
85
+ #
86
+ def update_data( new_val )
87
+ o = find_child DATA
88
+ return unless o
89
+
90
+ o.set_value new_val
91
+ end
92
+
93
+
94
+ # ---------------------------------------------------------------------
95
+ # Children
96
+ # ---------------------------------------------------------------------
97
+
98
+ #
99
+ # Does this object have children to add when an object
100
+ # is created in interactive mode?
101
+ # This does not apply during obj load, etc.
102
+ #
103
+ def add_children_on_create?
104
+ return true
105
+ end
106
+
107
+ #
108
+ # Add children to this object.
109
+ # This is used by containers to add children needed
110
+ # for default configurations.
111
+ #
112
+ def add_default_children
113
+ fac = @engine.factory
114
+ fac.create_string KEY, '', self
115
+ fac.create_string INIT_VECTOR, '', self
116
+ fac.create_string DATA, '', self
117
+ end
118
+
119
+ # ---------------------------------------------------------------------
120
+ # Messages
121
+ # ---------------------------------------------------------------------
122
+
123
+ #
124
+ # Get a list of message names that this object receives.
125
+ #
126
+ def self.messages
127
+ return super + %w[generate_keys encrypt decrypt]
128
+ end
129
+
130
+ #
131
+ # Generate random Key and Initialization Vector.
132
+ #
133
+ def msg_generate_keys
134
+ cipher = OpenSSL::Cipher.new( CIPHER_TYPE )
135
+
136
+ key = cipher.random_key
137
+ key = Base64.encode64 key
138
+ update_key key
139
+
140
+ iv = update_init_vector cipher.random_iv
141
+ iv = Base64.encode64 iv
142
+ update_init_vector iv
143
+ end
144
+
145
+ #
146
+ # Decrypt the encrypted child object.
147
+ #
148
+ def msg_decrypt
149
+ update_data Cipher.decrypt( data, key, init_vector )
150
+ end
151
+
152
+ #
153
+ # Encrypt the decrypted child object.
154
+ #
155
+ def msg_encrypt
156
+ update_data Cipher.encrypt( data, key, init_vector )
157
+ end
158
+
159
+ # ---------------------------------------------------------------------
160
+ # Static Methods
161
+ # ---------------------------------------------------------------------
162
+
163
+ #
164
+ # Encrypt the data using the key and initialization vector.
165
+ # Returns the encrypted data (base64 encoded).
166
+ #
167
+ def self.encrypt( data, key, iv )
168
+ cipher = OpenSSL::Cipher.new( CIPHER_TYPE )
169
+ cipher.encrypt
170
+ cipher.key = Base64.decode64( key )
171
+ cipher.iv = Base64.decode64( iv ) unless iv.blank?
172
+
173
+ encrypted_msg = cipher.update( data ) + cipher.final
174
+ return Base64.encode64( encrypted_msg )
175
+ end
176
+
177
+ #
178
+ # Decrypt the data using the key and initialization vector.
179
+ # Returns the decrypted data.
180
+ #
181
+ def self.decrypt( data, key, iv )
182
+ cipher = OpenSSL::Cipher.new( CIPHER_TYPE )
183
+ data = Base64.decode64( data )
184
+ cipher.decrypt
185
+ cipher.key = Base64.decode64( key )
186
+ cipher.iv = Base64.decode64( iv ) unless iv.blank?
187
+
188
+ return cipher.update( data ) + cipher.final
189
+ end
190
+
191
+ end
192
+ end
193
+ end