gloo-db 1.1 → 1.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 778e0ec24cdb55d18047a73c5d5b125b1c9e079b378b7e8611a90890cf66ba8b
4
- data.tar.gz: 30d8e53743f29cd3aa24f58e93fcd475c29ad8150f3c6377500a8c48eaae0c4e
3
+ metadata.gz: 3ad932a978b3c8ba5733dcd79d4188748ccbeae4077da55dfe096ced7bcc5fd0
4
+ data.tar.gz: 878fed77aa72d68a826043ac53026753b8cd50817efcf428d2df13b34998b729
5
5
  SHA512:
6
- metadata.gz: 8922b35f3b17002743dfd0d8366ccda1ebff342e78adcd5a1d60cb37cd3723a16ff4f0c6db4227f342a3067c57ee97aa1efc1a94f447c906909a8eeb6e45f1b2
7
- data.tar.gz: 10e12ef81de0fa63e309bff21257c3f9f66ffb66dc62f9b4001d2cc275ef402676aac3e88806a0da9a8a069561bc75bdc7f30349023e5aeaf30b1281c89f8b01
6
+ metadata.gz: 7fd86d6a95af8d73e481d8f6a589fb7bb7e4adf2708351e434fd2c28a677f3f804d01c2c75cd5f43d427829bccc8d1afcce455956cff2db692e2ddb97818f4c6
7
+ data.tar.gz: 6d166a12139e3e9cf9796c9e9cf49ff05df66ee13f17c80c3daebf25aebc7b48502d33346e65b069763de6bb617baa3b5dfc229db77a9396e37e72e403e80c45
data/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # gloo-db
2
+
3
+ A core gloo library for database operations.
4
+
5
+ Use this library and one of the database-specific driver libraries (`gloo-pg`, `gloo-mysql`, `gloo-sqlite`) to interact with databases.
6
+
7
+ ## Usage
8
+
9
+ Load this library, plus a driver, in a gloo script:
10
+
11
+ ```gloo
12
+ > load lib db
13
+ > load lib sqlite
14
+ ```
15
+
16
+ ## Objects
17
+
18
+ - `query` — a SQL query (SELECT, INSERT, UPDATE, or other statement)
19
+ - `table` — a data table
20
+
21
+ ## Full Reference
22
+
23
+ Every object's children and messages are documented in-app once the library is loaded — e.g. `help> object query`.
@@ -0,0 +1,60 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2026 Eric Crane. All rights reserved.
3
+ #
4
+ # Shared behaviour for database connection objects (gloo-sqlite,
5
+ # gloo-mysql, gloo-pg). A connection class includes this module so
6
+ # every driver reports problems the same way:
7
+ #
8
+ # - Database errors (bad SQL, a missing table, a lost connection)
9
+ # come out of the driver's query as exceptions; Query reports them
10
+ # as one readable error. database_errors says which exception
11
+ # classes those are.
12
+ # - Setup problems the driver can see (eg. no database name) are
13
+ # reported with setup_err, and the query returns no result.
14
+ # - verify puts true in it, or reports a readable error and puts
15
+ # false (verify_connection).
16
+ #
17
+ module DbConnection
18
+
19
+ #
20
+ # The exception classes that are database errors for this driver.
21
+ # Query reports these as 'Query failed: …'; anything else is an
22
+ # unexpected error. Drivers override this with their library's
23
+ # error classes.
24
+ #
25
+ def database_errors
26
+ return [ StandardError ]
27
+ end
28
+
29
+ #
30
+ # Verify the connection: run the block, which connects (and raises
31
+ # if it can't). It is true when it connects, false otherwise, with
32
+ # a readable error and no backtrace.
33
+ #
34
+ def verify_connection
35
+ yield
36
+ @engine.heap.it.set_to true
37
+ return true
38
+ rescue *database_errors, SystemCallError => e
39
+ @engine.err "Could not connect to #{connection_label}: #{e.message}"
40
+ @engine.heap.it.set_to false
41
+ return false
42
+ end
43
+
44
+ #
45
+ # Report a setup problem (eg. no database name) and return nil,
46
+ # meaning no result.
47
+ #
48
+ def setup_err( msg )
49
+ @engine.err msg
50
+ return nil
51
+ end
52
+
53
+ #
54
+ # How the connection is named in messages, eg. sqlite 'db'.
55
+ #
56
+ def connection_label
57
+ return "#{self.class.typename} '#{name}'"
58
+ end
59
+
60
+ end
data/lib/gloo-db.rb CHANGED
@@ -1,8 +1,9 @@
1
- #
2
- # Shim to allow `require 'gloo-mysql'`
3
- #
1
+ #
2
+ # Shim to allow `require 'gloo-db'`
3
+ #
4
4
  # This file is loaded when someone does `require 'gloo-mysql'`
