Writes to database empty records when I check validation - ruby-on-rails

I have a model with validates:
attr_accessor :name, :username, :email, :password
validates :name, :presence => { allow_blank: false, message: "Имя не может быть пустым" }
validates :username, :presence => { allow_blank: false, message: "Имя пользователя не может быть пустым" }
validates :email, :presence => { allow_blank: false, message: "Эл. адрес не может быть пустым" }
CREATE def:
def create
#user= User.new(user_params)
if #user.save
redirect_to "http://bookworm.az:3000"
else
session[:name] =#user.name
session[:username] =#user.username
session[:email] =#user.email
error =#user.errors.to_a.to_sentence(two_words_connector:',')
delimeter = error.split
error = error.gsub(delimeter[0], '')
show_error = error.split(",")
flash[:notice] = show_error[0]
redirect_to :back
end
end
When I delete ALL validates checking all it saves the user in the database. But with the validates LOG prints:
INSERT INTO "users" DEFAULT VALUES RETURNING "id"
What is the problem?
UPDATE
def user_params
params.require(:user).permit(:name, :username, :email, :password)
end

attr_accessor is overriding the rails attributes. Try removing it and it should work.
Read the answer for similar issue.

Related

Rails NoMethodError for method I did not call

I'm getting the following error
Error Message Image
When trying to save a message object in my MessageController. Here is my controller.
class MessagesController < ApplicationController
def new
#message = Message.new
#user = current_user
end
def create
#message = Message.new(message_params)
#message.user = current_user
#message.user_id = current_user.id
if #message.user_email.nil?
#message.user_email = current_user.email
end
if #message.save
# UserMailer.send_email_to_admin(current_user.email).deliver
else
# redirect_to new_message_path(#message)
end
end
private
def message_params
params.require(:message).permit(:subject, :body, :user_email)
end
end
And here is my model.
class Message < ActiveRecord::Base
belongs_to :user
validates :user_email, presence: true
validates :subject, presence: true, length: { minimum: 5 }
validates :message, presence: true, length: { minimum: 10 }
end
I have no idea why this error is appearing because there are no methods named "message" in my app. Any help would be much appreciated!
In your model you have a validation for the message field:
validates :message, presence: true, length: {minimum: 10}
Is that supposed to be a validation for your body field?:
validates :body, presence: true, length: {minimum: 10}
Before saving the record, the validator is calling the message method on the Message instance(#message) to check its presence and validate it. Since you don't have a column named message but have a validation for it, you will get the NoMethodError.

validate only on some method rails

Based on rails validation docs. I need to validate fullname field only on update
# encoding: utf-8
class User < ActiveRecord::Base
GENDER_MALE = true
GENDER_FEMALE = false
attr_accessor :password_confirm,
:term,
:year, :month, :day,
:captcha
validates :username, presence: {message: "Bạn phải nhập tài khoản"},
uniqueness: {message: 'Tài khoản đã tồn tại'}, :on => :update
# validates :password, presence: {message: "Bạn phải nhập mật khẩu"},
# confirmation: {message: 'Mật khẩu không chính xác'}
# validates :password_confirmation, presence: {message: "Bạn phải nhập xác nhận mật khẩu"}
# validates :fullname, presence: {message: "Bạn phải nhập họ tên"}
# validates :email, presence: {message: "Bạn phải nhập email"},
# uniqueness: {message: "Email đã tồn tại"}
# validates :email, format: {with: /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i, message: "Email không đúng định dạng"},
# unless: "email.blank?"
# validates :term, acceptance: {message: "Bạn phải đồng ý điều khoản"}
# # validates :gender, acceptance: {accept: [0,1], message: "Giới tính không hợp lệ"}
# validate :_birthday_validator
# validate :_captcha_validator
#
# before_save :_encrypt_password
def signup
self.birthday = "#{year.to_s}-#{month.to_s}-#{day.to_s}"
self.save
end
def self.human_attribute_name(attr, option = {})
"" || super
end
protected
def _encrypt_password
self.password = Digest::MD5::hexdigest(password)
end
private
def _birthday_validator
unless year.present? && month.present? && day.present?
errors.add(:birthday, 'Bạn phải nhập ngày sinh')
else
errors.add(:birthday, 'Ngày sinh không hợp lệ') unless Date.valid_date?(year.to_i, month.to_i, day.to_i)
end
end
def _captcha_validator
if !(captcha.nil?)
errors.add(:captcha, "Mã xác nhận không hợp lệ") if captcha == false
end
end
end
As understand, this validation rule only run when I call update method, but I have no idea why this rule run all the time
Can anyone tell me why or I missed somethings?
Ps: Can Rails validates only for user defined method, somethings like
validates :username, presence: true, only: [:my_func]
One way would be to set a virtual attribute which you'll only populate in the signup method:
#app/models/user.rb
class User < ActiveRecord::Base
attr_accessor :should_validate
validates :fullname, presence: true, on: :update, if: "should_validate.present?"
end
This way, you can then assign a value to should_validate only when you use signup:
def signup
self.birthday = "#{year.to_s}-#{month.to_s}-#{day.to_s}"
self.should_validate = true
self.save
end
you can use method like
validate :fullname , on: :update
def fullname
if self.fullname.present?
true
else
false
end
end

cant sort active records

I am a noobie and I am trying to simply get all the records in the database and display them in alphabetical order. Whenever I display the index page the records are always sorted in descending by their id. I used the console to try calling EvalTest.order("name") and again I kept getting the records sorted by their id in descending order instead of by name. Do I need to add an index on the name column to sort by it? This seems like the answer should be so easy but I can't seem to figure it out...
Here is my code:
User Model:
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_secure_password
has_many :eval_tests
before_save { |user| user.email = email.downcase }
before_save :create_remember_token
validates :name, presence: true, length: { maximum: 50 }
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, presence: true, length: { minimum: 6 }
validates :password_confirmation, presence: true
after_validation { self.errors.messages.delete(:password_digest) }
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
Eval_Test Model:
class EvalTest < ActiveRecord::Base
attr_accessible :description, :name
belongs_to :user
validates :user_id, presence: true
validates :name, presence: true
validates :description, presence: true, length: { maximum: 350 }
default_scope order: 'eval_tests.created_at DESC'
end
EvalTest Controller:
class EvalTestsController < ApplicationController
def show
#eval_test = EvalTest.find(params[:id])
end
def new
#eval_test = EvalTest.new
end
def index
#eval_tests = EvalTest.order("name")
end
def create
#eval_test = current_user.eval_tests.build(params[:eval_test])
if #eval_test.save
flash[:success] = "Nouveau test cree!"
redirect_to #eval_test
else
render 'new'
end
end
end
Evaluation Test index.html.erb:
<% provide(:title, 'Index des tests') %>
<h1>Index des tests</h1>
<ul class="eval_tests">
<% #eval_tests.each do |eval_test| %>
<li>
<%= link_to eval_test.name, eval_test %>
</li>
<% end %>
</ul>
This is happening because you have used default scope in your model. Try
#eval_tests = EvalTest.reorder("name")
This should solve your issue.

Generate reset password token don't save on model

Hello I trying to create a reset password for my rails app; but when I try to save I get the following error:
Validation failed: Password can't be blank, Password is too short
(minimum is 6 characters), Password confirmation can't be blank
This is my user model.
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation
has_secure_password
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 :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 6 }
validates :password_confirmation, presence: true
def send_password_reset
self.password_reset_token = SecureRandom.urlsafe_base64
self.password_reset_at = Time.zone.now
self.password = self.password
self.password_confirmation = self.password
save!
end
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
the method "send_password_reset" doesn't update the user and I don't understand why is trying to save the user instead on updating just password_reset_token and the password_reset_at.
Does anybody can help me, please?
When you call save! on the model instance, it's going to run the validations on your User model; all of them.
There are a number of ways to skip the password validations conditionally. One is to use a Proc
validates :password, presence: true, length: { minimum: 6 }, unless: Proc.new { |a| !a.new_record? && a.password.blank? }
This will allow the User instance to be saved and will skip the validation of the :password field if it's blank and the User is not new (already persisted to the database).
Here is most of a password validation I use in my applications
validates :password, confirmation: true,
length: {:within => 6..40},
format: {:with => /^(?=.*\d)(?=.*([a-z]|[A-Z]))([\x20-\x7E]){6,40}$/},
Notice, you don't need the separate validation on :password_confirmation. Instead just pass confirmation: true to the :password validator.
Suggested Reading:
http://guides.rubyonrails.org/active_record_validations_callbacks.html#confirmation

