Reputation: 15
I am a new to rails. I am working with rails 4.2 and I broke something in my user.rb model or controller so that I can't use save or update in console or in in the web browser(for some varibles in user, I can change the name, email, and password in the browser). I found a similar problem here but I couldn't get there advice to work.
Here is my code User db table
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "avatar_file_name"
t.string "avatar_content_type"
t.integer "avatar_file_size"
t.datetime "avatar_updated_at"
t.string "password_hash"
t.string "password_salt"
t.boolean "admin", default: false
end
User.rb
class User < ActiveRecord::Base
has_many :recipes
has_many :comments
#attr_accessible :email, :password, :password_confirmation
attr_accessor :password
before_save :encrypt_password
validates_confirmation_of :password, :on => :create
#validates :name, presence: true, uniqueness: true
#validates :email, presence: true, uniqueness: true
validates :password, presence: true, length: { in: 6..20 }
has_attached_file :avatar, :styles => {
:medium => "300x300>",
:thumb => "100x100>"
},
:default_url => "/images/:style/missing.png",
:bucket =>'davisrecipebook',
:storage => :s3,
:s3_credentials => "#{Rails.root}/config/s3.yml"
#:s3_credentials => Proc.new{|a| a.instance.s3_credentials }
#:storage => :dropbox,
#:dropbox_credentials => Rails.root.join("config/dropbox.yml")
#def s3_credentials
# {:bucket =>'davisrecipebook', :access_key_id => '', :secret_access_key => ''}
#end
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
validates_attachment_size :avatar, :less_than => 10.megabytes
def self.authenticate(email, password)
user = find_by_email(email)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end
console out
2.1.5 :073 > User.last
User Load (0.5ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT 1
=> #<User id: 7, name: "hate", email: "death", created_at: "2014-12-28 07:32:22", updated_at: "2014-12-28 08:24:01", avatar_file_name: nil, avatar_content_type: nil, avatar_file_size: nil, avatar_updated_at: nil, password_hash: "$2a$10$9rEtvSoO0DOzm5u/Vop0O.5matAM8gTI0t6QZ470UyF...", password_salt: "$2a$10$9rEtvSoO0DOzm5u/Vop0O.", admin: false>
2.1.5 :074 > s = _
=> #<User id: 7, name: "hate", email: "death", created_at: "2014-12-28 07:32:22", updated_at: "2014-12-28 08:24:01", avatar_file_name: nil, avatar_content_type: nil, avatar_file_size: nil, avatar_updated_at: nil, password_hash: "$2a$10$9rEtvSoO0DOzm5u/Vop0O.5matAM8gTI0t6QZ470UyF...", password_salt: "$2a$10$9rEtvSoO0DOzm5u/Vop0O.", admin: false>
2.1.5 :075 > s.admin = true
=> true
2.1.5 :076 > s.save
(0.2ms) BEGIN
User Exists (0.6ms) SELECT 1 AS one FROM "users" WHERE ("users"."name" = 'hate' AND "users"."id" != 7) LIMIT 1
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'death' AND "users"."id" != 7) LIMIT 1
(0.2ms) ROLLBACK
=> false
2.1.5 :077 > s.errors
@messages={:password=>["can't be blank", "can't be blank", "is too short (minimum is 6 characters)"]}>
Upvotes: 1
Views: 2667
Reputation: 42879
The thing is, the record doesn't contain a field called password, it has password_hash
and password_salt
, any time you try to save rails looks for the password field but can't find it, i'd say create a condition that tell rails when to look for a password, for example
validates :password, if: :password_required?
def password_required?
user.changes.include? :email # just an example
end
This will prevent asking for passowrd every time the user changes something minor in his account, and it will ask for the password if the user changes his email, you could add more options of course.
If you want to force the saving of the user in the time being, you could tell rails to skip validations
user.save(validate: false)
Also as a last note, it's better and safer to use a well developed and mature authentication gem like devise, it has all the features you'd need in any authentication.
Upvotes: 3
Reputation: 34216
Your validations are failing. You have a validation for password
, but there isn't a password field in the database (for security reasons, instead you save hash & salt).
To fix this, add the :on
parameter to scope the validation to only run when users are created.
validates :password, on: :create, ...
From the rails docs
:on - Specifies the contexts where this validation is active. Runs in all validation contexts by default (nil). You can pass a symbol or an array of symbols.
Upvotes: 1