pg_sql_triggers 1.0.0 → 1.1.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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/.erb_lint.yml +47 -0
  3. data/.rubocop.yml +4 -1
  4. data/CHANGELOG.md +112 -1
  5. data/COVERAGE.md +58 -0
  6. data/Goal.md +450 -123
  7. data/README.md +53 -215
  8. data/app/controllers/pg_sql_triggers/application_controller.rb +46 -0
  9. data/app/controllers/pg_sql_triggers/dashboard_controller.rb +4 -1
  10. data/app/controllers/pg_sql_triggers/generator_controller.rb +76 -8
  11. data/app/controllers/pg_sql_triggers/migrations_controller.rb +18 -0
  12. data/app/models/pg_sql_triggers/trigger_registry.rb +93 -12
  13. data/app/views/layouts/pg_sql_triggers/application.html.erb +34 -1
  14. data/app/views/pg_sql_triggers/dashboard/index.html.erb +70 -30
  15. data/app/views/pg_sql_triggers/generator/new.html.erb +22 -4
  16. data/app/views/pg_sql_triggers/generator/preview.html.erb +244 -16
  17. data/app/views/pg_sql_triggers/shared/_confirmation_modal.html.erb +221 -0
  18. data/app/views/pg_sql_triggers/shared/_kill_switch_status.html.erb +40 -0
  19. data/app/views/pg_sql_triggers/tables/index.html.erb +0 -2
  20. data/app/views/pg_sql_triggers/tables/show.html.erb +3 -4
  21. data/config/initializers/pg_sql_triggers.rb +69 -0
  22. data/db/migrate/20251222000001_create_pg_sql_triggers_tables.rb +3 -1
  23. data/db/migrate/20251229071916_add_timing_to_pg_sql_triggers_registry.rb +8 -0
  24. data/docs/README.md +66 -0
  25. data/docs/api-reference.md +681 -0
  26. data/docs/configuration.md +541 -0
  27. data/docs/getting-started.md +135 -0
  28. data/docs/kill-switch.md +586 -0
  29. data/docs/screenshots/.gitkeep +1 -0
  30. data/docs/screenshots/Generate Trigger.png +0 -0
  31. data/docs/screenshots/Triggers Page.png +0 -0
  32. data/docs/screenshots/kill error.png +0 -0
  33. data/docs/screenshots/kill modal for migration down.png +0 -0
  34. data/docs/usage-guide.md +493 -0
  35. data/docs/web-ui.md +353 -0
  36. data/lib/generators/pg_sql_triggers/templates/create_pg_sql_triggers_tables.rb +3 -1
  37. data/lib/generators/pg_sql_triggers/templates/initializer.rb +44 -2
  38. data/lib/pg_sql_triggers/drift/db_queries.rb +116 -0
  39. data/lib/pg_sql_triggers/drift/detector.rb +187 -0
  40. data/lib/pg_sql_triggers/drift/reporter.rb +179 -0
  41. data/lib/pg_sql_triggers/drift.rb +14 -11
  42. data/lib/pg_sql_triggers/dsl/trigger_definition.rb +15 -1
  43. data/lib/pg_sql_triggers/generator/form.rb +3 -1
  44. data/lib/pg_sql_triggers/generator/service.rb +82 -26
  45. data/lib/pg_sql_triggers/migration.rb +1 -1
  46. data/lib/pg_sql_triggers/migrator/pre_apply_comparator.rb +344 -0
  47. data/lib/pg_sql_triggers/migrator/pre_apply_diff_reporter.rb +143 -0
  48. data/lib/pg_sql_triggers/migrator/safety_validator.rb +258 -0
  49. data/lib/pg_sql_triggers/migrator.rb +85 -3
  50. data/lib/pg_sql_triggers/registry/manager.rb +100 -13
  51. data/lib/pg_sql_triggers/sql/kill_switch.rb +300 -0
  52. data/lib/pg_sql_triggers/testing/dry_run.rb +5 -7
  53. data/lib/pg_sql_triggers/testing/function_tester.rb +66 -24
  54. data/lib/pg_sql_triggers/testing/safe_executor.rb +23 -11
  55. data/lib/pg_sql_triggers/testing/syntax_validator.rb +24 -1
  56. data/lib/pg_sql_triggers/version.rb +1 -1
  57. data/lib/pg_sql_triggers.rb +24 -0
  58. data/lib/tasks/trigger_migrations.rake +33 -0
  59. data/scripts/generate_coverage_report.rb +129 -0
  60. metadata +45 -5
