LoginSignup
3
1

find_or_create_byメソッドについて

Last updated at Posted at 2024-03-22

はじめに

個人開発でOmniAuthを用いた認証機能を実装する際に、find_or_create_byメソッドの挙動について疑問が生じたので、その点について調べてみました。

初学者のため間違いがあるかもしれません。
その際はご指摘いただけると嬉しいです。

元々の認証機能の一部

既存のユーザーに対して変更があった際には、updateメソッドで更新をしたかったのですが、user_update(user_params)の部分が動いていないようだったのでなぜだろうと気になりました。

def self.find_or_create_from_auth_hash(auth_hash)
    user_params = user_params_from_auth_hash(auth_hash)
    User.find_or_create_by!(uid: user_params[:uid]) do |user|
     user.update(user_params)
    end
end
修正後のコード
def self.find_or_create_from_auth_hash(auth_hash)
  user_params = user_params_from_auth_hash(auth_hash)
  if guild_member?(user_params[:uid])
    user = User.find_by(uid: user_params[:uid])
    if user
      user.update(user_params)
    else
      user = User.create!(user_params) do |new_user|
        if new_user.image.nil?
          new_user.image = Discordrb::API::User.default_avatar(auth_hash[:extra][:raw_info][:discriminator])
        end
      end
    end
    return user
  end
  nil
end

挙動の解説

find_or_create_byメソッドは、すでにレコードが存在する場合はそのオブジェクトを返し、存在しない場合は新たにレコードを作成してそのオブジェクトを返します。
見落としていた点としては、find_or_create_byメソッドでは、レコードが既に存在する場合はブロック内の処理(この場合はuser.update(user_params))を実行しないということです。

ソースコード

activerecord/lib/active_record/relation.rb
def find_or_create_by(attributes, &block)
      find_by(attributes) || create(attributes, &block)
    end

||の挙動

result = a || b

このコードでは、aがnilやfalse以外の値であれば、resultにはaの値が代入されます。
aが偽なら、resultにはbの値が代入されます。
つまり、aかbのどちらかが真なら真を返し、両方偽なら偽を返します。

ソースコードの挙動

以下のコードの挙動は下記のようになります。

find_by(attributes) || create(attributes, &block)

find_by(attributes)がレコードを見つけた(nilfalse以外の値を返した)場合は、そのオブジェクトを返し、もしnilfalseになれば後半部のcreateを実行してその結果を戻り値として返します。

上記のコードからわかるように、find_or_create_byはまずfind_byでレコードを検索し、見つからない場合のみcreateメソッドを呼び出してブロックを実行します。

activerecord/lib/active_record/persistence.rb
def create(attributes = nil, &block)
        if attributes.is_a?(Array)
          attributes.collect { |attr| create(attr, &block) }
        else
          object = new(attributes, &block)
          object.save
          object
        end
      end

終わりに

コードが予想と違った動きをするときはソースコードなどをしっかり確認することも大事だということを再確認できました。これからも意識して確認していこうと思います。

3
1
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
3
1