Rails update_attributes using has_secure_password

3.1k views Asked by At

I am working in a project where users can either have admin: true / false. The application will only let admin users login in to the system, the other users are just clients whose settings only admins can edit. This is some sort of commerce application.(Rails 3.2.13)

I would like to keep both concepts in the same table since in the future there will possibly be the option of logging in for non-admin users and interact with their profiles, so all the logic will be already implemented.

I've got this User resource:

ActiveRecord::Schema.define(:version => 20131204200554) do

  create_table "users", :force => true do |t|
    t.string   "name"
    t.string   "surname"
    t.boolean  "admin"
    t.string   "password_digest"
    t.string   "email"
    t.string   "auth_token"
  end

end

This is the user model:

class User < ActiveRecord::Base
    has_secure_password
    attr_accessible :name, :surname,:email, :password, :password_confirmation

    validates :name, presence:true, length: { maximum:50}
    validates :first_surname, presence:true, length: { maximum:50}
    VALID_EMAIL_REGEX= /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i 
    validates :email, presence: true,format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive:false}
    validates :password, length:{minimum:6}
    validates :password_confirmation, presence:true
    before_save{ self.email.downcase! }
    before_save :generate_auth_token
    private
    def generate_auth_token
        self.auth_token=SecureRandom.urlsafe_base64
    end
end

And I am trying to implement the functionality of User editing, however I only want to allow the editing of name, surname and email. Hence, I present only those fields in the form:

<%= form_for(@user) do |f| %>
  <%= f.label :name,"Name" %>
  <%= f.text_field :name %>
  <%= f.label :surname,"Surname" %>
  <%= f.text_field :surname %>
  <%= f.label :email,"Email" %>
  <%= f.text_field :email %>
  <%= f.submit "Save" %>

I am trying to accomplish the goal with this code:

def update
  @user = User.find(params[:id])
  if @user.update_attributes(params[:user])
    # Handle a successful update.
    flash[:success]="User updated successfully"
    redirect_to @user
  else
    flash[:danger]="Error updating user"
    render 'edit'
  end
end

My problem is that when trying to update_attributes I get not unexpectedly an error validating password/password_confirmation, but since I'm using has_secure_password, these fields do not exist in the database, only password_digest. I am thinking about the best option to accomplish all this:

  1. Update @user object with new field values.
  2. Reflect this change in User table.
  3. Run the validations, i.e. email validation.

While using has_secure_password. These are the options so far:

  1. Use of update_attribute(best so far).
    1.1 Pro: updates the User table
    1.2 Con: I have to update_attribute(field1) for each of the fields, so more lines of code
    1.3 Con: Apparently this method no longer exists in Rails 4, problem in case an upgrade is desirable in the future.
    1.4 Con: No validations
  2. Use of @user.attributes=params[:user] in the controller method
    2.1 Pro: Updates multiple fields at once.
    2.2 Con: Does not update the User table.
  3. User of update_attributes
    3.1 Pro: Both multiple fields and table update
    3.2 Con: Not working ( duh!)

Suggestions?

1

There are 1 answers

3
benjaminjosephw On BEST ANSWER

It looks like you want to validate the presence of password on create only (also notice more concise way to validate password confirmation):

validates :password, length:{minimum:6}, confirmation: true, on: :create

You can then use update_attributes

You may want to restrict what params can be submitted by using strong params: