pg_objects 1.4.8 → 1.5.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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +1 -1
  3. data/.gitignore +3 -0
  4. data/.rubocop.yml +13 -2
  5. data/CHANGELOG.md +78 -0
  6. data/Gemfile +2 -0
  7. data/Gemfile.lock +71 -54
  8. data/README.md +174 -3
  9. data/Rakefile +2 -0
  10. data/bin/benchmark +42 -7
  11. data/bin/console +1 -0
  12. data/lib/generators/pg_objects/install/install_generator.rb +2 -0
  13. data/lib/pg_objects/config.rb +53 -2
  14. data/lib/pg_objects/db_object.rb +6 -1
  15. data/lib/pg_objects/db_object_factory.rb +11 -3
  16. data/lib/pg_objects/logger.rb +34 -3
  17. data/lib/pg_objects/manager.rb +66 -15
  18. data/lib/pg_objects/parsed_object/aggregate.rb +9 -1
  19. data/lib/pg_objects/parsed_object/base.rb +38 -0
  20. data/lib/pg_objects/parsed_object/base_type.rb +17 -0
  21. data/lib/pg_objects/parsed_object/conversion.rb +9 -1
  22. data/lib/pg_objects/parsed_object/domain.rb +16 -0
  23. data/lib/pg_objects/parsed_object/enum_type.rb +16 -0
  24. data/lib/pg_objects/parsed_object/event_trigger.rb +3 -1
  25. data/lib/pg_objects/parsed_object/extension.rb +10 -0
  26. data/lib/pg_objects/parsed_object/function.rb +9 -1
  27. data/lib/pg_objects/parsed_object/index.rb +17 -0
  28. data/lib/pg_objects/parsed_object/materialized_view.rb +9 -1
  29. data/lib/pg_objects/parsed_object/operator.rb +9 -1
  30. data/lib/pg_objects/parsed_object/operator_class.rb +9 -1
  31. data/lib/pg_objects/parsed_object/policy.rb +10 -0
  32. data/lib/pg_objects/parsed_object/range_type.rb +16 -0
  33. data/lib/pg_objects/parsed_object/rule.rb +10 -0
  34. data/lib/pg_objects/parsed_object/sequence.rb +16 -0
  35. data/lib/pg_objects/parsed_object/table.rb +9 -1
  36. data/lib/pg_objects/parsed_object/text_search_parser.rb +9 -1
  37. data/lib/pg_objects/parsed_object/text_search_template.rb +9 -1
  38. data/lib/pg_objects/parsed_object/trigger.rb +3 -1
  39. data/lib/pg_objects/parsed_object/type.rb +9 -1
  40. data/lib/pg_objects/parsed_object/view.rb +9 -1
  41. data/lib/pg_objects/parsed_object.rb +11 -0
  42. data/lib/pg_objects/parsed_object_factory.rb +52 -90
  43. data/lib/pg_objects/parser.rb +28 -12
  44. data/lib/pg_objects/railtie.rb +2 -0
  45. data/lib/pg_objects/version.rb +3 -1
  46. data/lib/pg_objects/yaml_configurable.rb +25 -7
  47. data/lib/pg_objects.rb +54 -4
  48. data/lib/tasks/pg_objects_tasks.rake +33 -8
  49. data/pg_objects.gemspec +5 -3
  50. metadata +16 -20
  51. data/.github/copilot-instructions.md +0 -131
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 600119793dcaf119077239c7b5703c5dc5f80e596efed30e790d158253cd4816
4
- data.tar.gz: 9cd2b19b7aaa2b977e87fc805cf9bd536ad6b7f1e8d2a11518df02ffcab8c33f
3
+ metadata.gz: 1807b3da6b645e40097e8bc76bba2681eba9550b4e0312d2b815e106760afa7d
4
+ data.tar.gz: 12782fe9f8582bf759646c86a2dbfa8fecffab4afc027aea744e11640db389e6
5
5
  SHA512:
