覚えておくと幸せになれるActiveRecord::Relationメソッド6選


参照元はMitch Crowe氏の[こちらのエントリー](http://blog.mitchcrowe.com/blog/2012/04/14/10-most-underused-activerecord-relation-methods/)。時期にして3年以上前の更新になりますが、未だに参照することが多いものをこの際敬意を込めてまとめることにしました。

## 1. first_or_create(ブロック付き)
任意のレコードを新規保存する際に、既に同じ情報を持ったレコードがある場合被らせたくない！というニーズにクリティカルに答えてくれるのが`first_or_create`メソッド。`where`メソッドと併用することで、検索条件に合致するレコードが存在する場合にはそのレコードを参照し、無ければ検索条件の内容で新しいレコードを新規保存してくれます。返り値は参照したレコードもしくは新規作成したレコードです。

```ruby:百聞は一見に如かず
Book.where(title: 'Frozen').first_or_create

# Book Load (1.7ms) SELECT `books`.* FROM `books` WHERE `books`.`title` = 'Frozen' ORDER BY `books`.`id` ASC LIMIT 1

## 該当するデータが存在しない場合のみ検索条件の内容で新規レコードを作成
# 	(0.1ms) BEGIN
# SQL(29.9ms) INSERT INTO `books` (`created_at`, `title`, `updated_at`) VALUES ('2015-06-26 15:58:59', 'Frozen', '2015-06-26 15:58:59')
# 	(1.9ms) COMMIT
```

しかしレコードの検索をかけたタイミングで別のプロパティ値を設定したくなる時が頻繁に出てくる。例えば既存のレコードを参照するにしろ新規レコードを作成するにしろ、新たに著者(author)の情報も追加して保存したい、といったケースです。`first_or_initialize`メソッドと`save`メソッドを併用すれば解決しますが、なんとふたつもメソッド使わなくていいんです。 **first_or_createメソッドはブロックを引数に持つことができます**。

```ruby:百聞は一見に如かず
Book.where(title: 'Frozen').first_or_create do |book|
	book.author = 'Charles Dickens'
end

# Book Load (1.7ms) SELECT `books`.* FROM `books` WHERE `books`.`title` = 'Frozen' ORDER BY `books`.`id` ASC LIMIT 1

## 検索条件ではないauthorのデータも同時に保存できる
# 	(0.1ms) BEGIN
# SQL(29.9ms) INSERT INTO `books` (`created_at`, `title`, `author`, `updated_at`) VALUES ('2015-06-26 15:58:59', 'Frozen', 'Charles Dickens', '2015-06-26 15:58:59')
# 	(1.9ms) COMMIT
```

## 2. none
Rails4からサポートされた機能。名前から想像できるとおり空のモデルオブジェクトを生成して返り値とします。例えばユーザーのコンディションによっては、何をやっても「該当するデータは存在しませんでした」というメッセージだけを返すなど違和感なく機能を制限したいときに使えるメソッドです。

```ruby:百聞は一見に如かず
@posts = current_user.available_posts

## 有効期限切れのユーザーに対してはレコードが何も無いように見せたい時
def available_posts
  case role
  when 'Admin'
    Post.all
  when 'Reviewer'
    Post.where(condition: published)
  when 'Expired'
    Post.none
  end
end
```

## 3. find_each
ぼくらのイテレータ`each`。しかし数千〜数万のレコードに対して同一の処理を繰り返したいときに使用すると、単一のクエリがレコードの数分発行されてしまうのでアプリが素敵にフリーズします。そんな時は`find_each`メソッドがよさげな様子。1000件のデータをまとめてバッチ処理してくれるために処理が高速化します。

```ruby:百聞は一見に如かず
Book.where(author: nil).find_each do |book|
  book.author = "not to be filled yet"
  book.save
end
```

## 4. find_by
テーブルから条件に該当するただひとつのレコードを取得したいとき`Book.where(title: 'Frozen', author: 'Charles Dickens').first`とするのはなんだかスマートじゃないですよね。その時に使えるのが`find_by`メソッドです。条件に合致するただひとつのレコードを取得し配列に入れずに返してくれます。

```ruby:百聞は一見に如かず
Book.find_by(title: 'Frozen', author: 'Charles Dickens')

## 返り値は配列に入っていない
# Book Load (0.3ms)  SELECT `books`.* FROM `books` WHERE `books`.`title` = 'frozen' AND `books`.`author` = 'Charles Dickens' LIMIT 1
# => #<Book id: 1, title: "Frozen", author: "Charles Dickens", created_at: "2015-06-27 03:11:23", updated_at: "2015-06-27 03:11:23">
```

## 5. pluck
任意のカラムの値で配列化したい時、`map`メソッドを使って以下のようにやっていませんか？

```ruby:百聞は一見に如かず
@book_titles = Book.all.map(&:title)
```

代わりに`pluck`メソッドを使ってあげましょう。
`all × map`メソッドの組み合わせと比較してみると、[実行速度の面からも著しい改善が見られるようです](http://blog.livedoor.jp/sasata299/archives/51847390.html)。

```ruby:百聞は一見に如かず
@book_titles = Book.pluck(:title)

# => ["Frozen", "Tower", "the Lion King", ...]
```

## 6. merge
2つのハッシュを結合し、キーとバリューを維持したまま1つのハッシュにして返り値としてくれます。なお同じキーの値があった際には、`merge`メソッドの引数として渡した値の方が優先される模様。`merge!`という破壊的メソッドも用意されていて、こちらはレシーバ自身の値を書き換えてしまいます。レコードを保存する際に、複数のハッシュを単一のハッシュにできる`merge`は大変重宝します。

```ruby:百聞は一見に如かず
book_data = {title: "Frozen", author: "Charles Dickens"}
book_review = {rate: 10, review: "Awesome!"}

book_data.merge(book_review)

# => {title: "Frozen", author: "Charles Dickens", rate: 10, review: "Awesome!"}
```

---

以上となります。

[オリジナルの英語記事](http://blog.mitchcrowe.com/blog/2012/04/14/10-most-underused-activerecord-relation-methods/)では、使用頻度の関係から紹介できなかった`scoping`などについても解説されていますので、ぜひ一度ご覧になってみてください。