Lee Eather
Lee Eather

Reputation: 355

Using attr_accessor in rails trying to skip validations

I am trying to skip password validation as facebook does not return a password for login.

I am getting a error;

"Validation failed: Password can't be blank" on line user.save!

Application trace;

app/models/user.rb:36:in block in 'from_omniauth'  
app/models/user.rb:29:in 'from_omniauth'  
app/controllers/sessions_controller.rb:6:in 'create'

Is this because from_omniauth is a class method, wrapped inside the user variable and then I am trying to skip password validation, as, a instance variable, when the instance is not created yet. Ie first_or_create do |user| has not created or registered the user before the instance?

If so, I was wondering how I could refactor my code to make this work?

Sessions controller

class SessionsController < ApplicationController
  def create
   user = User.from_omniauth(env["omniauth.auth"])
    user.skip_password_validation = true
    ...   

model.rb

class User < ApplicationRecord

  has_secure_password
  validates :password, presence: true, length: { minimum: 6 }, allow_nil: true,
  unless: :skip_password_validation

   attr_accessor :skip_password_validation

  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
      user.name = auth.info.name
      user.email = auth.info.email
      user.oauth_token = auth.credentials.token
      user.oauth_expires_at = Time.at(auth.credentials.expires_at)
      user.save!
    end
  end
end

Upvotes: 0

Views: 836

Answers (3)

Lee Eather
Lee Eather

Reputation: 355

With this, the problem is has_secure_password, which has 3 validation methods by default shown here api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html. You add in has_secure_password validations: false, and can add in validations manually in the validates method. The proper code should be,

Sessions controller

class SessionsController < ApplicationController
  def create
   user = User.from_omniauth(env["omniauth.auth"])

    ...   

Model

has_secure_password validations: false
validates :password, on: :create, presence: true, length: { minimum: 6 }, allow_nil: true,
unless: :skip_password_validation

attr_accessor :skip_password_validation

 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
   user.name = auth.info.name
   user.email = auth.info.email
   user.oauth_token = auth.credentials.token
   user.oauth_expires_at = Time.at(auth.credentials.expires_at)

   user.skip_password_validation = true

   user.save!
 end
...

EDIT I come back to this question again as you need to have the skip method in the model on the model object before save as rails evaluates the model method first. So you could do as what @Vladan Markovic has written or simply put user.skip_password_validation = true in the from_omniauth method in the model before save is called which I have edited to show.

Upvotes: 0

Pratap
Pratap

Reputation: 337

add in user model

attr_accessor :password

Upvotes: 0

Vladan Markovic
Vladan Markovic

Reputation: 354

You could pass skip_password parameter to your instance function:

 def self.from_omniauth(auth, skip_password)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
  user.provider = auth.provider
  user.uid = auth.uid
  user.name = auth.info.name
  user.email = auth.info.email
  user.oauth_token = auth.credentials.token
  user.oauth_expires_at = Time.at(auth.credentials.expires_at)

  user.skip_password_validation = skip_password

  user.save!
end

end

And then call it like this:

user = User.from_omniauth(env["omniauth.auth"], true)

Upvotes: 1

Related Questions