5
5
  #
6
+ require 'db_connection'
6
7
  require 'query'
7
8
  require 'query_result'
8
9
  require 'table'
data/lib/query.rb CHANGED
@@ -17,6 +17,7 @@ class Query < Gloo::Core::Obj
17
17
  SIMPLE_LIST = 'simple_list'.freeze
18
18
 
19
19
  DB_MISSING_ERR = 'The database connection is missing!'.freeze
20
+ QUERY_FAILED = 'Query failed: '.freeze
20
21
 
21
22
  #
22
23
  # The name of the object type.
@@ -79,45 +80,43 @@ class Query < Gloo::Core::Obj
79
80
 
80
81
  #
81
82
  # Run the query and process the results.
83
+ # It is true when the query ran, false if it failed.
82
84
  #
83
85
  def msg_run
84
86
  db = db_obj
85
- return unless db
87
+ return @engine.heap.it.set_to( false ) unless db
86
88
 
87
- begin
88
- clear_results
89
+ sql = sql_value
90
+ return @engine.heap.it.set_to( false ) unless sql
89
91
 
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
92
+ # Clear first, so a failed query doesn't leave the last results.
93
+ clear_results
94
+ result = execute( db, sql )
95
+ return @engine.heap.it.set_to( false ) unless result
96
+
97
+ process_result( result, db )
98
+ @engine.heap.it.set_to true
97
99
  end
98
100
 
99
101
  #
100
- # Run the query and return the results.
102
+ # Run the query and return the results (used by table).
103
+ # Returns nil if the query failed; the failure is reported.
101
104
  #
102
105
  def run_query
103
106
  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
107
+ return nil unless db
108
+
109
+ sql = sql_value
110
+ return nil unless sql
111
+
112
+ db_start = ::Time.now
113
+ result = execute( db, sql )
114
+ db_done = ::Time.now
115
+ elapsed = ( ( db_done - db_start ) * 1000.0 ).round(2)
116
+
117
+ app = @engine.running_app
118
+ app.add_db_time elapsed if app
119
+ return result
121
120
  end
122
121
 
123
122
  #
@@ -151,29 +150,71 @@ class Query < Gloo::Core::Obj
151
150
  private
152
151
 
153
152
  #
154
- # Get the database connection.
153
+ # Run the SQL on the database connection. A database error (one of
154
+ # the driver's database_errors) is reported as one readable error,
155
+ # with no backtrace. Returns the result, or nil if the query failed
156
+ # or the driver reported a setup problem.
157
+ #
158
+ def execute( db, sql )
159
+ params = param_array
160
+ log_query sql, params
161
+ return db.query( sql, params )
162
+ rescue *database_errors_for( db ) => e
163
+ @engine.err "#{QUERY_FAILED}#{e.message}"
164
+ return nil
165
+ end
166
+
167
+ #
168
+ # The exception classes that are database errors for the driver.
169
+ # A driver that doesn't say (an older one) counts every
170
+ # StandardError.
171
+ #
172
+ def database_errors_for( db )
173
+ return db.database_errors if db.respond_to?( :database_errors )
174
+
175
+ return [ StandardError ]
176
+ end
177
+
178
+ #
179
+ # Get the database connection. Reports an error and returns nil if
180
+ # there's no database child, its alias points at nothing, or it
181
+ # isn't a database connection.
155
182
  #
156
183
  def db_obj
157
184
  o = find_child DB
158
185
 
159
- unless o
186
+ # No database child, or an alias that doesn't point anywhere yet.
187
+ if o.nil? || o.value.to_s.strip.empty?
160
188
  @engine.err DB_MISSING_ERR
161
189
  return nil
162
190
  end
163
191
 
164
- return Gloo::Objs::Alias.resolve_alias( @engine, o )
192
+ db = Gloo::Objs::Alias.resolve_alias( @engine, o )
193
+ unless db
194
+ @engine.err Gloo::Core::NotFound.object( o.value )
195
+ return nil
196
+ end
197
+
198
+ unless db.respond_to?( :query )
199
+ @engine.err "'#{o.value}' is not a database connection."
200
+ return nil
201
+ end
202
+
203
+ return db
165
204
  end
166
205
 
167
206
  #
168
207
  # Get the SQL from the child object.
169
- # Returns nil if there is none.
208
+ # Reports an error and returns nil if there is none.
170
209
  #
171
210
  def sql_value
172
211
  o = find_child SQL
173
- return nil unless o
174
-
175
212
  o = Gloo::Objs::Alias.resolve_alias( @engine, o )
176
- return o.value
213
+ sql = o&.value.to_s
214
+ return sql unless sql.strip.empty?
215
+
216
+ @engine.err "query '#{name}' has no SQL; put a SQL statement into #{name}.sql first."
217
+ return nil
177
218
  end
