Rails partial validations depending on conrtoller - ruby-on-rails

I am using devise for user registrations, meaning, that by default new user is registered through registrations_controller. So by clicking button "Register" user is redirected to a new_user_registration path.
My registration form however has two steps. In first step (new_user_registration) I am asking for a name and password. In the second step (users_controller), when user is saved, I am asking for address. This I am doing with wizard gem:
def create
#user = User.new(params[:user])
if #user.save
session[:user_id] = #user.id
redirect_to user_steps_path
else
render :new
end
end
So, those a kind of partioal validations, but I cannnot validate depending in the step, as the first part of my form is handeled through registrations controller. The second part is however in the users_controller. It's getting complicated here and I would like to know, if I can validatede depending on the controller. Like:
validates :first_name, presence: true, if: -> { new_user_registration_path }
validates :last_name, presence: true, if: -> { new_user_registration_path }
validates :street, presence: true, if: -> { new_user_path }
I know, like this, it makes no sence, but maybe it helps to understand my thinking behind. Also, maybe I can work with smth. like:
validates :first_name, presence: true, if: -> { #user.save }
validates :last_name, presence: true, if: -> { #user.save }
validates :street, presence: true, if: -> { #user.update}
So basically, when creating a new user, I would validate if name and password is present. And when then adding address (it's an update action for user), I will check if address is present. Does anyone has experince with forms like this?
Another thought, maybe, I can skip registrations controller, redirecting directly to the user and creating two steps for partial validations? But as I am working with devise, I don't know, if I can just go throught users controller, skipping new_user_registration path. I did it, how it was advised on the wicked tutorial, but still ended up in the registrations controller:
def create
super
end
def update
super
end
Thanks!

The best solution to this type of problem is to use form objects. See this example and the gem Reform.
With form objects, each HTML form is processed via a form object. In your example you could have a "User Registration" form, and a "User update" form. The key point is that the validation is done by the form and not the model. That way the validation is relevant to the current form input, and you avoid the issues of classing validation rules.

It's as easy as that:
validates :first_name, presence: true, :on => :create
validates :last_name, presence: true, :on => :create
validates :street, presence: true, :on => :update
validates :house_number, presence: true, :on => :update
validates :city, presence: true, :on => :update
validates :zip_code, presence: true, :on => :update
Clearly "Sign-Up" creates a User. And the wizard_steps just update the user. So for this simple example this works perfectly and raises validation errors according to the controller action.

Related

Ruby - Validates field presence after create

Is it possible to validate a field for presence after the initial creation?
I want to make phone number mandatory if the user wants to update their account after signing up.
validates :phone, presence: true, if: .....
if I use on: :update I can no longer authenticate until the field is filled
There are many ways to accomplish this task assuming it is a normal Rails model backed by a DB table. Off the top of my head you can do:
validates :phone,
presence: true,
if: Proc.new{ |model| model.id.present? }
Or more to the point and doesn't fail if you assign an ID before saving:
validates :phone,
presence: true,
if: Proc.new{ |model| model.persisted? }

adding validations on a specific page

I'm trying to add validations to just a specific page.
right now I have a user put in their e-mail/pw/pw-confirmation when they sign up and after clicking submit it directs them to the users edit page.
I'm using the following line of code:
validates :first_name, :last_name, :date_of_birth, :goals, uniqueness: true, on: :edit
unfortunately it's not validating can someone help me explain why ?
The problem is you are trying to perform 'sexy validation' on multiple attributes at once. It won't work. try this instead:
validates_uniqueness_of :first_name, :last_name, :date_of_birth, :goals, on: :update
Previous answer is a bit misleading. Problem with your code wasn't the validation syntax. You can still use
validates :first_name, :last_name, :date_of_birth, :goals, uniqueness: true but instead of on: :edit you should have on: :update

How can I update particular fields of model with validation in Ruby on Rails?

There is an AcviteRecord Model named User like this:
class User < ActiveRecord::Base
validates :name, :presence => true
validates :email, :presence => true, :uniqueness => true
validates :plain_password, :presence => true, :confirmation => true
validates :plain_password_confirmation, :presence => true
#...other codes
end
It requires that the update of name and email and the update of password are separated.
When only update name and password, using update or update_attributes will cause password validation which is not needed. But using update_attribute will save name and email without validation.
Are there any ways to update particular fields of model with validation without causing the other fields' validation?
Give it a try, might help
class User < ActiveRecord::Base
validates :name, presence: true
validates :email, presence: true, :uniqueness => true
validates :plain_password, length: { in: 4..255, allow_nil: true }, confirmation: true
validates :plain_password_confirmation, presence: true, if: -> (user){ user.plain_password.present? }
# ......
# ......
end
Apart from this you should reconsider about saving plain_password ;)
You can adjust your validations to only run on create. Requiring confirmation ensures changes on edit are applied.
validates :plain_password,
confirmation: true,
presence: {
on: :create },
length: {
minimum: 8,
allow_blank: true }
validates :plain_password_confirmation,
presence: {
on: :create }
I am assuming you are hashing your passwords, so this would accompany code similar to:
attr_accessor :plain_password
before_save :prepare_password
def encrypted_password( bcrypt_computational_cost = Rails.env.test? ? 1 : 10)
BCrypt::Password.create plain_password, cost: bcrypt_computational_cost
end
private #===========================================================================================================
# Sets this users password hash to the encrypted password, if the password is not blank.
def prepare_password
self.password_hash = encrypted_password if plain_password.present?
end
A better way to handle this is to not include the fields in the rest of the edit form, but rather provide a link to "Change my password". This link would direct to a new form (perhaps in a modal window) which will require the new password, confirmation of the new password, and the old password, to prevent account hijacking.
In your case you can use has_secure_password The password presence is only validated on creation.

How to have exception validation in Rails

I'm a newbie in rails and I'm stuck with this problem: I have a model named User
class User < ActiveRecord::Base
attr_accessor :password
EMAIL_REGEX = /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i
validates :first_name, :presence => true,:format => /\A[a-zA-Z]+\z/
validates :last_name, :presence => true,:format => /\A[a-zA-Z]+\z/
validates :email, :presence => true, :uniqueness => true, :format => EMAIL_REGEX
validates :password, :presence => true
validates_length_of :password, :in => 6..20, :on => :create
end
with database attributes first_name, last_name, email, hashed_password and encrypted_password.
When I create new Object of User and saves it in the database there is no problem. NOW, here's the problem I want to edit attributes of my User Object EXCEPT email and password.
Once I try to edit the record through edit of rails resource it flags an error that password should not be empty. I am planning to have an exemption of validation if the user wants to edit his/her information but I know that it is not a good practice.
Hoping to find the best answer.
For starters check out http://guides.rubyonrails.org/active_record_validations.html#conditional-validation - once this approach becomes unDRY (repeated more then 2-3 times) see http://apidock.com/rails/Object/with_options
Once you get more advanced you will want to try something in the lines of a form/service class and extract form/action-specific validations there - http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
...and welcome to rails :)
Try using
validates :password, :presence => true, on: :create
And in the user edit form add every field except email and password.
But thing is that while you are going to change the password, there is no validation on password. So when you are implementing the change_password section, you need handle it and nee to add the errors manually for the password.
Problem Solved! Im not sure if this is the best solution. Once I instantiate a User object I use the after_initialize then set updatePassword to true as a default value then in the edit action of User Object I set updatePassword to false so that it will be exempted in the validation. I also use the conditional validation for the updatePassword. :D Thanks for all the ideas!

Case Insensitivy in Rails

I have a users sign up form, but when the users sign back in they are required to type their email address with the same cases as they signed up with. I have measures to prevent this, but for some strange reason they are not working.
In the users model:
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => { :case_sensitive => false }
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
This was not working... im not sure why. but then I added another method to see what it did:
before_save :downcase_fields
def downcase_fields
self.email.downcase
end
and down cased the fields when users type them in with this in my sessions controller:
def create
user = User.authenticate(params[:session][:email].downcase,
params[:session][:password])
#...
end
All of this still yields a case sensitive email field when the users sign back in... help?
Try this:
self.email.downcase!

Resources