質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.75%

  • Ruby on Rails 4

    2380questions

    Ruby on Rails4はRubyによって書かれたオープンソースのウェブフレームワークです。 Ruby on Railsは「設定より規約」の原則に従っており、効率的に作業を行うために再開発を行う必要をなくしてくれます。

  • Twitter

    616questions

    Twitterは、140文字以内の「ツイート」と呼ばれる短文を投稿できるサービスです。Twitter上のほぼ全ての機能に対応するAPIが存在し、その関連サービスが多く公開されています。

  • SQLite

    579questions

    SQLiteはリレーショナルデータベース管理システムの1つで、サーバーではなくライブラリとして使用されている。

  • Facebook

    390questions

    Facebookは、実名登録制のSNS(ソーシャル・ネットワーキング・サービス)です。開発者用のデベロッパーサイトが存在し、一般ユーザーによるFacebook向けアプリケーション開発が可能です。

persist?がfalseになる原因。

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 703

s.k

score 245

以下、①、②以外の原因を教えていただきたいです。

前提・実現したいこと

persist?の結果をtrueにしたい。

発生している問題・エラーメッセージ

persist?が返す値が、毎度falseになってしまいます。
調べたところ、falseを返す原因は大きく分けて2つだとわかりました。
①モデル構築しか行われていない(save or createがない)
②validationが働いて①の妨げになってしまっている。

①、②の問題が発生しているか確認をしましたが、
どうやらないようです。

persist?が他にfalseになる原因が他にもあるのかをこの質問を読んでくださった方にお聞きしたいです。

該当のソースコード

【users_controller.rb】

def facebook
    user = User.from_omniauth(request.env["omniauth.auth"])←★from_omniauthに原因があるはず。★
      if user.persisted? ←★falseになり、elseの処理しか行われない。★
        sign_in_and_redirect user
        set_flash_message(:notice, :success, kind: __callee__.to_s.capitalize) if is_navigational_format?
      else←★いつもこの処理が実行されてしまう。★
        session["devise.user_attributes"] = user.attributes
        redirect_to new_user_registration_url(from_omniauth_callback: "1")
      end
  end

【user.rb】

devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

  def self.from_omniauth(auth)
    where(provider: auth.provider, uid: auth.uid).first_or_create do |user| ←first_or_createがあるので①の問題は発生していない…?
      user.provider = auth.provider
      user.uid      = auth.uid
    end
  end

【omniauth.rb】

Rails.application.config.middleware.use OmniAuth::Builder do
  require 'omniauth/strategies/facebook_another'

  provider :facebook, "ID", "KEY"

  provider :facebook_another, "ID", "KEY"

end

試したこと

①【user.rb】のself.from_omniauthメソッドでデータがきちんと保存されているかを確認するため条件分岐を書きましたが、エラーがでてできませんでした…

補足情報(言語/FW/ツール等のバージョンなど)

①user.rbにはvalidationを書いていないので②の問題も発生していないと思われます。
②Userテーブルにはproviderとuidカラムは存在します。
③deviseで複数モデルを実装してます。deviseでのOmniAuthは単一モデルにしか対応できないそうなので。
④関連する質問はこちら

情報追加

hana-daさんへ

見づらくて申し訳ありません((+_+))

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:

  #microposts
  has_many :microposts, dependent: :destroy

  #plugin(SNS omuniauth)
  has_many :social_profiles, dependent: :destroy


  ##コメント機能

  #comment(user-to-micropost.user)
  has_many :comments

  #call(user-to-micropost.shop)
  has_many :calls

  #says(shop-to-micropost.shop)
  has_many :says

  #insists(shop-to-micropost.user)
  has_many :insists



  #relationship(users-to-users)
  has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy
  has_many :passive_relationships, class_name: "Relationship", foreign_key: "followed_id", dependent: :destroy

  has_many :following, through: :active_relationships, source: :followed #フォローしている人を表示できるメソッドを関連づける
  has_many :followers, through: :passive_relationships, source: :follower #フォローされている人を表示できるメソッドを関連付ける

  #associates(user-to-shop)
  has_many :shops
  has_many :associates, dependent: :destroy
  has_many :associate_shops, through: :associates, source: :shop

  #makes(shop-to-user)
  has_many :makes, dependent: :destroy
  has_many :make_shops, through: :makes, source: :shop

  #praises(user-to-micropost)
  has_many :praises, dependent: :destroy
  has_many :praise_microposts, through: :praises, source: :micropost

  #evaluates(user-to-micropost)
  has_many :evaluates, dependent: :destroy
  has_many :evaluate_microposts, through: :evaluates, source: :micropost


  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

  def self.from_omniauth(auth)★★
    where(provider: auth.provider, uid: auth.uid).first_or_create do |user| ←first_or_create
      user.provider = auth.provider
      user.uid      = auth.uid
    end
  end

    user
  end


  def password_required?
    super && provider.blank?  # provider属性に値があればパスワード入力免除
  end

  def self.new_with_session(params, session)
    super.tap do |user|
      if data = session["devise.facebook_data"] && session["devise.facebook_data"]["extra"]["raw_info"]
          user.email = data["email"] if user.email.blank?
      end
    end
  end

  def feed
    following_ids = "SELECT followed_id FROM relationships WHERE follower_id = :user_id"
    associate_shops_ids = "SELECT shop_id FROM associates WHERE user_id = :user_id"
    Micropost.where("user_id IN (#{following_ids}) OR shop_id IN (#{associate_shops_ids}) OR user_id = :user_id", user_id: id)
  end

  #follow

  def follow(other_user)
    active_relationships.create(followed_id: other_user.id)
  end

  def unfollow(other_user)
    active_relationships.find_by(followed_id: other_user.id).destroy
  end

  def following?(other_user)
    following.include?(other_user)
  end

  #お気に入りしているかどうかを、返すメソッド(shop-to-user)
  def maked_by? shop
     makes.where(shop_id: shop.id).exists?
  end



