pg_sql_triggers 1.0.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 (59) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +120 -0
  4. data/CHANGELOG.md +52 -0
  5. data/Goal.md +294 -0
  6. data/LICENSE +21 -0
  7. data/README.md +294 -0
  8. data/RELEASE.md +270 -0
  9. data/Rakefile +16 -0
  10. data/app/assets/javascripts/pg_sql_triggers/application.js +5 -0
  11. data/app/assets/stylesheets/pg_sql_triggers/application.css +179 -0
  12. data/app/controllers/pg_sql_triggers/application_controller.rb +35 -0
  13. data/app/controllers/pg_sql_triggers/dashboard_controller.rb +42 -0
  14. data/app/controllers/pg_sql_triggers/generator_controller.rb +145 -0
  15. data/app/controllers/pg_sql_triggers/migrations_controller.rb +84 -0
  16. data/app/controllers/pg_sql_triggers/tables_controller.rb +44 -0
  17. data/app/models/pg_sql_triggers/application_record.rb +7 -0
  18. data/app/models/pg_sql_triggers/trigger_registry.rb +93 -0
  19. data/app/views/layouts/pg_sql_triggers/application.html.erb +72 -0
  20. data/app/views/pg_sql_triggers/dashboard/index.html.erb +225 -0
  21. data/app/views/pg_sql_triggers/generator/new.html.erb +370 -0
  22. data/app/views/pg_sql_triggers/generator/preview.html.erb +77 -0
  23. data/app/views/pg_sql_triggers/tables/index.html.erb +105 -0
  24. data/app/views/pg_sql_triggers/tables/show.html.erb +126 -0
  25. data/config/routes.rb +35 -0
  26. data/db/migrate/20251222000001_create_pg_sql_triggers_tables.rb +29 -0
  27. data/lib/generators/pg_sql_triggers/install_generator.rb +36 -0
  28. data/lib/generators/pg_sql_triggers/templates/README +36 -0
  29. data/lib/generators/pg_sql_triggers/templates/create_pg_sql_triggers_tables.rb +36 -0
  30. data/lib/generators/pg_sql_triggers/templates/initializer.rb +27 -0
  31. data/lib/generators/pg_sql_triggers/templates/trigger_migration.rb.erb +32 -0
  32. data/lib/generators/pg_sql_triggers/trigger_migration_generator.rb +60 -0
  33. data/lib/generators/trigger/migration_generator.rb +60 -0
  34. data/lib/pg_sql_triggers/database_introspection.rb +251 -0
  35. data/lib/pg_sql_triggers/drift.rb +24 -0
  36. data/lib/pg_sql_triggers/dsl/trigger_definition.rb +67 -0
  37. data/lib/pg_sql_triggers/dsl.rb +15 -0
  38. data/lib/pg_sql_triggers/engine.rb +29 -0
  39. data/lib/pg_sql_triggers/generator/form.rb +78 -0
  40. data/lib/pg_sql_triggers/generator/service.rb +251 -0
  41. data/lib/pg_sql_triggers/generator.rb +8 -0
  42. data/lib/pg_sql_triggers/migration.rb +15 -0
  43. data/lib/pg_sql_triggers/migrator.rb +237 -0
  44. data/lib/pg_sql_triggers/permissions/checker.rb +33 -0
  45. data/lib/pg_sql_triggers/permissions.rb +35 -0
  46. data/lib/pg_sql_triggers/registry/manager.rb +47 -0
  47. data/lib/pg_sql_triggers/registry/validator.rb +15 -0
  48. data/lib/pg_sql_triggers/registry.rb +36 -0
  49. data/lib/pg_sql_triggers/sql.rb +21 -0
  50. data/lib/pg_sql_triggers/testing/dry_run.rb +74 -0
  51. data/lib/pg_sql_triggers/testing/function_tester.rb +118 -0
  52. data/lib/pg_sql_triggers/testing/safe_executor.rb +66 -0
  53. data/lib/pg_sql_triggers/testing/syntax_validator.rb +124 -0
  54. data/lib/pg_sql_triggers/testing.rb +10 -0
  55. data/lib/pg_sql_triggers/version.rb +15 -0
  56. data/lib/pg_sql_triggers.rb +41 -0
  57. data/lib/tasks/trigger_migrations.rake +254 -0
  58. data/sig/pg_sql_triggers.rbs +4 -0
  59. metadata +260 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 868dfd4da507455302b3e00e6dd27a8cc5440c6e81c60e978e10a031a339e8c1
