Class: ActiveRecord::Migration

Inherits:
Object
  • Object
show all
Defined in:
lib/active_record/migration/active_migration_patch.rb

Overview

If you would like to use these methods in a Rails migration, make sure you add this require line at the top of your migration file:

require "active_migration_patch"

Direct Known Subclasses

HasHelpersInitialSetup

Instance Method Summary collapse

Instance Method Details

#add_compound_foreign_key(from_table:, to_table:, from_columns:, to_columns:, name: nil, validate: true) ⇒ Object

Adds a compound foreign key based on the arguments passed in. from_table: name of the table the fk constraint is on (symbol or string) to_table: name of the table the fk references (symbol or string) from_columns (optional): name of the fk column(s) in from_table; defaults to using the standard foreign key name for the to_table - e.g., "client_id" if the to_table is "clients" (symbol, string, or array of symbols and/or strings) to_columns (optional): name of the referenced column(s) in to_table; defaults to "id" (symbol, string, or array of symbols and/or strings) name (optional): name for the foreign key constraint If you pass in from_columns and to_columns, the columns should be ordered so the first references the first, the second references the second etc.



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/active_record/migration/active_migration_patch.rb', line 82

def add_compound_foreign_key(from_table:, to_table:, from_columns:, to_columns:, name: nil, validate: true)
  name ||= compound_foreign_key_name(
    from_table: from_table,
    to_table: to_table,
    from_columns: from_columns,
    to_columns: to_columns
  )

  ActiveRecord::Base.connection_pool.with_connection do |conn|
    conn.execute <<-END_OF_SQL
      ALTER TABLE #{from_table}
          ADD CONSTRAINT #{name}
          FOREIGN KEY (#{from_columns.join(",")})
          REFERENCES #{to_table} (#{to_columns.join(",")})
          #{"NOT VALID" unless validate}
          DEFERRABLE INITIALLY DEFERRED;
    END_OF_SQL
  end
end

#add_org_scoped_foreign_key(from_table:, to_table:, from_columns: nil, to_columns: nil, name: nil, validate: true) ⇒ Object

Adds a foreign key based on the arguments passed in. If organization_id is not already part of the from_columns, it adds it to both the from_columns and to_columns. The default foreign key name will use the same format we used in our data migrations to initially update all foreign keys to include organization_id. from_table: name of the table the fk constraint is on (symbol or string) to_table: name of the table the fk references (symbol or string) from_columns (optional): name of the fk column(s) in from_table; defaults to using the standard foreign key name for the to_table - e.g., "client_id" if the to_table is "clients" (symbol, string, or array of symbols and/or strings) to_columns (optional): name of the referenced column(s) in to_table; defaults to "id" (symbol, string, or array of symbols and/or strings) name (optional): name for the foreign key constraint If you pass in from_columns and to_columns, the columns should be ordered so the first references the first, the second references the second etc.

EXAMPLES:

add_org_scoped_foreign_key(from_table: :trustees, to_table: :clients)
add_org_scoped_foreign_key(
from_table: :advisors,
to_table: :users,
from_columns: :created_by_id
)
add_org_scoped_foreign_key(
from_table: :toys,
to_table: "factories",
from_columns: [:factory_id, "toy_category_id"]
to_columns: ["id", :toy_category_id]
)


41
42
43
44
45
46
47
48
49
50
51
# File 'lib/active_record/migration/active_migration_patch.rb', line 41

def add_org_scoped_foreign_key(from_table:, to_table:, from_columns: nil, to_columns: nil, name: nil, validate: true)
  from_columns, to_columns = foreign_key_columns(to_table: to_table, from_columns: from_columns, to_columns: to_columns)
  add_compound_foreign_key(
    from_table: from_table,
    to_table: to_table,
    from_columns: from_columns,
    to_columns: to_columns,
    name: name,
    validate: validate
  )
end

#compound_foreign_key_name(from_table:, to_table:, from_columns:, to_columns:) ⇒ Object

Returns a name for the foreign key constraint. If the from_columns include organization_id, the name begins with "org_id_fk_"; otherwise, it starts with "compound_fk_".



104
105
106
107
108
109
110
# File 'lib/active_record/migration/active_migration_patch.rb', line 104

def compound_foreign_key_name(from_table:, to_table:, from_columns:, to_columns:)
  from_columns = from_columns.map(&:to_s)
  to_columns = to_columns.map(&:to_s)
  prefix = from_columns.include?("organization_id") ? "org_id" : "compound"

  "#{prefix}_fk_#{::Digest::MD5.hexdigest([from_table.to_s, from_columns, to_table.to_s, to_columns].flatten.join("."))}"
end

#conditionally_add_index(table, columns, opts = {}) ⇒ Object



7
8
9
# File 'lib/active_record/migration/active_migration_patch.rb', line 7

def conditionally_add_index(table, columns, opts = {})
  add_index(table, columns, opts) unless index_exists?(table, columns, opts)
end

#foreign_key_columns(to_table:, from_columns: nil, to_columns: nil) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
# File 'lib/active_record/migration/active_migration_patch.rb', line 112

def foreign_key_columns(to_table:, from_columns: nil, to_columns: nil)
  from_columns = from_columns.blank? ? [to_table.to_s.singularize.foreign_key] : Array(from_columns)
  to_columns = to_columns.blank? ? ["id"] : Array(to_columns)

  unless from_columns.map(&:to_s).include?("organization_id")
    from_columns << "organization_id"
    to_columns << "organization_id"
  end

  return from_columns, to_columns
end

#validate_org_scoped_foreign_key(from_table:, to_table:, from_columns: nil, to_columns: nil, name: nil) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/active_record/migration/active_migration_patch.rb', line 53

def validate_org_scoped_foreign_key(from_table:, to_table:, from_columns: nil, to_columns: nil, name: nil)
  from_columns, to_columns = foreign_key_columns(to_table: to_table, from_columns: from_columns, to_columns: to_columns)
  name ||= compound_foreign_key_name(
    from_table: from_table,
    to_table: to_table,
    from_columns: from_columns,
    to_columns: to_columns
  )

  ActiveRecord::Base.connection_pool.with_connection do |conn|
    conn.execute <<-END_OF_SQL
      ALTER TABLE #{from_table}
      VALIDATE CONSTRAINT #{name};
    END_OF_SQL
  end
end