pgtk 0.14.0 → 0.16.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 49420b7b7c01a8b32c8d7557ca7a7520cfaeca0a389b7ed9fc613728ce961e19
4
- data.tar.gz: b2a61b0c8c6412bea801612a07b14ae67e9549c5060d3364c907a9dceb4ab040
3
+ metadata.gz: f5d316eba88319837e0e0076de690cb533c6d384e84c3bf02caf4bc71737ce76
4
+ data.tar.gz: ffee85649afc871e9a639769ebee131019e93ad11a23a93726d4cc0768eb1186
5
5
  SHA512:
6
- metadata.gz: cc8fb8ab39be7276878f5031680cb9d9f2e4471863e87289a0d939aaf0c21a5b4cf4ec066c2a74fef959df6c1bafa74dba5309fc7abdacc201dd7eb214cb9b3e
7
- data.tar.gz: 128dbf6deb488e9798afc7ca7a76b9f409703591c2e16839e70755dac4c50b9a88baabe308d0503a0aafed241422fa53b2cfe2499172ce6cd81b7fad69ba0185
6
+ metadata.gz: 38d446cdd29cd57eff8ba863f7c6f9a1fb9b18f490e6655eac0259c6945d5da2f6f0f5c18a6a4e7f55f7184c0408bc5f03228514ccbb1375c3974081b176450f
7
+ data.tar.gz: d8fba0d7db2ea8645f33f843d2e304a24d3b4579854b0581a408258fa2d28eb455b05005b6a7a70ff44ec2643268632c2ab8bfc8f0ac7ddaf7f566414d03bb26
@@ -0,0 +1,19 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
2
+ # SPDX-License-Identifier: MIT
3
+ ---
4
+ # yamllint disable rule:line-length
5
+ name: typos
6
+ 'on':
7
+ push:
8
+ branches:
9
+ - master
10
+ pull_request:
11
+ branches:
12
+ - master
13
+ jobs:
14
+ typos:
15
+ timeout-minutes: 15
16
+ runs-on: ubuntu-24.04
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: crate-ci/typos@v1.32.0
data/.gitignore CHANGED
@@ -1,10 +1,12 @@
1
- *.gem
2
- .DS_Store
3
1
  .bundle/
2
+ .DS_Store
4
3
  .idea/
5
4
  .yardoc/
5
+ *.gem
6
6
  coverage/
7
7
  doc/
8
8
  node_modules/
9
9
  rdoc/
10
10
  vendor/
11
+
12
+ **/.claude/settings.local.json
data/.rubocop.yml CHANGED
@@ -10,6 +10,10 @@ AllCops:
10
10
  TargetRubyVersion: 2.3
11
11
  SuggestExtensions: false
12
12
  NewCops: enable
13
+ plugins:
14
+ - rubocop-rake
15
+ - rubocop-minitest
16
+ - rubocop-performance
13
17
  Minitest/EmptyLineBeforeAssertionMethods:
14
18
  Enabled: false
15
19
  Style/ClassAndModuleChildren:
@@ -23,17 +27,13 @@ Layout/EmptyLineAfterGuardClause:
23
27
  Metrics/AbcSize:
24
28
  Max: 100
25
29
  Metrics/CyclomaticComplexity:
26
- Max: 15
30
+ Max: 20
27
31
  Metrics/ClassLength:
28
32
  Max: 200
29
33
  Metrics/MethodLength:
30
34
  Max: 100
31
35
  Metrics/PerceivedComplexity:
32
- Max: 15
36
+ Max: 20
33
37
  Metrics/ParameterLists:
34
38
  Max: 6
35
- plugins:
36
- - rubocop-rake
37
- - rubocop-minitest
38
- - rubocop-performance
39
39
  require: []
data/Gemfile CHANGED
@@ -17,5 +17,6 @@ gem 'rubocop-rake', '>0', require: false
17
17
  gem 'rubocop-rspec', '>0', require: false
18
18
  gem 'simplecov', '~>0.22', require: false
19
19
  gem 'simplecov-cobertura', '~>2.1'
20
+ gem 'timeout', '>0'
20
21
  gem 'xcop', '>0', require: false
21
22
  gem 'yard', '~>0.9', require: false
data/Gemfile.lock CHANGED
@@ -3,6 +3,9 @@ PATH
3
3
  specs:
