Class: PgSqlTriggers::TriggerStructureDumper

Inherits:
Object
  • Object
show all
Defined in:
lib/pg_sql_triggers/trigger_structure_dumper.rb

Overview

Builds a SQL snapshot of PostgreSQL triggers for db/trigger_structure.sql and emits schema.rb annotations so teams know triggers are outside schema.rb.

Class Method Summary collapse

Class Method Details

.default_pathObject



18
19
20
21
22
# File 'lib/pg_sql_triggers/trigger_structure_dumper.rb', line 18

def default_path
  raise "Rails.root is required" unless defined?(Rails) && Rails.respond_to?(:root) && Rails.root

  Rails.root.join("db/trigger_structure.sql")
end

.dump_to(path = nil, connection: ActiveRecord::Base.connection) ⇒ Object



24
25
26
27
28
29
# File 'lib/pg_sql_triggers/trigger_structure_dumper.rb', line 24

def dump_to(path = nil, connection: ActiveRecord::Base.connection)
  target = resolve_path(path)
  target.dirname.mkpath
  target.write(generate_sql(connection: connection))
  target
end

.generate_sql(connection: ActiveRecord::Base.connection) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/pg_sql_triggers/trigger_structure_dumper.rb', line 42

def generate_sql(connection: ActiveRecord::Base.connection)
  header = <<~SQL
    -- pg_sql_triggers trigger_structure.sql
    -- Generated at: #{Time.now.utc.iso8601}
    --
    -- Apply with: bin/rails trigger:load
    -- Prefer checking this file into version control alongside db/triggers migrations.
  SQL

  rows = trigger_rows(connection)
  parts = [header]

  rows.each do |row|
    trigger_name = row["trigger_name"] || row[:trigger_name]
    parts << ""
    parts << "-- Trigger: #{trigger_name}"
    append_definition(parts, row["function_definition"] || row[:function_definition])
    append_definition(parts, row["trigger_definition"] || row[:trigger_definition])
  end

  parts.join("\n").strip.concat("\n")
end

.load_from(path = nil, connection: ActiveRecord::Base.connection) ⇒ Object

Raises:

  • (Errno::ENOENT)


31
32
33
34
35
36
37
38
39
40
# File 'lib/pg_sql_triggers/trigger_structure_dumper.rb', line 31

def load_from(path = nil, connection: ActiveRecord::Base.connection)
  target = resolve_path(path)
  raise Errno::ENOENT, target.to_s unless target.file?

  sql = target.read
  return if sql.strip.empty?

  connection.raw_connection.exec(sql)
  nil
end

.managed_trigger_names(connection) ⇒ Object



82
83
84
85
86
87
88
# File 'lib/pg_sql_triggers/trigger_structure_dumper.rb', line 82

def managed_trigger_names(connection)
  return [] unless connection.table_exists?("pg_sql_triggers_registry")

  connection.select_values(
    "SELECT trigger_name FROM pg_sql_triggers_registry ORDER BY trigger_name"
  )
end

.resolve_path(override = nil) ⇒ Object



11
12
13
14
15
16
# File 'lib/pg_sql_triggers/trigger_structure_dumper.rb', line 11

def resolve_path(override = nil)
  base = override || PgSqlTriggers.trigger_structure_sql_path
  resolved = base.respond_to?(:call) ? base.call : base
  resolved ||= default_path
  Pathname(resolved.to_s)
end

.schema_rb_annotation(connection: ActiveRecord::Base.connection) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/pg_sql_triggers/trigger_structure_dumper.rb', line 65

def schema_rb_annotation(connection: ActiveRecord::Base.connection)
  names = managed_trigger_names(connection)
  lines = []
  lines << "  # ---------------------------------------------------------------------------"
  lines << "  # pg_sql_triggers: PostgreSQL triggers are not captured in schema.rb."
  lines << "  # After db:schema:load, run: bin/rails trigger:migrate (or trigger:load)."
  lines << "  # SQL snapshot: db/trigger_structure.sql (bin/rails trigger:dump)."
  lines << "  # For full fidelity use config.active_record.schema_format = :sql."
  lines << if names.any?
             "  # Managed triggers (#{names.length}): #{names.join(', ')}"
           else
             "  # Managed triggers: (none registered in pg_sql_triggers_registry)"
           end
  lines << "  # ---------------------------------------------------------------------------"
  lines.join("\n")
end