178
219
 
179
220
  #
@@ -262,4 +303,43 @@ class Query < Gloo::Core::Obj
262
303
  end
263
304
  end
264
305
 
306
+ # ---------------------------------------------------------------------
307
+ # Object Documentation
308
+ # ---------------------------------------------------------------------
309
+
310
+ #
311
+ # Get the object's documentation data.
312
+ #
313
+ def self.doc_data
314
+ {
315
+ :name => KEYWORD,
316
+ :shortcut => KEYWORD_SHORT,
317
+ :description => 'A SQL Query — a SELECT, INSERT, UPDATE or other ' \
318
+ 'SQL statement. The query requires a valid database connection.',
319
+ :children => [
320
+ 'database (alias) — Reference to the database we\'re going to query.',
321
+ 'sql (string) — The SQL query to execute.',
322
+ 'result (container) — Optional. The result of the query will be a container for each row, with an object for each column. If not present, results are displayed in the console instead.',
323
+ 'params (container) — Optional list of parameters for the query.'
324
+ ],
325
+ :messages => [
326
+ 'run — Run the query and get back the data. It is true when the query ran, false if it failed (with or without a result container).'
327
+ ],
328
+ :notes => 'Errors (and it is false): no database, a database ' \
329
+ 'alias that points at nothing or at something that is not a ' \
330
+ 'database connection, no SQL, and a database error such as bad ' \
331
+ "SQL or a missing table, reported as 'Query failed: ' and the " \
332
+ "database's message. A failed query clears the result container.",
333
+ :examples => <<~EXAMPLES.strip
334
+ sqlite [can] :
335
+ on_load [script] : run sqlite.sql
336
+ db [sqlite] :
337
+ database : test.db
338
+ sql [query] :
339
+ database [alias] : sqlite.db
340
+ sql : SELECT id, key, value FROM key_values
341
+ EXAMPLES
342
+ }
343
+ end
344
+
265
345
  end
data/lib/table.rb CHANGED
@@ -73,17 +73,15 @@ class Table < Gloo::Core::Obj
73
73
 
74
74
  if o.is_a? Query
75
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
76
+ # Nil if the query failed (Query has reported it).
77
+ return o.run_query
83
78
  else
84
79
  cols = self.columns
85
80
 
86
- if o.children&.first.children.empty?
81
+ # No rows yet.
82
+ return [ cols, [] ] if o.children.empty?
83
+
84
+ if o.children.first.children.empty?
87
85
  # It is a simgle row table.
88
86
  rows = [ cols.map { |h| o.find_child( h )&.value } ]
89
87
  else
@@ -166,14 +164,24 @@ class Table < Gloo::Core::Obj
166
164
 
167
165
  #
168
166
  # Show the table in the CLI.
167
+ # Shows nothing if its query failed (the query reports the error).
169
168
  #
170
169
  def msg_show
170
+ result = data
171
+ return unless result
172
+
171
173
  title = self.value
172
- @engine.platform.table.show headers, data[1], title
174
+ @engine.platform.table.show headers, result[1], title
173
175
  end
174
176
 
177
+ #
178
+ # Render the table as HTML, inside a web app.
179
+ # It has the HTML, or false if the table couldn't be rendered.
180
+ #
175
181
  def msg_render
176
- return render
182
+ html = render
183
+ @engine.heap.it.set_to( html.nil? ? false : html )
184
+ return html
177
185
  end
178
186
 
179
187
 
@@ -185,9 +193,19 @@ class Table < Gloo::Core::Obj
185
193
  # Render the table.
186
194
  # The render_ƒ is 'render_html', 'render_text', 'render_json', etc.
187
195
  #
188
- def render render_ƒ
196
+ def render render_ƒ = :render_html
197
+ # Outside a web app (or without gloo-web) there's no renderer.
198
+ helper = table_renderer
199
+ unless helper
200
+ @engine.err "table '#{name}' can only render inside a web app (gloo-web); use show to see it in the console."
201
+ return nil
202
+ end
203
+
189
204
  begin
190
205
  result = self.data
206
+ # The query failed and has reported it.
207
+ return nil unless result
208
+
191
209
  head = self.headers
192
210
  head = result[0] if head.empty?
193
211
  rows = result[1]
@@ -207,20 +225,24 @@ class Table < Gloo::Core::Obj
207
225
  params[ :always_rows ] = true
208
226
  end
209
227
 
210
- # helper = Gloo::WebSvr::TableRenderer.new( @engine )
211
- helper = @engine.running_app&.create_table_renderer
212
- if helper
213
- return helper.data_to_table params
214
- else
215
- @engine.log.error "Table renderer not found."
216
- return nil
217
- end
228
+ return helper.data_to_table params
218
229
  rescue => e