4
4
  pgtk (0.0.0)
5
5
  backtrace (> 0)
6
+ concurrent-ruby (> 0)
7
+ joined (> 0)
8
+ logger (> 0)
6
9
  loog (> 0)
7
10
  pg (~> 1.1)
8
11
  qbash (> 0)
@@ -15,29 +18,33 @@ GEM
15
18
  ast (2.4.3)
16
19
  backtrace (0.4.0)
17
20
  builder (3.3.0)
21
+ concurrent-ruby (1.3.5)
18
22
  differ (0.1.2)
19
23
  docile (1.4.1)
20
24
  elapsed (0.0.1)
21
25
  loog (> 0)
22
26
  tago (> 0)
23
- json (2.10.2)
27
+ joined (0.1.0)
28
+ json (2.11.3)
24
29
  language_server-protocol (3.17.0.4)
25
30
  lint_roller (1.1.0)
26
- loog (0.6.0)
31
+ logger (1.7.0)
32
+ loog (0.6.1)
33
+ logger (~> 1.0)
27
34
  minitest (5.25.5)
28
35
  minitest-reporters (1.7.1)
29
36
  ansi
30
37
  builder
31
38
  minitest (>= 5.0)
32
39
  ruby-progressbar
33
- nokogiri (1.18.6-arm64-darwin)
40
+ nokogiri (1.18.8-arm64-darwin)
34
41
  racc (~> 1.4)
35
- nokogiri (1.18.6-x64-mingw-ucrt)
42
+ nokogiri (1.18.8-x64-mingw-ucrt)
36
43
  racc (~> 1.4)
37
- nokogiri (1.18.6-x86_64-linux-gnu)
44
+ nokogiri (1.18.8-x86_64-linux-gnu)
38
45
  racc (~> 1.4)
39
- parallel (1.26.3)
40
- parser (3.3.7.3)
46
+ parallel (1.27.0)
47
+ parser (3.3.8.0)
41
48
  ast (~> 2.4.1)
42
49
  racc
43
50
  pg (1.5.9)
@@ -49,14 +56,14 @@ GEM
49
56
  loog (> 0)
50
57
  tago (> 0)
51
58
  racc (1.8.1)
52
- rack (3.1.12)
59
+ rack (3.1.14)
53
60
  rainbow (3.1.1)
54
61
  rake (13.2.1)
55
62
  random-port (0.7.5)
56
63
  tago (> 0)
57
64
  regexp_parser (2.10.0)
58
65
  rexml (3.4.1)
59
- rubocop (1.75.1)
66
+ rubocop (1.75.5)
60
67
  json (~> 2.3)
61
68
  language_server-protocol (~> 3.17.0.2)
62
69
  lint_roller (~> 1.1.0)
@@ -64,24 +71,24 @@ GEM
64
71
  parser (>= 3.3.0.2)
65
72
  rainbow (>= 2.2.2, < 4.0)
66
73
  regexp_parser (>= 2.9.3, < 3.0)
67
- rubocop-ast (>= 1.43.0, < 2.0)
74
+ rubocop-ast (>= 1.44.0, < 2.0)
68
75
  ruby-progressbar (~> 1.7)
69
76
  unicode-display_width (>= 2.4.0, < 4.0)
70
- rubocop-ast (1.43.0)
77
+ rubocop-ast (1.44.1)
71
78
  parser (>= 3.3.7.2)
72
79
  prism (~> 1.4)
73
- rubocop-minitest (0.37.1)
80
+ rubocop-minitest (0.38.0)
74
81
  lint_roller (~> 1.1)
75
- rubocop (>= 1.72.1, < 2.0)
82
+ rubocop (>= 1.75.0, < 2.0)
76
83
  rubocop-ast (>= 1.38.0, < 2.0)
77
- rubocop-performance (1.24.0)
84
+ rubocop-performance (1.25.0)
78
85
  lint_roller (~> 1.1)
79
- rubocop (>= 1.72.1, < 2.0)
86
+ rubocop (>= 1.75.0, < 2.0)
80
87
  rubocop-ast (>= 1.38.0, < 2.0)
81
88
  rubocop-rake (0.7.1)
82
89
  lint_roller (~> 1.1)
