ActiveRecord includesとselectの別名カラム

# 概要
ActiveRecord includesを使う際、selectに別名カラムの記述があると
その別名カラムの参照ができない事象があったので、記事にしました。

# 開発環境
* ruby 2.6.5
* Rails 5.2.4.4

# ActiveRecordクラス 定義
```ruby
class User < ApplicationRecord
  has_one    :house
end

class House < ApplicationRecord
  belongs_to    :user
  belongs_to    :house_info
end

class HouseInfo < ApplicationRecord
  has_one    :house
end
```

# 問題ない場合のincludes構文
* 問題なくuserインスタンスが取得できた　（preloadの挙動になる）

```ruby
User.includes(house: :house_info).select('users.id, users.created_at as user_registed_at')

SELECT  users.id, users.created_at as user_registed_at FROM "users"
SELECT "houses".* FROM "houses" WHERE "houses"."user_id" = $1

 [#<User:0x000055a87e374e58 id: 1>,
 #<User:0x000055a87e3742c8 id: 2>,
 #<User:0x000055a87e37f858 id: 3>,
 #<User:0x000055a87e37ecc8 id: 4>,
 #<User:0x000055a87e37e1b0 id: 5>]
```

* selectで指定した別名カラムの値も取得できる

```rubyUser
User.includes(house: :house_info).select('users.id, users.created_at as user_registed_at').first.user_registed_at

SELECT  users.id, users.created_at as user_registed_at FROM "users"
SELECT "houses".* FROM "houses" WHERE "houses"."user_id" = $1
=> "2021-02-05T13:02:17.754+09:00"
```

# 問題ありのincludes構文
## includesしたテーブルに対し、検索条件(where)を追記してみた
* house_infoのownerカラムをwhere条件にいれると left outter joinとなり、テーブルそれぞれのカラムが別名のsqlが発行される(eager_loadの挙動になる)

```ruby
User.includes(house: :house_info).where(house_infos: { owner: '太郎' }).select('users.id, users.created_at as user_registed_at')

SELECT  users.id, users.created_at as user_registed_at, "users"."id" AS t0_r0, "users"."name" AS t0_r1, "interviews"."creator_id" AS t0_r2, "houses"."user_id" AS t0_r3, "houses"."house_info_id" AS t0_r4, "house_infos"."address" AS t0_r5, "house_infos"."owner" AS t0_r6
FROM "users" LEFT OUTER JOIN "houses" ON "houses"."interview_id" = "users"."id" LEFT OUTER JOIN "house_infos" ON "house_infos"."id" = "houses"."house_info_id" WHERE "house_infos"."owner" = '太郎'

[#<User:0x000055a879e296c8
  id: 1,
  created_at: 2020/12/12>
]
```

* select で別名指定した `user_registed_at`はsql上、存在するが、モデルから参照できない。 

```ruby
User.includes(house: :house_info).where(house_infos: { owner: '太郎' }).select('users.id, users.created_at as user_registed_at').first.user_registed_at

NoMethodError: undefined method `user_registed_at' for #<User:0x000055a87c1a93c8>
from /bundle/gems/activemodel-5.2.4.4/lib/active_model/attribute_methods.rb:430:in `method_missing'

```

# includesをやめて解決
* `house`と `house_info`を joinし、preloadでキャッシュするようにした

```ruby
User.joins(house: :house_info).preload(house: :house_info).where(house_infos: { owner: '太郎' }).select('users.id, users.created_at as user_registed_at')

SELECT users.id, users.created_at as user_registed_at FROM "users" INNER JOIN "houses" ON "houses"."user_id" = "users"."id" INNER JOIN "house_infos" ON "house_infos"."id" = "houses"."house_info_id" WHERE "house_infos"."owner" = '太郎'

SELECT "houses".* FROM "houses" WHERE "houses"."user_id" IN ($1, $2, $3, $4, $5)

SELECT "house_infos".* FROM "house_infos" WHERE "house_infos"."id" IN ($1, $2, $3, $4, $5)

[#<User:0x000055a87d6fb410 id: 1>,
 #<User:0x000055a87d6fa8f8 id: 2>,
 #<User:0x000055a87d6f9de0 id: 3>,
  .
  .
```

* 各モデルから `user_registed_at`が参照できるようになった。

```ruby
User.joins(house: :house_info).preload(house: :house_info).where(house_infos: { owner: '太郎' }).select('users.id, users.created_at as user_registed_at').first.user_registed_at

=> "2021-02-05T13:05:47.225+09:00"
```

# includesの挙動について調べてみた
* includesはjoinsがなければpreloadと同じ挙動となる
* 明示的にeager_loadと同じ挙動にするためにはreferencesというメソッドを併用する必要
  
  http://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-references

```ruby
User.includes(house: :house_info).references(house: :house_info).select('users.id, users.created_at as user_registed_at')

SELECT  users.id, users.created_at as user_registed_at, "users"."id" AS t0_r0, "users"."name" AS t0_r1, "interviews"."creator_id" AS t0_r2, "houses"."user_id" AS t0_r3, "houses"."house_info_id" AS t0_r4, "house_infos"."address" AS t0_r5, "house_infos"."owner" AS t0_r6
FROM "users" LEFT OUTER JOIN "houses" ON "houses"."interview_id" = "users"."id" LEFT OUTER JOIN "house_infos" ON "house_infos"."id" = "houses"."house_info_id"
```

# なぜ？別名カラムをモデルから参照できなかったのか？
https://github.com/rails/rails/issues/34889
バグだったらしく、Rails6.0.3で治っているよう。


# 最後に
includesはケースによって
preloadまたはeager_loadの挙動をするのは知っていたが、
selectで指定した別名カラムの参照問題は知らず、、１日ハマっていた。
やっぱり、ActiveRecordはハマりポイントが多そう。