219
230
  @engine.log_exception e
220
231
  return nil
221
232
  end
222
233
  end
223
234
 
235
+ #
236
+ # The web app's table renderer, or nil outside a web app or when
237
+ # gloo-web isn't loaded.
238
+ #
239
+ def table_renderer
240
+ return nil unless @engine.running_app
241
+ return nil unless defined?( WebSvr::TableRenderer )
242
+
243
+ return @engine.running_app.create_table_renderer
244
+ end
245
+
224
246
  #
225
247
  # Build the column list based on the result data and
226
248
  # the headers defined in the table object.
@@ -266,4 +288,55 @@ class Table < Gloo::Core::Obj
266
288
  return columns.sort_by { |hsh| hsh[ :display_index ] }
267
289
  end
268
290
 
291
+ # ---------------------------------------------------------------------
292
+ # Object Documentation
293
+ # ---------------------------------------------------------------------
294
+
295
+ #
296
+ # Get the object's documentation data.
297
+ #
298
+ def self.doc_data
299
+ {
300
+ :name => KEYWORD,
301
+ :shortcut => KEYWORD_SHORT,
302
+ :description => 'A data table.',
303
+ :children => [
304
+ 'headers (container) — A list of headers. The name of each header object must match the name of the object in the data container; the value of the header is what will be displayed.',
305
+ 'data (container) — The table\'s data. Contains one or more containers, each representing one row of data.',
306
+ 'cells (container) — Optional. If present, cells can be formatted with a child per column you want to format; you don\'t need to specify a child for every column.',
307
+ 'always_rows (boolean) — Optional. By default, if a table\'s data is only 1 row, it shows that row in a vertical table. Marking this true means a horizontal table is shown even if only one row is returned.'
308
+ ],
309
+ :messages => [
310
+ 'show — Show the contents of the table in the CLI.',
311
+ 'render — Render the table as HTML, inside a web app (gloo-web). It has the HTML. Outside a web app this is an error, and it is false; use show instead.'
312
+ ],
313
+ :notes => 'If data is a query and the query fails, the query ' \
314
+ 'reports the error and show and render stop without showing ' \
315
+ 'anything.',
316
+ :examples => <<~EXAMPLES.strip
317
+ t [tbl] :
318
+ on_load [script] :
319
+ tell t to show
320
+ headers [can] :
321
+ name : Name
322
+ phone : Tel
323
+ notes : Notes
324
+ data [alias] : table_data
325
+ table_data [can] :
326
+ 1 [can] :
327
+ name : Joe
328
+ phone : 312-555-1212
329
+ notes : Play golf with Joe
330
+ 2 [can] :
331
+ name : Sally
332
+ phone : 708-555-1212
333
+ notes : met Sally at the meetup last week
334
+ 3 [can] :
335
+ name : Frank
336
+ phone : 312-555-1213
337
+ notes : Frank Lee Speaking
338
+ EXAMPLES
339
+ }
340
+ end
341
+
269
342
  end
metadata CHANGED
@@ -1,15 +1,82 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gloo-db
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.1'
4
+ version: '1.3'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Crane
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-02-06 00:00:00.000000000 Z
12
- dependencies: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: gloo
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '7.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '7.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: bundler
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: minitest
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '5.1'
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 5.14.2
50
+ type: :development
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: '5.1'
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: 5.14.2
60
+ - !ruby/object:Gem::Dependency
61
+ name: rake
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '13.0'
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: 13.0.1
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '13.0'
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: 13.0.1
13
80
  description: Adds database support to Gloo.
14
81
  email:
15
82
  - eric.crane@mac.com
@@ -17,16 +84,18 @@ executables: []
17
84
  extensions: []
18
85
  extra_rdoc_files: []
19
86
  files:
87
+ - README.md
88
+ - lib/db_connection.rb
20
89
  - lib/gloo-db.rb
21
90
  - lib/query.rb
22
91
  - lib/query_result.rb
23
92
  - lib/table.rb
24
- homepage: https://gloo.ecrane.us/
93
+ homepage: https://github.com/ecrane/gloo
25
94
  licenses:
26
95
  - MIT
27
96
  metadata:
28
97
  gloo.type: core-library
29
- post_install_message:
98
+ documentation_uri: https://github.com/ecrane/gloo
30
99
  rdoc_options: []
31
100
  require_paths:
32
101
  - lib
@@ -41,8 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
41
110
  - !ruby/object:Gem::Version
42
111
  version: '0'
43
112
  requirements: []
44
- rubygems_version: 3.5.16
45
- signing_key:
113
+ rubygems_version: 4.0.17
46
114
  specification_version: 4
47
115
  summary: Gloo core library. Database support.
48
116
  test_files: []