pg_sql_triggers 1.4.0 → 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.
- checksums.yaml +4 -4
- data/.erb_lint.yml +0 -0
- data/.rspec +0 -0
- data/.rubocop.yml +6 -16
- data/AGENTS.md +8 -0
- data/CHANGELOG.md +104 -2
- data/COVERAGE.md +39 -41
- data/LICENSE +0 -0
- data/README.md +24 -3
- data/RELEASE.md +0 -0
- data/Rakefile +5 -0
- data/app/assets/javascripts/pg_sql_triggers/application.js +0 -0
- data/app/assets/javascripts/pg_sql_triggers/trigger_actions.js +0 -0
- data/app/assets/stylesheets/pg_sql_triggers/application.css +0 -0
- data/app/controllers/concerns/pg_sql_triggers/error_handling.rb +0 -0
- data/app/controllers/concerns/pg_sql_triggers/kill_switch_protection.rb +0 -0
- data/app/controllers/concerns/pg_sql_triggers/permission_checking.rb +6 -5
- data/app/controllers/pg_sql_triggers/application_controller.rb +0 -0
- data/app/controllers/pg_sql_triggers/audit_logs_controller.rb +81 -64
- data/app/controllers/pg_sql_triggers/dashboard_controller.rb +111 -34
- data/app/controllers/pg_sql_triggers/migrations_controller.rb +13 -14
- data/app/controllers/pg_sql_triggers/tables_controller.rb +8 -0
- data/app/controllers/pg_sql_triggers/triggers_controller.rb +1 -0
- data/app/helpers/pg_sql_triggers/dashboard_helper.rb +19 -0
- data/app/helpers/pg_sql_triggers/permissions_helper.rb +3 -2
- data/app/models/pg_sql_triggers/application_record.rb +0 -0
- data/app/models/pg_sql_triggers/audit_log.rb +29 -47
- data/app/models/pg_sql_triggers/trigger_registry.rb +105 -78
- data/app/views/layouts/pg_sql_triggers/application.html.erb +0 -0
- data/app/views/pg_sql_triggers/audit_logs/index.html.erb +9 -5
- data/app/views/pg_sql_triggers/dashboard/index.html.erb +107 -24
- data/app/views/pg_sql_triggers/shared/_confirmation_modal.html.erb +0 -0
- data/app/views/pg_sql_triggers/shared/_kill_switch_status.html.erb +0 -0
- data/app/views/pg_sql_triggers/tables/index.html.erb +26 -14
- data/app/views/pg_sql_triggers/tables/show.html.erb +0 -0
- data/app/views/pg_sql_triggers/triggers/_drop_modal.html.erb +0 -0
- data/app/views/pg_sql_triggers/triggers/_re_execute_modal.html.erb +0 -0
- data/app/views/pg_sql_triggers/triggers/show.html.erb +33 -0
- data/config/initializers/pg_sql_triggers.rb +0 -0
- data/config/routes.rb +0 -0
- data/db/migrate/{20251222000001_create_pg_sql_triggers_tables.rb → 20251222104327_create_pg_sql_triggers_tables.rb} +0 -0
- data/db/migrate/20251229071916_add_timing_to_pg_sql_triggers_registry.rb +0 -0
- data/db/migrate/{20260103000001_create_pg_sql_triggers_audit_log.rb → 20260103114508_create_pg_sql_triggers_audit_log.rb} +0 -0
- data/db/migrate/{20260228000001_add_for_each_to_pg_sql_triggers_registry.rb → 20260228162233_add_for_each_to_pg_sql_triggers_registry.rb} +0 -0
- data/db/migrate/20260412185841_add_constraint_deferral_to_pg_sql_triggers_registry.rb +9 -0
- data/docs/README.md +3 -0
- data/docs/api-reference.md +133 -0
- data/docs/audit-trail.md +1 -1
- data/docs/configuration.md +172 -0
- data/docs/getting-started.md +14 -0
- data/docs/kill-switch.md +0 -0
- data/docs/permissions.md +6 -9
- data/docs/troubleshooting.md +0 -0
- data/docs/ui-guide.md +0 -0
- data/docs/usage-guide.md +74 -0
- data/docs/web-ui.md +0 -0
- data/lib/generators/pg_sql_triggers/install_generator.rb +0 -0
- data/lib/generators/pg_sql_triggers/templates/README +0 -0
- data/lib/generators/pg_sql_triggers/templates/create_pg_sql_triggers_tables.rb +0 -0
- data/lib/generators/pg_sql_triggers/templates/initializer.rb +14 -0
- data/lib/generators/pg_sql_triggers/templates/trigger_dsl.rb.tt +0 -0
- data/lib/generators/pg_sql_triggers/templates/trigger_migration.rb.erb +0 -0
- data/lib/generators/pg_sql_triggers/templates/trigger_migration_full.rb.tt +0 -0
- data/lib/generators/pg_sql_triggers/trigger_generator.rb +0 -0
- data/lib/generators/pg_sql_triggers/trigger_migration_generator.rb +0 -0
- data/lib/pg_sql_triggers/alerting.rb +77 -0
- data/lib/pg_sql_triggers/database_introspection.rb +0 -0
- data/lib/pg_sql_triggers/deferral_checksum.rb +54 -0
- data/lib/pg_sql_triggers/drift/db_queries.rb +14 -5
- data/lib/pg_sql_triggers/drift/detector.rb +9 -1
- data/lib/pg_sql_triggers/drift/reporter.rb +0 -0
- data/lib/pg_sql_triggers/drift.rb +5 -0
- data/lib/pg_sql_triggers/dsl/trigger_definition.rb +56 -2
- data/lib/pg_sql_triggers/dsl.rb +0 -0
- data/lib/pg_sql_triggers/engine.rb +35 -0
- data/lib/pg_sql_triggers/errors.rb +0 -0
- data/lib/pg_sql_triggers/events_checksum.rb +114 -0
- data/lib/pg_sql_triggers/migration.rb +5 -6
- data/lib/pg_sql_triggers/migrator/pre_apply_comparator.rb +77 -73
- data/lib/pg_sql_triggers/migrator/pre_apply_diff_reporter.rb +0 -0
- data/lib/pg_sql_triggers/migrator/safety_validator.rb +3 -1
- data/lib/pg_sql_triggers/migrator.rb +90 -94
- data/lib/pg_sql_triggers/permissions/checker.rb +12 -15
- data/lib/pg_sql_triggers/permissions.rb +1 -0
- data/lib/pg_sql_triggers/rake_development_boot.rb +65 -0
- data/lib/pg_sql_triggers/registry/manager.rb +27 -13
- data/lib/pg_sql_triggers/registry/validator.rb +226 -2
- data/lib/pg_sql_triggers/registry.rb +0 -0
- data/lib/pg_sql_triggers/schema_dumper_extension.rb +32 -0
- data/lib/pg_sql_triggers/sql/kill_switch.rb +2 -1
- data/lib/pg_sql_triggers/sql.rb +0 -0
- data/lib/pg_sql_triggers/testing/dry_run.rb +0 -0
- data/lib/pg_sql_triggers/testing/function_tester.rb +97 -107
- data/lib/pg_sql_triggers/testing/safe_executor.rb +0 -0
- data/lib/pg_sql_triggers/testing/syntax_validator.rb +0 -0
- data/lib/pg_sql_triggers/testing.rb +0 -0
- data/lib/pg_sql_triggers/trigger_structure_dumper.rb +111 -0
- data/lib/pg_sql_triggers/version.rb +1 -1
- data/lib/pg_sql_triggers.rb +17 -0
- data/lib/tasks/trigger_migrations.rake +235 -152
- data/rakelib/pg_sql_triggers_environment.rake +9 -0
- data/scripts/generate_coverage_report.rb +4 -1
- data/sig/pg_sql_triggers.rbs +0 -0
- metadata +65 -13
- data/GEM_ANALYSIS.md +0 -368
- data/Goal.md +0 -742
- data/pg_sql_triggers.gemspec +0 -53
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7adcec137e32ecf9e1013a7dbb98a16844a5f8082b0d910a7380903cd2151678
|
|
4
|
+
data.tar.gz: b285ecac25cf7806a56f0205fca527a158b48c7f26b99adb4c1c304b3b2d17d7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0aeb0cb7beee89da7f9c8993fd166b569c4cacb684e1b82222c104d083c970b9878da4ae368449aeeda63839c991522148e6dc4996c0ffce79ca04e49063b1e0
|
|
7
|
+
data.tar.gz: 0d0034cf4c0ee07dc9f868fa44924a9fd737861ac1d067c67b189ae5f08172af8f9596fc625b029f5dce8d36fe00ec287e0bbb2ace5a4725ce43eabdfe66907a
|
data/.erb_lint.yml
CHANGED
|
File without changes
|
data/.rspec
CHANGED
|
File without changes
|
data/.rubocop.yml
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
require:
|
|
2
|
-
- rubocop-rspec
|
|
3
|
-
- rubocop-rspec_rails
|
|
4
|
-
|
|
5
1
|
plugins:
|
|
6
2
|
- rubocop-rails
|
|
3
|
+
- rubocop-rspec
|
|
4
|
+
- rubocop-rspec_rails
|
|
7
5
|
- rubocop-capybara
|
|
8
6
|
- rubocop-factory_bot
|
|
9
7
|
|
|
@@ -24,18 +22,14 @@ Metrics/AbcSize:
|
|
|
24
22
|
Max: 65
|
|
25
23
|
Exclude:
|
|
26
24
|
- 'spec/**/*'
|
|
27
|
-
- 'app/controllers/**/*'
|
|
28
|
-
- 'lib/generators/**/*'
|
|
29
25
|
- 'lib/pg_sql_triggers/testing/**/*'
|
|
30
26
|
|
|
31
27
|
Metrics/BlockLength:
|
|
32
28
|
Max: 50
|
|
33
29
|
Exclude:
|
|
34
30
|
- 'spec/**/*'
|
|
35
|
-
- 'config/routes.rb'
|
|
36
31
|
- '*.gemspec'
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
|
|
39
33
|
Metrics/ClassLength:
|
|
40
34
|
Max: 200
|
|
41
35
|
Exclude:
|
|
@@ -44,21 +38,17 @@ Metrics/ClassLength:
|
|
|
44
38
|
Metrics/CyclomaticComplexity:
|
|
45
39
|
Max: 18
|
|
46
40
|
Exclude:
|
|
47
|
-
- 'app/controllers/**/*'
|
|
48
41
|
- 'lib/pg_sql_triggers/testing/**/*'
|
|
49
42
|
|
|
50
43
|
Metrics/MethodLength:
|
|
51
44
|
Max: 55
|
|
52
45
|
Exclude:
|
|
53
46
|
- 'spec/**/*'
|
|
54
|
-
- 'app/controllers/**/*'
|
|
55
|
-
- 'lib/generators/**/*'
|
|
56
47
|
- 'lib/pg_sql_triggers/testing/**/*'
|
|
57
48
|
|
|
58
49
|
Metrics/PerceivedComplexity:
|
|
59
50
|
Max: 15
|
|
60
51
|
Exclude:
|
|
61
|
-
- 'app/controllers/**/*'
|
|
62
52
|
- 'lib/pg_sql_triggers/testing/**/*'
|
|
63
53
|
|
|
64
54
|
# Style
|
|
@@ -97,9 +87,6 @@ RSpec/ExampleLength:
|
|
|
97
87
|
Exclude:
|
|
98
88
|
- 'spec/**/*_spec.rb'
|
|
99
89
|
|
|
100
|
-
RSpec/FilePath:
|
|
101
|
-
Enabled: false
|
|
102
|
-
|
|
103
90
|
RSpec/InstanceVariable:
|
|
104
91
|
Enabled: false
|
|
105
92
|
|
|
@@ -115,6 +102,9 @@ RSpec/MultipleDescribes:
|
|
|
115
102
|
RSpec/MultipleExpectations:
|
|
116
103
|
Max: 10
|
|
117
104
|
|
|
105
|
+
RSpec/MultipleMemoizedHelpers:
|
|
106
|
+
Max: 8
|
|
107
|
+
|
|
118
108
|
RSpec/SpecFilePathFormat:
|
|
119
109
|
Enabled: false
|
|
120
110
|
|
data/AGENTS.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Agent / contributor notes
|
|
2
|
+
|
|
3
|
+
## This repository
|
|
4
|
+
|
|
5
|
+
- Run Ruby tooling (**bundle**, **rubocop**, **rspec**) via **WSL** with **asdf** (use a login shell, e.g. `wsl bash -lic '…'`).
|
|
6
|
+
- **PostgreSQL** is expected in **Docker**; for local tests use **`aswin` / `aswin`** (`TEST_DB_USER`, `TEST_DB_PASSWORD`, or `DATABASE_URL` as appropriate).
|
|
7
|
+
|
|
8
|
+
More detail and example commands: `.cursor/rules/dev-environment.mdc`.
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,108 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
No changes yet.
|
|
11
|
+
|
|
12
|
+
## [1.5.0] - 2026-04-15
|
|
13
|
+
|
|
14
|
+
These notes follow the phased gap analysis in [`IMPLEMENTATION_PLAN.md`](IMPLEMENTATION_PLAN.md)
|
|
15
|
+
(baseline **v1.4.0**). This release ships Phase 0 cleanup, Phase 1 routing-test hardening,
|
|
16
|
+
Phase 2 production readiness (drift alerting, dashboard search/filter/pagination), Phase 3
|
|
17
|
+
PostgreSQL feature parity (column-level `UPDATE OF`, constraint triggers with deferral,
|
|
18
|
+
`schema.rb` / `structure.sql` integration), and Phase 4 trigger ordering hints.
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **Drift alerting** — Configurable `PgSqlTriggers.drift_notifier` for external notification when
|
|
23
|
+
drift detection finds drifted, dropped, or unknown triggers; `PgSqlTriggers::Alerting` module;
|
|
24
|
+
`PgSqlTriggers::Drift.check_and_notify`; Rake task `trigger:check_drift` with optional
|
|
25
|
+
`FAIL_ON_DRIFT=1`; `ActiveSupport::Notifications` event `pg_sql_triggers.drift_check`.
|
|
26
|
+
The gem’s root `Rakefile` loads `rakelib/pg_sql_triggers_environment.rake` and
|
|
27
|
+
`lib/tasks/trigger_migrations.rake` so `bundle exec rake trigger:*` works when developing the
|
|
28
|
+
gem (not only from a host app).
|
|
29
|
+
([lib/pg_sql_triggers/alerting.rb](lib/pg_sql_triggers/alerting.rb),
|
|
30
|
+
[lib/tasks/trigger_migrations.rake](lib/tasks/trigger_migrations.rake),
|
|
31
|
+
[lib/pg_sql_triggers/rake_development_boot.rb](lib/pg_sql_triggers/rake_development_boot.rb),
|
|
32
|
+
[docs/configuration.md](docs/configuration.md))
|
|
33
|
+
|
|
34
|
+
- **Column-level `UPDATE OF` triggers** — DSL method `on_update_of(*columns)` sets the event to
|
|
35
|
+
`update` and records column names; SQL generation and checksums use `EventsChecksum` so
|
|
36
|
+
`UPDATE OF "col1", "col2"` is represented consistently in drift detection. Registry validation
|
|
37
|
+
rejects column lists unless an update event is present.
|
|
38
|
+
([lib/pg_sql_triggers/dsl/trigger_definition.rb](lib/pg_sql_triggers/dsl/trigger_definition.rb),
|
|
39
|
+
[lib/pg_sql_triggers/events_checksum.rb](lib/pg_sql_triggers/events_checksum.rb),
|
|
40
|
+
[lib/pg_sql_triggers/registry/validator.rb](lib/pg_sql_triggers/registry/validator.rb))
|
|
41
|
+
|
|
42
|
+
- **Constraint triggers and deferral** — DSL supports `constraint_trigger!` with `deferrable` /
|
|
43
|
+
`initially` (constraint triggers only). Registry migration
|
|
44
|
+
`20260412185841_add_constraint_deferral_to_pg_sql_triggers_registry.rb` adds `constraint_trigger`,
|
|
45
|
+
`deferrable`, and `initially` columns. Validator and drift checksum paths normalize deferral using
|
|
46
|
+
catalog fields such as `tgdeferrable` / `tginitdeferred` where applicable.
|
|
47
|
+
([db/migrate/20260412185841_add_constraint_deferral_to_pg_sql_triggers_registry.rb](db/migrate/20260412185841_add_constraint_deferral_to_pg_sql_triggers_registry.rb),
|
|
48
|
+
[lib/pg_sql_triggers/dsl/trigger_definition.rb](lib/pg_sql_triggers/dsl/trigger_definition.rb),
|
|
49
|
+
[lib/pg_sql_triggers/registry/validator.rb](lib/pg_sql_triggers/registry/validator.rb))
|
|
50
|
+
|
|
51
|
+
- **Trigger ordering hints (`depends_on`)** — Optional `depends_on(*names)` on the DSL records
|
|
52
|
+
intended ordering relative to PostgreSQL’s alphabetical firing order. `Registry::Validator` checks
|
|
53
|
+
same-table scope, timing/`FOR EACH` consistency, absence of cycles, and name ordering; Rake task
|
|
54
|
+
`trigger:validate_order` runs the same rules from the CLI.
|
|
55
|
+
([lib/pg_sql_triggers/dsl/trigger_definition.rb](lib/pg_sql_triggers/dsl/trigger_definition.rb),
|
|
56
|
+
[lib/pg_sql_triggers/registry/validator.rb](lib/pg_sql_triggers/registry/validator.rb),
|
|
57
|
+
[lib/tasks/trigger_migrations.rake](lib/tasks/trigger_migrations.rake))
|
|
58
|
+
|
|
59
|
+
- **Dashboard search, filters, and pagination** — Trigger index supports query parameters
|
|
60
|
+
(`table`, `state`, `source`, `q`) with offset/limit pagination for large registries.
|
|
61
|
+
([app/controllers/pg_sql_triggers/dashboard_controller.rb](app/controllers/pg_sql_triggers/dashboard_controller.rb))
|
|
62
|
+
|
|
63
|
+
- **`schema.rb` awareness and trigger SQL snapshots** — `ActiveRecord::SchemaDumper` is prepended
|
|
64
|
+
with `SchemaDumperExtension` to append optional comments pointing at trigger workflows when
|
|
65
|
+
`PgSqlTriggers.append_trigger_notes_to_schema_dump` is true (default). Rake tasks `trigger:dump`
|
|
66
|
+
and `trigger:load` round-trip `db/trigger_structure.sql` via `TriggerStructureDumper`; path is
|
|
67
|
+
configurable with `PgSqlTriggers.trigger_structure_sql_path`. When
|
|
68
|
+
`PgSqlTriggers.migrate_triggers_after_schema_load` is true (default), `db:schema:load` can chain
|
|
69
|
+
to `trigger:migrate` (skippable with `SKIP_TRIGGER_MIGRATE_AFTER_SCHEMA_LOAD`).
|
|
70
|
+
([lib/pg_sql_triggers/schema_dumper_extension.rb](lib/pg_sql_triggers/schema_dumper_extension.rb),
|
|
71
|
+
[lib/pg_sql_triggers/trigger_structure_dumper.rb](lib/pg_sql_triggers/trigger_structure_dumper.rb),
|
|
72
|
+
[lib/pg_sql_triggers/engine.rb](lib/pg_sql_triggers/engine.rb),
|
|
73
|
+
[lib/tasks/trigger_migrations.rake](lib/tasks/trigger_migrations.rake),
|
|
74
|
+
[lib/pg_sql_triggers.rb](lib/pg_sql_triggers.rb))
|
|
75
|
+
|
|
76
|
+
- **Engine routing coverage** — Declarative routing specs for the mountable engine.
|
|
77
|
+
([spec/routing/pg_sql_triggers_routes_spec.rb](spec/routing/pg_sql_triggers_routes_spec.rb))
|
|
78
|
+
|
|
79
|
+
### Changed
|
|
80
|
+
|
|
81
|
+
- **SQL permission helper naming** — View and controller helpers use `can_execute_sql_operations?`
|
|
82
|
+
instead of capsule-era `can_execute_sql?`, with comments aligned to migration-style SQL execution.
|
|
83
|
+
([app/helpers/pg_sql_triggers/permissions_helper.rb](app/helpers/pg_sql_triggers/permissions_helper.rb),
|
|
84
|
+
[app/controllers/concerns/pg_sql_triggers/permission_checking.rb](app/controllers/concerns/pg_sql_triggers/permission_checking.rb))
|
|
85
|
+
|
|
86
|
+
- **`TriggerRegistry#recreate_trigger`** — Uses an explicit `source == "dsl"` branch before the
|
|
87
|
+
`function_body` path so DSL re-execution is obvious to readers.
|
|
88
|
+
([app/models/pg_sql_triggers/trigger_registry.rb](app/models/pg_sql_triggers/trigger_registry.rb))
|
|
89
|
+
|
|
90
|
+
### Fixed
|
|
91
|
+
|
|
92
|
+
- **`can_generate_triggers?` permission alignment** — Helper and concern both use the
|
|
93
|
+
`:generate_trigger` action so UI and controller authorization stay consistent.
|
|
94
|
+
([app/helpers/pg_sql_triggers/permissions_helper.rb](app/helpers/pg_sql_triggers/permissions_helper.rb),
|
|
95
|
+
[app/controllers/concerns/pg_sql_triggers/permission_checking.rb](app/controllers/concerns/pg_sql_triggers/permission_checking.rb))
|
|
96
|
+
|
|
97
|
+
### Planned
|
|
98
|
+
|
|
99
|
+
- **Documentation accuracy** — Regenerate [`COVERAGE.md`](COVERAGE.md) from the current tree with
|
|
100
|
+
SimpleCov (plan §1.1); keep user-facing docs in sync with removed v1.4.0 features where files
|
|
101
|
+
still exist.
|
|
102
|
+
|
|
103
|
+
- **Test hardening** — Additional request/controller specs for migration error paths and
|
|
104
|
+
permission edge cases (plan §4.2–4.3); extend coverage for notifier and dashboard filter
|
|
105
|
+
behaviour where gaps remain (plan Phase 1–2).
|
|
106
|
+
|
|
107
|
+
- **Optional (Phase 4)** — Kill-switch time-window auto-lock; trigger definition export/import
|
|
108
|
+
(JSON/YAML) and related Rake tasks.
|
|
109
|
+
|
|
8
110
|
## [1.4.0] - 2026-03-01
|
|
9
111
|
|
|
10
112
|
### Added
|
|
@@ -15,7 +117,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
15
117
|
`for_each_statement`, let trigger definitions declare the desired granularity explicitly. The
|
|
16
118
|
value defaults to `"row"` so all existing definitions continue to produce `FOR EACH ROW` triggers
|
|
17
119
|
without modification. The field is stored in a new `for_each` column on the registry table
|
|
18
|
-
(migration `
|
|
120
|
+
(migration `20260228162233_add_for_each_to_pg_sql_triggers_registry.rb`), included in all three
|
|
19
121
|
checksum computations (`TriggerRegistry#calculate_checksum`, `Registry::Manager#calculate_checksum`,
|
|
20
122
|
and `Drift::Detector#calculate_db_checksum`), extracted from live trigger definitions via a new
|
|
21
123
|
`extract_trigger_for_each` helper, and validated by `Registry::Validator` (only `"row"` and
|
|
@@ -26,7 +128,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
26
128
|
[lib/pg_sql_triggers/registry/validator.rb](lib/pg_sql_triggers/registry/validator.rb),
|
|
27
129
|
[lib/pg_sql_triggers/drift/detector.rb](lib/pg_sql_triggers/drift/detector.rb),
|
|
28
130
|
[app/models/pg_sql_triggers/trigger_registry.rb](app/models/pg_sql_triggers/trigger_registry.rb),
|
|
29
|
-
[db/migrate/
|
|
131
|
+
[db/migrate/20260228162233_add_for_each_to_pg_sql_triggers_registry.rb](db/migrate/20260228162233_add_for_each_to_pg_sql_triggers_registry.rb))
|
|
30
132
|
|
|
31
133
|
### Changed
|
|
32
134
|
|
data/COVERAGE.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Code Coverage Report
|
|
2
2
|
|
|
3
|
-
**Total Coverage:
|
|
3
|
+
**Total Coverage: 96.04%**
|
|
4
4
|
|
|
5
|
-
Covered:
|
|
5
|
+
Covered: 2499 / 2602 lines
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -10,58 +10,56 @@ Covered: 2319 / 2416 lines
|
|
|
10
10
|
|
|
11
11
|
| File | Coverage | Covered Lines | Missed Lines | Total Lines |
|
|
12
12
|
|------|----------|---------------|--------------|-------------|
|
|
13
|
-
| `lib/pg_sql_triggers/drift.rb` | 100.0% ✅ | 13 | 0 | 13 |
|
|
14
|
-
| `lib/pg_sql_triggers/drift/db_queries.rb` | 100.0% ✅ | 24 | 0 | 24 |
|
|
15
|
-
| `lib/pg_sql_triggers/dsl.rb` | 100.0% ✅ | 9 | 0 | 9 |
|
|
16
|
-
| `lib/pg_sql_triggers/dsl/trigger_definition.rb` | 100.0% ✅ | 37 | 0 | 37 |
|
|
17
|
-
| `lib/pg_sql_triggers/generator.rb` | 100.0% ✅ | 4 | 0 | 4 |
|
|
18
|
-
| `lib/pg_sql_triggers/generator/form.rb` | 100.0% ✅ | 36 | 0 | 36 |
|
|
19
|
-
| `lib/pg_sql_triggers/generator/service.rb` | 100.0% ✅ | 101 | 0 | 101 |
|
|
20
|
-
| `lib/generators/pg_sql_triggers/install_generator.rb` | 100.0% ✅ | 18 | 0 | 18 |
|
|
21
|
-
| `lib/generators/trigger/migration_generator.rb` | 100.0% ✅ | 27 | 0 | 27 |
|
|
22
13
|
| `lib/pg_sql_triggers/migration.rb` | 100.0% ✅ | 4 | 0 | 4 |
|
|
14
|
+
| `lib/pg_sql_triggers/migrator/pre_apply_comparator.rb` | 100.0% ✅ | 125 | 0 | 125 |
|
|
15
|
+
| `lib/generators/pg_sql_triggers/install_generator.rb` | 100.0% ✅ | 18 | 0 | 18 |
|
|
16
|
+
| `lib/pg_sql_triggers/dsl/trigger_definition.rb` | 100.0% ✅ | 60 | 0 | 60 |
|
|
17
|
+
| `lib/pg_sql_triggers/dsl.rb` | 100.0% ✅ | 9 | 0 | 9 |
|
|
23
18
|
| `lib/pg_sql_triggers/migrator/pre_apply_diff_reporter.rb` | 100.0% ✅ | 75 | 0 | 75 |
|
|
24
|
-
| `lib/pg_sql_triggers/
|
|
19
|
+
| `lib/pg_sql_triggers/sql.rb` | 100.0% ✅ | 7 | 0 | 7 |
|
|
20
|
+
| `lib/pg_sql_triggers/migrator/safety_validator.rb` | 100.0% ✅ | 120 | 0 | 120 |
|
|
21
|
+
| `lib/pg_sql_triggers/drift.rb` | 100.0% ✅ | 15 | 0 | 15 |
|
|
25
22
|
| `lib/pg_sql_triggers/permissions.rb` | 100.0% ✅ | 11 | 0 | 11 |
|
|
26
|
-
| `lib/pg_sql_triggers/permissions/checker.rb` | 100.0% ✅ |
|
|
27
|
-
| `
|
|
28
|
-
| `
|
|
29
|
-
| `
|
|
30
|
-
| `lib/pg_sql_triggers
|
|
31
|
-
| `lib/pg_sql_triggers/
|
|
23
|
+
| `lib/pg_sql_triggers/permissions/checker.rb` | 100.0% ✅ | 16 | 0 | 16 |
|
|
24
|
+
| `app/controllers/pg_sql_triggers/triggers_controller.rb` | 100.0% ✅ | 76 | 0 | 76 |
|
|
25
|
+
| `app/helpers/pg_sql_triggers/dashboard_helper.rb` | 100.0% ✅ | 7 | 0 | 7 |
|
|
26
|
+
| `app/controllers/pg_sql_triggers/dashboard_controller.rb` | 100.0% ✅ | 72 | 0 | 72 |
|
|
27
|
+
| `lib/pg_sql_triggers.rb` | 100.0% ✅ | 51 | 0 | 51 |
|
|
28
|
+
| `lib/pg_sql_triggers/errors.rb` | 100.0% ✅ | 83 | 0 | 83 |
|
|
32
29
|
| `lib/pg_sql_triggers/testing/dry_run.rb` | 100.0% ✅ | 24 | 0 | 24 |
|
|
33
|
-
| `
|
|
34
|
-
| `
|
|
30
|
+
| `lib/pg_sql_triggers/testing/syntax_validator.rb` | 100.0% ✅ | 58 | 0 | 58 |
|
|
31
|
+
| `lib/pg_sql_triggers/testing.rb` | 100.0% ✅ | 6 | 0 | 6 |
|
|
32
|
+
| `config/initializers/pg_sql_triggers.rb` | 100.0% ✅ | 10 | 0 | 10 |
|
|
33
|
+
| `app/models/pg_sql_triggers/application_record.rb` | 100.0% ✅ | 3 | 0 | 3 |
|
|
34
|
+
| `app/models/pg_sql_triggers/audit_log.rb` | 100.0% ✅ | 32 | 0 | 32 |
|
|
35
35
|
| `app/controllers/concerns/pg_sql_triggers/error_handling.rb` | 100.0% ✅ | 19 | 0 | 19 |
|
|
36
36
|
| `app/controllers/concerns/pg_sql_triggers/kill_switch_protection.rb` | 100.0% ✅ | 17 | 0 | 17 |
|
|
37
|
-
| `app/
|
|
37
|
+
| `app/controllers/concerns/pg_sql_triggers/permission_checking.rb` | 100.0% ✅ | 41 | 0 | 41 |
|
|
38
38
|
| `app/controllers/pg_sql_triggers/application_controller.rb` | 100.0% ✅ | 13 | 0 | 13 |
|
|
39
39
|
| `app/helpers/pg_sql_triggers/permissions_helper.rb` | 100.0% ✅ | 16 | 0 | 16 |
|
|
40
|
-
| `app/controllers/pg_sql_triggers/
|
|
41
|
-
| `
|
|
42
|
-
| `lib/pg_sql_triggers/
|
|
43
|
-
| `
|
|
44
|
-
| `
|
|
45
|
-
| `lib/pg_sql_triggers/
|
|
46
|
-
| `lib/pg_sql_triggers/drift/detector.rb` | 98.48% ✅ | 65 | 1 | 66 |
|
|
47
|
-
| `app/controllers/pg_sql_triggers/audit_logs_controller.rb` | 97.73% ✅ | 43 | 1 | 44 |
|
|
48
|
-
| `app/controllers/pg_sql_triggers/sql_capsules_controller.rb` | 97.14% ✅ | 68 | 2 | 70 |
|
|
40
|
+
| `app/controllers/pg_sql_triggers/audit_logs_controller.rb` | 100.0% ✅ | 55 | 0 | 55 |
|
|
41
|
+
| `app/controllers/pg_sql_triggers/migrations_controller.rb` | 98.82% ✅ | 84 | 1 | 85 |
|
|
42
|
+
| `lib/pg_sql_triggers/registry/manager.rb` | 98.81% ✅ | 83 | 1 | 84 |
|
|
43
|
+
| `lib/pg_sql_triggers/events_checksum.rb` | 98.33% ✅ | 59 | 1 | 60 |
|
|
44
|
+
| `app/models/pg_sql_triggers/trigger_registry.rb` | 97.69% ✅ | 211 | 5 | 216 |
|
|
45
|
+
| `lib/pg_sql_triggers/sql/kill_switch.rb` | 96.51% ✅ | 83 | 3 | 86 |
|
|
49
46
|
| `lib/generators/pg_sql_triggers/trigger_migration_generator.rb` | 96.3% ✅ | 26 | 1 | 27 |
|
|
50
|
-
| `lib/pg_sql_triggers/
|
|
51
|
-
| `lib/pg_sql_triggers/
|
|
52
|
-
| `lib/pg_sql_triggers/
|
|
53
|
-
| `
|
|
47
|
+
| `lib/pg_sql_triggers/alerting.rb` | 96.3% ✅ | 26 | 1 | 27 |
|
|
48
|
+
| `lib/pg_sql_triggers/drift/db_queries.rb` | 96.15% ✅ | 25 | 1 | 26 |
|
|
49
|
+
| `lib/pg_sql_triggers/deferral_checksum.rb` | 96.0% ✅ | 24 | 1 | 25 |
|
|
50
|
+
| `lib/pg_sql_triggers/migrator.rb` | 95.78% ✅ | 159 | 7 | 166 |
|
|
51
|
+
| `lib/pg_sql_triggers/registry/validator.rb` | 94.83% ✅ | 165 | 9 | 174 |
|
|
54
52
|
| `lib/pg_sql_triggers/database_introspection.rb` | 94.29% ✅ | 66 | 4 | 70 |
|
|
55
53
|
| `lib/pg_sql_triggers/drift/reporter.rb` | 94.12% ✅ | 96 | 6 | 102 |
|
|
56
|
-
| `lib/pg_sql_triggers/
|
|
54
|
+
| `lib/pg_sql_triggers/drift/detector.rb` | 92.5% ✅ | 74 | 6 | 80 |
|
|
57
55
|
| `lib/pg_sql_triggers/testing/safe_executor.rb` | 91.89% ✅ | 34 | 3 | 37 |
|
|
58
56
|
| `lib/pg_sql_triggers/registry.rb` | 91.84% ✅ | 45 | 4 | 49 |
|
|
59
|
-
| `
|
|
60
|
-
| `lib/pg_sql_triggers/
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
| `
|
|
64
|
-
| `config/routes.rb` |
|
|
57
|
+
| `lib/pg_sql_triggers/trigger_structure_dumper.rb` | 90.32% ✅ | 56 | 6 | 62 |
|
|
58
|
+
| `lib/pg_sql_triggers/testing/function_tester.rb` | 88.31% ⚠️ | 68 | 9 | 77 |
|
|
59
|
+
| `app/controllers/pg_sql_triggers/tables_controller.rb` | 83.78% ⚠️ | 31 | 6 | 37 |
|
|
60
|
+
| `lib/pg_sql_triggers/schema_dumper_extension.rb` | 73.33% ⚠️ | 11 | 4 | 15 |
|
|
61
|
+
| `lib/pg_sql_triggers/engine.rb` | 72.97% ⚠️ | 27 | 10 | 37 |
|
|
62
|
+
| `config/routes.rb` | 17.65% ❌ | 3 | 14 | 17 |
|
|
65
63
|
|
|
66
64
|
---
|
|
67
65
|
|
data/LICENSE
CHANGED
|
File without changes
|
data/README.md
CHANGED
|
@@ -42,6 +42,8 @@ rails generate pg_sql_triggers:install
|
|
|
42
42
|
rails db:migrate
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
+
Schema migrations bundled with the gem are listed in [Getting Started — Gem schema migrations](docs/getting-started.md#gem-schema-migrations) (ordered `db/migrate/*.rb` filenames).
|
|
46
|
+
|
|
45
47
|
### Define a Trigger
|
|
46
48
|
|
|
47
49
|
```ruby
|
|
@@ -97,9 +99,27 @@ Files land in `app/triggers/` and `db/triggers/` for code review like any other
|
|
|
97
99
|
### Migration System
|
|
98
100
|
Manage trigger functions and definitions with a migration system similar to Rails schema migrations.
|
|
99
101
|
|
|
102
|
+
### schema.rb, structure.sql, and trigger snapshots
|
|
103
|
+
|
|
104
|
+
`db:schema:dump` does not capture PostgreSQL triggers. This gem addresses that in three ways:
|
|
105
|
+
|
|
106
|
+
1. **Comments in `schema.rb`** — When using the default Ruby schema format, `rails db:schema:dump` appends a short note listing managed triggers and pointing to `trigger:migrate` / `trigger:load`. Disable with `PgSqlTriggers.append_trigger_notes_to_schema_dump = false`.
|
|
107
|
+
2. **`db/trigger_structure.sql`** — Run `rails trigger:dump` to write `CREATE FUNCTION` / `CREATE TRIGGER` statements for registered triggers (or all non-internal triggers in `public` if the registry table is absent). Apply on a fresh DB with `rails trigger:load` (runs arbitrary SQL; kill switch applies in protected environments). Override the path with `FILE=...` or `TRIGGER_STRUCTURE_SQL=...`, or set `PgSqlTriggers.trigger_structure_sql_path`.
|
|
108
|
+
3. **`db:schema:load`** — After loading `schema.rb`, `trigger:migrate` runs automatically so pending trigger migrations apply. Opt out with `SKIP_TRIGGER_MIGRATE_AFTER_SCHEMA_LOAD=1` or `PgSqlTriggers.migrate_triggers_after_schema_load = false`.
|
|
109
|
+
|
|
110
|
+
For a single SQL artifact that includes tables and triggers, set `config.active_record.schema_format = :sql` and use Rails’ `structure.sql` workflow; keep `db/triggers` migrations as the source of truth and refresh `db/trigger_structure.sql` when you want a portable trigger-only snapshot.
|
|
111
|
+
|
|
100
112
|
### Drift Detection
|
|
101
113
|
Automatically detect when database triggers drift from your DSL definitions. N+1-free bulk detection across all triggers.
|
|
102
114
|
|
|
115
|
+
### Drift Alerting
|
|
116
|
+
Configure `PgSqlTriggers.drift_notifier` to push alerts to Slack, PagerDuty, email, or any external system when drift detection finds drifted, dropped, or unknown triggers. Schedule `rake trigger:check_drift` (with optional `FAIL_ON_DRIFT=1`) for CI or cron-based monitoring. An `ActiveSupport::Notifications` event `pg_sql_triggers.drift_check` is emitted on every run for APM instrumentation.
|
|
117
|
+
|
|
118
|
+
### Advanced PostgreSQL Trigger Features
|
|
119
|
+
- **Column-level `UPDATE OF` triggers** — `on_update_of(:email, :status)` fires only when specific columns change, a common audit/performance optimisation.
|
|
120
|
+
- **Constraint triggers with deferral** — `constraint_trigger!` plus `deferrable` / `initially` options emit `CREATE CONSTRAINT TRIGGER ... DEFERRABLE INITIALLY DEFERRED` for referential-integrity patterns that need to fire at transaction commit.
|
|
121
|
+
- **Ordering hints** — `depends_on "other_trigger"` captures intended firing order among same-table triggers. PostgreSQL fires same-kind triggers alphabetically; `rake trigger:validate_order` (and `Registry.validate!`) verifies declared dependencies, flags cycles, and enforces name-ordering so declared order matches execution order.
|
|
122
|
+
|
|
103
123
|
### Production Kill Switch
|
|
104
124
|
Multi-layered safety mechanism preventing accidental destructive operations in production environments.
|
|
105
125
|
|
|
@@ -109,6 +129,7 @@ Visual interface for managing triggers and running migrations. Includes:
|
|
|
109
129
|
- **Last Applied Tracking**: See when triggers were last applied with human-readable timestamps
|
|
110
130
|
- **Breadcrumb Navigation**: Easy navigation between dashboard, tables, and triggers
|
|
111
131
|
- **Permission-Aware UI**: Buttons show/hide based on user role
|
|
132
|
+
- **Search, Filter, and Pagination**: Filter triggers by table, drift state, or source; full-text search on name/table; offset/limit pagination (`?table=users&state=drifted&source=dsl&q=email&trigger_page=2&trigger_per_page=20`)
|
|
112
133
|
|
|
113
134
|
### Audit Logging
|
|
114
135
|
Comprehensive audit trail for all trigger operations:
|
|
@@ -171,9 +192,9 @@ To install this gem locally, run `bundle exec rake install`. To release a new ve
|
|
|
171
192
|
|
|
172
193
|
## Test Coverage
|
|
173
194
|
|
|
174
|
-
See [COVERAGE.md](COVERAGE.md) for detailed coverage information.
|
|
175
|
-
|
|
176
|
-
|
|
195
|
+
See [COVERAGE.md](COVERAGE.md) for detailed coverage information. The bundled report was
|
|
196
|
+
generated against an earlier tree and will be regenerated as part of the next release cycle
|
|
197
|
+
(run `bundle exec rspec` with SimpleCov, then `ruby scripts/generate_coverage_report.rb`).
|
|
177
198
|
|
|
178
199
|
## Contributing
|
|
179
200
|
|
data/RELEASE.md
CHANGED
|
File without changes
|
data/Rakefile
CHANGED
|
@@ -6,6 +6,11 @@
|
|
|
6
6
|
# - rake release: Build, tag, push to git, and publish to RubyGems.org
|
|
7
7
|
require "bundler/gem_tasks"
|
|
8
8
|
|
|
9
|
+
# Minimal Rails + engine task load so `bundle exec rake trigger:*` works from this repo
|
|
10
|
+
# (host apps load these via Rails::Engine#rake_tasks instead).
|
|
11
|
+
Dir[File.expand_path("rakelib/**/*.rake", __dir__)].each { |f| load f }
|
|
12
|
+
load File.expand_path("lib/tasks/trigger_migrations.rake", __dir__)
|
|
13
|
+
|
|
9
14
|
# RSpec tasks:
|
|
10
15
|
# - rake spec: Run the test suite
|
|
11
16
|
require "rspec/core/rake_task"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -7,7 +7,7 @@ module PgSqlTriggers
|
|
|
7
7
|
included do
|
|
8
8
|
# Helper methods available in views
|
|
9
9
|
helper_method :current_actor, :can_view_triggers?, :can_enable_disable_triggers?,
|
|
10
|
-
:can_drop_triggers?, :
|
|
10
|
+
:can_drop_triggers?, :can_execute_sql_operations?, :can_generate_triggers?, :can_apply_triggers?
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
# Returns the current actor (user) performing the action.
|
|
@@ -67,7 +67,7 @@ module PgSqlTriggers
|
|
|
67
67
|
redirect_to root_path, alert: "Insufficient permissions. Operator role required."
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
-
# Checks if current actor has admin permissions (drop/re-execute
|
|
70
|
+
# Checks if current actor has admin permissions (drop/re-execute).
|
|
71
71
|
#
|
|
72
72
|
# @raise [ActionController::RedirectError] Redirects if permission denied
|
|
73
73
|
def check_admin_permission
|
|
@@ -99,14 +99,15 @@ module PgSqlTriggers
|
|
|
99
99
|
PgSqlTriggers::Permissions.can?(current_actor, :drop_trigger, environment: current_environment)
|
|
100
100
|
end
|
|
101
101
|
|
|
102
|
-
# @return [Boolean] true if
|
|
103
|
-
|
|
102
|
+
# @return [Boolean] true if the +:execute_sql+ action is allowed (privileged SQL for host apps;
|
|
103
|
+
# not used by built-in UI)
|
|
104
|
+
def can_execute_sql_operations?
|
|
104
105
|
PgSqlTriggers::Permissions.can?(current_actor, :execute_sql, environment: current_environment)
|
|
105
106
|
end
|
|
106
107
|
|
|
107
108
|
# @return [Boolean] true if current actor can generate triggers
|
|
108
109
|
def can_generate_triggers?
|
|
109
|
-
PgSqlTriggers::Permissions.can?(current_actor, :
|
|
110
|
+
PgSqlTriggers::Permissions.can?(current_actor, :generate_trigger, environment: current_environment)
|
|
110
111
|
end
|
|
111
112
|
|
|
112
113
|
# @return [Boolean] true if current actor can apply triggers
|
|
File without changes
|