Rails Tutorial Chapter 7 user saves without email - ruby-on-rails

I've been going thru Rails Tutorial and got stucked on chapter 7. The problem is that after posting sign up information to rails thru rails console (with User.new), id, created_at and updated_at are becoming nil. By creating User thru creaded user web page, email becomes nil.
user.rb
class User < ApplicationRecord
before_save { self.email = email.downcase! }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :name, presence: true, length: { maximum: 50 }
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 6 }
has_secure_password
end
users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
flash[:success] = "Welcome to the Sample App!"
redirect_to #user
else
render 'new'
end
end
def show
#user = User.find(params[:id])
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
end
* new.html.erb *
<% provide(:titel, 'Sign Up') %>
<h1>Sign Up</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#user, url: signup_path) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name, class: "form-control" %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: "form-control" %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: "form-control" %>
<%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>
</div>
</div>
rails console output
irb(main):001:0> user = User.new(name: "Test User", email: "test#user.com", password: "secretsecret", password_confirmation: "secretsecret")
=> #<User id: nil, name: "Test User", email: "test#user.com", created_at: nil, updated_at: nil, password_digest: "$2a$10$E1SvEDh.aQSi809sQ7ecReacmSsnxRDUn3IbEawugQD...">
Showing /sample_app/app/views/users/show.html.erb where line #7 raised:
def gravatar_for(user, size: 100)
gravatar_id = Digest::MD5::hexdigest(user.email.downcase) # this line is highlighted
gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?=#{size}"
image_tag(gravatar_url, alt: user.name, class: "gravatar")
end
by checking development.sqlite3, email field is nil.
Where am I wrong? Thank you!
Ruby 2.4, Rail 5.1

User.new does not actually make a post request to the controller. It just creates a new instance of the User object in memory without saving it to the database. If you use User.create with the same arguments you will get an id, an updated at, and a created_at - but this still is not making a post request to your controller, its simply saving it to your database. If you'd like to make a post request to the new action of your controller you can make a front end with a new user form and submit it, or you could use something like postman or curl to make a request to your server. You'd also have to have your serving running either locally or elsewhere. I recommend reviewing pure ruby before going deeper into rails.
https://www.ruby-lang.org/en/documentation/quickstart/3/
https://www.getpostman.com/
https://curl.haxx.se/docs/manpage.html

After long time seeking for errror, it's finaly cought! The main casus on it were before_save { self.email = email.downcase! } pice of code. in user.rb model.before_save { self.email = email.downcase }, so, without forcing downcase!

Related

rails validations error messages not working