end

よろしくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+3

トラブルシュートの方法を書いてみます。

プログラムが思い通りの動作をしない場合は、小さい単位で1つづつ思い通りの動作をしているかを確認する必要があります。

def self.from_omniauth(auth)
    where(provider: auth.provider, uid: auth.uid).first_or_create do |user| ←first_or_createがあるので①の問題は発生していない…?
      user.provider = auth.provider
      user.uid      = auth.uid
    end
  end

①の問題は発生していない…?

ここ。自信が持てないんですよね。
こういうところから確認していくといいです。
(時に自信がある所が思い違いをしている場合もあります。厄介)

さて、どう確認するか?

first_or_create しているので User.from_omniauth の戻りは必ず persist? == true である事が確認できればいいわけです

本当ならテストを書いてそれが通る事を確認するのですが、テストがなくても確認はできます。

def self.from_omniauth(auth)
    result = where(provider: auth.provider, uid: auth.uid).first_or_create do |user| ←first_or_createがあるので①の問題は発生していない…?
      user.provider = auth.provider
      user.uid      = auth.uid
    end

    raise 'result.persist? == true になってないです!!!' unless result.persist? == true

    return result
  end

本当に user.persist? == true になっているのであれば例外は発生しないはずです。
例外が発生したのであれば first_or_createがあるので①の問題は発生していない は偽です。

といった感じで1つづつ潰していってください。


じゃぁ何が悪いのか persist? が true でないという事は create に失敗しています。
createに失敗する理由はいくつかありますが create! にする事で create に失敗した時点で例外が発生するので

def self.from_omniauth(auth)
    where(provider: auth.provider, uid: auth.uid).first_or_create! do |user| ←first_or_createがあるので①の問題は発生していない…?
      user.provider = auth.provider
      user.uid      = auth.uid
    end
  end

として例外メッセージを読めばなぜ first_or_create が失敗したのかわかるはずです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/11/08 10:10 編集

    hana-daさん

    教えていただいたコードを試しました。
    raiseで例外が発生したので、どうやら★unless result.persisted? == true★の条件を満たしていないようです!原因の特定はこのようにできるのですね。私はまだテストコードを書けませんので非常に助かりました!

    はい。
    first_or_create!で試したところ、Validation failed: Email can't be blank
    というメッセージが出てきました!

    キャンセル

0

【view/users/registration/new.html.erb】

<% if f.object.password_required? %>

がpersisted?をfalseにする原因になっていました。

原因を究明の過程(hana-daさんの指摘をもとに)
①first_or_create→first_or_create!に変更
【user.rb】

def self.from_omniauth(auth)
    where(provider: auth.provider, uid: auth.uid).first_or_create! do |user|★変更箇所★
      user.provider = auth.provider
      user.uid      = auth.uid
    end
  end


結果、このようなエラーが発生

Validation failed: Email can't be blank

②devise :valitableのvalitableを削除
deviseでバリデーションをかけてないつもりだったので、どこにvalidationがかけられているかを確かめるために削除。
結果、以下の箇所にエラーが発生。

def password_required?
    super && provider.blank?  # provider属性に値があればパスワード入力免除
  end

どうやら、このメソッドがvalidationをかけていたようです。
削除後persited?はtrueになりましたヽ(^o^)丿

hanadaさん、ありがとうございます!

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.75%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Ruby on Rails 4

    2380questions

    Ruby on Rails4はRubyによって書かれたオープンソースのウェブフレームワークです。 Ruby on Railsは「設定より規約」の原則に従っており、効率的に作業を行うために再開発を行う必要をなくしてくれます。

  • Twitter

    616questions

    Twitterは、140文字以内の「ツイート」と呼ばれる短文を投稿できるサービスです。Twitter上のほぼ全ての機能に対応するAPIが存在し、その関連サービスが多く公開されています。

  • SQLite

    579questions

    SQLiteはリレーショナルデータベース管理システムの1つで、サーバーではなくライブラリとして使用されている。

  • Facebook

    390questions

    Facebookは、実名登録制のSNS(ソーシャル・ネットワーキング・サービス)です。開発者用のデベロッパーサイトが存在し、一般ユーザーによるFacebook向けアプリケーション開発が可能です。