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,663 @@
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
+ when_env :production
337
+ end
338
+ ```
339
+
340
+ **Parameters**:
341
+ - `name` (String): Unique trigger name
342
+ - `block`: DSL block defining the trigger
343
+
344
+ ### DSL Methods
345
+
346
+ #### `table(table_name)`
347
+
348
+ Specifies the table for the trigger.
349
+
350
+ ```ruby
351
+ table :users
352
+ ```
353
+
354
+ **Parameters**:
355
+ - `table_name` (Symbol): Table name
356
+
357
+ #### `on(*events)`
358
+
359
+ Specifies trigger events.
360
+
361
+ ```ruby
362
+ on :insert
363
+ on :insert, :update
364
+ on :insert, :update, :delete
365
+ ```
366
+
367
+ **Parameters**:
368
+ - `events` (Symbols): One or more of `:insert`, `:update`, `:delete`
369
+
370
+ #### `function(function_name)`
371
+
372
+ Specifies the PostgreSQL function to execute.
373
+
374
+ ```ruby
375
+ function :validate_user_email
376
+ ```
377
+
378
+ **Parameters**:
379
+ - `function_name` (Symbol): Function name
380
+
381
+ #### `version(number)`
382
+
383
+ Sets the trigger version.
384
+
385
+ ```ruby
386
+ version 1
387
+ version 2
388
+ ```
389
+
390
+ **Parameters**:
391
+ - `number` (Integer): Version number
392
+
393
+ #### `enabled(state)`
394
+
395
+ Sets the initial enabled state.
396
+
397
+ ```ruby
398
+ enabled true
399
+ enabled false
400
+ ```
401
+
402
+ **Parameters**:
403
+ - `state` (Boolean): Initial state
404
+
405
+ #### `when_env(*environments)`
406
+
407
+ Restricts trigger to specific environments.
408
+
409
+ ```ruby
410
+ when_env :production
411
+ when_env :production, :staging
412
+ ```
413
+
414
+ **Parameters**:
415
+ - `environments` (Symbols): One or more environment names
416
+
417
+ ## TriggerRegistry Model
418
+
419
+ The `TriggerRegistry` ActiveRecord model represents a trigger in the registry.
420
+
421
+ ### Attributes
422
+
423
+ ```ruby
424
+ trigger = PgSqlTriggers::TriggerRegistry.first
425
+
426
+ trigger.trigger_name # => "users_email_validation"
427
+ trigger.table_name # => "users"
428
+ trigger.function_name # => "validate_user_email"
429
+ trigger.events # => ["insert", "update"]
430
+ trigger.version # => 1
431
+ trigger.enabled # => false
432
+ trigger.environments # => ["production"]
433
+ trigger.created_at # => 2023-12-15 12:00:00 UTC
434
+ trigger.updated_at # => 2023-12-15 12:00:00 UTC
435
+ ```
436
+
437
+ ### Instance Methods
438
+
439
+ #### `enable!(confirmation: nil)`
440
+
441
+ Enables the trigger.
442
+
443
+ ```ruby
444
+ trigger = PgSqlTriggers::TriggerRegistry.find_by(trigger_name: "users_email_validation")
445
+ trigger.enable!
446
+
447
+ # With kill switch override
448
+ trigger.enable!(confirmation: "EXECUTE TRIGGER_ENABLE")
449
+ ```
450
+
451
+ **Parameters**:
452
+ - `confirmation` (String, optional): Kill switch confirmation text
453
+
454
+ **Returns**: `true` on success
455
+
456
+ #### `disable!(confirmation: nil)`
457
+
458
+ Disables the trigger.
459
+
460
+ ```ruby
461
+ trigger = PgSqlTriggers::TriggerRegistry.find_by(trigger_name: "users_email_validation")
462
+ trigger.disable!
463
+
464
+ # With kill switch override
465
+ trigger.disable!(confirmation: "EXECUTE TRIGGER_DISABLE")
466
+ ```
467
+
468
+ **Parameters**:
469
+ - `confirmation` (String, optional): Kill switch confirmation text
470
+
471
+ **Returns**: `true` on success
472
+
473
+ #### `drop!(confirmation: nil)`
474
+
475
+ Drops the trigger from the database.
476
+
477
+ ```ruby
478
+ trigger = PgSqlTriggers::TriggerRegistry.find_by(trigger_name: "users_email_validation")
479
+ trigger.drop!(confirmation: "EXECUTE TRIGGER_DROP")
480
+ ```
481
+
482
+ **Parameters**:
483
+ - `confirmation` (String, optional): Kill switch confirmation text
484
+
485
+ **Returns**: `true` on success
486
+
487
+ #### `drift_status`
488
+
489
+ Returns the drift status of the trigger.
490
+
491
+ ```ruby
492
+ trigger = PgSqlTriggers::TriggerRegistry.first
493
+ status = trigger.drift_status
494
+ # => :in_sync, :drifted, :manual_override, :disabled, :dropped, or :unknown
495
+
496
+ case status
497
+ when :in_sync
498
+ puts "Trigger is synchronized"
499
+ when :drifted
500
+ puts "Trigger has drifted from DSL"
501
+ when :manual_override
502
+ puts "Trigger was manually modified"
503
+ end
504
+ ```
505
+
506
+ **Returns**: Symbol representing drift state
507
+
508
+ #### `apply!(confirmation: nil)`
509
+
510
+ Applies the trigger definition from DSL to the database.
511
+
512
+ ```ruby
513
+ trigger = PgSqlTriggers::TriggerRegistry.find_by(trigger_name: "users_email_validation")
514
+ trigger.apply!(confirmation: "EXECUTE TRIGGER_APPLY")
515
+ ```
516
+
517
+ **Parameters**:
518
+ - `confirmation` (String, optional): Kill switch confirmation text
519
+
520
+ **Returns**: `true` on success
521
+
522
+ ### Class Methods
523
+
524
+ #### `PgSqlTriggers::TriggerRegistry.find_by_name(name)`
525
+
526
+ Finds a trigger by name.
527
+
528
+ ```ruby
529
+ trigger = PgSqlTriggers::TriggerRegistry.find_by_name("users_email_validation")
530
+ # => #<PgSqlTriggers::TriggerRegistry...>
531
+ ```
532
+
533
+ **Parameters**:
534
+ - `name` (String): Trigger name
535
+
536
+ **Returns**: `TriggerRegistry` record or `nil`
537
+
538
+ #### `PgSqlTriggers::TriggerRegistry.for_environment(env)`
539
+
540
+ Returns triggers applicable to a specific environment.
541
+
542
+ ```ruby
543
+ prod_triggers = PgSqlTriggers::TriggerRegistry.for_environment("production")
544
+ # => [#<PgSqlTriggers::TriggerRegistry...>, ...]
545
+ ```
546
+
547
+ **Parameters**:
548
+ - `env` (String or Symbol): Environment name
549
+
550
+ **Returns**: Array of `TriggerRegistry` records
551
+
552
+ ## Usage Examples
553
+
554
+ ### Complete Workflow
555
+
556
+ ```ruby
557
+ # 1. Check pending migrations
558
+ pending = PgSqlTriggers::Migrator.pending_migrations
559
+ puts "#{pending.count} pending migrations"
560
+
561
+ # 2. Apply migrations with override
562
+ PgSqlTriggers::SQL::KillSwitch.override(confirmation: "EXECUTE MIGRATOR_RUN_UP") do
563
+ PgSqlTriggers::Migrator.run_up
564
+ end
565
+
566
+ # 3. List all triggers
567
+ triggers = PgSqlTriggers::Registry.list
568
+ puts "Total triggers: #{triggers.count}"
569
+
570
+ # 4. Check for drift
571
+ drift = PgSqlTriggers::Registry.diff
572
+ puts "Drifted triggers: #{drift[:drifted].count}"
573
+
574
+ # 5. Enable specific trigger
575
+ trigger = PgSqlTriggers::TriggerRegistry.find_by(trigger_name: "users_email_validation")
576
+ trigger.enable!(confirmation: "EXECUTE TRIGGER_ENABLE") if trigger
577
+
578
+ # 6. Validate all triggers
579
+ begin
580
+ PgSqlTriggers::Registry.validate!
581
+ puts "All triggers valid"
582
+ rescue PgSqlTriggers::ValidationError => e
583
+ puts "Validation error: #{e.message}"
584
+ end
585
+ ```
586
+
587
+ ### Batch Operations
588
+
589
+ ```ruby
590
+ # Enable all disabled triggers
591
+ PgSqlTriggers::SQL::KillSwitch.override(confirmation: "EXECUTE BATCH_ENABLE") do
592
+ disabled_triggers = PgSqlTriggers::Registry.disabled
593
+
594
+ disabled_triggers.each do |trigger|
595
+ trigger.enable!
596
+ puts "Enabled: #{trigger.trigger_name}"
597
+ end
598
+ end
599
+
600
+ # Disable all triggers for a specific table
601
+ PgSqlTriggers::SQL::KillSwitch.override(confirmation: "EXECUTE BATCH_DISABLE") do
602
+ user_triggers = PgSqlTriggers::Registry.for_table(:users)
603
+
604
+ user_triggers.each do |trigger|
605
+ trigger.disable!
606
+ puts "Disabled: #{trigger.trigger_name}"
607
+ end
608
+ end
609
+ ```
610
+
611
+ ### Inspection and Reporting
612
+
613
+ ```ruby
614
+ # Generate a drift report
615
+ drift = PgSqlTriggers::Registry.diff
616
+
617
+ puts "=== Drift Report ==="
618
+ puts "In Sync: #{drift[:in_sync].count}"
619
+ puts "Drifted: #{drift[:drifted].count}"
620
+ puts "Manual Override: #{drift[:manual_override].count}"
621
+ puts "Disabled: #{drift[:disabled].count}"
622
+ puts "Dropped: #{drift[:dropped].count}"
623
+ puts "Unknown: #{drift[:unknown].count}"
624
+
625
+ # List all triggers with details
626
+ triggers = PgSqlTriggers::Registry.list
627
+
628
+ puts "\n=== Trigger Inventory ==="
629
+ triggers.each do |trigger|
630
+ puts "#{trigger.trigger_name}:"
631
+ puts " Table: #{trigger.table_name}"
632
+ puts " Function: #{trigger.function_name}"
633
+ puts " Events: #{trigger.events.join(', ')}"
634
+ puts " Version: #{trigger.version}"
635
+ puts " Enabled: #{trigger.enabled}"
636
+ puts " Drift: #{trigger.drift_status}"
637
+ puts ""
638
+ end
639
+ ```
640
+
641
+ ### Error Handling
642
+
643
+ ```ruby
644
+ begin
645
+ # Attempt operation without proper confirmation
646
+ trigger = PgSqlTriggers::TriggerRegistry.first
647
+ trigger.enable!
648
+ rescue PgSqlTriggers::KillSwitchError => e
649
+ puts "Kill switch blocked operation: #{e.message}"
650
+ # Extract required confirmation from error message
651
+ # Re-attempt with proper confirmation
652
+ trigger.enable!(confirmation: "EXECUTE TRIGGER_ENABLE")
653
+ rescue StandardError => e
654
+ puts "Unexpected error: #{e.message}"
655
+ puts e.backtrace.first(5)
656
+ end
657
+ ```
658
+
659
+ ## Next Steps
660
+
661
+ - [Usage Guide](usage-guide.md) - Learn the DSL and migration system
662
+ - [Kill Switch](kill-switch.md) - Production safety features
663
+ - [Configuration](configuration.md) - Configure advanced settings