form_for validation it doesn't work

I have the following form_for to reset password.
<% provide(:title, "Reiniciar Password") %>
<%= form_for(#user, :url => password_reset_path(params[:id]) ) do |f| %>
<%= render 'shared/error_messages'%>
<div class="row">
<div class="span4">
&nbsp
</div>
<div class="span4" id="login-box">
<div id="login-controls">
<%= link_to(image_tag("logo.png"), root_path) %>
<br>
<br>
<%= f.password_field :password, :placeholder => "Contrasena", :tabindex => 1, :style => "height:25px;" %>
<%= f.password_field :password_confirmation, :placeholder => "Contrasena", :tabindex => 2, :style => "height:25px;" %>
<%= f.button "<i class=\"icon-lock icon-white\"></i> Actualizar Password".html_safe, :tabindex => 2, class: "btn btn-warning", :style => "width:220px;margin-bottom:5px;" %>
<% end %>
</div>
</div>
<div class="span4">
</div>
</div>
everything works perfect, exect the validations, if you press the buttom with out type anything on the password and password_confirmation field, the controller reset the password with a blank password. In my model I have the validation for the password and password confirmation and it works when you create a new user, but I don't know why in this form for reset the password it doesn't work.
Here is the model
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation
has_secure_password
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 :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 6 }, confirmation: true, unless: Proc.new { |a| !a.new_record? && a.password.blank? }
def send_password_reset
self.password_reset_token = SecureRandom.urlsafe_base64
self.password_reset_at = Time.zone.now
save!
UserMailer.password_reset(self).deliver
end
def reset_password_token
self.password_reset_token = nil
self.password_reset_at = nil
save!
end
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
Thank for your help.
UPDATE
here is the controler
class PasswordResetsController < ApplicationController
layout "sessions"
def new
end
def create
user = User.find_by_email!(params[:password_resets][:email] )
user.send_password_reset if user
redirect_to root_url, :notice => "Las instrucciones para reestrablecer la contrasena fueron enviadas."
end
def edit
#user = User.find_by_password_reset_token!(params[:id])
end
def update
#user = User.find_by_password_reset_token!(params[:id])
if #user.password_reset_at < (2.hours.ago).to_date
redirect_to new_password_reset_path, :alert => "El link para actualizar la contrasena ha expirado."
elsif #user.update_attributes(params[:user])
#user.reset_password_token
redirect_to root_url, :notice => "La contrasena ha sido cambiada."
else
render :edit
end
end
end
Ok, I think I found the cause of your problem :
validates :password, presence: true, length: { minimum: 6 }, confirmation: true, unless: Proc.new { |a| !a.new_record? && a.password.blank? }
This line skips the validation if the password is blank, so this is why your user is being saved without any password
I think your proc skips password validation in case of update:
validates :password, presence: true, length: { minimum: 6 }, confirmation: true,
unless: Proc.new { |a| !a.new_record? && a.password.blank? }
would validate unless the record is not new (update) and the password is blank (did not submit anything in the field). Can you try commenting out the proc and see what happens?
e.g.
validates :password, presence: true, length: { minimum: 6 }, confirmation: true
Guys the problem was on the model. Here is the new validation.
validates :password, presence: true, length: { minimum: 6 }, confirmation: true, unless: Proc.new { |a| !a.new_record? && a.password.blank? && a.password_reset_token.blank? }
Thanks for your help and your time!
It could be your Proc Block which checks if the model is not a new record and if the password is empty. In this case the validation does not happen.
unless: Proc.new { |a| !a.new_record? && a.password.blank? }
So if you have a already created User:
!a.new_record? = true
and since the password is not saved directly but as a crypted password (password_digest) will also be nil thus:
a.password.blank? = true
So editing an existing user with this validation will not validate the password / password_confirm
At least I think so ;)
Maybe you can change it to:
validates :password, presence: true, length: { minimum: 6 }, confirmation: true, :if => :password?

Resources