Unable to submit add action form - Rails 4 - ruby-on-rails

So my problem is more or less as follows. I have a rails applications with the following classes:
Users
Skills
Each user can have multiple skills and they are added from the users profile screen which is the show action for the users controller. Currently I have the Skills set up and able to be created but I cannot add a pre-built skill onto a users profile. When I try to submit I get the following error Empty list of attributes to change
The following is my add skill to user action:
def add_skill_to_user
user = User.find(params[:id])
user.skills.create(skill_params) #skill name, level...
#skills_options = Skill.all.map{|s| [ s.name, s.id] }
#whatever happens when this is is done, redirect, json answer etc...
if user.skills.update_all(skill_params)
flash[:success] = "Skill Added"
else
render 'add_skill_to_user'
end
end
private
# Set skills params whitelist
def skill_params
params.permit(:name, :user_id)
end
and the following to routes
post 'users/:id/add_skill_to_user' => 'users#add_skill_to_user'
And this is my form
<%= form_tag({controller: "users", action: "add_skill_to_user"}, method: "put") do %>
<%= collection_select(:skill, :name, Skill.all, :id, :name) %>
<%= submit_tag 'Submit' %>
<%end%>
Let me know any additional information you need.
---------Added Models Code------------------
User.rb
class User < ActiveRecord::Base
attr_accessor :remember_token, :activation_token, :reset_token
before_save :downcase_email
before_create :create_activation_digest
belongs_to :group
has_many :ranks
has_many :skills
has_many :mission_notes
has_and_belongs_to_many :training_events
accepts_nested_attributes_for :skills
validates :username, presence: true
validates :email, presence: true
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :group_id, presence: true
has_secure_password
validates :password, length: { minimum: 6 }, allow_blank: true
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token.
def User.new_token
SecureRandom.urlsafe_base64
end
# Remembers a user in the database for use in persistent sessions.
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
# Returns true if the given token matches the digest.
def authenticated?(remember_token)
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
# Forgets a user.
def forget
update_attribute(:remember_digest, nil)
end
# Converts email to all lower-case.
def downcase_email
self.email = email.downcase
end
# Creates and assigns the activation token and digest.
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
# Returns true if the given token matches the digest.
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
# Activates an account.
def activate
update_attribute(:activated, true)
update_attribute(:activated_at, Time.zone.now)
end
# Sends activation email.
def send_activation_email
UserMailer.account_activation(self).deliver
end
# Sets the password reset attributes.
def create_reset_digest
self.reset_token = User.new_token
update_attribute(:reset_digest, User.digest(reset_token))
update_attribute(:reset_sent_at, Time.zone.now)
end
# Sends password reset email.
def send_password_reset_email
UserMailer.password_reset(self).deliver
end
def password_reset_expired?
reset_sent_at < 2.hours.ago
end
end
Skill.rb
class Skill < ActiveRecord::Base
belongs_to :user
end

Your collection_select will generate something like
<select name="skill[name]">
<option value="1">First Skill</option>
...
</select>
where "skill[name]" doesn't reflect what's in the option values (skill ids).
Furthermore, your params whitelist doesn't handle the fact that the POST data generated by the select is an array.
I'd suggest
<%= collection_select(:skill, :id, Skill.all, :id, :name) %>
with this whitelist filter in the controller:
params.require(:skill).permit(:id)
But your add_skill_to_user method is confusing. If all you want to do is assign pre-existing skills to a user, there is a RailsCast for that.

Related

Partial validations in multistep forms (Wizard)