@@ -0,0 +1,681 @@
1
+ # API Reference
2
+
3
+ Complete reference for using PgSqlTriggers programmatically from the Rails console or within your application code.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Registry API](#registry-api)
8
+ - [Migrator API](#migrator-api)
9
+ - [Kill Switch API](#kill-switch-api)
10
+ - [DSL API](#dsl-api)
11
+ - [TriggerRegistry Model](#triggerregistry-model)
12
+
13
+ ## Registry API
14
+
15
+ The Registry API provides methods for inspecting and managing triggers.
16
+
17
+ ### `PgSqlTriggers::Registry.list`
18
+
19
+ Returns all registered triggers.
20
+
21
+ ```ruby
22
+ triggers = PgSqlTriggers::Registry.list
23
+ # => [#<PgSqlTriggers::TriggerRegistry...>, ...]
24
+
25
+ triggers.each do |trigger|
26
+ puts "#{trigger.trigger_name} - #{trigger.status}"
27
+ end
28
+ ```
29
+
30
+ **Returns**: Array of `TriggerRegistry` records
31
+
32
+ ### `PgSqlTriggers::Registry.enabled`
33
+
34
+ Returns only enabled triggers.
35
+
36
+ ```ruby
37
+ enabled_triggers = PgSqlTriggers::Registry.enabled
38
+ # => [#<PgSqlTriggers::TriggerRegistry...>, ...]
39
+
40
+ puts "Enabled triggers: #{enabled_triggers.count}"
41
+ ```
42
+
43
+ **Returns**: Array of `TriggerRegistry` records
44
+
45
+ ### `PgSqlTriggers::Registry.disabled`
46
+
47
+ Returns only disabled triggers.
48
+
49
+ ```ruby
50
+ disabled_triggers = PgSqlTriggers::Registry.disabled
51
+ # => [#<PgSqlTriggers::TriggerRegistry...>, ...]
52
+
53
+ disabled_triggers.each do |trigger|
54
+ puts "Disabled: #{trigger.trigger_name}"
55
+ end
56
+ ```
57
+
58
+ **Returns**: Array of `TriggerRegistry` records
59
+
60
+ ### `PgSqlTriggers::Registry.for_table(table_name)`
61
+
62
+ Returns triggers for a specific table.
63
+
64
+ ```ruby
65
+ user_triggers = PgSqlTriggers::Registry.for_table(:users)
66
+ # => [#<PgSqlTriggers::TriggerRegistry...>, ...]
67
+
68
+ user_triggers.each do |trigger|
69
+ puts trigger.trigger_name
70
+ end
71
+ ```
72
+
73
+ **Parameters**:
74
+ - `table_name` (Symbol or String): The table name
75
+
76
+ **Returns**: Array of `TriggerRegistry` records
77
+
78
+ ### `PgSqlTriggers::Registry.diff`
79
+
80
+ Checks for drift between DSL definitions and database state.
81
+
82
+ ```ruby
83
+ drift_info = PgSqlTriggers::Registry.diff
84
+ # => {
85
+ # in_sync: [...],
86
+ # drifted: [...],
87
+ # manual_override: [...],
88
+ # disabled: [...],
89
+ # dropped: [...],
90
+ # unknown: [...]
91
+ # }
92
+
93
+ drift_info[:drifted].each do |trigger|
94
+ puts "Drifted: #{trigger.trigger_name}"
95
+ end
96
+ ```
97
+
98
+ **Returns**: Hash with drift categories
99
+
100
+ ### `PgSqlTriggers::Registry.validate!`
101
+
102
+ Validates all triggers and raises an error if any are invalid.
103
+
104
+ ```ruby
105
+ begin
106
+ PgSqlTriggers::Registry.validate!
107
+ puts "All triggers valid"
108
+ rescue PgSqlTriggers::ValidationError => e
109
+ puts "Validation failed: #{e.message}"
110
+ end
111
+ ```
112
+
113
+ **Raises**: `PgSqlTriggers::ValidationError` if validation fails
114
+
115
+ **Returns**: `true` if all triggers are valid
116
+
117
+ ## Migrator API
118
+
119
+ The Migrator API manages trigger migrations programmatically.
120
+
121
+ ### `PgSqlTriggers::Migrator.run_up(version = nil, confirmation: nil)`
122
+
123
+ Applies pending migrations.
124
+
125
+ ```ruby
126
+ # Apply all pending migrations
127
+ PgSqlTriggers::Migrator.run_up
128
+
129
+ # Apply up to a specific version
130
+ PgSqlTriggers::Migrator.run_up(20231215120000)
131
+
132
+ # With kill switch override
133
+ PgSqlTriggers::Migrator.run_up(nil, confirmation: "EXECUTE MIGRATOR_RUN_UP")
134
+ ```
135
+
136
+ **Parameters**:
137
+ - `version` (Integer, optional): Target version to migrate to
138
+ - `confirmation` (String, optional): Kill switch confirmation text
139
+
140
+ **Returns**: Array of applied migration versions
141
+
142
+ ### `PgSqlTriggers::Migrator.run_down(version = nil, confirmation: nil)`
143
+
144
+ Rolls back migrations.
145
+
146
+ ```ruby
147
+ # Rollback last migration
148
+ PgSqlTriggers::Migrator.run_down
149
+
150
+ # Rollback to a specific version
151
+ PgSqlTriggers::Migrator.run_down(20231215120000)
152
+
153
+ # With kill switch override
154
+ PgSqlTriggers::Migrator.run_down(nil, confirmation: "EXECUTE MIGRATOR_RUN_DOWN")
155
+ ```
156
+
157
+ **Parameters**:
158
+ - `version` (Integer, optional): Target version to rollback to
159
+ - `confirmation` (String, optional): Kill switch confirmation text
160
+
161
+ **Returns**: Array of rolled back migration versions
162
+
163
+ ### `PgSqlTriggers::Migrator.redo(confirmation: nil)`
164
+
165
+ Rolls back and re-applies the last migration.
166
+
167
+ ```ruby
168
+ # Redo last migration
169
+ PgSqlTriggers::Migrator.redo
170
+
171
+ # With kill switch override
172
+ PgSqlTriggers::Migrator.redo(confirmation: "EXECUTE MIGRATOR_REDO")
173
+ ```
174
+
175
+ **Parameters**:
176
+ - `confirmation` (String, optional): Kill switch confirmation text
177
+
178
+ **Returns**: Migration version that was redone
179
+
180
+ ### `PgSqlTriggers::Migrator.pending_migrations`
181
+
182
+ Returns list of pending migrations.
183
+
184
+ ```ruby
185
+ pending = PgSqlTriggers::Migrator.pending_migrations
186
+ # => [#<PgSqlTriggers::Migration...>, ...]
187
+
188
+ pending.each do |migration|
189
+ puts "Pending: #{migration.name} (#{migration.version})"
190
+ end
191
+ ```
192
+
193
+ **Returns**: Array of pending migration objects
194
+
195
+ ### `PgSqlTriggers::Migrator.migration_status`
196
+
197
+ Returns status of all migrations.
198
+
199
+ ```ruby
200
+ status = PgSqlTriggers::Migrator.migration_status
201
+ # => [
202
+ # { version: 20231215120000, name: "AddValidationTrigger", status: :up },
203
+ # { version: 20231216130000, name: "AddBillingTrigger", status: :down },
204
+ # ...
205
+ # ]
206
+
207
+ status.each do |migration|
208
+ puts "#{migration[:version]} - #{migration[:name]}: #{migration[:status]}"
209
+ end
210
+ ```
211
+
212
+ **Returns**: Array of hashes with migration information
213
+
214
+ ### `PgSqlTriggers::Migrator.current_version`
215
+
216
+ Returns the current migration version.
217
+
218
+ ```ruby
219
+ version = PgSqlTriggers::Migrator.current_version
220
+ # => 20231215120000
221
+
222
+ puts "Current version: #{version}"
223
+ ```
224
+
225
+ **Returns**: Integer (migration timestamp) or `nil` if no migrations applied
226
+
227
+ ## Kill Switch API
228
+
229
+ The Kill Switch API provides methods for checking and overriding production protections.
230
+
231
+ ### `PgSqlTriggers::SQL::KillSwitch.active?`
232
+
233
+ Checks if kill switch is currently active.
234
+
235
+ ```ruby
236
+ if PgSqlTriggers::SQL::KillSwitch.active?
237
+ puts "Kill switch is enabled for this environment"
238
+ else
239
+ puts "Kill switch is not active"
240
+ end
241
+ ```
242
+
243
+ **Returns**: Boolean
244
+
245
+ ### `PgSqlTriggers::SQL::KillSwitch.protected_environment?`
246
+
247
+ Checks if current environment is protected.
248
+
249
+ ```ruby
250
+ if PgSqlTriggers::SQL::KillSwitch.protected_environment?
251
+ puts "Current environment is protected"
252
+ else
253
+ puts "Current environment is not protected"
254
+ end
255
+ ```
256
+
257
+ **Returns**: Boolean
258
+
259
+ ### `PgSqlTriggers::SQL::KillSwitch.environment`
260
+
261
+ Returns the current environment.
262
+
263
+ ```ruby
264
+ env = PgSqlTriggers::SQL::KillSwitch.environment
265
+ # => "production"
266
+
267
+ puts "Current environment: #{env}"
268
+ ```
269
+
270
+ **Returns**: String
271
+
272
+ ### `PgSqlTriggers::SQL::KillSwitch.check!(operation:, actor:, confirmation: nil)`
273
+
274
+ Checks if an operation is allowed and raises an error if blocked.
275
+
276
+ ```ruby
277
+ begin
278
+ PgSqlTriggers::SQL::KillSwitch.check!(
279
+ operation: :trigger_migrate,
280
+ actor: { type: 'console', user: 'admin@example.com' },
281
+ confirmation: "EXECUTE TRIGGER_MIGRATE"
282
+ )
283
+ # Operation is allowed
284
+ puts "Operation allowed"
285
+ rescue PgSqlTriggers::KillSwitchError => e
286
+ puts "Operation blocked: #{e.message}"
287
+ end
288
+ ```
289
+
290
+ **Parameters**:
291
+ - `operation` (Symbol): The operation being performed
292
+ - `actor` (Hash): Information about who is performing the operation
293
+ - `confirmation` (String, optional): Confirmation text for override
294
+
295
+ **Raises**: `PgSqlTriggers::KillSwitchError` if operation is blocked
296
+
297
+ **Returns**: `true` if operation is allowed
298
+
299
+ ### `PgSqlTriggers::SQL::KillSwitch.override(confirmation:, &block)`
300
+
301
+ Executes a block with kill switch override.
302
+
303
+ ```ruby
304
+ PgSqlTriggers::SQL::KillSwitch.override(confirmation: "EXECUTE BATCH_ENABLE") do
305
+ # Operations in this block bypass kill switch
306
+ trigger = PgSqlTriggers::TriggerRegistry.find_by(trigger_name: "users_email_validation")
307
+ trigger.enable!
308
+
309
+ puts "Trigger enabled"
310
+ end
311
+ ```
312
+
313
+ **Parameters**:
314
+ - `confirmation` (String): Confirmation text
315
+ - `block`: Code to execute with override
316
+
317
+ **Raises**: `PgSqlTriggers::KillSwitchError` if confirmation is invalid
318
+
319
+ **Returns**: Result of the block
320
+
321
+ ## DSL API
322
+
323
+ The DSL API is used to define triggers in your application.
324
+
325
+ ### `PgSqlTriggers::DSL.pg_sql_trigger(name, &block)`
326
+
327
+ Defines a trigger.
328
+
329
+ ```ruby
330
+ PgSqlTriggers::DSL.pg_sql_trigger "users_email_validation" do
331
+ table :users
332
+ on :insert, :update
333
+ function :validate_user_email
334
+ version 1
335
+ enabled false
336
+ timing :before
337
+ when_env :production
338
+ end
339
+ ```
340
+
341
+ **Parameters**:
342
+ - `name` (String): Unique trigger name
343
+ - `block`: DSL block defining the trigger
344
+
345
+ ### DSL Methods
346
+
347
+ #### `table(table_name)`
348
+
349
+ Specifies the table for the trigger.
350
+
351
+ ```ruby
352
+ table :users
353
+ ```
354
+
355
+ **Parameters**:
356
+ - `table_name` (Symbol): Table name
357
+
358
+ #### `on(*events)`
359
+
360
+ Specifies trigger events.
361
+
362
+ ```ruby
363
+ on :insert
364
+ on :insert, :update
365
+ on :insert, :update, :delete
366
+ ```
367
+
368
+ **Parameters**:
369
+ - `events` (Symbols): One or more of `:insert`, `:update`, `:delete`
370
+
371
+ #### `function(function_name)`
372
+
373
+ Specifies the PostgreSQL function to execute.
374
+
375
+ ```ruby
376
+ function :validate_user_email
377
+ ```
378
+
379
+ **Parameters**:
380
+ - `function_name` (Symbol): Function name
381
+
382
+ #### `version(number)`
383
+
384
+ Sets the trigger version.
385
+
386
+ ```ruby
387
+ version 1
388
+ version 2
389
+ ```
390
+
391
+ **Parameters**:
392
+ - `number` (Integer): Version number
393
+
394
+ #### `enabled(state)`
395
+
396
+ Sets the initial enabled state.
397
+
398
+ ```ruby
399
+ enabled true
400
+ enabled false
401
+ ```
402
+
403
+ **Parameters**:
404
+ - `state` (Boolean): Initial state
405
+
406
+ #### `when_env(*environments)`
407
+
408
+ Restricts trigger to specific environments.
409
+
410
+ ```ruby
411
+ when_env :production
412
+ when_env :production, :staging
413
+ ```
414
+
415
+ **Parameters**:
416
+ - `environments` (Symbols): One or more environment names
417
+
418
+ #### `timing(timing_value)`
419
+
420
+ Specifies when the trigger fires relative to the event.
421
+
422
+ ```ruby
423
+ timing :before # Trigger fires before constraint checks (default)
424
+ timing :after # Trigger fires after constraint checks
425
+ ```
426
+
427
+ **Parameters**:
428
+ - `timing_value` (Symbol or String): Either `:before` or `:after`
429
+
430
+ **Returns**: Current timing value if called without argument
431
+
432
+ ## TriggerRegistry Model
433
+
434
+ The `TriggerRegistry` ActiveRecord model represents a trigger in the registry.
435
+
436
+ ### Attributes
437
+
438
+ ```ruby
439
+ trigger = PgSqlTriggers::TriggerRegistry.first
440
+
441
+ trigger.trigger_name # => "users_email_validation"
442
+ trigger.table_name # => "users"
443
+ trigger.function_name # => "validate_user_email"
444
+ trigger.events # => ["insert", "update"]
445
+ trigger.version # => 1
446
+ trigger.enabled # => false
447
+ trigger.timing # => "before" or "after"
448
+ trigger.environments # => ["production"]
449
+ trigger.condition # => "NEW.status = 'active'" or nil
450
+ trigger.created_at # => 2023-12-15 12:00:00 UTC
451
+ trigger.updated_at # => 2023-12-15 12:00:00 UTC
452
+ ```
453
+
454
+ ### Instance Methods
455
+
456
+ #### `enable!(confirmation: nil)`
457
+
458
+ Enables the trigger.
459
+
460
+ ```ruby
461
+ trigger = PgSqlTriggers::TriggerRegistry.find_by(trigger_name: "users_email_validation")
462
+ trigger.enable!
463
+
464
+ # With kill switch override
465
+ trigger.enable!(confirmation: "EXECUTE TRIGGER_ENABLE")
466
+ ```
467
+
468
+ **Parameters**:
469
+ - `confirmation` (String, optional): Kill switch confirmation text
470
+
471
+ **Returns**: `true` on success
472
+
473
+ #### `disable!(confirmation: nil)`
474
+
475
+ Disables the trigger.
476
+
477
+ ```ruby
478
+ trigger = PgSqlTriggers::TriggerRegistry.find_by(trigger_name: "users_email_validation")
479
+ trigger.disable!
480
+
481
+ # With kill switch override
482
+ trigger.disable!(confirmation: "EXECUTE TRIGGER_DISABLE")
483
+ ```
484
+
485
+ **Parameters**:
486
+ - `confirmation` (String, optional): Kill switch confirmation text
487
+
488
+ **Returns**: `true` on success
489
+
490
+ #### `drop!(confirmation: nil)`
491
+
492
+ Drops the trigger from the database.
493
+
494
+ ```ruby
495
+ trigger = PgSqlTriggers::TriggerRegistry.find_by(trigger_name: "users_email_validation")
496
+ trigger.drop!(confirmation: "EXECUTE TRIGGER_DROP")
497
+ ```
498
+
499
+ **Parameters**:
500
+ - `confirmation` (String, optional): Kill switch confirmation text
501
+
502
+ **Returns**: `true` on success
503
+
504
+ #### `drift_status`
505
+
506
+ Returns the drift status of the trigger.
507
+
508
+ ```ruby
509
+ trigger = PgSqlTriggers::TriggerRegistry.first
510
+ status = trigger.drift_status
511
+ # => :in_sync, :drifted, :manual_override, :disabled, :dropped, or :unknown
512
+
513
+ case status
514
+ when :in_sync
515
+ puts "Trigger is synchronized"
516
+ when :drifted
517
+ puts "Trigger has drifted from DSL"
518
+ when :manual_override
519
+ puts "Trigger was manually modified"
520
+ end
521
+ ```
522
+
523
+ **Returns**: Symbol representing drift state
524
+
525
+ #### `apply!(confirmation: nil)`
526
+
527
+ Applies the trigger definition from DSL to the database.
528
+
529
+ ```ruby
530
+ trigger = PgSqlTriggers::TriggerRegistry.find_by(trigger_name: "users_email_validation")
531
+ trigger.apply!(confirmation: "EXECUTE TRIGGER_APPLY")
532
+ ```
533
+
534
+ **Parameters**:
535
+ - `confirmation` (String, optional): Kill switch confirmation text
536
+
537
+ **Returns**: `true` on success
538
+
539
+ ### Class Methods
540
+
541
+ #### `PgSqlTriggers::TriggerRegistry.find_by_name(name)`
542
+
543
+ Finds a trigger by name.
544
+
545
+ ```ruby
546
+ trigger = PgSqlTriggers::TriggerRegistry.find_by_name("users_email_validation")
547
+ # => #<PgSqlTriggers::TriggerRegistry...>
548
+ ```
549
+
550
+ **Parameters**:
551
+ - `name` (String): Trigger name
552
+
553
+ **Returns**: `TriggerRegistry` record or `nil`
554
+
555
+ #### `PgSqlTriggers::TriggerRegistry.for_environment(env)`
556
+
557
+ Returns triggers applicable to a specific environment.
558
+
559
+ ```ruby
560
+ prod_triggers = PgSqlTriggers::TriggerRegistry.for_environment("production")
561
+ # => [#<PgSqlTriggers::TriggerRegistry...>, ...]
562
+ ```
563
+
564
+ **Parameters**:
565
+ - `env` (String or Symbol): Environment name
566
+
567
+ **Returns**: Array of `TriggerRegistry` records
568
+
569
+ ## Usage Examples
570
+
571
+ ### Complete Workflow
572
+
573
+ ```ruby
574
+ # 1. Check pending migrations
575
+ pending = PgSqlTriggers::Migrator.pending_migrations
576
+ puts "#{pending.count} pending migrations"
577
+
578
+ # 2. Apply migrations with override
579
+ PgSqlTriggers::SQL::KillSwitch.override(confirmation: "EXECUTE MIGRATOR_RUN_UP") do
580
+ PgSqlTriggers::Migrator.run_up
581
+ end
582
+
583
+ # 3. List all triggers
584
+ triggers = PgSqlTriggers::Registry.list
585
+ puts "Total triggers: #{triggers.count}"
586
+
587
+ # 4. Check for drift
588
+ drift = PgSqlTriggers::Registry.diff
589
+ puts "Drifted triggers: #{drift[:drifted].count}"
590
+
591
+ # 5. Enable specific trigger
592
+ trigger = PgSqlTriggers::TriggerRegistry.find_by(trigger_name: "users_email_validation")
593
+ trigger.enable!(confirmation: "EXECUTE TRIGGER_ENABLE") if trigger
594
+
595
+ # 6. Validate all triggers
596
+ begin
597
+ PgSqlTriggers::Registry.validate!
598
+ puts "All triggers valid"
599
+ rescue PgSqlTriggers::ValidationError => e
600
+ puts "Validation error: #{e.message}"
601
+ end
602
+ ```
603
+
604
+ ### Batch Operations
605
+
606
+ ```ruby
607
+ # Enable all disabled triggers
608
+ PgSqlTriggers::SQL::KillSwitch.override(confirmation: "EXECUTE BATCH_ENABLE") do
609
+ disabled_triggers = PgSqlTriggers::Registry.disabled
610
+
611
+ disabled_triggers.each do |trigger|
612
+ trigger.enable!
613
+ puts "Enabled: #{trigger.trigger_name}"
614
+ end
615
+ end
616
+
617
+ # Disable all triggers for a specific table
618
+ PgSqlTriggers::SQL::KillSwitch.override(confirmation: "EXECUTE BATCH_DISABLE") do
619
+ user_triggers = PgSqlTriggers::Registry.for_table(:users)
620
+
621
+ user_triggers.each do |trigger|
622
+ trigger.disable!
623
+ puts "Disabled: #{trigger.trigger_name}"
624
+ end
625
+ end
626
+ ```
627
+
628
+ ### Inspection and Reporting
629
+
630
+ ```ruby
631
+ # Generate a drift report
632
+ drift = PgSqlTriggers::Registry.diff
633
+
634
+ puts "=== Drift Report ==="
635
+ puts "In Sync: #{drift[:in_sync].count}"
636
+ puts "Drifted: #{drift[:drifted].count}"
637
+ puts "Manual Override: #{drift[:manual_override].count}"
638
+ puts "Disabled: #{drift[:disabled].count}"
639
+ puts "Dropped: #{drift[:dropped].count}"
640
+ puts "Unknown: #{drift[:unknown].count}"
641
+
642
+ # List all triggers with details
643
+ triggers = PgSqlTriggers::Registry.list
644
+
645
+ puts "\n=== Trigger Inventory ==="
646
+ triggers.each do |trigger|
647
+ puts "#{trigger.trigger_name}:"
648
+ puts " Table: #{trigger.table_name}"
649
+ puts " Function: #{trigger.function_name}"
650
+ puts " Events: #{trigger.events.join(', ')}"
651
+ puts " Timing: #{trigger.timing}"
652
+ puts " Version: #{trigger.version}"
653
+ puts " Enabled: #{trigger.enabled}"
654
+ puts " Drift: #{trigger.drift_status}"
655
+ puts ""
656
+ end
657
+ ```
658
+
659
+ ### Error Handling
660
+
661
+ ```ruby
662
+ begin
663
+ # Attempt operation without proper confirmation
664
+ trigger = PgSqlTriggers::TriggerRegistry.first
665
+ trigger.enable!
666
+ rescue PgSqlTriggers::KillSwitchError => e
667
+ puts "Kill switch blocked operation: #{e.message}"
668
+ # Extract required confirmation from error message
669
+ # Re-attempt with proper confirmation
670
+ trigger.enable!(confirmation: "EXECUTE TRIGGER_ENABLE")
671
+ rescue StandardError => e
672
+ puts "Unexpected error: #{e.message}"
673
+ puts e.backtrace.first(5)
674
+ end
675
+ ```
676
+
677
+ ## Next Steps
678
+
679
+ - [Usage Guide](usage-guide.md) - Learn the DSL and migration system
680
+ - [Kill Switch](kill-switch.md) - Production safety features
681
+ - [Configuration](configuration.md) - Configure advanced settings