6
- metadata.gz: 718fde409d5f772221fc25f6aeb10c8cd5bb73dfb43122de3751654b2f4624045cff146df6b8afe4a0a05d3074e22f5e04b85aea7b08bb64c06a2d01f9420806
7
- data.tar.gz: b19c8055c4bb64b4ededd54e606eef8aa665a3fa484bbabca61dab824c1fe5bce7959898e6d039e7cd17db06bb585ab2a2faf957a272c0cf668fc8b8e0b32cd0
6
+ metadata.gz: 01b441d4ba5a5dcbb8d753a5550785a8a67de5a97dcf3f94acb58cbe1925885c32a942c6d28a631188d4654f57ec4324795761dca02d32f3385af374d3cca6eb
7
+ data.tar.gz: a20c356f384c0eed96665aa535d33e3521aec5cbdfd13fd486b027427629094d54de8c62b700e2b13513c744f174ae0041fda053224c00d5533a54a211418e97
@@ -12,7 +12,7 @@ jobs:
12
12
  runs-on: ubuntu-latest
13
13
  strategy:
14
14
  matrix:
15
- ruby-version: ['3.2', '3.3', '3.4', '4.0']
15
+ ruby-version: ['3.3', '3.4', '4.0']
16
16
  steps:
17
17
  - name: Checkout
18
18
  uses: actions/checkout@v4
data/.gitignore CHANGED
@@ -13,3 +13,6 @@
13
13
  # rspec failure tracking
14
14
  .rspec_status
15
15
  *.gem
16
+
17
+ # test-generated SQL fixtures (created/cleaned by FixtureHelpers)
18
+ /spec/fixtures/objects/
data/.rubocop.yml CHANGED
@@ -8,7 +8,7 @@ inherit_from: .rubocop_todo.yml
8
8
 
9
9
  AllCops:
10
10
  # EnabledByDefault: true
11
- TargetRubyVersion: 3.2
11
+ TargetRubyVersion: 3.3
12
12
  # Cop names are not displayed in offense messages by default. Change behavior
13
13
  # by overriding DisplayCopNames, or by giving the -D/--display-cop-names
14
14
  # option.
@@ -186,6 +186,11 @@ RSpec/SpecFilePathFormat:
186
186
  RSpec/SpecFilePathSuffix:
187
187
  Enabled: true
188
188
 
189
+ # Integration specs describe cross-class behaviour, not a single class
190
+ RSpec/DescribeClass:
191
+ Exclude:
192
+ - 'spec/integration/**/*'
193
+
189
194
  # Style #######################################################################
190
195
 
191
196
  Style/AccessorGrouping:
@@ -217,11 +222,17 @@ Style/ExponentialNotation:
217
222
 
218
223
  # Checks if there is a magic comment to enforce string literals
219
224
  Style/FrozenStringLiteralComment:
220
- Enabled: false
225
+ Enabled: true
226
+ EnforcedStyle: always
221
227
 
222
228
  Style/HashAsLastArrayItem:
223
229
  Enabled: true
224
230
 
231
+ # Use explicit named block arguments instead of numbered parameters (_1)
232
+ Style/NumberedParameters:
233
+ EnforcedStyle: disallow
234
+ Enabled: true
235
+
225
236
  Style/HashEachMethods:
226
237
  Enabled: true
227
238
 