4
+ data.tar.gz: e8b013e8485431fac4d30bfd7f3faade2130b4b55d1be4cfbf0f9ffdbad2ef6f
5
+ SHA512:
6
+ metadata.gz: 1265add39a041703438f7406a6a4b9c6f7b6e5dcf06240c8dc53c53f97c747cea850f1fa603d9eacbe072a43fbc4fe0cc371536e2bd8a215f30609171ccb8ffa
7
+ data.tar.gz: e0d649df16316ef840fd5d42ef199b5dcf9f22d4ebe20134250ffe8b369d52c16f3774640ab95294ca487f4cb065d624bb7e386d1c223de536fc3dfc16d03511
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,120 @@
1
+ require:
2
+ - rubocop-rspec
3
+ - rubocop-rspec_rails
4
+
5
+ plugins:
6
+ - rubocop-rails
7
+ - rubocop-capybara
8
+ - rubocop-factory_bot
9
+
10
+ AllCops:
11
+ TargetRubyVersion: 2.7
12
+ NewCops: enable
13
+ SuggestExtensions: false
14
+ Exclude:
15
+ - 'bin/**/*'
16
+ - 'vendor/**/*'
17
+ - 'node_modules/**/*'
18
+ - 'db/schema.rb'
19
+ - 'tmp/**/*'
20
+ - 'spec/dummy/**/*'
21
+
22
+ # Metrics
23
+ Metrics/AbcSize:
24
+ Max: 65
25
+ Exclude:
26
+ - 'spec/**/*'
27
+ - 'app/controllers/**/*'
28
+ - 'lib/generators/**/*'
29
+ - 'lib/pg_sql_triggers/testing/**/*'
30
+
31
+ Metrics/BlockLength:
32
+ Max: 50
33
+ Exclude:
34
+ - 'spec/**/*'
35
+ - 'config/routes.rb'
36
+ - '*.gemspec'
37
+ - 'lib/tasks/**/*'
38
+
39
+ Metrics/ClassLength:
40
+ Max: 200
41
+ Exclude:
42
+ - 'spec/**/*'
43
+
44
+ Metrics/CyclomaticComplexity:
45
+ Max: 18
46
+ Exclude:
47
+ - 'app/controllers/**/*'
48
+ - 'lib/pg_sql_triggers/testing/**/*'
49
+
50
+ Metrics/MethodLength:
51
+ Max: 55
52
+ Exclude:
53
+ - 'spec/**/*'
54
+ - 'app/controllers/**/*'
55
+ - 'lib/generators/**/*'
56
+ - 'lib/pg_sql_triggers/testing/**/*'
57
+
58
+ Metrics/PerceivedComplexity:
59
+ Max: 15
60
+ Exclude:
61
+ - 'app/controllers/**/*'
62
+ - 'lib/pg_sql_triggers/testing/**/*'
63
+
64
+ # Style
65
+ Style/Documentation:
66
+ Enabled: false
67
+
68
+ Style/StringLiterals:
69
+ EnforcedStyle: double_quotes
70
+
71
+ Style/FrozenStringLiteralComment:
72
+ Enabled: true
73
+
74
+ # Layout
75
+ Layout/LineLength:
76
+ Max: 120
77
+ Exclude:
78
+ - 'spec/**/*'
79
+
80
+ # Gemspec
81
+ Gemspec/DevelopmentDependencies:
82
+ Enabled: false
83
+
84
+ # Rails
85
+ Rails:
86
+ Enabled: true
87
+
88
+ Rails/I18nLocaleTexts:
89
+ Enabled: false
90
+
91
+ # RSpec
92
+ RSpec/ExampleLength:
93
+ Max: 10
94
+ Exclude:
95
+ - 'spec/**/*_spec.rb'
96
+
97
+ RSpec/FilePath:
98
+ Enabled: false
99
+
100
+ RSpec/InstanceVariable:
101
+ Enabled: false
102
+
103
+ RSpec/MessageChain:
104
+ Enabled: false
105
+
106
+ RSpec/MessageSpies:
107
+ Enabled: false
108
+
109
+ RSpec/MultipleDescribes:
110
+ Enabled: false
111
+
112
+ RSpec/MultipleExpectations:
113
+ Max: 10
114
+
115
+ RSpec/SpecFilePathFormat:
116
+ Enabled: false
117
+
118
+ # Capybara
119
+ Capybara/RSpec/PredicateMatcher:
120
+ Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,52 @@
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.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2025-12-27
9
+
10
+ ### Added
11
+ - Initial gem structure
12
+ - PostgreSQL trigger DSL for defining triggers with version and environment support
13
+ - Trigger registry system for tracking trigger metadata (trigger_name, table_name, version, enabled, checksum, source, environment)
14
+ - Drift detection between DSL definitions and database state (Managed & In Sync, Managed & Drifted, Manual Override, Disabled, Dropped, Unknown)
15
+ - Permission system with three levels (Viewer, Operator, Admin)
16
+ - Mountable Rails Engine with web UI for trigger management
17
+ - Production kill switch for safety (blocks destructive operations in production by default)
18
+ - Console introspection APIs (list, enabled, disabled, for_table, diff, validate!)
19
+ - Migration system for registry table
20
+ - Install generator (`rails generate pg_sql_triggers:install`)
21
+ - Trigger migration system similar to Rails schema migrations
22
+ - Generate trigger migrations
23
+ - Run pending migrations (`rake trigger:migrate`)
24
+ - Rollback migrations (`rake trigger:rollback`)
25
+ - Migration status and individual migration controls
26
+ - Combined schema and trigger migration tasks (`rake db:migrate:with_triggers`)
27
+ - Web UI for trigger migrations (up/down/redo)
28
+ - Apply all pending migrations from dashboard
29
+ - Rollback last migration
30
+ - Redo last migration
31
+ - Individual migration actions (up/down/redo) for each migration
32
+ - Flash messages for success, error, warning, and info states
33
+ - Database introspection for trigger state detection
34
+ - SQL execution support with safety checks
35
+ - Trigger generator with form and service layer
36
+ - Testing utilities for safe execution and syntax validation
37
+
38
+ ### Changed
39
+ - Initial release
40
+
41
+ ### Deprecated
42
+ - Nothing yet
43
+
44
+ ### Removed
45
+ - Nothing yet
46
+
47
+ ### Fixed
48
+ - Initial release
49
+
50
+ ### Security
51
+ - Production kill switch prevents destructive operations in production environments
52
+ - Permission system enforces role-based access control (Viewer, Operator, Admin)
data/Goal.md ADDED
@@ -0,0 +1,294 @@
1
+ You are building a **production-grade Ruby on Rails gem** named **pg_sql_triggers**.
2
+
3
+ This gem is **not a toy generator**.
4
+ It is a **PostgreSQL Trigger Control Plane for Rails**, designed for real teams running production systems.
5
+
6
+ You must follow everything below strictly.
7
+
8
+ ---
9
+
10
+ ## 1. Problem Statement
11
+
12
+ Rails teams use PostgreSQL triggers for:
13
+ - data integrity
14
+ - performance
15
+ - billing logic
16
+
17
+ But triggers today are:
18
+ - managed manually
19
+ - invisible to Rails
20
+ - unsafe to deploy
21
+ - easy to drift
22
+
23
+ This gem brings triggers into the Rails ecosystem with:
24
+ - lifecycle management
25
+ - safe deploys
26
+ - versioning
27
+ - UI control
28
+ - emergency SQL escape hatches
29
+
30
+ ---
31
+
32
+ ## 2. Core Philosophy
33
+
34
+ - Rails-native
35
+ - Explicit over magic
36
+ - Safe by default
37
+ - Power with guardrails
38
+
39
+ This gem **manages lifecycle**, not business logic.
40
+
41
+ ---
42
+
43
+ ## 3. Supported Capabilities (MUST IMPLEMENT)
44
+
45
+ ### A. Trigger Declaration (DSL)
46
+
47
+ Developers declare triggers using Ruby DSL:
48
+
49
+ ```ruby
50
+ pg_sql_trigger "users_email_validation" do
51
+ table :users
52
+ on :insert, :update
53
+ function :validate_user_email
54
+
55
+ version 3
56
+ enabled true
57
+
58
+ when_env :production
59
+ end
60
+ ```
61
+
62
+ Rules:
63
+ - DSL generates metadata, NOT raw SQL
64
+ - Every trigger has a version
65
+ - Triggers are environment-aware
66
+ - Triggers can be enabled or disabled
67
+
68
+ ---
69
+
70
+ ### B. Trigger Generation
71
+
72
+ The gem must generate triggers safely.
73
+
74
+ Generators create:
75
+ 1. Trigger DSL file
76
+ 2. Function stub (PL/pgSQL)
77
+ 3. Manifest metadata
78
+
79
+ Rules:
80
+ - Generated triggers are **disabled by default**
81
+ - Nothing executes automatically
82
+ - Developers must explicitly apply
83
+
84
+ ---
85
+
86
+ ### C. Trigger Registry (Source of Truth)
87
+
88
+ All triggers must be tracked in a registry table.
89
+
90
+ Registry tracks:
91
+ - trigger_name
92
+ - table_name
93
+ - version
94
+ - enabled
95
+ - checksum
96
+ - source (dsl / generated / manual_sql)
97
+ - environment
98
+ - installed_at
99
+ - last_verified_at
100
+
101
+ Rails must always know:
102
+ - what exists
103
+ - how it was created
104
+ - whether it drifted
105
+
106
+ ---
107
+
108
+ ### D. Safe Apply & Deploy
109
+
110
+ Applying triggers must:
111
+ - Run in a transaction
112
+ - Diff expected vs actual
113
+ - Never blindly DROP + CREATE
114
+ - Support rollback on failure
115
+ - Update registry atomically
116
+
117
+ ---
118
+
119
+ ### E. Drift Detection
120
+
121
+ System must detect:
122
+ - Missing triggers
123
+ - Version mismatch
124
+ - Function body drift
125
+ - Manual SQL overrides
126
+ - Unknown external triggers
127
+
128
+ Drift states:
129
+ 1. Managed & In Sync
130
+ 2. Managed & Drifted
131
+ 3. Manual Override
132
+ 4. Disabled
133
+ 5. Dropped (Recorded)
134
+ 6. Unknown (External)
135
+
136
+ ---
137
+
138
+ ### F. Rails Console Introspection
139
+
140
+ Provide console APIs:
141
+
142
+ PgSqlTrigger.list
143
+ PgSqlTrigger.enabled
144
+ PgSqlTrigger.disabled
145
+ PgSqlTrigger.for_table(:users)
146
+ PgSqlTrigger.diff
147
+ PgSqlTrigger.validate!
148
+
149
+ No raw SQL required by users.
150
+
151
+ ---
152
+
153
+ ## 4. Free-Form SQL Execution (MANDATORY)
154
+
155
+ The gem MUST support free-form SQL execution.
156
+
157
+ This is required for:
158
+ - emergency fixes
159
+ - complex migrations
160
+ - DB-owner workflows
161
+
162
+ ### SQL Capsules
163
+
164
+ Free-form SQL is wrapped in **named SQL capsules**:
165
+
166
+ - Must be named
167
+ - Must declare environment
168
+ - Must declare purpose
169
+ - Must be applied explicitly
170
+
171
+ Rules:
172
+ - Runs in a transaction
173
+ - Checksum verified
174
+ - Registry updated
175
+ - Marked as `source = manual_sql`
176
+
177
+ ---
178
+
179
+ ## 5. Permissions Model v1
180
+
181
+ Three permission levels:
182
+
183
+ ### Viewer
184
+ - Read-only
185
+ - View triggers
186
+ - View diffs
187
+
188
+ ### Operator
189
+ - Enable / Disable triggers
190
+ - Apply generated triggers
191
+ - Re-execute triggers in non-prod
192
+ - Dry-run SQL
193
+
194
+ ### Admin
195
+ - Drop triggers
196
+ - Execute free-form SQL
197
+ - Re-execute triggers in any env
198
+ - Override drift
199
+
200
+ Permissions enforced in:
201
+ - UI
202
+ - CLI
203
+ - Console
204
+
205
+ ---
206
+
207
+ ## 6. Kill Switch for Production SQL (MANDATORY)
208
+
209
+ Production mutations must be gated.
210
+
211
+ ### Levels:
212
+ 1. Global disable (default)
213
+ 2. Runtime ENV override
214
+ 3. Explicit confirmation text
215
+ 4. Optional time-window auto-lock
216
+
217
+ Kill switch must:
218
+ - Block UI
219
+ - Block CLI
220
+ - Block console
221
+ - Always log attempts
222
+
223
+ ---
224
+
225
+ ## 8. UI (Mountable Rails Engine)
226
+
227
+ UI is operational, not decorative.
228
+
229
+ ### Dashboard
230
+ - Trigger name
231
+ - Table
232
+ - Version
233
+ - Status
234
+ - Source
235
+ - Drift state
236
+ - Environment
237
+ - Last applied
238
+
239
+ ### Trigger Detail Page
240
+ - Summary panel
241
+ - SQL diff
242
+ - Registry state
243
+
244
+ ### Actions (State-Based)
245
+ - Enable
246
+ - Disable
247
+ - Drop
248
+ - Re-execute
249
+ - Execute SQL capsule
250
+
251
+ Buttons must:
252
+ - Be permission-aware
253
+ - Be env-aware
254
+ - Respect kill switch
255
+
256
+ ---
257
+
258
+ ## 9. Drop & Re-Execute Flow (CRITICAL)
259
+
260
+ Re-execute must:
261
+ 1. Show diff
262
+ 2. Require reason
263
+ 3. Require typed confirmation
264
+ 4. Execute transactionally
265
+ 5. Update registry
266
+
267
+ No silent operations allowed.
268
+
269
+ ---
270
+
271
+ ## 10. What This Gem Is NOT
272
+
273
+ - Not a raw SQL editor
274
+ - Not a trigger playground
275
+ - Not auto-executing
276
+ - Not unsafe
277
+ - Not magic
278
+
279
+ ---
280
+
281
+ ## 11. Non-Negotiable Constraints
282
+
283
+ - No silent prod changes
284
+ - No hidden state
285
+ - No bypassing registry
286
+ - No bypassing permissions
287
+
288
+ ---
289
+
290
+ ## 12. Final Framing (VERY IMPORTANT)
291
+
292
+ This gem must be described as:
293
+
294
+ > **A PostgreSQL Trigger Control Plane for Rails**
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 samaswin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.