83
90
  rubocop (>= 1.72.1)
84
- rubocop-rspec (3.5.0)
91
+ rubocop-rspec (3.6.0)
85
92
  lint_roller (~> 1.1)
86
93
  rubocop (~> 1.72, >= 1.72.1)
87
94
  ruby-progressbar (1.13.0)
@@ -96,6 +103,7 @@ GEM
96
103
  simplecov_json_formatter (0.1.4)
97
104
  slop (4.10.1)
98
105
  tago (0.1.0)
106
+ timeout (0.4.3)
99
107
  unicode-display_width (3.1.4)
100
108
  unicode-emoji (~> 4.0, >= 4.0.4)
101
109
  unicode-emoji (4.0.4)
@@ -126,6 +134,7 @@ DEPENDENCIES
126
134
  rubocop-rspec (> 0)
127
135
  simplecov (~> 0.22)
128
136
  simplecov-cobertura (~> 2.1)
137
+ timeout (> 0)
129
138
  xcop (> 0)
130
139
  yard (~> 0.9)
131
140
 
data/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # Ruby + PostgreSQL + Liquibase + Rake
2
2
 
3
3
  [![EO principles respected here](https://www.elegantobjects.org/badge.svg)](https://www.elegantobjects.org)
4
- [![DevOps By Rultor.com](http://www.rultor.com/b/yegor256/pgtk)](http://www.rultor.com/p/yegor256/pgtk)
4
+ [![DevOps By Rultor.com](https://www.rultor.com/b/yegor256/pgtk)](https://www.rultor.com/p/yegor256/pgtk)
5
5
  [![We recommend RubyMine](https://www.elegantobjects.org/rubymine.svg)](https://www.jetbrains.com/ruby/)
6
6
 
7
7
  [![rake](https://github.com/yegor256/pgtk/actions/workflows/rake.yml/badge.svg)](https://github.com/yegor256/pgtk/actions/workflows/rake.yml)
8
- [![PDD status](http://www.0pdd.com/svg?name=yegor256/pgtk)](http://www.0pdd.com/p?name=yegor256/pgtk)
9
- [![Gem Version](https://badge.fury.io/rb/pgtk.svg)](http://badge.fury.io/rb/pgtk)
8
+ [![PDD status](https://www.0pdd.com/svg?name=yegor256/pgtk)](https://www.0pdd.com/p?name=yegor256/pgtk)
9
+ [![Gem Version](https://badge.fury.io/rb/pgtk.svg)](https://badge.fury.io/rb/pgtk)
10
10
  [![Maintainability](https://api.codeclimate.com/v1/badges/3a5bebac001e5288b00d/maintainability)](https://codeclimate.com/github/yegor256/pgtk/maintainability)
11
11
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/yegor256/pgtk/blob/master/LICENSE.txt)
12
12
  [![Test Coverage](https://img.shields.io/codecov/c/github/yegor256/pgtk.svg)](https://codecov.io/github/yegor256/pgtk?branch=master)
@@ -99,7 +99,7 @@ bundle exec rake pgsql liquibase
99
99
 
100
100
  A temporary PostgreSQL server will be started and the entire set of
101
101
  Liquibase SQL changes will be applied. You will be able to connect
102
- to it from your application, using the file `target/config.yml`.
102
+ to it from your application, using the file `target/pgsql-config.yml`.
103
103
 
104
104
  From inside your app you may find this class useful:
105
105
 
@@ -123,7 +123,7 @@ Now you can fetch some data from the DB:
123
123
  name = pgsql.exec('SELECT name FROM user WHERE id = $1', [id])[0]['name']
124
124
  ```
125
125
 
126
- You may also use it if you need to run a transaction:
126
+ You may also use it when you need to run a transaction:
127
127
 
128
128
  ```ruby
129
129
  pgsql.transaction do |t|
@@ -132,7 +132,7 @@ pgsql.transaction do |t|
132
132
  end
133
133
  ```
134
134
 
135
- To make your PostgreSQL database visible in your unit test, I would
135
+ To make your PostgreSQL database visible in your unit tests, I would
136
136
  recommend you create a method `test_pgsql` in your `test__helper.rb` file
137
137
  (which is `required` in all unit tests) and implement it like this:
138
138
 
@@ -143,7 +143,6 @@ require 'pgtk/pool'
143
143
  module Minitest
144
144
  class Test
145
145
  def test_pgsql
146
- config = YAML.load_file()
147
146
  @@test_pgsql ||= Pgtk::Pool.new(
148
147
  Pgtk::Wire::Yaml.new('target/pgsql-config.yml')
149
148
  ).start
@@ -152,7 +151,10 @@ module Minitest
152
151
  end
153
152
  ```
154
153
 
155
- You can also track all SQL queries sent through, with the help of `Pgtk::Pool`:
154
+ ## Logging with `Pgtk::Spy`
155
+
156
+ You can also track all SQL queries sent through the pool,
157
+ with the help of `Pgtk::Spy`:
156
158
 
157
159
  ```ruby
158
160
  require 'pgtk/spy'
@@ -161,11 +163,46 @@ pool = Pgtk::Spy.new(pool) do |sql|
161
163
  end
162
164
  ```
163
165
 
164
- Well, it works in
166
+ ## Query Caching with `Pgtk::Stash`
167
+
168
+ For applications with frequent read queries,
169
+ you can use `Pgtk::Stash` to add a caching layer:
170
+
171
+ ```ruby
172
+ require 'pgtk/stash'
173
+ stash = Pgtk::Stash.new(pgsql)
174
+ ```
175
+
176
+ `Stash` automatically caches read queries and invalidates the cache
177
+ when tables are modified:
178
+
179
+ ```ruby
180
+ # First execution runs the query against the database
181
+ result1 = stash.exec('SELECT * FROM users WHERE id = $1', [123])
182
+ # Second execution with the same query and parameters returns cached result
183
+ result2 = stash.exec('SELECT * FROM users WHERE id = $1', [123])
184
+ # This modifies the 'users' table, invalidating any cached queries for that table
185
+ stash.exec('UPDATE users SET name = $1 WHERE id = $2', ['John', 123])
186
+ # This will execute against the database again since cache was invalidated
187
+ result3 = stash.exec('SELECT * FROM users WHERE id = $1', [123])
188
+ ```
189
+
190
+ Note that the caching implementation is basic and only suitable
191
+ for simple queries:
192
+
193
+ 1. Queries must reference tables (using `FROM` or `JOIN`)
194
+ 2. Cache is invalidated by table, not by specific rows
195
+ 3. Write operations (`INSERT`, `UPDATE`, `DELETE`) bypass
196
+ the cache and invalidate all cached queries for affected tables
197
+
198
+ ## Some Examples
199
+
200
+ This library works in
165
201
  [netbout.com](https://github.com/yegor256/netbout),
166
202
  [wts.zold.io](https://github.com/zold-io/wts.zold.io),
167
203
  [mailanes.com](https://github.com/yegor256/mailanes), and
168
204
  [0rsk.com](https://github.com/yegor256/0rsk).
205
+
169
206
  They are all open source, you can see how they use `pgtk`.
170
207
 
171
208
  ## How to contribute
data/REUSE.toml CHANGED
@@ -4,9 +4,17 @@
4
4
  version = 1
5
5
  [[annotations]]
6
6
  path = [
7
+ ".DS_Store",
8
+ ".gitattributes",
9
+ ".gitignore",
10
+ ".pdd",
7
11
  "**.json",
8
12
  "**.md",
13
+ "**.png",
9
14
  "**.txt",
15
+ "**/.DS_Store",
16
+ "**/.gitignore",
17
+ "**/.pdd",
10
18
  "**/*.csv",
11
19
  "**/*.jpg",
12
20
  "**/*.json",
@@ -16,15 +24,8 @@ path = [
16
24
  "**/*.svg",
17
25
  "**/*.txt",
18
26
  "**/*.vm",
19
- "**/.DS_Store",
20
- "**/.gitignore",
21
- "**/.pdd",
22
27
  "**/CNAME",
23
28
  "**/Gemfile.lock",
24
- ".DS_Store",
25
- ".gitattributes",
26
- ".gitignore",
27
- ".pdd",
28
29
  "Gemfile.lock",
29
30
  "README.md",
30
31
  "renovate.json",
data/Rakefile CHANGED
@@ -5,7 +5,6 @@
5
5
 
6
6
  require 'rubygems'
7
7
  require 'rake'
8
- require 'rdoc'
9
8
  require 'rake/clean'
10
9
 
11
10
  def name
@@ -37,7 +36,6 @@ require 'rubocop/rake_task'
37
36
  desc 'Run Rubocop on all directories'
38
37
  RuboCop::RakeTask.new(:rubocop) do |task|
39
38
  task.fail_on_error = true
40
- task.requires << 'rubocop-rspec'
41
39
  end
42
40
 
43
41
  require 'xcop/rake_task'
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require 'timeout'
7
+ require_relative '../pgtk'
8
+
9
+ # Impatient is a decorator for Pool that enforces timeouts on all database operations.
10
+ # It ensures that SQL queries don't run indefinitely, which helps prevent application
11
+ # hangs and resource exhaustion when database operations are slow or stalled.
12
+ #
13
+ # This class implements the same interface as Pool but wraps each database operation
14
+ # in a timeout block. If a query exceeds the specified timeout, it raises a Timeout::Error
15
+ # exception, allowing the application to handle slow queries gracefully.
16
+ #
17
+ # Basic usage:
18
+ #
19
+ # # Create and configure a regular pool
20
+ # pool = Pgtk::Pool.new(wire).start(4)
21
+ #
22
+ # # Wrap the pool in an impatient decorator with a 2-second timeout
23
+ # impatient = Pgtk::Impatient.new(pool, 2)
24
+ #
25
+ # # Execute queries with automatic timeout enforcement
26
+ # begin
27
+ # impatient.exec('SELECT * FROM large_table WHERE complex_condition')
28
+ # rescue Timeout::Error
29
+ # puts "Query timed out after 2 seconds"
30
+ # end
31
+ #
32
+ # # Transactions also enforce timeouts on each query
33
+ # begin
34
+ # impatient.transaction do |t|
35
+ # t.exec('UPDATE large_table SET processed = true')
36
+ # t.exec('DELETE FROM queue WHERE processed = true')
37
+ # end
38
+ # rescue Timeout::Error
39
+ # puts "Transaction timed out"
40
+ # end
41
+ #
42
+ # # Combining with Spy for timeout monitoring
43
+ # spy = Pgtk::Spy.new(impatient) do |sql, duration|
44
+ # puts "Query completed in #{duration} seconds: #{sql}"
45
+ # end
46
+ #
47
+ # # Now queries are both timed and monitored
48
+ # spy.exec('SELECT * FROM users')
49
+ #
50
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
51
+ # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
52
+ # License:: MIT
53
+ class Pgtk::Impatient
54
+ # Constructor.
55
+ #
56
+ # @param [Pgtk::Pool] pool The pool to decorate
57
+ # @param [Integer] timeout Timeout in seconds for each SQL query
58
+ def initialize(pool, timeout = 1)
59
+ @pool = pool
60
+ @timeout = timeout
61
+ end
62
+
63
+ # Get the version of PostgreSQL server.
64
+ #
65
+ # @return [String] Version of PostgreSQL server
66
+ def version
67
+ @pool.version
68
+ end
69
+
70
+ # Execute a SQL query with a timeout.
71
+ #
72
+ # @param [String] sql The SQL query with params inside (possibly)
73
+ # @param [Array] args List of arguments
74
+ # @return [Array] Result rows
75
+ # @raise [Timeout::Error] If the query takes too long
76
+ def exec(sql, *args)
77
+ Timeout.timeout(@timeout) do
78
+ @pool.exec(sql, *args)
79
+ end
80
+ end
81
+
82
+ # Run a transaction with a timeout for each query.
83
+ #
84
+ # @yield [Pgtk::Impatient] Yields an impatient transaction
85
+ # @return [Object] Result of the block
86
+ def transaction
87
+ @pool.transaction do |t|
88
+ yield Pgtk::Impatient.new(t, @timeout)
89
+ end
90
+ end
91
+ end
@@ -16,8 +16,38 @@ require_relative '../pgtk'
16
16
  # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
17
17
  # License:: MIT
18
18
  class Pgtk::LiquibaseTask < Rake::TaskLib
19
- attr_accessor :name, :master, :yaml, :quiet, :liquibase_version, :postgresql_version, :contexts
19
+ # Task name
20
+ # @return [Symbol]
21
+ attr_accessor :name
20
22
 
23
+ # Path to Liquibase master XML file
24
+ # @return [String]
25
+ attr_accessor :master
26
+
27
+ # Path to YAML file with PostgreSQL connection details
28
+ # @return [String, Array<String>]
29
+ attr_accessor :yaml
30
+
31
+ # Whether to suppress output
32
+ # @return [Boolean]
33
+ attr_accessor :quiet
34
+
35
+ # Liquibase version to use
36
+ # @return [String]
37
+ attr_accessor :liquibase_version
38
+
39
+ # PostgreSQL JDBC driver version to use
40
+ # @return [String]
41
+ attr_accessor :postgresql_version
42
+
43
+ # Liquibase contexts to apply
44
+ # @return [String]
45
+ attr_accessor :contexts
46
+
47
+ # Initialize a new Liquibase task.
48
+ #
49
+ # @param [Array] args Task arguments
50
+ # @yield [Pgtk::LiquibaseTask, Object] Yields self and task arguments
21
51
  def initialize(*args, &task_block)
22
52
  super()
23
53
  @name = args.shift || :liquibase
@@ -19,8 +19,50 @@ require_relative '../pgtk'
19
19
  # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
20
20
  # License:: MIT
21
21
  class Pgtk::PgsqlTask < Rake::TaskLib
22
- attr_accessor :name, :dir, :fresh_start, :user, :password, :dbname, :yaml, :quiet, :port, :config
22
+ # Task name
23
+ # @return [Symbol]
24
+ attr_accessor :name
23
25
 
26
+ # Directory where PostgreSQL server files will be stored
27
+ # @return [String]
28
+ attr_accessor :dir
29
+
30
+ # Whether to delete the PostgreSQL data directory on each run
31
+ # @return [Boolean]
32
+ attr_accessor :fresh_start
33
+
34
+ # PostgreSQL username
35
+ # @return [String]
36
+ attr_accessor :user
37
+
38
+ # PostgreSQL password
39
+ # @return [String]
40
+ attr_accessor :password
41
+
42
+ # PostgreSQL database name
43
+ # @return [String]
44
+ attr_accessor :dbname
45
+
46
+ # Path to YAML file where configuration will be written
47
+ # @return [String]
48
+ attr_accessor :yaml
49
+
50
+ # Whether to suppress output
51
+ # @return [Boolean]
52
+ attr_accessor :quiet
53
+
54
+ # TCP port for PostgreSQL server (random if nil)
55
+ # @return [Integer, nil]
56
+ attr_accessor :port
57
+
58
+ # Configuration options for PostgreSQL server
59
+ # @return [Hash]
60
+ attr_accessor :config
61
+
62
+ # Initialize a new PostgreSQL server task.
63
+ #
64
+ # @param [Array] args Task arguments
65
+ # @yield [Pgtk::PgsqlTask, Object] Yields self and task arguments
24
66
  def initialize(*args, &task_block)
25
67
  super()
26
68
  @name = args.shift || :pgsql
@@ -129,6 +171,8 @@ class Pgtk::PgsqlTask < Rake::TaskLib
129
171
  }
130
172
  }.to_yaml
131
173
  )
132
- puts "PostgreSQL has been started in process ##{pid}, port #{port}" unless @quiet
174
+ return if @quiet
175
+ puts "PostgreSQL has been started in process ##{pid}, port #{port}"
176
+ puts "YAML config saved to #{@yaml}"
133
177
  end
134
178
  end
data/lib/pgtk/pool.rb CHANGED
@@ -8,7 +8,40 @@ require 'loog'
8
8
  require_relative '../pgtk'
9
9
  require_relative 'wire'
10
10
 
11
- # Pool.
11
+ # Pool provides a connection pool for PostgreSQL database connections.
12
+ # It manages a fixed number of connections to optimize performance and
13
+ # resource usage while providing a simple interface for database operations.
14
+ #
15
+ # The Pool class handles connection lifecycle, reconnects on errors,
16
+ # and provides transaction support. It's the core class for interacting
17
+ # with a PostgreSQL database in this library.
18
+ #
19
+ # Basic usage:
20
+ #
21
+ # # Create a wire that knows how to connect to PostgreSQL
22
+ # wire = Pgtk::Wire::Direct.new(
23
+ # host: 'localhost',
24
+ # port: 5432,
25
+ # dbname: 'mydatabase',
26
+ # user: 'postgres',
27
+ # password: 'secret'
28
+ # )
29
+ #
30
+ # # Create and start a connection pool with 4 connections
31
+ # pool = Pgtk::Pool.new(wire).start(4)
32
+ #
33
+ # # Execute a simple query
34
+ # pool.exec('SELECT * FROM users')
35
+ #
36
+ # # Execute a parameterized query
37
+ # pool.exec('SELECT * FROM users WHERE email = $1', ['user@example.com'])
38
+ #
39
+ # # Use transactions for multiple operations
40
+ # pool.transaction do |t|
41
+ # t.exec('UPDATE accounts SET balance = balance - $1 WHERE id = $2', [100, 42])
42
+ # t.exec('UPDATE accounts SET balance = balance + $1 WHERE id = $2', [100, 43])
43
+ # end
44
+ #
12
45
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
13
46
  # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
14
47
  # License:: MIT
data/lib/pgtk/spy.rb CHANGED
@@ -8,20 +8,65 @@ require 'loog'
8
8
  require_relative '../pgtk'
9
9
  require_relative 'wire'
10
10
 
11
- # A pool tha spies on another pool.
11
+ # Spy is a decorator for Pool that intercepts and tracks SQL queries.
12
+ # It provides observability into database operations by invoking a callback
13
+ # with the SQL query and its execution time for each database operation.
14
+ #
15
+ # This class implements the same interface as Pool, but adds instrumentation
16
+ # functionality while delegating actual database operations to the decorated pool.
17
+ # Use Spy for debugging, performance monitoring, or audit logging.
18
+ #
19
+ # Basic usage:
20
+ #
21
+ # # Create and configure a regular pool
22
+ # pool = Pgtk::Pool.new(wire).start(4)
23
+ #
24
+ # # Wrap the pool in a spy that tracks all executed queries
25
+ # queries = []
26
+ # spy = Pgtk::Spy.new(pool) do |sql, duration|
27
+ # puts "Query: #{sql}"
28
+ # puts "Duration: #{duration} seconds"
29
+ # queries << sql
30
+ # end
31
+ #
32
+ # # Use the spy just like a regular pool, with automatic tracking
33
+ # spy.exec('SELECT * FROM users')
34
+ #
35
+ # # Transactions also track each query inside the transaction
36
+ # spy.transaction do |t|
37
+ # t.exec('UPDATE users SET active = true WHERE id = $1', [42])
38
+ # t.exec('INSERT INTO audit_log (user_id, action) VALUES ($1, $2)', [42, 'activated'])
39
+ # end
40
+ #
41
+ # # Examine collected queries for analysis
42
+ # puts "Total queries: #{queries.size}"
43
+ # puts "First query: #{queries.first}"
44
+ #
12
45
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
13
46
  # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
14
47
  # License:: MIT
15
48
  class Pgtk::Spy
49
+ # Constructor.
50
+ #
51
+ # @param [Pgtk::Pool] pool The pool to spy on
52
+ # @yield [String, Float] Yields the SQL query and execution time
16
53
  def initialize(pool, &block)
17
54
  @pool = pool
18
55
  @block = block
19
56
  end
20
57
 
58
+ # Get the version of PostgreSQL server.
59
+ #
60
+ # @return [String] Version of PostgreSQL server
21
61
  def version
22
62
  @pool.version
23
63
  end
24
64
 
65
+ # Execute a SQL query and track its execution.
66
+ #
67
+ # @param [String] sql The SQL query with params inside (possibly)
68
+ # @param [Array] args List of arguments
69
+ # @return [Array] Result rows
25
70
  def exec(sql, *args)
26
71
  start = Time.now
27
72
  ret = @pool.exec(sql, *args)
@@ -29,6 +74,10 @@ class Pgtk::Spy
29
74
  ret
30
75
  end
31
76
 
77
+ # Run a transaction with spying on each SQL query.
78
+ #
79
+ # @yield [Pgtk::Spy] Yields a spy transaction
80
+ # @return [Object] Result of the block
32
81
  def transaction
33
82
  @pool.transaction do |t|
34
83
  yield Pgtk::Spy.new(t, &@block)