I am trying to make a login in and sign up page but when i try to validate the email and password no error messages pop up I though they are supposed to pop up the the condition isn't met.
i have tried error messages through layouts view and helper but none work or are too confusing for me to understand.
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.create(password: params[:password], email: params[:email], firstname: params[:firstname], lastname: params[:lastname])
if #user.save
redirect_to user_path(#user)
else
redirect_to root_path
end
end
def show
#user = User.find(params[:id])
end
end
This is my users controller
This is my user model
class User < ApplicationRecord
has_secure_password
validates :email, presence: true, uniqueness: true
validates :password, length: { minimum: 4 }
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+
[a-z]{2,})\Z/i, :message => "hollksd"(just for testing)
end
This is my new user view
<%= form_for #user do |form| %>
<p> First name:<%= form.text_field :firstname %></p>
<p>Last name:<%= form.text_field :lastname %></p>
<p>Email:<%= form.email_field :email %></p>
<p>Create password:<%= form.password_field :password %></p>
<%= form.submit %>
<%end%>
if anyone could help it would be appreciated!
It seems like your html elements are not associated to User model please try to use form_for tag instead somewhat like below
<%= form_for #user do |form| %>
<%= form.text_field :first_name %>
<%= form.text_field :last_name %>
<%= form.submit %>
<%end%>
Also please use render :new instead of redirect_to root_path
You you need to update your create action to something like this:
def create
#user = User.new(password: params[:password], email: params[:email], firstname: params[:firstname], lastname: params[:lastname])
if #user.save
redirect_to user_path(#user)
else
render :new
end
end
Why render new when it fail to save? Because we need to let know know that this form is not valid, and by rendering new with the #user object, you get access to #user.errors where you can do whatever you like (formally form will render a red border and error message next to input).

Rails Sorcery update attributes of model without password

I use sorcery for user authentication in a rails 4.1 application. Everything works fine. But when I try to update specific attributes of the user model (which is authenticated by sorcery), I get an error that the password is blank and is too short.
Here's a snippet from the console
> user = User.last
=> # I get the user
> user.update(about_me: "I'm a user")
=> false
> user.update(about_me: "I'm a user", password: "secret")
=> true
Here's my model code
app/models/user.rb
class User < ActiveRecord::Base
authenticates_with_sorcery!
validates :password, presence: true, length: { minimum: 6 }
.....
end
My controller code
app/controllers/users_controller.rb
class UsersController < ApplicationController
.....
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update(user_params)
redirect_to #user
flash[:notice] = "Profile successfully updated"
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:username, :name, :email, :password, :about_me)
end
end
And my update form
app/views/users/edit.html.erb
<%= form_for #user, method: :put do |f| %>
<% if #user.errors.any? %>
<div class="alert">
<p><%= pluralize(#user.errors.count, 'error') %></p>
<ul>
<% #user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.text_field :username, placeholder: 'Username' %>
<%= f.text_field :name, placeholder: 'Name' %>
<%= f.email_field :email, placeholder: 'Email' %>
<%= f.text_area :about_me, placeholder: 'About me' %>
<%= f.password_field :password, placeholder: 'Password' %>
<%= f.submit 'Save Changes', class: 'button' %>
<% end %>
If I remove the password field from the form, I get errors about the password being blank and about it's length.
Is this something to do with sorcery or is it something I'm missing with rails itself?
Is there a better way to update let's say only the email field without affecting anything else?
class User < ActiveRecord::Base
authenticates_with_sorcery!
validates :password, presence: true, length: { minimum: 6 }, if: :new_user?
private
def new_user?
new_record?
end
end
The validation will be checked only if it's a new_record, for which we have added our own private validation method new_user?. This function will return true during your normal signups/registrations. Hence, at those signups only the password validation will be needed.
During the edit, off course the user will be an existing user / new_record? will return false. Hence the validation for password will be skipped.
2nd way:
class User < ActiveRecord::Base
attr_accessor :skip_password
validates :password, presence: true, length: { minimum: 6 }, unless: :skip_password
end
#users_controller.rb
def update
#user = User.find(params[:id])
#user.skip_password = true
if #user.update(user_params)
redirect_to #user
else
render 'edit'
end
end
Here we have added our own custom attr_accessor skip_password. If the skip_password value is set to true, then during edit/update the password validation will be skipped.
I hope both of those ways will help you :)
If someone looks for this topic in future, it is possible to use changes map of ActiveRecord model:
class User < ActiveRecord::Base
authenticates_with_sorcery!
validates :password, presence: true, length: { minimum: 6 }, if: -> {new_record? || changes[:crypted_password]}
.....
end
where :crypted_password is the value of sorcery_config.crypted_password_attribute_name.
Also currently such condition of validates pointed in Simple Password Authentication sorcery wiki article.

Rails association cannot be blank

