Rails NoMethodError: undefined method `password_digest= - ruby-on-rails

I am new to rails and have seen all possible answers for my problem, as this is asked quite frequently by other developers, yet I'm unable to resolve it. Please have a look.
I am getting this error when I try to add a data from the console
User.create(name: "Michael Hartl", email: "mhartl#example.com", phone: "0123456789", password: "foobar", password_confirmation: "foobar")
ERROR SHOWN
undefined method `password_digest=' for #<User:0x0000000375c788>
Did you mean? password=
Controller
def create
#candidate = User.new(user_params)
if #candidate.save
flash[:notice] = "New Candidate Added Successfully"
redirect_to(users_path)
else
render('new')
end
end
private
def user_params
#Whitelisting for strng parameters
params.require(:user).permit(:name, :email, :password, :password_confirmation, :qualification, :college, :stream, :phone)
end
Migration:
class CreateUsers < ActiveRecord::Migration[5.1]
def change
create_table :users do |t|
t.string :name, null: false
t.boolean :admin_user, default: false
t.string :email, null: false
t.string :password_digest, null: false
t.string :qualification
t.string :college
t.string :stream
t.string :phone
t.timestamps
end
end
end
Model
require 'bcrypt'
class User < ApplicationRecord
include BCrypt
has_many :results, dependent: :destroy
has_many :exams, through: :results
accepts_nested_attributes_for :results
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
before_save { self.email = email.downcase }
has_secure_password
validates :email, presence: true
validates :email, presence: true, length: { maximum: 255 },format: { with: VALID_EMAIL_REGEX },uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 6 }
validates_confirmation_of :password
validates_presence_of :password_confirmation
validates :name, presence: true
validates :phone, numericality: {only_integer: true}, length: {is: 10 , message: "length should be 10"}
scope :visible, lambda { where(:visible => true) }
scope :invisible, lambda { where(:visible => false) }
scope :sorted, lambda { order("id ASC") }
scope :newest_first, lambda { order("created_at DESC") }
scope :search, lambda {|query| where(["name LIKE ?", "%#{query}%"]) }
end

Have you checked this https://github.com/plataformatec/devise/issues/1845
As per described in the issue, you are using ActiveModel's has_secure_password with Devise. You must not mix the two.
May be removing has_secure_password from User model will resolve your issue.

The code was completely fine,
few server restarts and don't really know how but restarting my code editor ( Visual Studio Code ) worked for me.
I am leaving the question as it is, as I did reach to this code after going through several stackoverflow threads.
NB: For newbies like me, please see how to check for errors while inserting into database from console, it helps a lot. Synatctical example is as:
rails c
user = User.new ( ... . . . . .. )
user.save
user.errors
Thankyou

This happened to me as well, and I found out that sometimes vscode doesn't actually kill the process running. You will need to kill rails from the terminal.
use ps aux | grep rails to find out what processes are running and use kill -9 [rails-pid] to stop the process.
On your next attempt, everything should work fine.
This answer helped me.

Related

Lifecycle of with_options in Rails. Why not working?

Im with a very tricky problem with a validation code on my app.
I have a user and a talent. A User has_one Talent. And, depending on the steps of the application, I need to validate some cases.
Those are my classes.
class User < ApplicationRecord
has_one :talent, dependent: :destroy
has_one :client, dependent: :destroy
has_one :agency, dependent: :destroy
before_create :set_initial_step
after_create :build_type
def build_type
logger.debug("Im inside build_type ***************** with #{type}")
if type.nil?
t = Talent.create(user: self)
Rails.logger.debug("checking errors ******#{type}*********** with #{t.errors.full_messages}")
else
o = Object.const_get(type.capitalize).create(user: self)
Rails.logger.debug("Im inside build_type ****#{type}************* with #{o.errors.full_messages}")
end
end
class Talent < ApplicationRecord
with_options if: Proc.new { |t| t.user.approval_submission_step >= 2 } do |t|
byebug
t.validates :talent_type_id,
:rate,
presence: true
t.validates :rate, numericality: { greater_than_or_equal_to: 25 }
end
with_options if: Proc.new { |t| t.user.approval_submission_step >= 3 && t.talent_type.is_model } do |t|
t.validates :gender,
:ethnicity,
:height_feet,
:height_inches,
:weight,
:eye_color,
:hair_color,
presence: true
t.validates :men_pant_size_waist,
:men_pant_size_length,
:men_shirt,
:men_dress_shirt_size,
:men_dress_shirt_neck,
:men_dress_shirt_sleeve,
:men_jacket,
:men_shoe_size,
presence: true, if: Proc.new { |t| t.gender == 'male' }
t.validates :ethnicity,
:inclusion => {in: VALID_ETHNICITY_TYPES}
t.validates :womens_dress_size,
:women_shoe_size,
:women_shirt,
:womens_dress_size,
:women_pant,
:women_bra_cup,
:women_bra_size,
presence: true, if: Proc.new { |t| t.gender == 'female' }
end
First weird thing. The byebug in the middle of the with_options its called when the server starts or when I do a rails console.
When I create a new User and save it (using the callback after_create of the user)
t = Talent.create(user: self)
The byebug its not called.
What Im doing wrong? Why the with_options on Talents are being called on the class-loading process but not when creating a new one?
First weird thing. The byebug in the middle of the with_options its called when the server starts or when I do a rails console.
with_options is evaluated when the class is loaded, that's why byebug only stops the excecution on those moments. That's how it works.
I'm not really sure what you want to do with those byebug calls, but I think you want to add them inside the Proc's, that code is actually evaluated at the moment with the current instance.
with_options if: Proc.new { |t| byebug; t.user.approval_submission_step >= 2 } do |t|

