ruby-on-railssorcery

Rails Sorcery update attributes of model without password


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?


Solution

  • 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 :)