Module: SchemaPlus::ActiveRecord::Migration::ClassMethods

Defined in:
lib/schema_plus/active_record/migration.rb

Overview

SchemaPlus extends ActiveRecord::Migration with the following enhancements.

Instance Method Summary collapse

Instance Method Details

#add_column(table_name, column_name, type, options = {}) ⇒ Object

Enhances ActiveRecord::Migration#add_column to support indexes and foreign keys, with automatic creation

Indexes

The :index option takes a hash of parameters to pass to ActiveRecord::Migration.add_index. Thus

add_column('books', 'isbn', :string, :index => {:name => "ISBN-index", :unique => true })

is equivalent to:

add_column('books', 'isbn', :string)
add_index('books', ['isbn'], :name => "ISBN-index", :unique => true)

In order to support multi-column indexes, an special parameter :with may be specified, which takes another column name or an array of column names to include in the index. Thus

add_column('contacts', 'phone_number', :string, :index => { :with => [:country_code, :area_code], :unique => true })

is equivalent to:

add_column('contacts', 'phone_number', :string)
add_index('contacts', ['phone_number', 'country_code', 'area_code'], :unique => true)

Some convenient shorthands are available:

add_column('books', 'isbn', :index => true) # adds index with no extra options
add_column('books', 'isbn', :index => :unique) # adds index with :unique => true

Foreign Key Constraints

The :references option takes the name of a table to reference in a foreign key constraint. For example:

add_column('widgets', 'color', :integer, :references => 'colors')

is equivalent to

add_column('widgets', 'color', :integer)
add_foreign_key('widgets', 'color', 'colors', 'id')

The foreign column name defaults to id, but a different column can be specified using :references => [table_name,column_name]

Additional options :on_update and :on_delete can be spcified, with values as described at ConnectionAdapters::ForeignKeyDefinition. For example:

add_column('comments', 'post', :integer, :references => 'posts', :on_delete => :cascade)

Global default values for :on_update and :on_delete can be specified in SchemaPlus.steup via, e.g., config.foreign_keys.on_update = :cascade

Automatic Foreign Key Constraints

SchemaPlus supports the convention of naming foreign key columns with a suffix of _id. That is, if you define a column suffixed with _id, SchemaPlus assumes an implied :references to a table whose name is the column name prefix, pluralized. For example, these are equivalent:

add_column('posts', 'author_id', :integer)
add_column('posts', 'author_id', :integer, :references => 'authors')

As a special case, if the column is named ‘parent_id’, SchemaPlus assumes it’s a self reference, for a record that acts as a node of a tree. Thus, these are equivalent:

add_column('sections', 'parent_id', :integer)
add_column('sections', 'parent_id', :integer, :references => 'sections')

If the implicit :references value isn’t what you want (e.g., the table name isn’t pluralized), you can explicitly specify :references and it will override the implicit value.

If you don’t want a foreign key constraint to be created, specify :references => nil. To disable automatic foreign key constraint creation globally, set config.foreign_keys.auto_create = false in SchemaPlus.steup.

Automatic Foreign Key Indexes

Since efficient use of foreign key constraints requires that the referencing column be indexed, SchemaPlus will automatically create an index for the column if it created a foreign key. Thus

add_column('widgets', 'color', :integer, :references => 'colors')

is equivalent to:

add_column('widgets', 'color', :integer, :references => 'colors', :index => true)

If you want to pass options to the index, you can explcitly pass index options, such as :index => :unique.

If you don’t want an index to be created, specify :index => nil. To disable automatic foreign key index creation globally, set config.foreign_keys.auto_index = false in SchemaPlus.steup. (Note: If you’re using MySQL, it will automatically create an index for foreign keys if you don’t.)



147
148
149
150
# File 'lib/schema_plus/active_record/migration.rb', line 147

def add_column(table_name, column_name, type, options = {})
  super
  handle_column_options(table_name, column_name, options)
end

#add_foreign_key(table_name, column_names, references_table_name, references_column_names, options = {}) ⇒ Object

Define a foreign key constraint. Valid options are :on_update, :on_delete, and :deferrable, with values as described at ConnectionAdapters::ForeignKeyDefinition

(NOTE: Sqlite3 does not support altering a table to add foreign-key constraints; they must be included in the table specification when it’s created. If you’re using Sqlite3, this method will raise an error.)



32
33
34
# File 'lib/schema_plus/active_record/migration.rb', line 32

def add_foreign_key(table_name, column_names, references_table_name, references_column_names, options = {})
  connection.add_foreign_key(table_name, column_names, references_table_name, references_column_names, options)
end

#change_column(table_name, column_name, type, options = {}) ⇒ Object

Enhances ActiveRecord::Migration#change_column to support indexes and foreign keys same as add_column.



153
154
155
156
157
# File 'lib/schema_plus/active_record/migration.rb', line 153

def change_column(table_name, column_name, type, options = {})
  super
  remove_foreign_key_if_exists(table_name, column_name)
  handle_column_options(table_name, column_name, options)
end

#create_view(view_name, definition) ⇒ Object

Create a new view, given its name and SQL definition



15
16
17
# File 'lib/schema_plus/active_record/migration.rb', line 15

def create_view(view_name, definition)
  connection.create_view(view_name, definition)
end

#drop_view(view_name) ⇒ Object

Drop the named view



20
21
22
# File 'lib/schema_plus/active_record/migration.rb', line 20

def drop_view(view_name)
  connection.drop_view(view_name)
end

#get_references(table_name, column_name, options = {}, config = nil) ⇒ Object

Determines referenced table and column. Used in migrations.

If auto_create is true:

get_references('comments', 'post_id') # => ['posts', 'id']

And if column_name is parent_id it references to the same table

get_references('pages', 'parent_id')  # => ['pages', 'id']

If :references option is given, it is used (whether or not auto_create is true)

get_references('widgets', 'main_page_id', :references => 'pages')) 
# => ['pages', 'id']

Also the referenced id column may be specified:

get_references('addresses', 'member_id', :references => ['users', 'uuid'])
# => ['users', 'uuid']


175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/schema_plus/active_record/migration.rb', line 175

def get_references(table_name, column_name, options = {}, config=nil) #:nodoc:
  column_name = column_name.to_s
  if options.has_key?(:references)
    references = options[:references]
    references = [references, :id] unless references.nil? || references.is_a?(Array)
    references
  elsif (config || SchemaPlus.config).foreign_keys.auto_create? && !ActiveRecord::Schema.defining?
    if column_name == 'parent_id'
      [table_name, :id]
    elsif column_name =~ /^(.*)_id$/
      determined_table_name = ActiveRecord::Base.pluralize_table_names ? $1.to_s.pluralize : $1
      [determined_table_name, :id]
    end
  end
end

#remove_foreign_key(table_name, foreign_key_name) ⇒ Object

Remove a foreign key constraint

(NOTE: Sqlite3 does not support altering a table to remove foreign-key constraints. If you’re using Sqlite3, this method will raise an error.)



41
42
43
# File 'lib/schema_plus/active_record/migration.rb', line 41

def remove_foreign_key(table_name, foreign_key_name)
  connection.remove_foreign_key(table_name, foreign_key_name)
end