Getting two errors for password presence in activerecord

I am testing my user input validation in my application and I am getting two errors in regards to my password presence.
This is what I have written for my model.
class User < ActiveRecord::Base
include Slugifiable
extend Slugifiable::Find
has_secure_password
has_many :posts
validates :email, uniqueness: true, presence: true
validates :username, uniqueness: true, presence: true
validates :password, presence: true
end
Below is my migration table:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :email
t.string :username
t.string :password
t.string :password_digest
end
end
end
Each time I run my application with no input it should give me three error messages: "Password can't be blank", "Email can't be blank", "Username can't be blank". Instead I get an extra "Password can't be blank" error. I am using a password_digest variable which is a salted hash of the users password once the data persists in the database.
has_secure_password comes with its own presence validation on the create action. Therefore, validating the presence of password is redundant and is causing you to get two "Password can't be blank" error messages.
Simply remove validates :password, presence: true or add a condition to the validation for a specific controller action/other context...ie
validates :password, presence: true, on: :some_action

Rails - Unknown attribute password

I have been following Michael Hartl's Ruby on Rails tutorial book to try and add users to my application. Reading chapter 6, I have added what I believe to be the necessary fields for my user, specifically password and password confirmation via "has_secure_password".
I thought that adding "has_secure_password" to my user model would include the attributes "password" and "password_confirmation" provided I add a "password_digest" to the model. I have done that as the book instructed me to. However, when I run a test, Rails gives me the following error:
Error:
UserTest#test_should_be_valid:
ActiveModel::UnknownAttributeError: unknown attribute 'password' for User.
test/models/user_test.rb:8:in `setup'
I tried this solution and it still gave me the same error, not recognizing the attributes "password" or "password_confirmation". I installed bcrypt using "gem install bcrypt" and included the following in my gem file:
gem 'bcrypt-ruby', :require => 'bcrypt'
I am using Rails 5 and it seems like "has_secure_password" is not supplying the password attributes that I need. Can anyone see what I missed or did wrong that caused "has_secure_password" to not work as intended? Thanks
User Model:
class User < ApplicationRecord
has_many :activities
class User < ActiveRecord::Base
attr_accessor :name, :email, :password, :password_confirmation
has_secure_password
validates :first_name, presence: true, length: {minimum: 1}
validates :last_name, presence: true, length: {minimum: 1}
validates :email, presence: true, uniqueness: true, length: {minimum: 5}
validates :username, presence: true, uniqueness: true, length: {minimum: 1}
validates :password_digest, length: {minimum: 6}
validates :password, :confirmation => true, length: {minimum: 4}
validates :password_confirmation, presence: true
#-----------------------New Stuff ---------------------------------------
acts_as_authentic do |c|
c.crypto_provider = Authlogic::CryptoProviders::Sha512
end
#------------------------------------------------------------------------
#---------------Unsure if working--------------
#validates_presence_of :password, :on => :create
#validates_presence_of :email
#validates_uniqueness_of :email
#----------------------------------------------
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
end
Apologies for the messy code on the model as I am still learning Rails.
User Controller:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
flash[:success] = 'Account created'
else
flash[:notice] ='ERROR: Account was not created'
redirect_to 'users/new'
end
end
def show
#user = User.find(params[:id])
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation)
end
end
User Table:
create_table "users", force: :cascade do |t|
t.string "first_name"
t.string "last_name"
t.string "username"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "persistence_token"
t.string "password_digest"
t.index ["email"], name: "index_users_on_email", unique: true, using: :btree
end
User Test:
require 'test_helper'
class UserTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
def setup
#user = User.new(first_name: 'test', last_name: 'tester', password: '1234',email: 'test1#mail.com',
password: 'foobar', password_confirmation: 'foobar')
end
test 'should be valid' do
assert #user.valid?
end
end
Update:
I have tested this out and it works. So hope will work for you as well :) Looks like MiniTest doesn't work well with BCrypt. I received the same error - undefined password, but later implemented my change and it went further well.
Original answer:
As of your founded solution it made me think that this makes no sence - adding getter and especially setter methods for :password and :password_confirmation. Because has_secure_password creates those virtually that runs through BCrypt. So doesn't it goes around crypting / encrypting? If so it is not safe. So only option left for testing I see take the BYcript into the testing suite. I think something like this might do the trck:
In User Test:
require 'bcrypt'
def setup
#user = User.new(first_name: 'test', last_name: 'tester', password: BCrypt::Password.create("my password") ,email: 'test1#mail.com', password_confirmation: 'my password')
end
test 'should be valid' do
assert #user.valid?
end
Note that I removed duplicated password: 'foobar. Since with that particular test you are testing if User can be created, so shouldn't pass a different password or even duplicated attribute... Make another test for this (also checkout fixtures, they are great for creating test objects, as well as factories for more complicated cases).
And of course, remove the atr_accessor :password, :password_confirmation form your User model.
p.s. and please fix you code snippet for User class. Or is it really defined twice like this?:
class User < ApplicationRecord
has_many :activities
class User < ActiveRecord::Base

Creating a has_and_belongs_to_many relationship in Rails

I have a User model which is designed after the Michael Hartl RoR tutorial and I am trying to create a new Teacher model. I would like the teacher to have many users but each user to have only one teacher. I created the teacher model with
class CreateTeachers < ActiveRecord::Migration
def change
create_table :teachers do |t|
t.string :name
t.string :email
t.string :phone
t.references :user, index: true, foreign_key: true
t.timestamps null: false
end
end
end
and added has_one :teacher to user.rb. Here is the teachers.rb model
class Teacher < ActiveRecord::Base
has_and_belongs_to_many :users
validates :user_id, presence: true
before_save :downcase_email
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 }
private
# Converts email to all lower-case.
def downcase_email
self.email = email.downcase
end
end
However in my teacher_test.rb test file, things get a little fuzzy. I try this
def setup
#user = users(:michael)
#user2 = users(:archer)
#user3 = users(:lana)
#user4 = users(:mallory)
#teacher = Teacher.new(name: "Phred Willard",
email: "pwillard#test.com",
phone: "1234567890",
user_id: [#user.id,
#user2.id,
#user3.id,
#user4.id])
end
test "should be valid" do
assert #uac.valid?
end
but that fails outright. Did I set my relationship up correctly? I obviously am not adding users correctly since the model fails a validity test. How would I add more users to that teacher? Thanks in advance.
I would like the teacher to have many users but each user to have only one teacher
You only need has_many / belongs_to...
#app/models/user.rb
class User < ActiveRecord::Base
belongs_to :teacher
end
#app/models/teacher.rb
class Teacher < ActiveRecord::Base
has_many :users
end
You'll need to add a teacher_id column in your users table (the opposite of what you have now):
class UpdateUsers < ActiveRecord::Migration
def change
change_table :users do |t|
t.references :teacher, index: true, foreign_key: true #-> teacher_id
end
end
end
--
The error you have is that you're calling user_id on teacher; it should be teacher_id on user:
#teacher = Teacher.new(name: "Phred Willard",
email: "pwillard#test.com",
phone: "1234567890",
user_ids: [#user.id,
#user2.id,
#user3.id,
#user4.id])
This should associate #teacher with the defined #users you've listed.
You'll also want to look at collection_singular_ids for the has_many association, which is why your test is failing.
Your teacher.rb should be
class Teacher < ActiveRecord::Base
has_many :users
before_save :downcase_email
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 }
private
# Converts email to all lower-case.
def downcase_email
self.email = email.downcase
end
end
and user.rb
class User < ActiveRecord::Base
belongs_to :teacher
# rest of your code here ....
end
You need teacher_id column in users table.

Rails 4 and Devise : Allow email blank on certain conditions

I'm currently working on a system where the email is only required if the user is not a student and username is required if the user is a student.
So here is what I did in my model :
class User < ActiveRecord::Base
validates :email, presence: true, unless: :student?
validates :username, presence: true, if: :student?
end
This works fine on username attributes, but for the email, I'm still getting Email cannot be blank error. I guess Devise has it's own email validation rule.
How can I make this works, I mean overriding Devise validate presence rule on email?
Thanks
Devise has an email_required? method that can be overrided with some custom logic.
class User < ActiveRecord::Base
validates :username, presence: true, if: :student?
protected
def email_required?
true unless student?
end
end
I think Devise uses email as a key in its model.
add_index :users, :email, unique: true
If you used the generated devise migrations you could make the user_name the key with a migration.
add_index :users, :user_name, unique: true
remove_index(users, column: users)
change_column :users, :email, :string, :null => true

Resources