I'm building off the tutorial app in ruby on rails for a project, and I'm trying to create an association between two models.
In my database, there are users, events, and an attendance table that associates with the email from a user and a code from an event.
I've tried to research how to do this myself, but every time I try to validate the attendance email to a user, it states that the user cannot be blank as if I were trying to create a new one.
Still quite new to Ruby on Rails, so any advice would be appreciated! The models are below.
User Model:
class User < ActiveRecord::Base
attr_accessible :email, :name, :password, :password_confirmation
has_secure_password
has_many :attendances, inverse_of: :user
accepts_nested_attributes_for :attendances
before_save { |user| user.email = email.downcase }
before_save :create_remember_token
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates(:name, presence: true, length: { maximum: 50 })
validates(:email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: {case_sensitive: false})
validates(:password, length: { minimum: 6 } )
validates(:password_confirmation, presence: true)
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
Attendance Model:
class Attendance < ActiveRecord::Base
attr_accessible :code, :email
belongs_to :user
validates_presence_of :user
end
So far I'm only trying to enforce the association between User and Attendance, once I get that working I'll do the same to Events. Also, this is Rails 3.2.19 and Ruby 1.9.3.
EDIT: Here is the code I'm using for the form, I believe that it works because until I put the validation into the model it was creating rows into the Attendance table.
<% provide(:title, 'Test Event') %>
<h1>Attendance Registration</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(#attendance) do |f| %>
<%= render 'shared/attendance_error_messages' %>
<%= f.label :email %>
<%= f.text_field :email %>
<%= f.label :code %>
<%= f.text_field :code %>
<%= f.submit "Submit", class: "btn btn-large btn-primary" %>
<% end %>
</div>
</div>
Also, here's the attendance controller, if that helps.
class AttendancesController < ApplicationController
def new
#attendance = Attendance.new
end
def create
#attendance = Attendance.new(params[:attendance])
if #attendance.save
flash[:success] = "Attendance logged."
redirect_to root_path
else
render 'new'
end
end
end
Add this line to your form to avoid the user presence error
<%= f.hidden_field :user_id, current_user.id %>

Rails 4: Password Validation Error: "{:password=>["can't be blank"]}" even though form is filled out

I'm relatively new to Rails (using Rails 4), and am having a problem with validation for my user model. Even when the form is fully filled in with both the passwords, when I submit the code two errors print out:
{:password=>["can't be blank"], :password_confirmation=>["doesn't match Password"]}
I would like the user to be saved into the database, but these validation errors are preventing that from happening. What I would like to know is what I need to change in order to get rid of these errors.
I am printing out the params object and it looks like this (the authenticity token is omitted here):
params: {"utf8"=>"✓","authenticity_token"=>"[omitted]",
"user"=>{"username"=>"testuser1", "password"=>"test",
"password_confirmation"=>"test", "email_attributes"=>{"email"=>"d#d.com"},
"first_name"=>"test", "last_name"=>"user", "gender"=>"male", "city"=>"la",
"state"=>"ca", "country"=>"usa", "dob"=>"1980-11-20"},
"commit"=>"Create Account", "action"=>"create", "controller"=>"users"}
So it appears that the password and password_confirmation attributes are getting passed correctly. I am wondering if this may have to do with the virtual attribute password I have defined in the user model, but if that is the case I am still not quite sure how to solve this problem. Any help would be greatly appreciated. Let me know if I need to elaborate further.
Here is relevant code for reference:
Controller:
class UsersController < ApplicationController
def new
#user = User.new
#user.build_email
end
def create
if #user = User.create(user_params)
logger.debug "#{#user.errors.messages}"
logger.debug "params: #{params}"
redirect_to :action => "new"
else
logger.debug "#{#user.errors.messages}"
logger.flush
redirect_to :action => "new"
end
end
private
def user_params
params.require(:user).permit(:username, :password, :password_confirmation, :first_name, :last_name, :gender, :dob, :city, :state, :country, :admin_level, email_attributes: [:email])
end
end
Model:
class User < ActiveRecord::Base
has_one :email
validates_presence_of :username, :email, :password
validates_confirmation_of :password, :on => :create
accepts_nested_attributes_for :email
def password_valid?(candidatePass)
candidatePassAndSalt = "#{candidatePass}#{self.salt}"
candidatePasswordDigest = Digest::SHA1.hexdigest(candidatePassAndSalt)
if (candidatePasswordDigest == self.password_digest)
return true
else
return false
end
end
def password
end
def password=(text)
self.salt = Random.new.rand
passAndSalt = "#{text}#{self.salt}"
self.password_digest = Digest::SHA1.hexdigest(passAndSalt)
end
end
View:
<%= form_for #user, url: {action: "create"}, html: {class: "user-creation-form"} do |f| %>
<%= f.text_field :username %>username<br/>
<%= f.password_field :password %>pw<br/>
<%= f.password_field :password_confirmation %>pwcopy<br/>
<%= f.fields_for :email do |email_form| %>
<%= email_form.text_field :email %>email<br />
<% end %>
<%= f.text_field :first_name %>first<br/>
<%= f.text_field :last_name %>last<br/>
<%= f.radio_button :gender, "male" %>
<%= f.label :gender_male, "M" %>
<%= f.radio_button :gender, "female" %>
<%= f.label :gender_female, "F" %><br />
<%= f.text_field :city %>city<br/>
<%= f.text_field :state %>state<br/>
<%= f.text_field :country %>country<br/>
<%= f.date_field :dob %>dob<br/>
<%= f.submit "Create Account" %><br/>
<% end %>
The issue is your empty getter:
def password
end
It always return nil.
2 small additions to the previous answer, which should resolve your issue by the way.
1) If you're using Rails >3 (I assume you are by looking at your user_params method in the controller) you don't have to specify all those password fields and validations.
ActiveRecord automatically includes this ActiveModel method :
has_secure_password
More details at : http://api.rubyonrails.org/classes/ActiveModel/SecurePassword/ClassMethods.html#method-i-has_secure_password
2) If the uncrypted password/password_confirmation are shown in your log files your app is insecure. Add this to your config/application.rb :
config.filter_parameters = [:password, :password_confirmation]
This should not be needed if you are using has_secure_password in your User model.

