0

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)"]}>
Community
  • 1
  • 1
NAthan Davis
  • 5
  • 1
  • 3
  • I know my syntax for the above is not ideal so please forgive me. I can't seem to see how to make large blocks of code – NAthan Davis Dec 28 '14 at 10:03

2 Answers2

3

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.

Mohammad AbuShady
  • 40,884
  • 11
  • 78
  • 89
  • Thanks small edit to your code. Should be self not user self.changes.include? :email. I plan on using devise eventually but their own doc recommends making your own first. – NAthan Davis Dec 28 '14 at 18:52
  • Depends on where you call it, I was talking about rails console, if you run it inside the user class just call save directly, though I don't recommend having a save that bypasses the validations inside your model. – Mohammad AbuShady Dec 28 '14 at 18:55
1

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.

AJcodez
  • 31,780
  • 20
  • 84
  • 118
  • I added :on => :create for password confirm. So I need on on password also? Also is my syntax deprecated? I still get a roll back adding on: :create to password validadtion – NAthan Davis Dec 28 '14 at 09:58