pg_sql_triggers 1.0.0 → 1.0.1

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/.erb_lint.yml +47 -0
  3. data/.rubocop.yml +4 -1
  4. data/CHANGELOG.md +29 -1
  5. data/Goal.md +408 -123
  6. data/README.md +47 -215
  7. data/app/controllers/pg_sql_triggers/application_controller.rb +46 -0
  8. data/app/controllers/pg_sql_triggers/generator_controller.rb +10 -4
  9. data/app/controllers/pg_sql_triggers/migrations_controller.rb +18 -0
  10. data/app/models/pg_sql_triggers/trigger_registry.rb +20 -2
  11. data/app/views/layouts/pg_sql_triggers/application.html.erb +34 -1
  12. data/app/views/pg_sql_triggers/dashboard/index.html.erb +70 -30
  13. data/app/views/pg_sql_triggers/generator/new.html.erb +4 -4
  14. data/app/views/pg_sql_triggers/generator/preview.html.erb +14 -6
  15. data/app/views/pg_sql_triggers/shared/_confirmation_modal.html.erb +189 -0
  16. data/app/views/pg_sql_triggers/shared/_kill_switch_status.html.erb +40 -0
  17. data/app/views/pg_sql_triggers/tables/index.html.erb +0 -2
  18. data/app/views/pg_sql_triggers/tables/show.html.erb +3 -4
  19. data/db/migrate/20251222000001_create_pg_sql_triggers_tables.rb +1 -1
  20. data/docs/README.md +66 -0
  21. data/docs/api-reference.md +663 -0
  22. data/docs/configuration.md +541 -0
  23. data/docs/getting-started.md +135 -0
  24. data/docs/kill-switch.md +586 -0
  25. data/docs/screenshots/.gitkeep +1 -0
  26. data/docs/screenshots/Generate Trigger.png +0 -0
  27. data/docs/screenshots/Triggers Page.png +0 -0
  28. data/docs/screenshots/kill error.png +0 -0
  29. data/docs/screenshots/kill modal for migration down.png +0 -0
  30. data/docs/usage-guide.md +420 -0
  31. data/docs/web-ui.md +339 -0
  32. data/lib/generators/pg_sql_triggers/templates/create_pg_sql_triggers_tables.rb +1 -1
  33. data/lib/generators/pg_sql_triggers/templates/initializer.rb +36 -2
  34. data/lib/pg_sql_triggers/generator/service.rb +1 -1
  35. data/lib/pg_sql_triggers/migration.rb +1 -1
  36. data/lib/pg_sql_triggers/migrator.rb +27 -3
  37. data/lib/pg_sql_triggers/registry/manager.rb +6 -6
  38. data/lib/pg_sql_triggers/sql/kill_switch.rb +300 -0
  39. data/lib/pg_sql_triggers/testing/dry_run.rb +5 -7
  40. data/lib/pg_sql_triggers/testing/safe_executor.rb +23 -11
  41. data/lib/pg_sql_triggers/version.rb +1 -1
  42. data/lib/pg_sql_triggers.rb +12 -0
  43. data/lib/tasks/trigger_migrations.rake +33 -0
  44. metadata +35 -5
