Foreigner 雑記

# Foreigner
## 概要

Foreigner は migrations に外部キー制約を(付加|削除)する為のメソッドをちょこっと提供するgem。下記のDBアダプタをサポートしている。

* mysql2
* postgres
* sqlite (foreign key methods are a no-op)


## インストール

Gemfileに追加:

```ruby
  gem 'foreigner'
```
## APIの例

Foreigner は2つの methods を migrations に追加する。

* `add_foreign_key(from_table, to_table, options)`
* `remove_foreign_key(from_table, options)`

(Optionsの詳細は `connection_adapters/abstract/schema_statements.rb`に記載):
本文末に記載する。

##modelの例

```ruby

  class Comment < ActiveRecord::Base
    belongs_to :post
  end

  class Post < ActiveRecord::Base
    has_many :comments, dependent: :delete_all
  end


  #Post has many Commnets.
  #Postを削除すると、それに紐づくCommentsもすべて削除される。
```  

この場合、CommentにPostへの外部キーを設ける。

##migrationの例
migrationに`add_foreign_key`を追記。

```ruby
  add_foreign_key(:comments, :posts)
```

migrationで `:dependent` オプションを記述すると、modelでの `has_many` で`dependent:`オプションの代わりになる。

```ruby
  add_foreign_key(:comments, :posts, dependent: :delete)
```

`column:`オプションで外部キーとなるカラム名を指定する。


```ruby
  add_foreign_key(:comments, :posts, column: 'article_id')
```

さらに、`name:`オプションで制約に制約名称を付与できる。

```ruby
  add_foreign_key(:comments, :posts, name: 'comment_article_foreign_key')
```
外部キー制約を削除するときには、`:name`でつけた制約名称も指定する事。

```ruby
  remove_foreign_key(:comments, name: 'comment_article_foreign_key')
```

## テーブルメソッドの変更

Foreigner はActiverecordの`create_table` と `change_table`にメソッドを追加する。

`foreign key`の追加

```ruby
  create_table :products do |t|
    t.string :name
    t.integer :factory_id
    t.foreign_key :factories
    #t.foreign_key :外部参照先のmodel名
  end
```

```ruby
  change_table :comments do |t|
    t.foreign_key :posts, dependent: :delete
  end
```

不要な外部キーを削除:

```ruby
  change_table :comments do |t|
    t.remove_foreign_key :users
  end
```
## Database-specific options

個別のDBMSのオプション指定には、foreignerは対応する予定はない。 `:options`にて各自直接記述する事。

```ruby
  add_foreign_key(:comments, :posts, options: 'ON UPDATE DEFERRED')
```

## ライセンス
下記の通り。
Copyright (c) 2012 Matthew Higgins, released under the MIT license

##foreigner/connection_adapters/abstract/schema_statements.rb

```rb
module Foreigner
  module ConnectionAdapters
    module SchemaStatements
      def self.included(base)
        base::AbstractAdapter.class_eval do
          include Foreigner::ConnectionAdapters::AbstractAdapter
        end
      end
    end

    module AbstractAdapter
      def create_table(table_name, *args, &block)
        definition = nil
        super do |td|
          definition = td # This is my trick to get the definition
          block.call(td) unless block.nil?
        end
        definition.foreign_keys.each do |to_table, options_list|
          options_list.each do |options|
            add_foreign_key(table_name, to_table, options)
          end
        end
      end

      def supports_foreign_keys?
        false
      end

      # Checks to see if a foreign key exists on a table for a given constraint.
      #
      #   # Check a foreign key exists
      #   foreign_key_exists?(:suppliers, :companies)
      #
      #   # Check a foreign key with a custom name exists
      #   foreign_key_exists?(:suppliers, name: "fk_company_id")
      #
      #   # Check a foreign key on a column
      #   foreign_key_exists?(:suppliers, column: "company_id")
      #
      def foreign_key_exists?(table_name, options)
      end

      # Adds a new foreign key to the +from_table+, referencing the primary key of +to_table+
      #
      # The foreign key will be named after the from and to tables unless you pass
      # <tt>:name</tt> as an option.
      #
      # ===== Examples
      # ====== Creating a foreign key
      #  add_foreign_key(:comments, :posts)
      # generates
      #  ALTER TABLE `comments` ADD CONSTRAINT
      #     `comments_post_id_fk` FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`)
      #
      # ====== Creating a named foreign key
      #  add_foreign_key(:comments, :posts, name: 'comments_belongs_to_posts')
      # generates
      #  ALTER TABLE `comments` ADD CONSTRAINT
      #     `comments_belongs_to_posts` FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`)
      #
      # ====== Creating a cascading foreign_key on a custom column
      #  add_foreign_key(:people, :people, column: 'best_friend_id', dependent: :nullify)
      # generates
      #  ALTER TABLE `people` ADD CONSTRAINT
      #     `people_best_friend_id_fk` FOREIGN KEY (`best_friend_id`) REFERENCES `people` (`id`)
      #     ON DELETE SET NULL
      #
      # === Supported options
      # [:column]
      #   Specify the column name on the from_table that references the to_table. By default this is guessed
      #   to be the singular name of the to_table with "_id" suffixed. So a to_table of :posts will use "post_id"
      #   as the default <tt>:column</tt>.
      # [:primary_key]
      #   Specify the column name on the to_table that is referenced by this foreign key. By default this is
      #   assumed to be "id".
      # [:name]
      #   Specify the name of the foreign key constraint. This defaults to use from_table and foreign key column.
      # [:dependent]
      #   If set to <tt>:delete</tt>, the associated records in from_table are deleted when records in to_table table are deleted.
      #   If set to <tt>:nullify</tt>, the foreign key column is set to +NULL+.
      # [:options]
      #   Any extra options you want appended to the foreign key definition.
      def add_foreign_key(from_table, to_table, options = {})
      end

      # Remove the given foreign key from the table.
      #
      # ===== Examples
      # ====== Remove the suppliers_company_id_fk in the suppliers table.
      #   remove_foreign_key :suppliers, :companies
      # ====== Remove the foreign key named accounts_branch_id_fk in the accounts table.
      #   remove_foreign_key :accounts, column: :branch_id
      # ====== Remove the foreign key named party_foreign_key in the accounts table.
      #   remove_foreign_key :accounts, name: :party_foreign_key
      def remove_foreign_key(from_table, options)
      end

      # Return the foreign keys for the schema_dumper
      def foreign_keys(table_name)
        []
      end
    end
  end
end
```