Ruby on Rails - 未定义的方法`持久化?'为零:NilClass

时间:2016-05-21 01:12:04

标签: ruby-on-rails ruby facebook

我是ruby on rails的新手,我正试图在Facebook上使用oauth,但我收到了这个错误:undefined method 'persisted?' for nil:NilClass。当我尝试使用Facebook登录时会发生这种情况。有什么想法吗?

模特:

usuarios.rb

class Usuario < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :c    onfirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
     :recoverable, :rememberable, :trackable, :validatable
  devise :omniauthable, omniauth_providers: [:facebook]

  def self.find_or_create_by_omniauth(auth)
    usuario = Usuario.where(provider: auth[:provider], uid: [:uid]).first
    return usuario if usuario

    usuario = Usuario.create[
        nombre: auth[:nombre],
        apellido: auth[:apellido],
        username: auth[:username],
        email: auth[:email],
        uid: auth[:uid],
        prvider: auth[:provider],
        password: Devise.friendly_token[0,20]
    ]


  end

end

控制器

omniauth_callback_controller.rb

class OmniauthCallbacksController < ApplicationController
    def facebook
        auth = request.env["omniauth.auth"]
          data = {
          nombre: auth.info.first_name,
          apellido: auth.info.last_name,
          username: auth.info.nickname,
          email: auth.info.email,
          provider: auth.provider,
          uid: auth.uid
        }
        @usuario = Usuario.find_or_create_by_omniauth(data)

        if @usuario.persisted?
          sign_in_and_redirect @usuario, event: :authentication
        else
          session[:omniauth_errors] = @usuario.errors.full_messages.to_sentence unless @usuario.save

          session[:omniauth_data] = data

          redirect_to new_usuario_registration_url
      end
  end

  def failure
      redirect_to new_usuario_registration_url
  end

end

非常感谢任何帮助。

3 个答案:

答案 0 :(得分:1)

问题是您使用的是create[]而不是create()

真正发生了什么

ARSubClass.create[foo: 1, bar: 2, baz: 3]等于
ARSubClass.create[ {foo: 1, bar: 2, baz: 3} ]

ARSubClass.create尝试创建ARSubClass的实例,但它失败了,因为我猜测验证并返回非持久记录。
然后用一个参数[]
{foo: 1, bar: 2, baz: 3}调用方法instance[{foo: 1, bar: 2, baz: 3}] => nil

解决方案是使用create这样的create(...)

更多&#34; RailsWay&#34;方法是使用find_or_create_by来实施find_or_create_by_omniauth
请注意,如果您将一个块传递给find_or_create_by,则只会在create情况下进行评估。

附加说明:

  • 而不是where(foo: 'baz', bar: 'mar').first,使用find_by(foo: 'baz', bar: 'mar')
  • 在控制器中不要将@usuario设置为实例变量(带@前缀),如果您不打算在某些视图中使用它。
    只需将其设置为变量usuario = ...,其范围仅在当前方法中。

答案 1 :(得分:0)

find_or_create_by_omniauth方法在创建新的Usuario时返回nil。 rails方法返回方法中的最后一个语句,当您创建记录时,它返回nil。您只需在方法的末尾添加usuario,例如:

def self.find_or_create_by_omniauth(auth)
    usuario = Usuario.where(provider: auth[:provider], uid: [:uid]).first
    return usuario if usuario

    usuario = Usuario.create[
        nombre: auth[:nombre],
        apellido: auth[:apellido],
        username: auth[:username],
        email: auth[:email],
        uid: auth[:uid],
        prvider: auth[:provider],
        password: Devise.friendly_token[0,20]
    ]

    usuario
  end

答案 2 :(得分:0)

你获得nil的原因是你使用括号而不是parens。

# creates two things
Thing.create [{ name: foo }, { name: bar}]
# => nil

当您执行Usuario.create[nombre: auth[:nombre],...实际传递数组时 - ActiveRecord将尝试创建多条记录并始终返回nil。

此外,您的代码充斥着拼写错误ActiveRecord actually provides a few methods to do this with a lot less fuss

.first_or_create(attributes, &block) # when you don't want duplicates
.first_or_inialize(attributes, &block) # very useful for updates

您还应该注意,omniauth创建的request.env["omniauth.auth"]哈希遵循Auth Hash Schema - 您没有使用正确的密钥,因此模型中的许多属性最终都将为nil。

所以让重构.find_or_create_by_omniauth

def self.find_or_create_by_omniauth(auth)
  self.where(auth.slice(:uid, :provider))
    # see the auth hash schema for available keys
    .create_with(auth[:info].slice(:email)) 
    .first_or_create do |user|
      user.nombre = auth[:nombre] # will most likely be nil!
      user.apellido = auth[:apellido] # will most likely be nil!
      user.password = Devise.friendly_token[0,20]
    end
end

Hash#slice是一个很酷的ActiveSupport方法,它允许您复制哈希值,但只能使用所需的键 - 而无需手动复制它:

hash = { a: 1, b: 2, c: 3}
{ a: hash[:a], c: hash[:c] } # wow, much typing, such boring
hash.slice(:a,:c)

# => { a: 1, c: 3}