@@ -0,0 +1,420 @@
1
+ # Usage Guide
2
+
3
+ This guide covers the core features of PgSqlTriggers: trigger definitions using the DSL, migration management, and drift detection.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Declaring Triggers](#declaring-triggers)
8
+ - [Trigger Migrations](#trigger-migrations)
9
+ - [Combined Schema and Trigger Migrations](#combined-schema-and-trigger-migrations)
10
+ - [Drift Detection](#drift-detection)
11
+
12
+ ## Declaring Triggers
13
+
14
+ PgSqlTriggers provides a Ruby DSL for defining triggers. Trigger definitions are declarative and separate from their implementation.
15
+
16
+ ### Basic Trigger Definition
17
+
18
+ Create trigger definition files in `app/triggers/`:
19
+
20
+ ```ruby
21
+ # app/triggers/users_email_validation.rb
22
+ PgSqlTriggers::DSL.pg_sql_trigger "users_email_validation" do
23
+ table :users
24
+ on :insert, :update
25
+ function :validate_user_email
26
+
27
+ version 1
28
+ enabled false
29
+
30
+ when_env :production
31
+ end
32
+ ```
33
+
34
+ ### DSL Reference
35
+
36
+ #### `table`
37
+ Specifies which table the trigger is attached to:
38
+
39
+ ```ruby
40
+ table :users
41
+ ```
42
+
43
+ #### `on`
44
+ Defines when the trigger fires (one or more events):
45
+
46
+ ```ruby
47
+ on :insert # Single event
48
+ on :insert, :update # Multiple events
49
+ on :delete # Delete operations
50
+ ```
51
+
52
+ #### `function`
53
+ The PostgreSQL function that the trigger executes:
54
+
55
+ ```ruby
56
+ function :validate_user_email
57
+ ```
58
+
59
+ #### `version`
60
+ Version number for tracking changes:
61
+
62
+ ```ruby
63
+ version 1 # Increment when trigger logic changes
64
+ ```
65
+
66
+ #### `enabled`
67
+ Initial state of the trigger:
68
+
69
+ ```ruby
70
+ enabled true # Trigger is active
71
+ enabled false # Trigger is inactive
72
+ ```
73
+
74
+ #### `when_env`
75
+ Environment-specific activation:
76
+
77
+ ```ruby
78
+ when_env :production # Only in production
79
+ when_env :staging, :production # Multiple environments
80
+ ```
81
+
82
+ ### Complete Example
83
+
84
+ ```ruby
85
+ # app/triggers/orders_billing_trigger.rb
86
+ PgSqlTriggers::DSL.pg_sql_trigger "orders_billing_trigger" do
87
+ table :orders
88
+ on :insert, :update
89
+ function :calculate_order_total
90
+
91
+ version 2
92
+ enabled true
93
+
94
+ when_env :production, :staging
95
+ end
96
+ ```
97
+
98
+ ## Trigger Migrations
99
+
100
+ Trigger migrations work similarly to Rails schema migrations but are specifically for PostgreSQL triggers and functions.
101
+
102
+ ### Generating Migrations
103
+
104
+ Create a new trigger migration:
105
+
106
+ ```bash
107
+ rails generate trigger:migration add_validation_trigger
108
+ ```
109
+
110
+ This creates a timestamped file in `db/triggers/`:
111
+
112
+ ```
113
+ db/triggers/20231215120000_add_validation_trigger.rb
114
+ ```
115
+
116
+ ### Migration Structure
117
+
118
+ Migrations have `up` and `down` methods:
119
+
120
+ ```ruby
121
+ # db/triggers/20231215120000_add_validation_trigger.rb
122
+ class AddValidationTrigger < PgSqlTriggers::Migration
123
+ def up
124
+ execute <<-SQL
125
+ CREATE OR REPLACE FUNCTION validate_user_email()
126
+ RETURNS TRIGGER AS $$
127
+ BEGIN
128
+ IF NEW.email !~ '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$' THEN
129
+ RAISE EXCEPTION 'Invalid email format';
130
+ END IF;
131
+ RETURN NEW;
132
+ END;
133
+ $$ LANGUAGE plpgsql;
134
+
135
+ CREATE TRIGGER user_email_validation
136
+ BEFORE INSERT OR UPDATE ON users
137
+ FOR EACH ROW
138
+ EXECUTE FUNCTION validate_user_email();
139
+ SQL
140
+ end
141
+
142
+ def down
143
+ execute <<-SQL
144
+ DROP TRIGGER IF EXISTS user_email_validation ON users;
145
+ DROP FUNCTION IF EXISTS validate_user_email();
146
+ SQL
147
+ end
148
+ end
149
+ ```
150
+
151
+ ### Running Migrations
152
+
153
+ #### Apply All Pending Migrations
154
+
155
+ ```bash
156
+ rake trigger:migrate
157
+ ```
158
+
159
+ #### Rollback Last Migration
160
+
161
+ ```bash
162
+ rake trigger:rollback
163
+ ```
164
+
165
+ #### Rollback Multiple Steps
166
+
167
+ ```bash
168
+ rake trigger:rollback STEP=3
169
+ ```
170
+
171
+ #### Check Migration Status
172
+
173
+ ```bash
174
+ rake trigger:migrate:status
175
+ ```
176
+
177
+ Output example:
178
+ ```
179
+ Status Migration ID Migration Name
180
+ --------------------------------------------------
181
+ up 20231215120000 Add validation trigger
182
+ up 20231216130000 Add billing trigger
183
+ down 20231217140000 Add audit trigger
184
+ ```
185
+
186
+ #### Run Specific Migration Up
187
+
188
+ ```bash
189
+ rake trigger:migrate:up VERSION=20231215120000
190
+ ```
191
+
192
+ #### Run Specific Migration Down
193
+
194
+ ```bash
195
+ rake trigger:migrate:down VERSION=20231215120000
196
+ ```
197
+
198
+ #### Redo Last Migration
199
+
200
+ ```bash
201
+ rake trigger:migrate:redo
202
+ ```
203
+
204
+ This rolls back and re-applies the last migration.
205
+
206
+ ### Migration Best Practices
207
+
208
+ 1. **Always Provide Down Method**: Ensure migrations are reversible
209
+ 2. **Use Idempotent SQL**: Use `CREATE OR REPLACE FUNCTION` and `DROP ... IF EXISTS`
210
+ 3. **Test in Development**: Verify migrations work before applying to production
211
+ 4. **Version Control**: Commit migration files to git
212
+ 5. **Incremental Changes**: Keep migrations small and focused
213
+
214
+ ### Complex Migration Example
215
+
216
+ ```ruby
217
+ # db/triggers/20231218150000_add_order_audit.rb
218
+ class AddOrderAudit < PgSqlTriggers::Migration
219
+ def up
220
+ execute <<-SQL
221
+ -- Create audit table
222
+ CREATE TABLE IF NOT EXISTS order_audits (
223
+ id SERIAL PRIMARY KEY,
224
+ order_id INTEGER NOT NULL,
225
+ operation VARCHAR(10) NOT NULL,
226
+ old_data JSONB,
227
+ new_data JSONB,
228
+ changed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
229
+ );
230
+
231
+ -- Create audit function
232
+ CREATE OR REPLACE FUNCTION audit_order_changes()
233
+ RETURNS TRIGGER AS $$
234
+ BEGIN
235
+ IF TG_OP = 'DELETE' THEN
236
+ INSERT INTO order_audits (order_id, operation, old_data)
237
+ VALUES (OLD.id, 'DELETE', row_to_json(OLD));
238
+ RETURN OLD;
239
+ ELSIF TG_OP = 'UPDATE' THEN
240
+ INSERT INTO order_audits (order_id, operation, old_data, new_data)
241
+ VALUES (NEW.id, 'UPDATE', row_to_json(OLD), row_to_json(NEW));
242
+ RETURN NEW;
243
+ ELSIF TG_OP = 'INSERT' THEN
244
+ INSERT INTO order_audits (order_id, operation, new_data)
245
+ VALUES (NEW.id, 'INSERT', row_to_json(NEW));
246
+ RETURN NEW;
247
+ END IF;
248
+ END;
249
+ $$ LANGUAGE plpgsql;
250
+
251
+ -- Create trigger
252
+ CREATE TRIGGER order_audit_trigger
253
+ AFTER INSERT OR UPDATE OR DELETE ON orders
254
+ FOR EACH ROW
255
+ EXECUTE FUNCTION audit_order_changes();
256
+ SQL
257
+ end
258
+
259
+ def down
260
+ execute <<-SQL
261
+ DROP TRIGGER IF EXISTS order_audit_trigger ON orders;
262
+ DROP FUNCTION IF EXISTS audit_order_changes();
263
+ DROP TABLE IF EXISTS order_audits;
264
+ SQL
265
+ end
266
+ end
267
+ ```
268
+
269
+ ## Combined Schema and Trigger Migrations
270
+
271
+ For convenience, PgSqlTriggers provides rake tasks that run both schema and trigger migrations together.
272
+
273
+ ### Run Both Migrations
274
+
275
+ ```bash
276
+ rake db:migrate:with_triggers
277
+ ```
278
+
279
+ This runs:
280
+ 1. `rake db:migrate` (Rails schema migrations)
281
+ 2. `rake trigger:migrate` (Trigger migrations)
282
+
283
+ ### Rollback Both
284
+
285
+ ```bash
286
+ rake db:rollback:with_triggers
287
+ ```
288
+
289
+ This rolls back:
290
+ 1. The most recent trigger migration
291
+ 2. The most recent schema migration
292
+
293
+ ### Check Status of Both
294
+
295
+ ```bash
296
+ rake db:migrate:status:with_triggers
297
+ ```
298
+
299
+ Shows status of both schema and trigger migrations.
300
+
301
+ ### Get Versions of Both
302
+
303
+ ```bash
304
+ rake db:version:with_triggers
305
+ ```
306
+
307
+ Displays current versions of both migration types.
308
+
309
+ ## Drift Detection
310
+
311
+ PgSqlTriggers automatically detects when the actual database state differs from your DSL definitions.
312
+
313
+ ### Drift States
314
+
315
+ #### Managed & In Sync
316
+ The trigger exists in the database and matches the DSL definition exactly.
317
+
318
+ ```
319
+ Status: ✓ Managed & In Sync
320
+ ```
321
+
322
+ #### Managed & Drifted
323
+ The trigger exists but its definition doesn't match the DSL (e.g., function modified outside PgSqlTriggers).
324
+
325
+ ```
326
+ Status: ⚠ Managed & Drifted
327
+ ```
328
+
329
+ **Actions:**
330
+ - Review the differences
331
+ - Update the DSL to match the database
332
+ - Re-apply the migration to restore the DSL definition
333
+
334
+ #### Manual Override
335
+ The trigger was modified outside of PgSqlTriggers (e.g., via direct SQL).
336
+
337
+ ```
338
+ Status: ⚠ Manual Override
339
+ ```
340
+
341
+ **Actions:**
342
+ - Document the manual changes
343
+ - Update the DSL to reflect the changes
344
+ - Create a new migration if needed
345
+
346
+ #### Disabled
347
+ The trigger is disabled via PgSqlTriggers.
348
+
349
+ ```
350
+ Status: ○ Disabled
351
+ ```
352
+
353
+ **Actions:**
354
+ - Enable via console or Web UI
355
+ - Verify the trigger is needed
356
+
357
+ #### Dropped
358
+ The trigger was dropped but still exists in the registry.
359
+
360
+ ```
361
+ Status: ✗ Dropped
362
+ ```
363
+
364
+ **Actions:**
365
+ - Re-apply the migration
366
+ - Remove from registry if no longer needed
367
+
368
+ #### Unknown
369
+ The trigger exists in the database but not in the PgSqlTriggers registry.
370
+
371
+ ```
372
+ Status: ? Unknown
373
+ ```
374
+
375
+ **Actions:**
376
+ - Add a DSL definition for the trigger
377
+ - Create a migration to bring it under management
378
+ - Drop it if it's no longer needed
379
+
380
+ ### Checking for Drift
381
+
382
+ #### Via Console
383
+
384
+ ```ruby
385
+ # Get drift information for all triggers
386
+ PgSqlTriggers::Registry.diff
387
+ ```
388
+
389
+ #### Via Web UI
390
+
391
+ Navigate to the dashboard at `/pg_sql_triggers` to see drift status visually.
392
+
393
+ ### Resolving Drift
394
+
395
+ 1. **Review the Drift**: Understand what changed and why
396
+ 2. **Choose Resolution**:
397
+ - Update DSL to match database
398
+ - Re-apply migration to match DSL
399
+ - Create new migration for intentional changes
400
+ 3. **Verify**: Check that drift is resolved
401
+
402
+ Example:
403
+
404
+ ```ruby
405
+ # Check current state
406
+ drift = PgSqlTriggers::Registry.diff
407
+
408
+ # Review specific trigger
409
+ trigger = PgSqlTriggers::Registry.find_by(trigger_name: "users_email_validation")
410
+ trigger.drift_status # => "drifted"
411
+
412
+ # Re-apply migration to fix drift
413
+ rake trigger:migrate:redo
414
+ ```
415
+
416
+ ## Next Steps
417
+
418
+ - [Web UI Documentation](web-ui.md) - Manage triggers through the web interface
419
+ - [Kill Switch](kill-switch.md) - Production safety features
420
+ - [API Reference](api-reference.md) - Console commands and programmatic access