I have a multistep form, which I created with wizard. Basically the first tep of the form is user/sign_up - which in my understanding not a step yet. After hitting the sign-up button, user moves to the "real" first step, which is :address.
class UserStepsController < ApplicationController
include Wicked::Wizard
steps :address
def show
#user = current_user || User.from_omniauth(request.env["omniauth.auth"])
render_wizard
end
def update
#user = current_user || User.from_omniauth(request.env["omniauth.auth"])
#user.update!(user_params)
render_wizard #user
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation, :remember_me, :first_name, :last_name, :street, :house_number, :city, :zip_code)
end
def redirect_to_finish_wizard(options = nil, params = nil)
redirect_to new_user_profile_path(current_user)
end
end
This is basically the end of the form already. All gets saved to the user. Now I am stuck with validations.
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
:omniauthable, omniauth_providers: %i[facebook]
has_one :profile, dependent: :destroy
after_create :create_profile
accepts_nested_attributes_for :profile
validates :first_name, presence: true
validates :last_name, presence: true
validates :street, presence: true
validates :house_number, presence: true
validates :city, presence: true
validates :zip_code, presence: true
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.email = auth.info.email
user.password = Devise.friendly_token[0, 20]
name = auth.info.name
user.first_name = name.split(" ")[0]
user.last_name = name.split(" ")[1]
end
end
end
I would love to work with the the conditional validations in my model and only validate presence if on a certain step. This should be easy, as I theoretically only have one step, which is address. All I find on the internet, is way too complicated. Question is, do I have to somehow change user/sign_up to a first step in the form and address would be the second step? Or is it fine like this? And if so, can I just add the "if" statements to the address attributes in my validations, somehow defining what is the address step? Would it work like this?
def on_address_step?
wizard.steps = wizard.steps.first
end
Or how do I define it? The validations would look like this then:
validates :first_name, presence: true
validates :last_name, presence: true
validates :street, presence: true, if: :on_address_step?
validates :house_number, presence: true, if: :on_address_step?
validates :city, presence: true, if: :on_address_step?
validates :zip_code, presence: true, if: :on_address_step?
This is surely not that easy. For now this also doesn't work. How do I need to change it? Thanks.
P.S: here is also my Users Controller:
class UsersController < ApplicationController
def index
#users = User.all
end
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
session[:user_id] = #user.id
redirect_to user_steps_path
else
render :new
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation, :remember_me, :first_name, :last_name, :street, :house_number, :city, :zip_code)
end
end
If filling in the address is a completely separate process I would just branch the address out into its own model and controller.
class User < ApplicationRecord
# ...
has_one :address
end
class Address < ApplicationRecord
# ...
belongs_to :user
validates :first_name, :last_name, :street,
:house_number, :city, :zip_code, presence: true
end
This avoids turning your user model into even more of a god object and removes the need for the conditional validation that makes your model much more aware of the UX steps than it should be.
# routes.rb
resources :addresses, only: [:new, :create]
class UsersController < ApplicationController
# ...
def create
#user = User.new(params[:user])
if #user.save
session[:user_id] = #user.id
redirect_to new_address_path
else
render :new
end
end
end
class AddressesController < ApplicationController
# You should have some sort of method that checks if the user
# is signed in and redirect otherwise
before_action :authenticate_user!
# GET /addresses/new
def new
# I'm assuming you have some sort of method to fetch the signed in user
#address = current_user.build_address
end
# POST /addresses
def create
#address = current_user.build_address(address_params)
if #address.save
redirect_to '/somepath'
else
render :new
end
end
def address_params
params.require(:address).permit(
:first_name, :last_name, :street,
:house_number, :city, :zip_code
)
end
end
<%= form_with(model: #address) %>
# ... inputs
<% end %>
I doubt you really want the complexity involved with using Wicked which is ok if you really need a long multiple step form but in this case there is a far simpler and better design choice.

Rails Validation with two attributes of a variable

I really need your help, because I can't find a solution on my own since 2 hours. I tried many different approaches, but I just don't get how I can validate an attribute of a variable by using another attribute as a condition.
This was my last try:
class User < ApplicationRecord
validates :admin, absence: true,
if: ["student?", :email]
def student?(user)
user.email.include? 'stud'
end
end
I want to validate, that a user with the attribute "admin" can't have an email address, which includes 'stud'. Even better would be a validation, that 'stud' users can't be admin.
When I run db:seed I get this error, which stays if I put the 'stud' in brackets:
rails aborted!
ArgumentError: wrong number of arguments (given 0, expected 1)
/home/studi/Bachelorarbeitsapp/sample_app/app/models/user.rb:22:in `student?'
Please have a look at this issue!
I am willing to share any other code, which is necessary to solve the problem. Thanks!
Here is the full helper code:
class User < ApplicationRecord
has_many :preferences, dependent: :destroy
accepts_nested_attributes_for :preferences
attr_accessor :remember_token, :activation_token, :reset_token
before_save :downcase_email
before_create :create_activation_digest
has_secure_password
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#(stud.uni-hannover.de|wiwi.uni-hannover.de)+\z/i
validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: {case_sensitive: false}
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
validates :mat_number, presence: true, length: { minimum: 7, maximum: 8 }, numericality: { only_integer: true }
validates :admin, absence: true,
if: ["student?", :email]
def student?(user)
user.email.include? 'stud'
end
validates :ects, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 170, only_integer: true }, allow_nil: true
validates :grade_avg, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 4 }, allow_nil: true
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token
def User.new_token
SecureRandom.urlsafe_base64
end
# Remembers a user in the database for use in persistent sessions
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
# Returns true if the given token matches the digest.
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
# Forgets a user.
def forget
update_attribute(:remember_digest, nil)
end
# Activates an account.
def activate
update_columns(activated: true, activated_at: Time.zone.now)
end
# Sends activation email.
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
# Sets the password reset attributes.
def create_reset_digest
self.reset_token = User.new_token
update_columns(reset_digest: User.digest(reset_token), reset_sent_at: Time.zone.now)
end
# Sends password reset email.
def send_password_reset_email
UserMailer.password_reset(self).deliver_now
end
# Returns true if a password reset has expired.
def password_reset_expired?
reset_sent_at < 2.hours.ago
end
private
# Converts email to all lower-case.
def downcase_email
self.email = email.downcase
end
# Creates and assigns the activation token and digest.
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end
Based on the Active Record Validations Guide section 6.2 Custom Methods, it seems like you should be able to do something like:
class User < ApplicationRecord
validate :admin_cant_be_student
def admin_cant_be_student
if admin and email.include?('stud')
errors.add(:admin, "can't have a student email address")
end
end
end
You don’t need a custom validation:
Could use validates_exclusion_of
validates_exclusion_of :email, in: %w( stud ), if: -> { |user| user.email.include? “admin” }, message: “admins can’t have stud in email”
validates_exclusion_of :email, in: %w( admin ), if: -> { |user|user.email.include? “stud” }, message: “students can’t have admin in email”

Create users without a password in Rails

I have three user roles defined using enums and a single users table with STI. My three user roles are staff, clinician, and listed. The aim is that staff will upload lists of names and emails which will create users of role listed. Once these listed users create their own passwords, their role will change to clinician.
I am working on adjusting my model and controller to allow to create these listed users but running into difficulty as most of the answers I've found deal with a Devise implementation. I am using an authentication system mostly built with Michael Hartl's Rails Tutorial. Do you have any tips on how I can get this to work? Currently when I try to sign up as a listed user, I get the following error:
unknown attribute 'password' for Listed.
def create
#user = User.new(user_params)
if #user.save
#user.send_activation_email
flash[:info] = "Please check your email to activate your account."
Any help at all would be greatly appreciated. Thanks!
Here are my user models:
class User < ApplicationRecord
self.inheritance_column = :role
enum role: { Staff: 0, Clinician: 1, Listed: 2 }
attr_accessor :remember_token, :activation_token, :reset_token
before_save :downcase_email
before_create :create_activation_digest
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :role, presence: true
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token.
def User.new_token
SecureRandom.urlsafe_base64
end
# Remembers a user in the database for use in persistent sessions.
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
# Returns true if the given token matches the digest.
def authenticated?(remember_token)
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
# Forgets a user.
def forget
update_attribute(:remember_digest, nil)
end
# Returns true if the given token matches the digest.
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
# Activates an account.
def activate
update_attribute(:activated, true)
update_attribute(:activated_at, Time.zone.now)
end
# Sends activation email.
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
# Sets the password reset attributes.
def create_reset_digest
self.reset_token = User.new_token
update_attribute(:reset_digest, User.digest(reset_token))
update_attribute(:reset_sent_at, Time.zone.now)
end
# Sends password reset email.
def send_password_reset_email
UserMailer.password_reset(self).deliver_now
end
# Returns true if a password reset has expired.
def password_reset_expired?
reset_sent_at < 2.hours.ago
end
def feed
ReferralRequest.where("user_id = ?", id)
end
private
# Converts email to all lower-case.
def downcase_email
self.email = email.downcase
end
# Creates and assigns the activation token and digest.
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end
class Staff < User
validates :university_id, presence: true
belongs_to :university
has_many :referral_requests
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
end
class Clinician < User
has_many :lists
has_many :universities, through: :lists
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
end
class Listed < User
has_many :lists
has_many :universities, through: :lists
end
Here is my users controller:
class UsersController < ApplicationController
before_action :logged_in_user, only: [:show, :index, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
def index
#users = User.paginate(page: params[:page])
end
def show
#user = User.find(params[:id])
#referral_requests = #user.referral_requests.paginate(page: params[:page])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
#user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
def edit
end
def update
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to users_url
end
private
def user_params
params.require(:user).permit(:name, :email, :role, :university_id, :password, :password_confirmation)
end
# Before filters
# Confirms the correct user.
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
end
I'm not sure if STI is the best approach to manage roles, anyway, if your User class has the attribute password and you are correctly making the inheritance, Listed should have that attribute too.
Your Listed model should look like this:
Class Listed < User
end
Same for all your models that inherit from User

User attribute will not save

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
Thanks ahead of time.
My User model:
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
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.
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
This should work.
This does not solve the problem why saving an existing user could fail, so you still need to check your validations and before_save actions.
Hope this helps.
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) and 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.

Auth issue when changing a column in Users table

I'm very new to rails and I'm trying to accomplish the following authentication issue:
User makes a comment or grants "absolution" (similar to comment) and he gets some coins for it. Coins is the virtual currency in my app and is also a column in the users table.
Because of your kind help, I was already capable to update the coins value after writing a comment or grant absolution. However, when I write a comment and log out after that, my login name or password gets changed(?)...I can't login anymore with this account.
This is how my User model looks like:
require 'digest'
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :email, :password, :password_confirmation, :twitter_url, :homepage_url, :coins
has_many :comments, :dependent => :destroy
has_many :absolutions, :dependent => :destroy
has_many :ratings
has_many :rated_sins, :through => :ratings, :source => :sins
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
homepage_regex = /(^$)|(^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$)/ix
validates :name, :presence => true,
:length => { :maximum => 50 }
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => { :case_sensitive => false }
validates :twitter_url, :format => { :with => homepage_regex }
validates :homepage_url, :format => { :with => homepage_regex }
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 }
before_save :encrypt_password
def has_password?(submitted_password)
encrypted_password == 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
class << self
def authenticate(email, submitted_password)
user = find_by_email(email)
(user && user.has_password?(submitted_password)) ? user : nil
end
def authenticate_with_salt(id, cookie_salt)
user = find_by_id(id)
(user && user.salt == cookie_salt) ? user : nil
end
end
private
def encrypt_password
self.salt = make_salt if new_record?
self.encrypted_password = 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
And this is my comments controller:
class CommentsController < ApplicationController
before_filter :authenticate, :only => [:create, :destroy]
def new
#comment = Comment.new
end
def create
#sin = Sin.find(params[:sin_id])
#comment = current_user.comments.build(params[:comment])
#comment.sin_id = #sin.id
if #comment.save
flash[:success] = "Comment created! Earned 20 coins."
coins_new = current_user.coins.to_i + 20
current_user.update_attribute(:coins, coins_new)
redirect_to sin_path(#sin)
else
flash[:error] = "Comment should have 1 - 1000 chars."
redirect_to sin_path(#sin)
end
end
def destroy
end
private
def authenticate
deny_access unless signed_in?
end
end
I assume, that it has something to do with the before_save encrypt_password method, but its only a guess. I really appreciate your help and suggestions!
Edit:
It gets warmer...It has something to do with the following line in the Comments Controller:
current_user.update_attribute(:coins, coins_new)
When he updates the :coins column, something seems to go wrong. If you need further info, just drop a comment. Thanks for your help!
Your problem is that you're encrypting the already encrypted password in your "encrypt_password" method.
So, when the user is a new_record?, you're taking the password (say it's "cat"), hashing it, and storing it in the database.
So, what gets stored is a cryptographic hash of "cat", which we'll say is "dog"
Then, the next time you're saving the user record, you're taking the hashed password ("dog") in line #2 of your "encrypt_password" method, and hashing it again, which we'll say generates "kangaroo".
Next time you log in, your app is hashing the password you enter into the login form "cat", hashing it to "dog", and comparing it to the hashed version in the database, "kangaroo". Oh, but "dog" doesn't match "kangaroo", so the login fails.
So change:
def encrypt_password
self.salt = make_salt if new_record?
self.encrypted_password = encrypt(password)
end
To either:
def encrypt_password
self.salt = make_salt if new_record?
self.password = decrypt(password) # decrypt it first to the plain text
self.encrypted_password = encrypt(password) # then re-encrypt the plain text with the salt
end
Or:
def encrypt_password
self.salt = make_salt if new_record?
if (password_has_changed?) # somehow you'll have to figure this out
self.encrypted_password = encrypt(password)
end

Resources