El atributo de usuario no se guardará

My User model has an attribute called "points" and when I try to update it in another model controller (decrementing points), the attribute will not save (even after adding it to attr_accessible).

The method in my Venture Controller code:

def upvote
@venture = Venture.find(params[:id])
if current_user.points < UPVOTE_AMOUNT
  flash[:error] = "Not enough points!"
else
  flash[:success] = "Vote submitted!"
  current_user.vote_for(@venture)
  decremented = current_user.points - UPVOTE_AMOUNT
  current_user.points = decremented
  current_user.save
  redirect_to :back
end

I have even tried using the update_attributes method, but to no avail.

I added a quick little test with flash to see if it was saving:

if current_user.save
    flash[:success] = "Yay"
else
    flash[:error] = "No"
end 

and the error was returned.

current_user comes from my Sessions helper:

def current_user
   @current_user ||= user_from_remember_token
end

Gracias de antemano.

Mi modelo de usuario:

class User < ActiveRecord::Base
attr_accessor :password, :points
attr_accessible :name, :email, :password, :password_confirmation, :points

STARTING_POINTS = 50

acts_as_voter
has_karma :ventures

has_many :ventures, :dependent => :destroy

email_regex = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i

validates :name, :presence => true,
                 :length   => { :maximum => 50 }
validates :email, :presence => true,
          :format => { :with => email_regex },
          :uniqueness => { :case_sensitive => false }
validates :password, :presence     => true,
                     :confirmation => true,
                     :length       => { :within => 6..40 }

before_save :encrypt_password
after_initialize :initialize_points

def has_password?(submitted_password)
  password_digest == encrypt(submitted_password)
end

def self.authenticate(email, submitted_password)
  user = find_by_email(email)
  return nil  if user.nil?
  return user if user.has_password?(submitted_password)
end

def self.authenticate_with_salt(id, cookie_salt)
  user = find_by_id(id)
  (user && user.salt == cookie_salt) ? user : nil
end

private

def initialize_points
  self.points = STARTING_POINTS
end  

def encrypt_password
  self.salt = make_salt if new_record?
  self.password_digest = encrypt(password)
end

def encrypt(string)
  secure_hash("#{salt}--#{string}")
end

def make_salt
  secure_hash("#{Time.now.utc}--#{password}")
end

def secure_hash(string)
  Digest::SHA2.hexdigest(string)
end
end

This is what I get after printing <%= debug current_user %>

--- !ruby/object:User 
attributes: 
id: 1
name: Test User
email: a@s.com
created_at: 2011-08-27 21:03:01.391918
updated_at: 2011-08-27 21:03:01.418370
password_digest: 40d5ed415df384adaa5182a5fe59964625f9e65a688bb3cc9e30b4eef2a0614b
salt: ac7a332f5d63bc6ad0f61ceacb66bc154e1cad1164fcaed6189d8cea2b55ffe4
admin: t
points: 50
longitude_user: 
latitude_user: 
attributes_cache: {}

changed_attributes: {}

destroyed: false
errors: !omap []

marked_for_destruction: false
new_record: false
points: 50
previously_changed: {}

readonly: false

preguntado el 27 de agosto de 11 a las 23:08

What happens when you print out current_user.errors.full_messages after the save? -

No description of what's in the User model that could be making it fail? :( -

@Maz I don't know if I did it correctly, but I did <% print current_user.errors.full_messages %> in my view and nothing new printed. -

what happens when you '<%= debug current_user %>'? -

Did you restart your server after adding attr_accessible? -

2 Respuestas

You are requiring the user's password to be present any time the user is saved. When the upvote is submitting, the password is not present, therefor validation is not passing.

Respondido 28 ago 11, 17:08

According to me the validation only expresses that a password needs to be set on the user. Once a user is succesfully saved, this condition/validation is always met. Secondly, the password-confirmation is only checked if it is supplied. So I think that is not the validation that is causing the problem. - nathanvda

I changed the encrypt_password implementation by adding an if self.password_digest == nil logic, but that didn't solve the problem. I definitely think now that the authentication system I have in place is creating the headache, so I will definitely try to use something more refined with Devise, Authlogic, etc. Thanks for your help! - John Bullhuo

This would suggest some kind of validation failed. An easy way to circumvent this, is to use update_attribute. This will update a single attribute and save without running the validations.

So instead write

current_user.update_attribute :points, current_user.points - UPVOTE_AMOUNT

Esto debería funcionar.

This does not solve the problem why saving an existing user could fail, so you still need to check your validations and before_save acciones.

Espero que esto ayude.

Ha. Indeed. The update_attribute does skip validations, but not the before_save. So, if the before_save is the problem, you only want to trigger if the password has changed, so you could do something like

def encrypt_password
  self.salt = make_salt if new_record?      
  self.password_digest = encrypt(password) if self.password_changed?
end

But this would only work if password is an actual attribute of your model, which seems unlikely. Why would you store the hash (for safety reasons) y the password in cleartext. So ... I guess you only have a password_digest field, and then it should become something like:

def encrypt_password
  self.salt = make_salt if new_record?      
  self.password_digest = encrypt(password) if password.present?
end

Only if a password was given, try to recreate the digest.

Respondido 28 ago 11, 22:08

I would not encourage having this logic in the controller, nor would I recommend bypassing validation in this case. There's a reason why it's invalid, and ignoring that is only going to lead to unpredictable bugs and even hackier code. - Peter Brown

I understand your view, but I do think that in some cases one can safely assume that updating a simple field will not invalidate any validation of the user. In this case, the points of a user, seems such a case. Of course, as I say in the post itself: it does not solve the validation problem. - nathanvda

Unfortunately the update_attribute did not work, but thanks for your help! before_save is probably where the funny business is going on - John Bullhuo

Just temporarily disable the before_save? Are you trying stuff out in the rails console? you can just load a user, try to update the points and then save. Add some logger.debug or puts statements to see where you get. - nathanvda

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.