I have a simple ruby class with ActiveModel::Validations and ActiveModel::SecurePassword included, I provide all required attributes (below) to the new obkject but when I validate it it says it's false.
require 'active_model'
require 'bcrypt'
class User
include ActiveModel::Validations
include ActiveModel::SecurePassword
attr_accessor :name, :email, :password, :password_digest
def initialize(name:, email:, password:)
#name, #email, #password = name, email, password
end
validates :name, :email, presence: true
has_secure_password
end
user = User.new(
name: "TestUser1",
email: "test#gmail.com",
password: "password"
)
puts user.valid? => false
puts user.errors.messages => {:password=>["can't be blank"]}
puts user.password => password
According to the documentation here has_secure_password provides validations on password accessor:
Password should be present.
Password should be equal to its confirmation (provided password_confirmation is passed along).
The maximum length of a password is 72 (required by bcrypt on which ActiveModel::SecurePassword depends)
What am I doing wrong? How's this object false?
EDIT
I have also tried adding password_confirmation attribute but it didn't work either.
user.password_confirmation = "password"
puts user.valid? => false
puts user.errors.messages => {:password=>["can't be blank"]}
Since you need to have password_digested filled by ActiveModel::SecurePassword you have to call User#password= setter method. But it's not happening when you set your password using #password = password in your initializer. To fix it you have set it using self.password = password:
def initialize(name:, email:, password:)
#name, #email = name, email
self.password = password
end
Also you need to remove :password from attr_accessor call because SecurePassword provides it.
I believe you need to set the user.password_confirmation attribute as well.
http://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html#method-i-has_secure_password
Related
I have a weird behaviour when using User.find_or_create_by! in before_action filter as follows:
class ApplicationController < ActionController::API
before_action :authorize_request
attr_reader :current_user
private
def authorize_request
#current_user = (AuthorizeApiRequest.new(request.headers).call)[:user]
end
end
Then in AuthorizeApiRequest I'm checking for existence or creating a new User by name:
class AuthorizeApiRequest
def initialize(headers = {})
#headers = headers
end
def call
{
user: user
}
end
def user
if decoded_auth_token && decoded_auth_token[:sub]
#user ||= User.find_or_create_by!(username: decoded_auth_token[:sub])
Rails.logger.silence do
#user.update_column(:token, http_auth_header)
end
#user
end
rescue ActiveRecord::RecordInvalid => e
raise(
ExceptionHandler::InvalidToken,
("#{Message.invalid_token} #{e.message}")
)
end
end
Example of UsersController:
class UsersController < ApplicationController
def me
if user_info_service.call
json_response current_user, :ok, include: 'shop'
else
raise AuthenticationError
end
end
private
def user_info_service_class
#user_info_service_class ||= ServiceProvider.get(:user_info_service)
end
def user_info_service
#user_info_service ||= user_info_service_class.new(user: current_user)
end
end
What is weird is that sometimes the User is created twice with the same username, sometimes not.
I'm using Ember JS in the front and another call is made to shops right after the authentication with JWT. All the routes are protected. I have the impression that calling current_user is not always in the same thread or sth like that and it results in having 2 identical users:
- the first one with just a username attribute set
- another one with all the others User attributes.
Here is the User model:
class User < ApplicationRecord
validates :username, presence: true, uniqueness: { case_sensitive: false }, on: :create
validates :shop_identifier, numericality: { only_integer: true, greater_than: 0 }, on: :update
validates :first_name, presence: true, on: :update
validates :last_name, presence: true, uniqueness: { case_sensitive: false, scope: :first_name }, on: :update
before_update do |user|
user.first_name = first_name.strip.capitalize
user.last_name = last_name.strip.upcase
end
Any ideas ? Thank you
I am working through Michael Hartl's Rails book and I am about halfway through chapter 10-working on account activation.
I had everything working with the mailers but then when I tried to add a new user, I got the following error message: "undefined method `activation_digest=' for #"
I have been trying to follow along in the book the best that I can. I have my users_controller.rb here:
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update]
before_action :correct_user, only: [:edit, :update]
def new
#user = User.new
end
def index
#users = User.paginate(page: params[:page], :per_page => 10)
end
def show
#user = User.find(params[:id])
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 update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
else
render 'edit'
end
end
def edit
#user = User.find(params[:id])
end
#confirms if a user is logged in
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please Log In."
redirect_to login_url
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
end
Here is my Model/user.rb:
class User < ActiveRecord::Base
attr_accessor :remember_token, :activation_token
before_save :downcase_email
before_create :create_activation_digest
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, length: { minimum: 6 }
# 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
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
The routes I have this:
root 'static_pages#home'
get 'sessions/new'
get 'users/new'
get 'help' => 'static_pages#help'
get 'about' => 'static_pages#about'
get 'contact' => 'static_pages#contact'
get 'signup' => 'users#new'
get 'login' => 'sessions#new'
post 'login' => 'sessions#create'
delete 'logout' => 'sessions#destroy'
resources :users
resources :account_activations, only: [:edit]
Please let me know if anything more is needed to be seen. I do have my App up on Github under the name sample_app, my username is ravenusmc.
Looking at your project on Github, your User model doesn't have an activation_token or activation_digest column, nor does the model define them as attributes.
Your User model is trying to write to these columns in the User#create_activation_digest function which is most likely causing the issue.
You'll need to write a migration to add those columns to your User model or add them is attributes (ie attr_accessor) if they are not meant to be persisted.
I'm working on a web app that has users model. You either sign up or try the app and be a 'guest'. I had the sign up functionality working well until I added the guest option. I based on Railscast to do this (http://railscasts.com/episodes/393-guest-user-record) and that's when I ran into troubles
Here is my user model code
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_many :bookings
has_one :account, dependent: :destroy
# before_save :downcase_email, allow_nil: true
before_save :create_remember_token
validates_presence_of :name, :email, :password, :password_confirmation, unless: :guest?
validates_uniqueness_of :email, case_sensitive: false , allow_blank: true
validates :name, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, format: { with: VALID_EMAIL_REGEX }, unless: :guest?
validates :password, length: { minimum: 6 }, unless: :guest?
def downcase_email
{ |user| user.email = email.downcase }
end
# has_secure_password
# override has_secure_password to customize validation until Rails 4.
require 'bcrypt'
attr_reader :password, :password_confirmation
include ActiveModel::SecurePassword::InstanceMethodsOnActivation
##def to_param
## "#{id}.#{name.parameterize}"
##end
def self.new_guest
new { |u| u.guest = true }
end
def name
guest ? "Guest" : name
end
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
and here is my User Controller code:
class UsersController < ApplicationController
before_filter :signed_in_user, only: [:edit, :update]
before_filter :correct_user, only: [:edit, :update]
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = params[:user] ? User.new(params[:user]) : User.new_guest
if #user.save
sign_in #user
#user.create_account.accountPlan = "Free"
##flash[:success] = "Welcome to the HighTide!"
redirect_to #user
else
render 'new'
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
#flash[:success] = "Profile updated"
sign_in #user
redirect_to #user
else
render 'edit'
end
end
private
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user?(#user)
end
def admin_user
redirect_to(root_path) unless current_user.admin?
end
end
you may notice some code is commented out. I wasn't able to store in the database a user without an email (and for have a quick and dirty thing working now I comment the callback before_save, I also might add that I use the guidelines of Michael Hartl tut)
What happens is, when I run this code I get a unknown attribute: password_confirmation error, but when I comment out
require 'bcrypt'
attr_reader :password, :password_confirmation
include ActiveModel::SecurePassword::InstanceMethodsOnActivation
and use has_secure_password instead
I get a stack level too deep
I haven't figured out what to do
I'm attempting to create a rails app where a user will sign up, then immediately be directed to fill out a profile with more detailed information.
I'm currently attempting this by having both a users and a profile model, with a has_one/belongs_to relationship between the two models.
I'm having trouble with createing the profile for the user. Tests fail with undefined methodprofiles' for #when testing the creation, and using an automated profile builder calledsample_data.rake`:
namespace :db do
desc "Fill database with sample data"
task :populate => :environment do
Rake::Task['db:reset'].invoke
admin = User.create!(:name => "name name",
:email => "fakename#fake.com",
:password => "password",
:password_confirmation => "password")
admin.toggle!(:admin)
99.times do |n|
name = Faker::Name.name
email = Faker::Internet.email
password = "password"
User.create!(:name => name,
:email => email,
:password => password,
:password_confirmation => password)
end
User.all.each do |user|
User.profiles.create(:city => Faker::Address.city,
:state => Faker::Address.us_state_abbr,
...
)
end
end
end
Also fails on
I'm having trouble with createing the profile for the user. Tests fail with undefined method 'profiles'
profiles_controller.rb is:
class ProfilesController < ApplicationController
before_filter :authenticate, :only => [:create, :edit]
def create
#profile = current_user.profiles.build(params[:profile])
if #profile.save
flash[:success] = "Profile Created!"
redirect_to root_path
else
render 'pages/home'
end
end
def edit
end
end
profile.rb is
class Profile < ActiveRecord::Base
attr_accessible :city, :state, ...
belongs_to :user
validates :city, :presence => true
validates :state, :presence => true
...
end
Can anyone see what I'm doing wrong? Is there a way to merge all the items I need under "users", validate the presence of all the required information, and have the signup process be two pages?
Other suggestions for this?
Why do you do this?
1.times do...end
You dont need that.
The failure comes up because you need to create ONE profile, not profiles, for one certain user.
So try this:
User.all.each do |user|
user.create_profile(:city => "bla", ...)
end
In you controller the same. You have just one profile, using singular will help out.
I am using OmniAuth in an application that requires authentication.
I have 3 ways for users to authenticate:
Create an account on my site
Facebook via OmniAuth
Twitter via OmniAuth
For option 1 I have validations in the form of:
validates_presence_of :email, :role
validates_presence_of :password, :if => :password_required
validates_presence_of :password_confirmation, :if => :password_required
validates_length_of :password, :within => 4..40, :if => :password_required
validates_confirmation_of :password, :if => :password_required
validates_length_of :email, :within => 3..100
validates_uniqueness_of :email, :case_sensitive => false
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
validates_format_of :role, :with => /[A-Za-z]/
The problem is that when I allow a user to login via twitter/facebook for the first time an Account is created, the validations are triggered, and fail.
For example:
ActiveRecord::RecordInvalid - Validation failed: Password can't be blank, Password is too short (minimum is 4 characters), Password confirmation can't be blank:
This makes sense as OmniAuth created accounts will not be submitting a password but i'm not sure exactly how i should make my model aware of this and skip (specific?) validations.
If it's of any use, the full account.rb model is here: http://pastie.org/private/wzpftprrzfg42uifetfhpa
Thanks a lot!!
try to extract the condition into the encrypt_password method from here, this line seems incorrect:
# Callbacks
before_save :encrypt_password, :if => :password_required
can you also copy-paste stack trace please?