gloo-db 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8103ee6d8fd76866fec307a477c10bc663ab8a4ec22630d7c6a7ac84a504ffc7
4
+ data.tar.gz: de20b8674829888238469725e8243ab78427fb1019ae6769546c38214bf3a094
5
+ SHA512:
6
+ metadata.gz: c358924ba730e80f0ee3102db7edae06e4efd9e20a67029fd2687dfe170583ee3229857c90b897d447cf276f6a5d45b1f5b1e1c756058be855968ef9708dd90f
7
+ data.tar.gz: 62bed0c0675a00d55f28e830795a65f47653534002b9a1cfe87d456b395e9ecdd0912b0c8d20d76f939d523036f2bdcb98ce3cf99a05ab3133e6ffee110a7599
data/lib/gloo-db.rb ADDED
@@ -0,0 +1,23 @@
1
+ #
2
+ # Shim to allow `require 'gloo-mysql'`
3
+ #
4
+ # This file is loaded when someone does `require 'gloo-mysql'`
5
+ #
6
+ require 'query'
7
+ require 'query_result'
8
+ require 'table'
9
+
10
+ #
11
+ # Registers the extension.
12
+ #
13
+ class DbInit < Gloo::Plugin::Base
14
+
15
+ #
16
+ # Register verbs and objects.
17
+ #
18
+ def register( callback )
19
+ callback.register_obj( Query )
20
+ callback.register_obj( Table )
21
+ end
22
+
23
+ end
data/lib/query.rb ADDED
@@ -0,0 +1,265 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2020 Eric Crane. All rights reserved.
3
+ #
4
+ # A SQL database query.
5
+ # Relies on a database connection object.
6
+ #
7
+
8
+ class Query < Gloo::Core::Obj
9
+
10
+ KEYWORD = 'query'.freeze
11
+ KEYWORD_SHORT = 'sql'.freeze
12
+
13
+ DB = 'database'.freeze
14
+ SQL = 'sql'.freeze
15
+ RESULT = 'result'.freeze
16
+ PARAMS = 'params'.freeze
17
+ SIMPLE_LIST = 'simple_list'.freeze
18
+
19
+ DB_MISSING_ERR = 'The database connection is missing!'.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 result container if it exists.
37
+ #
38
+ def get_result_can
39
+ result_can = find_child RESULT
40
+ result_can = Gloo::Objs::Alias.resolve_alias( @engine, result_can )
41
+ return result_can
42
+ end
43
+
44
+ # ---------------------------------------------------------------------
45
+ # Children
46
+ # ---------------------------------------------------------------------
47
+
48
+ #
49
+ # Does this object have children to add when an object
50
+ # is created in interactive mode?
51
+ # This does not apply during obj load, etc.
52
+ #
53
+ def add_children_on_create?
54
+ return true
55
+ end
56
+
57
+ #
58
+ # Add children to this object.
59
+ # This is used by containers to add children needed
60
+ # for default configurations.
61
+ #
62
+ def add_default_children
63
+ fac = @engine.factory
64
+ fac.create_alias DB, nil, self
65
+ fac.create_string SQL, nil, self
66
+ fac.create_can RESULT, self
67
+ end
68
+
69
+ # ---------------------------------------------------------------------
70
+ # Messages
71
+ # ---------------------------------------------------------------------
72
+
73
+ #
74
+ # Get a list of message names that this object receives.
75
+ #
76
+ def self.messages
77
+ return super + [ 'run' ]
78
+ end
79
+
80
+ #
81
+ # Run the query and process the results.
82
+ #
83
+ def msg_run
84
+ db = db_obj
85
+ return unless db
86
+
87
+ begin
88
+ clear_results
89
+
90
+ log_query sql_value, param_array
91
+ result = db.query( sql_value, param_array )
92
+ process_result( result, db )
93
+ rescue => e
94
+ @engine.log_exception e
95
+ return
96
+ end
97
+ end
98
+
99
+ #
100
+ # Run the query and return the results.
101
+ #
102
+ def run_query
103
+ db = db_obj
104
+ return unless db
105
+
106
+ begin
107
+ log_query sql_value, param_array
108
+
109
+ db_start = ::Time.now
110
+ result = db.query( sql_value, param_array )
111
+ db_done = ::Time.now
112
+ elapsed = ( ( db_done - db_start ) * 1000.0 ).round(2)
113
+
114
+ app = @engine.running_app
115
+ app.add_db_time elapsed if app
116
+ return result
117
+ rescue => e
118
+ @engine.log_exception e
119
+ return
120
+ end
121
+ end
122
+
123
+ #
124
+ # Write the query to the log.
125
+ #
126
+ def log_query sql, params
127
+ @engine.log.info "SQL PARAMS: #{params}" if params
128
+ @engine.log.info "SQL: #{sql}"
129
+ end
130
+
131
+
132
+ # ---------------------------------------------------------------------
133
+ # Output as simple list
134
+ # ---------------------------------------------------------------------
135
+
136
+ #
137
+ # Should the output be put in a simple list?
138
+ #
139
+ def simple_list?
140
+ o = find_child SIMPLE_LIST
141
+ return false unless o
142
+
143
+ return o.value
144
+ end
145
+
146
+
147
+ # ---------------------------------------------------------------------
148
+ # Private functions
149
+ # ---------------------------------------------------------------------
150
+
151
+ private
152
+
153
+ #
154
+ # Get the database connection.
155
+ #
156
+ def db_obj
157
+ o = find_child DB
158
+
159
+ unless o
160
+ @engine.err DB_MISSING_ERR
161
+ return nil
162
+ end
163
+
164
+ return Gloo::Objs::Alias.resolve_alias( @engine, o )
165
+ end
166
+
167
+ #
168
+ # Get the SQL from the child object.
169
+ # Returns nil if there is none.
170
+ #
171
+ def sql_value
172
+ o = find_child SQL
173
+ return nil unless o
174
+
175
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
176
+ return o.value
177
+ end
178
+
179
+ #
180
+ # Do something with the result of the SQL Query call.
181
+ # If there's a result container, we'll create objects in it.
182
+ # If not, we'll just show the output in the console.
183
+ #
184
+ def process_result( result, db )
185
+ return if result.nil?
186
+
187
+ query_result = db.get_query_result( result )
188
+ return unless query_result
189
+ return unless query_result.has_data_to_show?
190
+
191
+ result_can = get_result_can
192
+
193
+ if result_can
194
+ if simple_list?
195
+ query_result.update_result_container_simple result_can
196
+ else
197
+ query_result.update_result_container result_can
198
+ end
199
+ else
200
+ query_result.show
201
+ end
202
+ end
203
+
204
+ #
205
+ # Get the array of parameters.
206
+ # If there is no PARAM container of if it is empty,
207
+ # we'll return a nil value.
208
+ #
209
+ def param_array
210
+ o = find_child PARAMS
211
+ return nil unless o
212
+
213
+ return nil if o.child_count.zero?
214
+
215
+ params = []
216
+ o.children.each do |p|
217
+ p = Gloo::Objs::Alias.resolve_alias( @engine, p )
218
+ params << p.sql_value
219
+ end
220
+
221
+ return params
222
+ end
223
+
224
+ #
225
+ # Clear out results container.
226
+ # Prevents data from the last use being used in this
227
+ # one if no data was found.
228
+ #
229
+ def clear_results
230
+ result_can = get_result_can
231
+ return unless result_can
232
+ return unless result_can.child_count.positive?
233
+
234
+ if result_is_values?
235
+ clear_values
236
+ else
237
+ get_result_can.delete_children
238
+ end
239
+ end
240
+
241
+ #
242
+ # Is the result container a list of values?
243
+ # If not it is a list of rows.
244
+ #
245
+ def result_is_values?
246
+ first_child = get_result_can.children.first
247
+
248
+ if first_child && first_child&.is_container?
249
+ return false
250
+ end
251
+
252
+ return true
253
+ end
254
+
255
+ #
256
+ # Clear out the values in the results container.
257
+ #
258
+ def clear_values
259
+ get_result_can.children.each do |c|
260
+ c = Gloo::Objs::Alias.resolve_alias( @engine, c )
261
+ c.value = nil
262
+ end
263
+ end
264
+
265
+ end
@@ -0,0 +1,152 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2022 Eric Crane. All rights reserved.
3
+ #
4
+ # The result of a SQL database query.
5
+ #
6
+
7
+ class QueryResult
8
+
9
+ DB = 'database'.freeze
10
+ SQL = 'sql'.freeze
11
+ RESULT = 'result'.freeze
12
+ PARAMS = 'params'.freeze
13
+
14
+
15
+ # ---------------------------------------------------------------------
16
+ # Set up the Result
17
+ # ---------------------------------------------------------------------
18
+
19
+ #
20
+ # Create the Result object
21
+ def initialize( heads, data, engine=nil )
22
+ @heads = heads
23
+ @data = data
24
+ @engine = engine
25
+ end
26
+
27
+
28
+ # ---------------------------------------------------------------------
29
+ # Helper Functions
30
+ # ---------------------------------------------------------------------
31
+
32
+ #
33
+ # Does the data contain a single row?
34
+ # OR, if the result is empty, return false.
35
+ #
36
+ def single_row_result?
37
+ if @result_can && ( @result_can.child_count == 0 )
38
+ return false
39
+ end
40
+
41
+ return @data.count == 1
42
+ end
43
+
44
+ #
45
+ # Does this query result have data to show?
46
+ #
47
+ def has_data_to_show?
48
+ return false unless @heads
49
+ return false unless @data
50
+ return false if @heads.count == 0
51
+ return false if @data.count == 0
52
+
53
+ return true
54
+ end
55
+
56
+
57
+ # ---------------------------------------------------------------------
58
+ # Show Results
59
+ # ---------------------------------------------------------------------
60
+
61
+ #
62
+ # Show the result of the query
63
+ #
64
+ def show
65
+ single_row_result? ? show_single_row : show_rows
66
+ end
67
+
68
+ #
69
+ # Show a single row in a vertical, form style view.
70
+ #
71
+ def show_single_row
72
+ arr = []
73
+ row = @data[0]
74
+ @heads.each_with_index do |h, i|
75
+ arr << [ h, row[i] ]
76
+ end
77
+ @engine.platform.table.show [ 'Field', 'Value' ], arr
78
+ end
79
+
80
+ #
81
+ # Show multiple rows in a table view.
82
+ #
83
+ def show_rows
84
+ @engine.platform.table.show @heads, @data
85
+ end
86
+
87
+ # ---------------------------------------------------------------------
88
+ # Update results in object(s)
89
+ # ---------------------------------------------------------------------
90
+
91
+ #
92
+ # Update the result container with the data from the query.
93
+ #
94
+ def update_result_container( in_can )
95
+ @result_can = in_can
96
+ single_row_result? ? update_single_row : update_rows
97
+ end
98
+
99
+ #
100
+ # Update the result container with the data from the query.
101
+ #
102
+ def update_result_container_simple( in_can )
103
+ @result_can = in_can
104
+ single_row_result? ? update_single_row : update_rows_simple
105
+ end
106
+
107
+ #
108
+ # The result has a single row.
109
+ # Map values from the result set to objects that are present.
110
+ #
111
+ def update_single_row
112
+ row = @data[0]
113
+ @heads.each_with_index do |h, i|
114
+ child = @result_can.find_child h
115
+ child = Gloo::Objs::Alias.resolve_alias( @engine, child )
116
+ child.set_value row[i] if child
117
+ end
118
+ end
119
+
120
+ #
121
+ # Put all rows in the result object.
122
+ #
123
+ def update_rows
124
+ @data.each_with_index do |row, i|
125
+ can = @result_can.find_add_child( i.to_s, 'can' )
126
+ row.each_with_index do |v, i|
127
+ o = can.find_add_child( @heads[i], 'untyped' )
128
+ o.set_value v
129
+ end
130
+ end
131
+ end
132
+
133
+ #
134
+ # Put all rows in the result object.
135
+ #
136
+ def update_rows_simple
137
+ @data.each do |row|
138
+ row.each do |val|
139
+ o = @result_can.find_add_child( val, 'untyped' )
140
+ o.set_value val
141
+ end
142
+ end
143
+ end
144
+
145
+
146
+ # ---------------------------------------------------------------------
147
+ # Private functions
148
+ # ---------------------------------------------------------------------
149
+
150
+ private
151
+
152
+ end
data/lib/table.rb ADDED
@@ -0,0 +1,263 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2020 Eric Crane. All rights reserved.
3
+ #
4
+ # A data table.
5
+ # The table container headers and data.
6
+ #
7
+
8
+ class Table < Gloo::Core::Obj
9
+
10
+ KEYWORD = 'table'.freeze
11
+ KEYWORD_SHORT = 'tbl'.freeze
12
+ HEADERS = 'headers'.freeze
13
+ DATA = 'data'.freeze
14
+ CELLS = 'cells'.freeze
15
+ STYLES = 'styles'.freeze
16
+ ALWAYS_ROWS = 'always_rows'.freeze
17
+
18
+ #
19
+ # The name of the object type.
20
+ #
21
+ def self.typename
22
+ return KEYWORD
23
+ end
24
+
25
+ #
26
+ # The short name of the object type.
27
+ #
28
+ def self.short_typename
29
+ return KEYWORD_SHORT
30
+ end
31
+
32
+ #
33
+ # Get the list of headers.
34
+ # Returns nil if there is none.
35
+ #
36
+ def headers
37
+ o = find_child HEADERS
38
+ return [] unless o
39
+
40
+ return o.children.map( &:value )
41
+ end
42
+
43
+ #
44
+ # Always show rows, even if only 1 row is found.
45
+ #
46
+ def always_rows
47
+ o = find_child ALWAYS_ROWS
48
+
49
+ return false unless o
50
+ return o.value
51
+ end
52
+
53
+ #
54
+ # Get the list of column names.
55
+ # Returns nil if there is none.
56
+ #
57
+ def columns
58
+ o = find_child HEADERS
59
+ return [] unless o
60
+
61
+ return o.children.map( &:name )
62
+ end
63
+
64
+ #
65
+ # Get the list of data elements.
66
+ #
67
+ def data
68
+ o = find_child DATA
69
+ return [] unless o
70
+
71
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
72
+ return [] unless o
73
+
74
+ if o.is_a? Query
75
+ @engine.log.debug "Table getting data from query."
76
+ begin
77
+ result = o.run_query
78
+ return result
79
+ rescue => e
80
+ @engine.log_exception e
81
+ return nil
82
+ end
83
+ else
84
+ cols = self.columns
85
+
86
+ if o.children&.first.children.empty?
87
+ # It is a simgle row table.
88
+ rows = [ cols.map { |h| o.find_child( h )&.value } ]
89
+ else
90
+ rows = o.children.map do |e|
91
+ cols.map { |h| e.find_child( h )&.value }
92
+ end
93
+ end
94
+
95
+ return [ cols, rows ]
96
+ end
97
+ end
98
+
99
+ #
100
+ # Get the styles for the table, if any.
101
+ #
102
+ def styles
103
+ style_h = {}
104
+ o = find_child STYLES
105
+ return style_h unless o
106
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
107
+
108
+ o.children.each do |c|
109
+ style_h[ c.name ] = c.value
110
+ end
111
+
112
+ return style_h
113
+ end
114
+
115
+ #
116
+ # Get cell renderer hash keyed by column name.
117
+ #
118
+ def cell_renderers
119
+ h = {}
120
+ o = find_child CELLS
121
+ return h unless o
122
+
123
+ o.children.each do |c|
124
+ h[ c.name ] = c.value
125
+ end
126
+
127
+ return h
128
+ end
129
+
130
+
131
+ # ---------------------------------------------------------------------
132
+ # Children
133
+ # ---------------------------------------------------------------------
134
+
135
+ #
136
+ # Does this object have children to add when an object
137
+ # is created in interactive mode?
138
+ # This does not apply during obj load, etc.
139
+ #
140
+ def add_children_on_create?
141
+ return true
142
+ end
143
+
144
+ #
145
+ # Add children to this object.
146
+ # This is used by containers to add children needed
147
+ # for default configurations.
148
+ #
149
+ def add_default_children
150
+ fac = @engine.factory
151
+ fac.create_can HEADERS, self
152
+ fac.create_can DATA, self
153
+ end
154
+
155
+
156
+ # ---------------------------------------------------------------------
157
+ # Messages
158
+ # ---------------------------------------------------------------------
159
+
160
+ #
161
+ # Get a list of message names that this object receives.
162
+ #
163
+ def self.messages
164
+ return super + %w[show render]
165
+ end
166
+
167
+ #
168
+ # Show the table in the CLI.
169
+ #
170
+ def msg_show
171
+ title = self.value
172
+ @engine.platform.table.show headers, data[1], title
173
+ end
174
+
175
+ def msg_render
176
+ return render
177
+ end
178
+
179
+
180
+ # ---------------------------------------------------------------------
181
+ # Render
182
+ # ---------------------------------------------------------------------
183
+
184
+ #
185
+ # Render the table.
186
+ # The render_ƒ is 'render_html', 'render_text', 'render_json', etc.
187
+ #
188
+ def render render_ƒ
189
+ begin
190
+ result = self.data
191
+ head = self.headers
192
+ head = result[0] if head.empty?
193
+ rows = result[1]
194
+
195
+ columns = build_columns result[0]
196
+
197
+ params = {
198
+ head: head,
199
+ cols: result[0],
200
+ columns: columns,
201
+ rows: rows,
202
+ styles: self.styles,
203
+ cell_renderers: self.cell_renderers
204
+ }
205
+
206
+ if self.always_rows
207
+ params[ :always_rows ] = true
208
+ end
209
+
210
+ helper = Gloo::WebSvr::TableRenderer.new( @engine )
211
+ return helper.data_to_table params
212
+ rescue => e
213
+ @engine.log_exception e
214
+ return nil
215
+ end
216
+ end
217
+
218
+ #
219
+ # Build the column list based on the result data and
220
+ # the headers defined in the table object.
221
+ #
222
+ def build_columns result_data
223
+ head_children = find_child HEADERS
224
+ cell_renderers = find_child CELLS
225
+
226
+ columns = []
227
+ return columns unless result_data
228
+
229
+ result_data.each_with_index do |c,index|
230
+ visible = true
231
+ name = c
232
+ title = c
233
+ display_index = index
234
+
235
+ if head_children
236
+ child = head_children.find_child c
237
+ if child
238
+ title = child.value
239
+ display_index = head_children.child_index( c )
240
+ else
241
+ visible = false
242
+ end
243
+ end
244
+
245
+ cell_renderer = nil
246
+ if cell_renderers
247
+ this_cr = cell_renderers.find_child( c )
248
+ cell_renderer = this_cr.value if this_cr
249
+ end
250
+
251
+ columns << {
252
+ name: name,
253
+ title: title,
254
+ visible: visible,
255
+ data_index: index,
256
+ display_index: display_index,
257
+ cell_renderer: cell_renderer
258
+ }
259
+ end
260
+ return columns.sort_by { |hsh| hsh[ :display_index ] }
261
+ end
262
+
263
+ end
metadata ADDED
@@ -0,0 +1,48 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gloo-db
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.0'
5
+ platform: ruby
6
+ authors:
7
+ - Eric Crane
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-02-06 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Adds database support to Gloo.
14
+ email:
15
+ - eric.crane@mac.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/gloo-db.rb
21
+ - lib/query.rb
22
+ - lib/query_result.rb
23
+ - lib/table.rb
24
+ homepage: https://gloo.ecrane.us/
25
+ licenses:
26
+ - MIT
27
+ metadata:
28
+ gloo.type: core-library
29
+ post_install_message:
30
+ rdoc_options: []
31
+ require_paths:
32
+ - lib
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ requirements: []
44
+ rubygems_version: 3.5.16
45
+ signing_key:
46
+ specification_version: 4
47
+ summary: Gloo core library. Database support.
48
+ test_files: []