4

I use sorcery for user authentication in a rails 4.1 application. Everything works fine. But when I try to update specific attributes of the user model (which is authenticated by sorcery), I get an error that the password is blank and is too short.

Here's a snippet from the console

> user = User.last  
=> # I get the user  
> user.update(about_me: "I'm a user")  
=> false  
> user.update(about_me: "I'm a user", password: "secret")  
=> true

Here's my model code
app/models/user.rb

class User < ActiveRecord::Base  
  authenticates_with_sorcery!  
  validates :password, presence: true, length: { minimum: 6 }  
  .....
end  

My controller code
app/controllers/users_controller.rb

class UsersController < ApplicationController
  .....
  def edit
    @user = User.find(params[:id])
  end

  def update
    @user = User.find(params[:id])
    if @user.update(user_params)
        redirect_to @user
        flash[:notice] = "Profile successfully updated"
    else
        render 'edit'
    end
  end

  private
      def user_params
        params.require(:user).permit(:username, :name, :email, :password, :about_me)
      end

end

And my update form
app/views/users/edit.html.erb

<%= form_for @user, method: :put do |f| %>
  <% if @user.errors.any? %>
    <div class="alert">
      <p><%= pluralize(@user.errors.count, 'error') %></p>
      <ul>
        <% @user.errors.full_messages.each do |msg| %>
          <li><%= msg %></li>
        <% end %>
      </ul>
    </div>
  <% end %>
  <%= f.text_field :username, placeholder: 'Username' %>
  <%= f.text_field :name, placeholder: 'Name' %>
  <%= f.email_field :email, placeholder: 'Email' %>
  <%= f.text_area :about_me, placeholder: 'About me' %>
  <%= f.password_field :password, placeholder: 'Password' %>
  <%= f.submit 'Save Changes', class: 'button' %>
<% end %>

If I remove the password field from the form, I get errors about the password being blank and about it's length. Is this something to do with sorcery or is it something I'm missing with rails itself? Is there a better way to update let's say only the email field without affecting anything else?

prajwaldp
  • 178
  • 1
  • 14
  • **update** : Found out that we could use allow_blank: true in the model. Since this would also allow users to sign up without a password, So the catch maybe to use a condition in the create action of the user controller. Will post the answer if I get it working. Anyhow, I don't think this is the right way to do it. – prajwaldp Jan 31 '15 at 13:45
  • marking allow_blank true is not the correct way to do it. Rather, you can have conditional validation in the user model. Like at what condition the validation will be checked and when that should be skipped. As it looks like, if it's an existing user you are trying to edit then just by pass the validation with a condition: it's an existing user. Check my below answer, that'll clear your doubt. – Ajay Jan 31 '15 at 14:13

2 Answers2

5
class User < ActiveRecord::Base  
  authenticates_with_sorcery!  
  validates :password, presence: true, length: { minimum: 6 }, if: :new_user?

  private
  def new_user?
    new_record?
  end
end  

The validation will be checked only if it's a new_record, for which we have added our own private validation method new_user?. This function will return true during your normal signups/registrations. Hence, at those signups only the password validation will be needed.

During the edit, off course the user will be an existing user / new_record? will return false. Hence the validation for password will be skipped.

2nd way:

class User < ActiveRecord::Base 
  attr_accessor :skip_password
  validates :password, presence: true, length: { minimum: 6 }, unless: :skip_password
end

 #users_controller.rb 
def update
  @user = User.find(params[:id])
  @user.skip_password = true 
  if @user.update(user_params)
     redirect_to @user
  else
     render 'edit'
  end
end

Here we have added our own custom attr_accessor skip_password. If the skip_password value is set to true, then during edit/update the password validation will be skipped.

I hope both of those ways will help you :)

Ajay
  • 4,199
  • 4
  • 27
  • 47
  • Both your methods worked like a charm. The 2nd way requires a colon (unless: :skip_password and not unless: skip_password). I preferred to use the first way as the logic is within the model. Had my doubts about the password being updated to an empty string after a user updates his/her attributes but turns out that it doesn't. Thanks! – prajwaldp Feb 01 '15 at 02:33
  • Yes 1st one is more preferable as logic is accumulated in model only. Thanks for pointing out the typo (in unless: :skip_password). updated that one. – Ajay Feb 01 '15 at 05:37
3

If someone looks for this topic in future, it is possible to use changes map of ActiveRecord model:

class User < ActiveRecord::Base  
  authenticates_with_sorcery!  
  validates :password, presence: true, length: { minimum: 6 }, if: -> {new_record? || changes[:crypted_password]}
  .....
end

where :crypted_password is the value of sorcery_config.crypted_password_attribute_name.

Also currently such condition of validates pointed in Simple Password Authentication sorcery wiki article.

ych
  • 1,946
  • 2
  • 10
  • 15