関連付けの拡張（Railsガイド写経）

&nbsp;&nbsp;Railsガイド「Active Record の関連付け (アソシエーション)」の章にある、 [4.6 関連付けの拡張](https://railsguides.jp/association_basics.html#%E9%96%A2%E9%80%A3%E4%BB%98%E3%81%91%E3%81%AE%E6%8B%A1%E5%BC%B5)
の```Customer```クラスを写経しました。

# モデル作成・マイグレーションの実行

```shell-session
$> rails g model customer name
$> rails g model order name region_id customer:references
$> rails db:migrate
```

上記により、以下の2モデルが作成されました。

```ruby:models/customer.rb
class Customer < ApplicationRecord
end
```
```models/order.rb
class Order < ApplicationRecord
  belongs_to :customer
end
```

MySQLの```SHOW CREATE TABLE``` でテーブルを確認します。

```sql:customersテーブル
CREATE TABLE `customers` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

```sql:ordersテーブル
CREATE TABLE `orders` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `region_id` varchar(255) DEFAULT NULL,
  `customer_id` int(11) DEFAULT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `index_orders_on_customer_id` (`customer_id`),
  CONSTRAINT `fk_rails_3dad120da9` FOREIGN KEY (`customer_id`) REFERENCES `customers` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

# テストデータの作成

## 以下の要領で作成

* customersとordersとで、idの開始値を分かりやすく、なるべく離れた値になるように変えておく。
* Railsガイドのコード```region_id: order_number[0..2]```にそって、region_id は3文字の文字列で、数個作成
* orders のnameカラムには長さ16のランダムな文字列を作成（ [ランダムな文字列生成](http://qiita.com/diaphragm/items/50b2703c3cd4ba8490a9)を使わせて頂きました。）
* 一つの```Customer```につき、20個以上30個以下の```Order```を紐づける。

## seeds.rb

上記にそって、```seeds.rb```を以下

```ruby:db/seeds.rb
ActiveRecord::Base.connection_pool.with_connection { |con|
  con.execute('ALTER TABLE customers AUTO_INCREMENT=201')
  con.execute('ALTER TABLE orders AUTO_INCREMENT=9001')
}

region_ids = %w(A01 F06 K55 P24 S99 X40)

customer_names = (1..3).map {|n| "顧客-#{n}"}

customer_names.each do |n|

  c = Customer.create(name: n)

  num_orders = (20..30).to_a.sample
  num_orders.times do
    order_name = Array.new(16){[*:a..:z,*0..9].sample}.join
    Order.create(name: order_name, customer: c, region_id: region_ids.sample)
  end

end
```
のように作成してからの、

```shell-session
$> rails db:seed
```
にてサンプルデータ生成

# 確認

Customerに、まずは単に ```has_many :orders```を付加して以下とする。

```ruby:models/customer.rb
class Customer < ApplicationRecord
  has_many :orders
end
```

```rails console　で確認```

```irb
[ykt68@macbook testapp]$ rails c
Running via Spring preloader in process 15958
Loading development environment (Rails 5.0.1)
irb(main):001:0> customer = Customer.find 202
  Customer Load (0.5ms)  SELECT  `customers`.* FROM `customers` WHERE `customers`.`id` = 202 LIMIT 1
=> #<Customer id: 202, name: "顧客-2", created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">
irb(main):002:0> customer.orders
  Order Load (0.6ms)  SELECT `orders`.* FROM `orders` WHERE `orders`.`customer_id` = 202
=> #<ActiveRecord::Associations::CollectionProxy [#<Order id: 9031, name: "illm3h622nq1744u", region_id: "K55", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9032, name: "s76ak83jlys0oww4", region_id: "X40", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9033, name: "ep2jkr5htseo9l4m", region_id: "A01", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9034, name: "t8t9s6xs3jwtnwjs", region_id: "A01", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9035, name: "wx72xtzslx6cbtin", region_id: "A01", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9036, name: "7qqdapbggj35dffv", region_id: "K55", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9037, name: "m7vyztkdxv6hgbkr", region_id: "F06", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9038, name: "0enruf5cfxj930tn", region_id: "S99", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9039, name: "zg9zdgpjom8lpitn", region_id: "K55", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9040, name: "ujwk6eklncw4idru", region_id: "S99", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, ...]>
irb(main):003:0>
```

と、ここまでは基本の範囲内。

ここからが本題で、```Customer``` の ```ordes``` にRailsガイドに記載のようにブロックを付加して、以下とする。

```ruby:models/customer.rb
class Customer < ApplicationRecord
  has_many :orders do
    def find_by_order_prefix(order_number)
      find_by(region_id: order_number[0..2])
    end
  end
end
```

&nbsp;&nbsp;上記の状態で、```rails console``` で、```find_by_order_prefix```を使ってみる。
```find_by_order_prefix```メソッドの引数```order_number```の最初の3文字が
```region_id```であるという想定のコードなので、それっぽい```order_number```の
値を渡してやる。


```irb
[ykt68@macbook testapp]$ rails c
Running via Spring preloader in process 16540
Loading development environment (Rails 5.0.1)
irb(main):001:0> customer = Customer.find 202
  Customer Load (0.5ms)  SELECT  `customers`.* FROM `customers` WHERE `customers`.`id` = 202 LIMIT 1
=> #<Customer id: 202, name: "顧客-2", created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">
irb(main):002:0> customer.orders.find_by_order_prefix('K55-1234-9999')
  Order Load (0.5ms)  SELECT  `orders`.* FROM `orders` WHERE `orders`.`customer_id` = 202 AND `orders`.`region_id` = 'K55' LIMIT 1
=> #<Order id: 9031, name: "illm3h622nq1744u", region_id: "K55", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">
irb(main):003:0>
```

It worked !

で、```find_by_order_prefix```メソッドの中で、```self```（つまり、```orders```）のクラス何ですの？と思ったので、

```ruby:models/customer.rb
    def find_by_order_prefix(order_number)
      p self
      find_by(region_id: order_number[0..2])
    end
```

として、再度```rails console```で確認してみると、こんなん出ました。

```irb
[ykt68@macbook testapp]$ rails c
Running via Spring preloader in process 17124
Loading development environment (Rails 5.0.1)
irb(main):001:0> customer = Customer.find 202
  Customer Load (0.5ms)  SELECT  `customers`.* FROM `customers` WHERE `customers`.`id` = 202 LIMIT 1
=> #<Customer id: 202, name: "顧客-2", created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">
irb(main):002:0> customer.orders.find_by_order_prefix('K55-1234-9999')
  Order Load (0.6ms)  SELECT `orders`.* FROM `orders` WHERE `orders`.`customer_id` = 202
#<ActiveRecord::Associations::CollectionProxy [#<Order id: 9031, name: "illm3h622nq1744u", region_id: "K55", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9032, name: "s76ak83jlys0oww4", region_id: "X40", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9033, name: "ep2jkr5htseo9l4m", region_id: "A01", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9034, name: "t8t9s6xs3jwtnwjs", region_id: "A01", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9035, name: "wx72xtzslx6cbtin", region_id: "A01", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9036, name: "7qqdapbggj35dffv", region_id: "K55", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9037, name: "m7vyztkdxv6hgbkr", region_id: "F06", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9038, name: "0enruf5cfxj930tn", region_id: "S99", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9039, name: "zg9zdgpjom8lpitn", region_id: "K55", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9040, name: "ujwk6eklncw4idru", region_id: "S99", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, ...]>
  Order Load (0.7ms)  SELECT  `orders`.* FROM `orders` WHERE `orders`.`customer_id` = 202 AND `orders`.`region_id` = 'K55' LIMIT 1
=> #<Order id: 9031, name: "illm3h622nq1744u", region_id: "K55", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">
irb(main):003:0>
```

なので、```self```は、```ActiveRecord::Associations::CollectionProxy```オブジェクトでした。

さらに、Railsガイドに

```
関連付けプロキシの内部を参照するには、proxy_association アクセサにある以下の 3 つの属性を使用します。

・proxy_association.owner は、関連付けを所有するオブジェクトを返します。 

・proxy_association.reflection は、関連付けを記述するリフレクションオブジェクトを返します。

・proxy_association.target は、belongs_to または has_one 関連付けのオブジェクトを返すか、has_many または has_and_belongs_to_many 関連付けオブ ジェクトのコレクションを返します。
```
との記載があるので、以下、これらを確認。

```irb
[ykt68@macbook testapp]$ rails c
Running via Spring preloader in process 18584
Loading development environment (Rails 5.0.1)
irb(main):001:0> customer = Customer.find 202
  Customer Load (0.6ms)  SELECT  `customers`.* FROM `customers` WHERE `customers`.`id` = 202 LIMIT 1
=> #<Customer id: 202, name: "顧客-2", created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">
```

```proxy_association.owner``` は、

```irb
irb(main):002:0> customer.orders.proxy_association.owner
=> #<Customer id: 202, name: "顧客-2", created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">
```
Customerオブジェクトへの参照が入っている。
```proxy_association.tager```は

```irb
irb(main):003:0> customer.orders.proxy_association.target
=> []
```
現時点では空の配列。
今回追加した、```find_by_order_prefix```　を呼んでみる。

```irb
irb(main):004:0> customer.orders.find_by_order_prefix('K55-1234-9999')
  Order Load (0.9ms)  SELECT `orders`.* FROM `orders` WHERE `orders`.`customer_id` = 202
#<ActiveRecord::Associations::CollectionProxy [#<Order id: 9031, name: "illm3h622nq1744u", region_id: "K55", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9032, name: "s76ak83jlys0oww4", region_id: "X40", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9033, name: "ep2jkr5htseo9l4m", region_id: "A01", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9034, name: "t8t9s6xs3jwtnwjs", region_id: "A01", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9035, name: "wx72xtzslx6cbtin", region_id: "A01", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9036, name: "7qqdapbggj35dffv", region_id: "K55", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9037, name: "m7vyztkdxv6hgbkr", region_id: "F06", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9038, name: "0enruf5cfxj930tn", region_id: "S99", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9039, name: "zg9zdgpjom8lpitn", region_id: "K55", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9040, name: "ujwk6eklncw4idru", region_id: "S99", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, ...]>
  Order Load (0.5ms)  SELECT  `orders`.* FROM `orders` WHERE `orders`.`customer_id` = 202 AND `orders`.`region_id` = 'K55' LIMIT 1
=> #<Order id: 9031, name: "illm3h622nq1744u", region_id: "K55", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">
```

そして、```proxy_association.target```を見ると、

```irb
irb(main):005:0> customer.orders.proxy_association.target
=> [#<Order id: 9031, name: "illm3h622nq1744u", region_id: "K55", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9032, name: "s76ak83jlys0oww4", region_id: "X40", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9033, name: "ep2jkr5htseo9l4m", region_id: "A01", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9034, name: "t8t9s6xs3jwtnwjs", region_id: "A01", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9035, name: "wx72xtzslx6cbtin", region_id: "A01", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9036, name: "7qqdapbggj35dffv", region_id: "K55", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9037, name: "m7vyztkdxv6hgbkr", region_id: "F06", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9038, name: "0enruf5cfxj930tn", region_id: "S99", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9039, name: "zg9zdgpjom8lpitn", region_id: "K55", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9040, name: "ujwk6eklncw4idru", region_id: "S99", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9041, name: "n8muqxe3xob3gxw0", region_id: "A01", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9042, name: "e98oq2wchhq8az7o", region_id: "K55", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9043, name: "7mfqve9aflehtb4e", region_id: "F06", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9044, name: "9vcsti7fg2sdsspa", region_id: "K55", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9045, name: "ygd6iu22qkhhgq3s", region_id: "P24", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9046, name: "h9wbvnizy2gc1m0w", region_id: "A01", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9047, name: "nso9r1xf03kl0pq4", region_id: "F06", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9048, name: "wuz20tg3fodypio2", region_id: "X40", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9049, name: "95is5jiwnpfttpfw", region_id: "F06", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9050, name: "4q4h20379rjgneac", region_id: "S99", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9051, name: "89tojln9j1vyraqd", region_id: "P24", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9052, name: "ge19q22g3xfjvlpq", region_id: "K55", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9053, name: "2x3c9tg62rsgsjze", region_id: "X40", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9054, name: "f8b78zxvfjeks2f7", region_id: "X40", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9055, name: "qmdncvg63tx1hkfx", region_id: "S99", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9056, name: "tfgtgan6w4q2vxtx", region_id: "K55", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9057, name: "0h7tvjuy31mk8mul", region_id: "F06", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9058, name: "v1nbec4njebsm08x", region_id: "K55", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9059, name: "e3uyudy3jiust1gp", region_id: "X40", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">, #<Order id: 9060, name: "cun0uhbwj4a9m0za", region_id: "A01", customer_id: 202, created_at: "2017-02-21 05:17:17", updated_at: "2017-02-21 05:17:17">]
irb(main):006:0> 
```

と、```region_id```が、```K55```<b>ではない</b>```Order```<b>も含めた</b>
```customer_id``` が```202```の```Order```の配列となっていた。

最後に、```proxy_association.reflection```は、

```irb
irb(main):006:0> customer.orders.proxy_association.reflection
=> #<ActiveRecord::Reflection::HasManyReflection:0x007fb3f4b6a0d0 @name=:orders, @scope=#<Proc:0x007fb3f4b6a0f8@/Users/ykt68/.rbenv/versions/2.2.6/lib/ruby/gems/2.2.0/gems/activerecord-5.0.1/lib/active_record/associations/builder/collection_association.rb:79>, @options={}, @active_record=Customer(id: integer, name: string, created_at: datetime, updated_at: datetime), @klass=Order(id: integer, name: string, region_id: string, customer_id: integer, created_at: datetime, updated_at: datetime), @plural_name="orders", @automatic_inverse_of=false, @type=nil, @foreign_type="orders_type", @constructable=true, @association_scope_cache={}, @scope_lock=#<Mutex:0x007fb3f4b69f18>, @class_name="Order", @foreign_key="customer_id", @active_record_primary_key="id">
irb(main):007:0>
```

と、```ActiveRecord::Reflection::HasManyReflection``` オブジェクトへの参照となっており、
色々、あれやこれやの実行時情報が入っている。


以上です。
何か確認のやり方がおかしい点などあれば、ご指摘ください。

### 参考
[ActiveRecord::Associations::CollectionProxy](http://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html)