data/CHANGELOG.md ADDED
@@ -0,0 +1,78 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [1.5.0] - 2026-07-29
11
+
12
+ ### Added
13
+
14
+ - Multi-database support: `Manager` accepts a `connection:` argument, and the
15
+ rake tasks resolve a connection class from the `PG_OBJECTS_CONNECTION_CLASS`
16
+ environment variable ([#296](https://github.com/marinazzio/pg_objects/issues/296))
17
+ - New parsed object types: enum, range, base and shell `TYPE` variants,
18
+ `DOMAIN` ([#299](https://github.com/marinazzio/pg_objects/issues/299)),
19
+ `EXTENSION`, `INDEX`, `POLICY`, `RULE`, `SEQUENCE`
20
+ ([#300](https://github.com/marinazzio/pg_objects/issues/300))
21
+ - Schema-qualified object names (`schema.name`) recognized in `--!depends_on`
22
+ directives for unambiguous dependency resolution
23
+ - Manual `db:create_objects:before` / `db:create_objects:after` rake tasks and
24
+ the `auto_hook_migrations` / `hook_tasks` settings controlling migration hooks
25
+ - `transactional` setting wrapping each run in a single database transaction
26
+ - Logger severity levels (`info`, `warn`, `error`) with an injectable output
27
+ stream; `error` messages print even in silent mode
28
+ ([#308](https://github.com/marinazzio/pg_objects/issues/308))
29
+ - `MalformedStatementError` for statements whose object name cannot be extracted
30
+ - README sections on supported object types, multiple databases, and re-run
31
+ idempotency patterns ([#307](https://github.com/marinazzio/pg_objects/issues/307))
32
+
33
+ ### Changed
34
+
35
+ - Dependency errors now carry context: `CyclicDependencyError#cycle_path`
36
+ (full A → B → A chain), `AmbiguousDependencyError#candidates`/`#referrer`,
37
+ `DependencyNotExistError#referrer`
38
+ ([#301](https://github.com/marinazzio/pg_objects/issues/301),
39
+ [#302](https://github.com/marinazzio/pg_objects/issues/302),
40
+ [#303](https://github.com/marinazzio/pg_objects/issues/303))
41
+ - YAML configuration loading is deferred to first config access and resolved
42
+ against `Rails.root` when available, fixing preloader/CWD issues
43
+ ([#304](https://github.com/marinazzio/pg_objects/issues/304))
44
+ - Object files are loaded in sorted path order, and the object list is reset on
45
+ each `load_files` call
46
+ - Statement type dispatch refactored from a Try chain to a hash lookup
47
+
48
+ ## [1.4.8] - 2026-05-15
49
+
50
+ ### Added
51
+
52
+ - Mutation testing (Evilution) development configuration
53
+
54
+ ### Changed
55
+
56
+ - Dependency updates
57
+
58
+ ## [1.4.7] - 2026-02-07
59
+
60
+ ### Changed
61
+
62
+ - Dependency updates (notably pg_query 6.2.2 in the development lockfile)
63
+
64
+ ## [1.4.6] - 2026-01-21
65
+
66
+ ### Added
67
+
68
+ - Ruby 4 support
69
+
70
+ ### Changed
71
+
72
+ - Dependency updates
73
+
74
+ [Unreleased]: https://github.com/marinazzio/pg_objects/compare/v1.5.0...HEAD
75
+ [1.5.0]: https://github.com/marinazzio/pg_objects/compare/v1.4.8...v1.5.0
76
+ [1.4.8]: https://github.com/marinazzio/pg_objects/compare/v1.4.7...v1.4.8
77
+ [1.4.7]: https://github.com/marinazzio/pg_objects/compare/v1.4.6...v1.4.7
78
+ [1.4.6]: https://github.com/marinazzio/pg_objects/compare/v1.4.5...v1.4.6
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  gemspec
data/Gemfile.lock CHANGED
@@ -1,12 +1,11 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pg_objects (1.4.8)
4
+ pg_objects (1.5.0)
5
5
  activerecord (>= 6.1.7.0, < 9)
6
6
  dry-auto_inject (~> 1)
7
7
  dry-configurable (~> 1)
8
- dry-container (= 0.11.0)
9
- dry-monads (~> 1.6)
8
+ dry-container (~> 0.11)
10
9
  memery (>= 1.5, < 1.9)
11
10
  pg_query (>= 5, < 7)
12
11
  railties (>= 4, < 9)
@@ -54,7 +53,7 @@ GEM
54
53
  base64 (0.3.0)
55
54
  benchmark (0.5.0)
56
55
  bigdecimal (4.1.2)
57
- binding_of_caller (1.0.1)
56
+ binding_of_caller (2.0.0)
58
57
  debug_inspector (>= 1.2.0)
59
58
  builder (3.3.0)
60
59
  bundler-audit (0.9.3)
@@ -63,18 +62,17 @@ GEM
63
62
  byebug (13.0.0)
64
63
  reline (>= 0.6.0)
65
64
  coderay (1.1.3)
66
- concurrent-ruby (1.3.6)
65
+ concurrent-ruby (1.3.7)
67
66
  connection_pool (3.0.2)
68
- crass (1.0.6)
69
- date (3.5.1)
67
+ crass (1.0.7)
70
68
  debug_inspector (1.2.0)
71
69
  diff-lcs (1.6.2)
72
70
  drb (2.2.3)
73
- dry-auto_inject (1.1.0)
71
+ dry-auto_inject (1.2.1)
74
72
  dry-core (~> 1.1)
75
73
  zeitwerk (~> 2.6)
76
- dry-configurable (1.3.0)
77
- dry-core (~> 1.1)
74
+ dry-configurable (1.4.0)
75
+ dry-core (~> 1.0)
78
76
  zeitwerk (~> 2.6)
79
77
  dry-container (0.11.0)
80
78
  concurrent-ruby (~> 1.0)
@@ -82,29 +80,44 @@ GEM
82
80
  concurrent-ruby (~> 1.0)
83
81
  logger
84
82
  zeitwerk (~> 2.6)
85
- dry-monads (1.10.0)
86
- concurrent-ruby (~> 1.0)
87
- dry-core (~> 1.1)
88
- zeitwerk (~> 2.6)
89
- erb (6.0.1.1)
83
+ erb (6.0.4)
90
84
  erubi (1.13.1)
91
85
  faker (3.8.0)
92
86
  i18n (>= 1.8.11, < 2)
93
- google-protobuf (4.33.5)
87
+ google-protobuf (4.35.1)
88
+ bigdecimal
89
+ rake (~> 13.3)
90
+ google-protobuf (4.35.1-aarch64-linux-gnu)
91
+ bigdecimal
92
+ rake (~> 13.3)
93
+ google-protobuf (4.35.1-aarch64-linux-musl)
94
94
  bigdecimal
95
- rake (>= 13)
96
- i18n (1.14.8)
95
+ rake (~> 13.3)
96
+ google-protobuf (4.35.1-arm64-darwin)
97
+ bigdecimal
98
+ rake (~> 13.3)
99
+ google-protobuf (4.35.1-x86_64-darwin)
100
+ bigdecimal
101
+ rake (~> 13.3)
102
+ google-protobuf (4.35.1-x86_64-linux-gnu)
103
+ bigdecimal
104
+ rake (~> 13.3)
105
+ google-protobuf (4.35.1-x86_64-linux-musl)
106
+ bigdecimal
107
+ rake (~> 13.3)
108
+ i18n (1.15.2)
97
109
  concurrent-ruby (~> 1.0)
98
110
  io-console (0.8.2)
99
- irb (1.16.0)
111
+ irb (1.18.0)
100
112
  pp (>= 0.6.0)
113
+ prism (>= 1.3.0)
101
114
  rdoc (>= 4.0.0)
102
115
  reline (>= 0.4.2)
103
- json (2.19.5)
104
- language_server-protocol (3.17.0.5)
116
+ json (2.21.1)
117
+ language_server-protocol (3.17.0.6)
105
118
  lint_roller (1.1.0)
106
119
  logger (1.7.0)
107
- loofah (2.25.1)
120
+ loofah (2.25.2)
108
121
  crass (~> 1.0.2)
109
122
  nokogiri (>= 1.12.0)
110
123
  memery (1.8.0)
@@ -112,29 +125,29 @@ GEM
112
125
  minitest (6.0.6)
113
126
  drb (~> 2.0)
114
127
  prism (~> 1.5)
115
- nokogiri (1.19.3-aarch64-linux-gnu)
128
+ nokogiri (1.19.4-aarch64-linux-gnu)
116
129
  racc (~> 1.4)
117
- nokogiri (1.19.3-aarch64-linux-musl)
130
+ nokogiri (1.19.4-aarch64-linux-musl)
118
131
  racc (~> 1.4)
119
- nokogiri (1.19.3-arm-linux-gnu)
132
+ nokogiri (1.19.4-arm-linux-gnu)
120
133
  racc (~> 1.4)
121
- nokogiri (1.19.3-arm-linux-musl)
134
+ nokogiri (1.19.4-arm-linux-musl)
122
135
  racc (~> 1.4)
123
- nokogiri (1.19.3-arm64-darwin)
136
+ nokogiri (1.19.4-arm64-darwin)
124
137
  racc (~> 1.4)
125
- nokogiri (1.19.3-x86_64-darwin)
138
+ nokogiri (1.19.4-x86_64-darwin)
126
139
  racc (~> 1.4)
127
- nokogiri (1.19.3-x86_64-linux-gnu)
140
+ nokogiri (1.19.4-x86_64-linux-gnu)
128
141
  racc (~> 1.4)
129
- nokogiri (1.19.3-x86_64-linux-musl)
142
+ nokogiri (1.19.4-x86_64-linux-musl)
130
143
  racc (~> 1.4)
131
- parallel (1.28.0)
144
+ parallel (2.1.0)
132
145
  parser (3.3.11.1)
133
146
  ast (~> 2.4.1)
134
147
  racc
135
148
  pg_query (6.2.2)
136
149
  google-protobuf (>= 3.25.3)
137
- pp (0.6.3)
150
+ pp (0.6.4)
138
151
  prettyprint
139
152
  prettyprint (0.2.0)
140
153
  prism (1.9.0)
@@ -149,9 +162,6 @@ GEM
149
162
  pry-byebug (3.12.0)
150
163
  byebug (~> 13.0)
151
164
  pry (>= 0.13, < 0.17)
152
- psych (5.3.1)
153
- date
154
- stringio
155
165
  racc (1.8.1)
156
166
  rack (3.2.6)
157
167
  rack-session (2.1.2)
@@ -165,8 +175,8 @@ GEM
165
175
  activesupport (>= 5.0.0)
166
176
  minitest
167
177
  nokogiri (>= 1.6)
168
- rails-html-sanitizer (1.6.2)
169
- loofah (~> 2.21)
178
+ rails-html-sanitizer (1.7.1)
179
+ loofah (~> 2.25, >= 2.25.2)
170
180
  nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
171
181
  railties (8.1.3)
172
182
  actionpack (= 8.1.3)
@@ -181,14 +191,20 @@ GEM
181
191
  rake (13.4.2)
182
192
  rake-hooks (1.2.3)
183
193
  rake
184
- rdoc (7.1.0)
194
+ rbs (4.0.3)
195
+ logger
196
+ prism (>= 1.6.0)
197
+ tsort
198
+ rdoc (8.0.0)
185
199
  erb
186
- psych (>= 4.0.0)
200
+ prism (>= 1.6.0)
201
+ rbs (>= 4.0.0)
187
202
  tsort
188
203
  regexp_parser (2.12.0)
189
204
  reline (0.6.3)
190
205
  io-console (~> 0.5)
191
- rouge (4.7.0)
206
+ rouge (5.0.0)
207
+ strscan (~> 3.1)
192
208
  rspec (3.13.2)
193
209
  rspec-core (~> 3.13.0)
194
210
  rspec-expectations (~> 3.13.0)
@@ -198,23 +214,23 @@ GEM
198
214
  rspec-expectations (3.13.5)
199
215
  diff-lcs (>= 1.2.0, < 2.0)
200
216
  rspec-support (~> 3.13.0)
201
- rspec-mocks (3.13.7)
217
+ rspec-mocks (3.13.8)
202
218
  diff-lcs (>= 1.2.0, < 2.0)
203
219
  rspec-support (~> 3.13.0)
204
220
  rspec-parameterized (2.0.1)
205
221
  rspec-parameterized-core (>= 2, < 3)
206
222
  rspec-parameterized-table_syntax (>= 2, < 3)
207
- rspec-parameterized-core (2.0.1)
223
+ rspec-parameterized-core (2.0.2)
208
224
  parser
209
225
  prism
210
226
  proc_to_ast (>= 0.2.0)
211
227
  rspec (>= 2.13, < 4)
212
228
  unparser
213
- rspec-parameterized-table_syntax (2.1.0)
214
- binding_of_caller
229
+ rspec-parameterized-table_syntax (2.1.1)
230
+ binding_of_caller (>= 2)
215
231
  rspec-parameterized-core (>= 2, < 3)
216
- rspec-support (3.13.6)
217
- rubocop (1.86.1)
232
+ rspec-support (3.13.7)
233
+ rubocop (1.88.2)
218
234
  json (~> 2.3)
219
235
  language_server-protocol (~> 3.17.0.2)
220
236
  lint_roller (~> 1.1.0)
@@ -225,10 +241,10 @@ GEM
225
241
  rubocop-ast (>= 1.49.0, < 2.0)
226
242
  ruby-progressbar (~> 1.7)
227
243
  unicode-display_width (>= 2.4.0, < 4.0)
228
- rubocop-ast (1.49.1)
244
+ rubocop-ast (1.50.0)
229
245
  parser (>= 3.3.7.2)
230
246
  prism (~> 1.7)
231
- rubocop-rails (2.35.0)
247
+ rubocop-rails (2.36.0)
232
248
  activesupport (>= 4.2.0)
233
249
  lint_roller (~> 1.1)
234
250
  rack (>= 1.1)
@@ -237,12 +253,13 @@ GEM
237
253
  rubocop-rake (0.7.1)
238
254
  lint_roller (~> 1.1)
239
255
  rubocop (>= 1.72.1)
240
- rubocop-rspec (3.9.0)
256
+ rubocop-rspec (3.10.2)
241
257
  lint_roller (~> 1.1)
242
- rubocop (~> 1.81)
258
+ regexp_parser (>= 2.0)
259
+ rubocop (~> 1.86, >= 1.86.2)
243
260
  ruby-progressbar (1.13.0)
244
261
  securerandom (0.4.1)
245
- stringio (3.2.0)
262
+ strscan (3.1.8)
246
263
  thor (1.5.0)
247
264
  timeout (0.6.1)
248
265
  tsort (0.2.0)
@@ -251,13 +268,13 @@ GEM
251
268
  unicode-display_width (3.2.0)
252
269
  unicode-emoji (~> 4.1)
253
270
  unicode-emoji (4.2.0)
254
- unparser (0.8.1)
255
- diff-lcs (~> 1.6)
271
+ unparser (0.9.0)
272
+ diff-lcs (>= 1.6, < 3)
256
273
  parser (>= 3.3.0)
257
274
  prism (>= 1.5.1)
258
275
  uri (1.1.1)
259
276
  useragent (0.16.11)
260
- zeitwerk (2.7.5)
277
+ zeitwerk (2.8.2)
261
278
 
262
279
  PLATFORMS
263
280
  aarch64-linux
data/README.md CHANGED
@@ -32,6 +32,18 @@ Run the installation procedure to initialize directories structure and configura
32
32
  bundle exec rails generate pg_objects:install
33
33
  ```
34
34
 
35
+ ## Supported object types
36
+
37
+ The following `CREATE` statements are recognized as manageable objects:
38
+
39
+ `AGGREGATE`, `CONVERSION`, `DOMAIN`, `EVENT TRIGGER`, `EXTENSION`, `FUNCTION`,
40
+ `INDEX`, `MATERIALIZED VIEW`, `OPERATOR`, `OPERATOR CLASS`, `POLICY`, `RULE`,
41
+ `SEQUENCE`, `TABLE`, `TEXT SEARCH PARSER`, `TEXT SEARCH TEMPLATE`, `TRIGGER`,
42
+ `TYPE` (composite, enum, range, base, and shell forms), `VIEW`.
43
+
44
+ Files containing any other statement raise
45
+ `PgObjects::UnknownObjectTypeError` during loading.
46
+
35
47
  ## Usage
36
48
 
37
49
  Store DB objects as CREATE (or CREATE OR UPDATE) queries in files within a directory structure (default: *db/objects*).
@@ -46,6 +58,21 @@ CREATE FUNCTION my_func()
46
58
 
47
59
  The string after the directive should be the name of the file that the dependency refers to, without the file extension.
48
60
 
61
+ ### Directive syntax
62
+
63
+ - The directive must start the line — no leading whitespace.
64
+ - Both `--!` (SQL comment) and `#!` prefixes are supported.
65
+ - List several dependencies on one line, separated by commas and/or whitespace,
66
+ or use a separate directive line for each:
67
+
68
+ ```sql
69
+ --!depends_on func_a, func_b, func_c
70
+ --!depends_on func_d
71
+ #!depends_on func_e
72
+ CREATE FUNCTION my_func()
73
+ ...
74
+ ```
75
+
49
76
  ## Configuration
50
77
 
51
78
  You have the option to configure the gem using either a YAML file or a Ruby initializer. The priority order for configuration is as follows:
@@ -55,7 +82,13 @@ You have the option to configure the gem using either a YAML file or a Ruby init
55
82
 
56
83
  ### YAML
57
84
 
58
- Create `pg_objects.yml` in the application *config* directory:
85
+ Create `pg_objects.yml` in the application *config* directory.
86
+
87
+ The file is loaded on first configuration access and resolved against
88
+ `Rails.root` in Rails applications, so it is found even when the gem is
89
+ required before the process changes into the app root (e.g. under the Spring
90
+ preloader). Outside Rails the path is relative to the current working
91
+ directory.
59
92
 
60
93
  ```yaml
61
94
  # pg_objects.yml
@@ -70,8 +103,18 @@ extensions:
70
103
  - sql
71
104
  - txt
72
105
 
73
- # Specify whether to suppress output to console
106
+ # Suppress non-error output to console (error messages are always printed)
74
107
  silent: false
108
+
109
+ # Whether to wrap each object-creation run in a database transaction so a
110
+ # failure rolls back everything created in that run (default: true).
111
+ # Note: some PostgreSQL statements cannot run inside a transaction
112
+ # (e.g. CREATE INDEX CONCURRENTLY, VACUUM) — set to false if your
113
+ # object files contain them.
114
+ transactional: true
115
+
116
+ # Whether to install the Rake hooks that auto-create objects (default: true)
117
+ auto_hook_migrations: true
75
118
  ```
76
119
 
77
120
  ### Initializer
@@ -83,7 +126,8 @@ PgObjects.configure do |config|
83
126
  config.before_path = 'path/to/objects/before' # default: 'db/objects/before'
84
127
  config.after_path = 'path/to/objects/after' # default: 'db/objects/after'
85
128
  config.extensions = ['sql', 'txt'] # default: 'sql'
86
- config.silent = true # whether to suppress output to console, default: false
129
+ config.silent = true # suppress non-error output to console (errors are always printed), default: false
130
+ config.transactional = false # opt out of the wrapping transaction, default: true
87
131
  end
88
132
  ```
89
133
 
@@ -91,6 +135,118 @@ Otherwise, the default values will be used.
91
135
 
92
136
  Please make sure to verify that the specified directories actually exist.
93
137
 
138
+ > [!NOTE]
139
+ > Object creation runs inside a single database transaction by default, so a
140
+ > failure mid-run rolls back every object created in that run. PostgreSQL
141
+ > rejects some statements inside a transaction block — for example
142
+ > `CREATE INDEX CONCURRENTLY`, `VACUUM`, or `ALTER TYPE ... ADD VALUE` on
143
+ > PostgreSQL versions before 12. If your object files contain such statements,
144
+ > set `config.transactional = false`.
145
+
146
+ ### Rake tasks that trigger object creation
147
+
148
+ Object creation is hooked into Rake tasks. By default:
149
+
150
+ | Task | `before` objects | `after` objects |
151
+ | --- | :---: | :---: |
152
+ | `db:migrate` | ✓ | ✓ |
153
+ | `db:schema:load` | | ✓ |
154
+ | `db:migrate:redo` | ✓ | ✓ |
155
+
156
+ `db:rollback` is **not** hooked — rolling a migration back should not recreate
157
+ objects.
158
+
159
+ You can also invoke the underlying tasks directly: `db:create_objects:before`
160
+ and `db:create_objects:after`.
161
+
162
+ ### Multiple databases
163
+
164
+ By default objects are created over the global connection
165
+ (`ActiveRecord::Base.connection`). In a Rails multi-database setup, target a
166
+ specific database by passing its connection to the manager:
167
+
168
+ ```ruby
169
+ PgObjects::Manager.new(connection: AnimalsRecord.connection).load_files(:before).create_objects
170
+ ```
171
+
172
+ For the rake tasks, set `PG_OBJECTS_CONNECTION_CLASS` to the name of the
173
+ Active Record class whose connection should be used:
174
+
175
+ ```sh
176
+ PG_OBJECTS_CONNECTION_CLASS=AnimalsRecord bin/rails db:create_objects:before
177
+ ```
178
+
179
+ To disable all hooks entirely, set `auto_hook_migrations` to `false` in an
180
+ initializer; then create objects only by invoking those tasks manually:
181
+
182
+ ```ruby
183
+ PgObjects.configure do |config|
184
+ config.auto_hook_migrations = false # default: true
185
+ end
186
+ ```
187
+
188
+ ### Re-running and idempotency
189
+
190
+ The hooked tasks re-execute **every** object file on each run — the gem keeps
191
+ no state about what was already created. Files must therefore contain
192
+ re-runnable (idempotent) SQL, or the second `db:migrate` fails with errors
193
+ like `relation "..." already exists`.
194
+
195
+ What PostgreSQL offers per object type:
196
+
197
+ | Object type | Idempotent form |
198
+ | --- | --- |
199
+ | `FUNCTION`, `VIEW` | `CREATE OR REPLACE` |
200
+ | `RULE` | `CREATE OR REPLACE` |
201
+ | `AGGREGATE` | `CREATE OR REPLACE` (PostgreSQL 12+) |
202
+ | `TRIGGER` | `CREATE OR REPLACE` (PostgreSQL 14+), otherwise drop-and-recreate |
203
+ | `TABLE`, `INDEX`, `SEQUENCE`, `MATERIALIZED VIEW`, `EXTENSION` | `IF NOT EXISTS` |
204
+ | `TYPE`, `DOMAIN`, `POLICY`, `CONVERSION`, `OPERATOR`, `OPERATOR CLASS`, `EVENT TRIGGER`, `TEXT SEARCH PARSER/TEMPLATE` | none — use a guard (below) |
205
+
206
+ For types without an idempotent form, either drop first:
207
+
208
+ ```sql
209
+ DROP POLICY IF EXISTS user_policy ON users;
210
+ CREATE POLICY user_policy ON users USING (user_id = current_user_id());
211
+ ```
212
+
213
+ or swallow the duplicate error in a `DO` block:
214
+
215
+ ```sql
216
+ DO $$ BEGIN
217
+ CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
218
+ EXCEPTION WHEN duplicate_object THEN NULL;
219
+ END $$;
220
+ ```
221
+
222
+ > [!NOTE]
223
+ > Object files are classified by parsing their **first** statement. When that
224
+ > statement is a `DROP` or a `DO` block (as in the guards above), the SQL
225
+ > object name cannot be extracted. The file still executes normally, but it
226
+ > can only be referenced by its **file identifiers** — the extensionless file
227
+ > name or the file path — while `--!depends_on` directives referring to the
228
+ > SQL object name (or its schema-qualified form) will not match it.
229
+ > The plain `IF NOT EXISTS` / `OR REPLACE` forms keep full name resolution.
230
+
231
+ Also note that object creation runs inside a single transaction by default
232
+ (see above), so one failing statement rolls back the entire run — a
233
+ half-idempotent set of files either all applies or not at all.
234
+
235
+ Override `hook_tasks` to customize which tasks/stages are hooked (e.g. an empty
236
+ hash also disables all hooks). Configure it in a Rails initializer: the hooks are installed when the
237
+ gem's rake tasks load, which happens after initializers run, so an initializer
238
+ value is always picked up. Changing `hook_tasks` later (after task loading) has
239
+ no effect on which hooks are installed.
240
+
241
+ ```ruby
242
+ PgObjects.configure do |config|
243
+ config.hook_tasks = {
244
+ 'db:migrate' => %i[before after],
245
+ 'db:schema:load' => %i[after]
246
+ }
247
+ end
248
+ ```
249
+
94
250
  ## Development
95
251
 
96
252
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -145,3 +301,18 @@ Full Workflow Performance:
145
301
  ## Contributing
146
302
 
147
303
  Bug reports and pull requests are welcome on GitHub at https://github.com/marinazzio/pg_objects.
304
+
305
+ Every pull request that changes gem behavior must add an entry to the
306
+ `[Unreleased]` section of [CHANGELOG.md](CHANGELOG.md) (following the
307
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format). Pure
308
+ refactorings, spec-only, and dependency-bump PRs may skip this.
309
+
310
+ ## Releasing
311
+
312
+ 1. Move the `[Unreleased]` entries in `CHANGELOG.md` under a new version
313
+ heading with the release date, and update the comparison links at the
314
+ bottom of the file.
315
+ 2. Bump `PgObjects::VERSION` in `lib/pg_objects/version.rb` accordingly
316
+ (semantic versioning).
317
+ 3. Tag the commit `vX.Y.Z` and publish a GitHub release — the publish
318
+ workflow builds and pushes the gem to RubyGems.
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/gem_tasks'
2
4
  require 'rspec/core/rake_task'
3
5