Nested Model validation errors show on create but not update

I have a Developer model that :has_one User model. This allows for authentication and stuff across different user types.
When I create a new Developer with incorrect User data, it renders the list of validation errors. When I update a Developer with incorrect User data, it just re-renders the edit form (as it should) but doesn't show the validation errors.
My validation error display code sits in my fields partial for the form so that shouldn't make a difference.
I feel like the issue is in the way I'm trying to update my models.
def update
#developer = Developer.find(params[:id])
if #developer.user.update_attributes(params[:user]) && #developer.update_attributes(params[:developer])
flash[:success] = "Profile Updated"
sign_in #developer.user
redirect_to #developer
else
render 'edit'
end
end
and my User validations aren't anything fancy:
validates :name, presence: true, length: {maximum: 30}
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, length: {minimum: 6}
validates :password_confirmation, presence: true
I've read at least 10 different similar-sounding posts but I haven't found anything that's helped. Any help would be great.
Edit
When I submit my update form, the following params come through
Parameters: {"utf8"=>"✓", "authenticity_token"=>"70xmNVxxES7lK2bSIIul/i5GaiJhB9+B5bV/bUVFlTs=", "user"=>{"name"=>"foo", "email"=>"foo#example.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "developer"=>{"skype_name"=>""}, "commit"=>"Save changes", "id"=>"11"}
It still doesn't do the User validations. If I do the following via the console, it works though (i.e. it saves when the params are good and fails when the params are bad):
Developer.last.update_attributes(:user_attributes => {:name => "test updated", :email => "test#example.com", :password => "123456", :password_confirmation => "123456"})
So the only thing that seems different to me is the :user_attributes rather than just :user that my form is giving me. How do I change that?
Edit 2
Relevant part of my _fields partial for the form:
<%= render 'shared/error_messages' %>
<%= fields_for :user do |user| %>
<%= user.label :name %>
<%= user.text_field :name %>
<%= user.label :email %>
<%= user.text_field :email %>
<%= user.label :password %>
<%= user.password_field :password %>
<%= user.label :password_confirmation, "Confirm Password" %>
<%= user.password_field :password_confirmation %>
<% end %>
and my Developer#edit action:
def edit
#developer = Developer.find(params[:id])
end
No need to save user and developer separately, you can manage to save the user through developer model like this,
<%= form_for(#developer) do |f| %>
... developer's attribute ...
<%= f.fields_for :user do |ff| %>
... user's attribute ...
in controller, only
#developer = Developer.find(params[:id])
if #developer.update_attributes(params[:developer])
....
In developer model, you just need to add,
accepts_nested_attributes_for :user
and
attr_accessible :user_attribute
now form_for will automatically display the validation errors of user's model as well.
see this link for more details http://rubysource.com/complex-rails-forms-with-nested-attributes/

Resources