LoginSignup
21
3

More than 5 years have passed since last update.

ActiveRecord::Relationのソースを見てみる。 初心者→中級者へのSTEP25/25

Posted at

ActiveRecord::Relation

はじめに

最後ですね。25日目に何を書こうか悩みましたが、このカレンダー全体を通してActiveRecordを主体として書いてきたつもりですので、最後も結局ActiveRecord関連です。また初心者が中級者になる段階として、ソースを見るという段階があると思います。最後はそんな、メソッドのソースを見るということをやってみます。

環境
Rails 5.1.6
ruby 2.3.1

ActiveRecord::Relation

まず最初にActiveRecord::RelationとはいわゆるO/Rマッパーというやつです。簡単に説明すると、オブジェクティブ指向プログラミング言語と、 データベース間を繋ぐ役割を果たしています。クエリを発行したり、そのクエリをプログラム内で再利用可能にしたり、データベースから取得したデータをプログラム内で扱えるように変換したりしています。今回はソースと共にActiveRecord::Relationのメソッドの内容を見ていきます。

ソースはこちら
https://github.com/rails/rails/blob/66da5ea1286c2a3b428a18546f7f076722218bd4/activerecord/lib/active_record/relation.rb

find_or_initialize_by

ではメソッドたちを見てみましょう。まずはfind_or_initialize_byこいつは確かomniauthを使うときに出てきた奴。

def find_or_initialize_by(attributes, &block)
  find_by(attributes) || new(attributes, &block)
end

find_byしてあったらそのまま返し、なかったらnewしてくれる奴。sns認証の時に用いられたりします。

first_or_create

def first_or_create(attributes = nil, &block) # :nodoc:
  first || create(attributes, &block)
end

最初見たとき、こんなもんUser.first_or_createとかやったら絶対最初のレコードが帰ってくるやんって思ってました。違います。whereと併用して使います。

User.where(name: 'taro').first_or_create(age: 18)

whereで条件を指定し、名前がtaroという人がいなければ歳を18としてレコードを作成します。
同じ情報を持ったレコードを作らせたくない時に使います。

find_or_create_by

first_or_createに似た奴がこちら、ていうかほぼ同じ。こっちの方がメソッドチェーンしなくて済むので好き。

def find_or_create_by(attributes, &block)
  find_by(attributes) || create(attributes, &block)
end

find_each

以前アンチパターンで紹介したこやつ。eachによって数千、数万のレコードに対する同じ処理を一気にやるのではなく分割してくれる奴。whereとチェーンして使える。batch_sizeに1000が指定されてますね。ここで分割数を設定しているのでしょう。

 def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)
  if block_given?
    find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do |records|
      records.each { |record| yield record }
    end
  else
    enum_for(:find_each, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore) do
      relation = self
      apply_limits(relation, start, finish).size
    end
  end
end

下記のように使えば21歳以上のユーザーを1000件づつ処理してくれる。

Person.where("age > 21").find_each do |person|
 person.party_all_night!
end

まだまだありますが、メソッドのソースはこんな感じでした。今回はメソッドの紹介風にソースを眺めてみましたが、実際としては、書いたプログラムがうまく機能しなかったり、メソッドの帰り値が予想と違うだとか、そうゆう時にメソッドの中身を見て、原因を探っていく時に見るものです。
他の記事を書いた時も何回かサンプルコードがうまく動作しないとなってはソースを見て、の繰り返しでした。
ソースコードの読解力は中級者になるには必須の力でしょう。

まとめ

今回はActiveRecord::Relationはなんぞやってとこと、そのメソッドたちをソースコードと共に紹介しました。
が、一番伝えたかったのはソースコードを見よう!ということです。
実は最初書こうと思ってた記事はUser.newnewをソースコードを見ながら紐解いてみようという記事を書くつもりでした。ですが、しっかり説明しようとするとかなりボリューミーになりそうだと途中で気づき、というかそもそも伝えるべきはnewじゃなくてソースコードを読むべしってことなんじゃねってなってこんな記事になりました。(一応テーマも初心者→中級者ですし)

最後に(エモエモ)

この25日間、とても大変だったです、ええ。最初の数日は記事の溜め込みがあったので良かったのですが、ストックが尽きた途端、時間に終われる日々が始まりました。仕事終わりにコワーキングスペースに駆け込み20時から3時間ほどで、調べ、サンプルコードを書き、記事にまとめる。ご存知の通りいつもギリギリでした。また、調べの段階で詰まった時は取り急ぎの記事を書いてたりしました。(すみません...ていうかほぼ今回にも当てはまる...)
でもまあ得たものは大きいです。取り急ぎだろうが何だろうが毎日知識をinputしoutputする。そうすると自分に必要な知識も見えてきます。

後何より見てくれている人がいると、モチベにもなるし間違った情報は書けないと必死になりますね。ほんと見てくれた方には感謝します。

また来年も頑張ろうかなと思ってたりする。次は濃厚な記事を毎日書けるよう頑張ります!!

参考にしたの

ActiveRecord::Relationとは一体なんなのか
https://doruby.jp/users/whale/entries/ActiveRecord--Relation%E3%81%A8%E3%81%AF%E4%B8%80%E4%BD%93%E3%81%AA%E3%82%93%E3%81%AA%E3%81%AE%E3%81%8B

覚えておくと幸せになれるActiveRecord::Relationメソッド6選
https://qiita.com/Yama-to/items/4696e9d43ebec6012129

What is the difference between Class and Klass in ruby?
https://stackoverflow.com/questions/4299289/what-is-the-difference-between-class-and-klass-in-ruby

Rubyでメソッドの定義場所を見つける方法
https://qiita.com/jnchito/items/fc8a61b421d026a23ffe

21
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
3