I have two registration forms: one that can register with any email address and the other with just one specific domain. How to allow that from the User.rb model?
Something like this may work:
validates_format_of :email, :with => /.io/
But because I have two registration forms and the url for that form has an id of 2:
validates_format_of :email, :with => /.io/ if params[id] == 2
I do understand that params is not available in the model but based on what I want to achieve, how to accomplish this?
Basically form with id = 1 can register with any email. Form with id = 2 can only register with a .io email address (domain).
This sounds like a good use for validation contexts. A validates* method can accept an :on option, which is the name of the "context" in which the validation will be triggered; for example:
validates_format_of :email, :with => /\.io\z/, on: :restricted_email
This validation will only be triggered if the option context: :restricted_email is passed to the save or save! method. Here's how you might use it in your controller:
def create
#user = User.new(params[:user])
if params[:id] == 2
#user.save!(context: :restricted_email)
else
#user.save!
end
end
Here's a good blog post on the topic: http://blog.arkency.com/2014/04/mastering-rails-validations-contexts/
You could add an attribute to the model that would indicate which form registration was coming from, like :registered_io, sent to your model through a hidden field in the form
#...
<%= f.hidden_field :registered_io, value="true" # or false %>
Then in your model you could just do an
validates :format_of_io_email_should if self.registered_io
validates :format_of_universal_email_should if !self.registered_io
def format_of_io_email_should
# regex ahoy or whatever
end
def format_of_universal_email_should
# same same but different
end
Don't forget to run your migrations to store the attribute, too!
$ rails migration add_column_registered_io_to_user registered_io:boolean
Also don't forget to allow :registered_io in the strong params in the controller.
Related
I have a set of custom fields attached to a devise model called Entrant.
I have two forms, one for registration form (three fields) and one which sits in the account area (12 fields). Most of the custom fields area required but only within the form the sits in the account area.
How do I achieve this?
I am using rails 4.2 and ruby 2.1
You can simply specify validations on actions, that is:
validates :name, presence: true, on: :create # which won't validate presence of name on update action
If you ask where to put your custom fields, then generate devise's views and update corresponding ones with these fields.
There are several ways! You could do conditional validations, for instance
class Entrant < ActiveRecord::Base
validate :foo, if: :account_area?
def account_area?
!new_record? # Assumes that Entrant that has already been saved
# is in the account area
end
end
However, it sounds like your needs are advanced enough that you should consider making a Form Object
A form object is an object that accepts parameters, performs validations on that data, then saves a model instance.
class AccountForm
include ActiveModel::Model
include Virtus # Provides AR like attribute functionality and mass assignment
def initialize(entrant)
#entrant = entrant
end
attribute :foo, String
validates :foo, presence: true # This is only used on the account page, so no need to mess with conditional logic
def save
if valid?
persist!
true
else
false
end
end
def persist!
#entrant.update_attributes(foo: self.foo)
end
end
This is just a great example of how non-rails-specific object oriented programming can make your life easier and your app more maintainable. Make a class like above, stick it in app/forms and restart your server. Then in your controller, you'll just pass it the model
class EntrantController < ApplicationController
def update
#form = Form.new(Entrant.find(params[:id]))
#form.attributes = params[:entrant]
if #form.save
redirect_to some_path
else
render "edit"
end
end
end
By default devise only asks for a combination of email/password, you can add other fields by adding a sanitizer (see there -> Devise how to add a addtional field to the create User form?).
If you want to add other fileds to validate, you should create a secondary Entrant controller and add a specific callback to your model.
Typically:
after_update :validate_entrant_form, if: :property_changed?
I hope this will help you.
validates :name, presence: true, if: :condition_holds?
def condition_holds?
# some code here that evaluates to a boolean
end
Maybe this way help you.
Add attribute in devise model : say attr_accessor :validate_certain. In your controller action, devise model instance say #user have to update like this #user.validate_certain = true. and change your appropriate validation conditions in devise model
validates :name, presence: true, if: :validate_certain_changed?
def validate_certain_changed?
validate_certain.present?
end
When I have to do something like this I like to think of it as it validates if something in in the field but you can also take a nil value
Entrant.validates_presence_of(:foo, :allow_nil => true)
I also have this concern when using devise on customer with forms on separate pages updating different set of customer fields
I believe most of the solution works but I was looking for the simplest, easiest and foolproof way to implement the solution
Thus came this.
validates :phone, :country, :postal_code, :street_address, presence: true, allow_nil: true
The allow_nil: true instruct the model to validate the fields ONLY if it exists on the submitted form. If you want more protection, you can use extra para like :on => :update
In Rails 3, I would like to see if a value for an attribute is valid using the model's validates options without trying to save or create.
I'm writing the back end of a AJAX API, that should check a username against the
validates :username, :length => {:minimum => 2, :maximum => 50}, :exclusion => {:in => RESERVED_USERNAMES}, :format => MY_REGEX, .etc
In the User model. This it to create a little tick or cross next to the username field in the register form, so the user doesn't have to wait to see if the username is taken or not.
I could just compare it to a regex, but to try to keep my code DRY, I thought it would be better to use the validation in the user model.
Anyone know how I could do something of the line of:
username = params[:username]
if User.not_found(:username => username) && User.validate(:username => username)
#yay!
else
#nope
end
(I already have the not_found working).
You could try checking for specific errors related to the username, in addition to running all validations (you need to in order to get the error messages).
#user = User.new(params[:user])
if #user.invalid? && #user.errors[:username].any?
# yay!
else
# nope
end
You can run that without persisting your user to the database, since none of the methods used (including #new and #valid?) actually save the object.
You can create new user with username param and then inspect errors:
#user = User.new( :username => params[:username] )
#user.valid?
if #user.errors.include? :username
# username error
end
I've got a User model with three fields, :email, :display_name and :handle. Handle is created behind the scenes from the :display_name.
I'm using the following validations:
validates :display_name, :presence => :true, :uniqueness => { :message => "Sorry, another user has already chosen that name."}, :on => :update
validates :email, :presence => :true, :uniqueness => { :message => "An account with that email already exists." }
I use the handle as the to_param in the model. If the user fails the validation by submitting a :display_name that already exists, then tries to change it and resubmit the form, Rails seems to use the new handle as the validation for the email -- in other words, it assumes that the email doesn't belong to the current user and validation on the email then fails. At this point, Rails assumes that the changed display name/handle is the one to use for the look up and the update action can't complete at all, because it can't find the user based on the new handle.
Here's the update method:
def update
#user = User.find_by_handle(params[:id])
#handle = params[:user][:display_name]
#user.handle = #handle.parameterize
...
end
This problem doesn't happen when the validation first fails on a duplicate email, so I'm assuming it's something about the way I've written the update method -- maybe I should try setting the handle in the model?
maybe I should try setting the handle in the model?
^ This.
The controller isn't the place to do something like this. If it's model logic that's happening behind the scenes, beyond the user's control, why put it in controller code?
Do it instead in a before_save filter, which is guaranteed to run only after the chosen display name is determined to be available and the record is deemed valid. In this way the handle won't be changed on the cached record until it is actually committed to the db, eliminating the problem of the incorrectly generated URL.
before_save :generate_handle
...
def generate_handle
self.handle = display_name.parameterize
end
Let's say I have a form_tag in a view gathering a view user input . With this input that I get through the params hash, I want to verify some of htis information such as email phone name.
Can I just verify in the controller or is this a bad thing? I don't plan to save the thing I get to a db or anything, it's just getting put into an email and sent off, but I want to verify these things before it's sent.
Thank you.
EDIT:
Found this http://www.viddler.com/explore/rails3/videos/6/
class Test
include ActiveRecord::Validations
validates_presence_of :name, :email
attr_accessor :name, :email
end
You can use the model for whatever you need that is related to the object and don't need to save it. Keeping stuff like this in the model is desirable in order to keep controller tidy. Say you have a user model:
#controller
#user.new params[:user]
#user.check_valid
#user.save # is optional
#user.rb
def check_valid
!email.blank? and !phone.blank?
end
My User model contains :name, :email, and :password fields. All 3 have validations for length. An "update account" web page allows the user to update his name and email address, but not password. When submitted, params[:user] is
{"name"=>"Joe User", "email"=>"user#example.com"}
Note there is no "password" key because the form doesn't contain such an input field.
When I call
#user.update_attributes(params[:user])
the password validation fails. However, since I'm not attempting to update the password, I don't want the password validation to run on this update. I'm confused why the password validation is running when params[:user] doesn't contain a "password" key.
Note that I want to have a separate web page elsewhere that allows the user to update his password. And for that submission, the password validation should run.
Thank you.
My application does something like this
attr_accessor :updating_password
validates_confirmation_of :password, :if => should_validate_password?
def should_validate_password?
updating_password || new_record?
end
so you have to model.updating_password = true for the verification to take place, and you don't have to do this on creation.
Which I found at a good railscast at http://railscasts.com/episodes/41-conditional-validations
In your user model, you could just ignore the password validation if it's not set.
validates_length_of :password, :minimum => N, :unless => lambda {|u| u.password.nil? }
Using update_attributes will not change the value of the password if there is no key for it in the params hash.
Validation doesn't run against the changed fields only. It validates existing values too.
Your validation must be failing because the password field contains some invalid content that's already saved in the database. I'm guessing it's probably because you're hashing it after validation and you're trying to validate the hashed string.
You can use a virtual attribute (an instance variable or method) that you validate with a custom method, and then assign the hash to the stored password field. Have a look at this technique for ideas.
An app that I am working on uses the following:
validates_confirmation_of :password,
:if => Proc.new { |account|
!account.password.blank?
|| !account.password_confirmation.blank?
|| account.new_record? }
Depending on your requirements, you might want to remove the new_record? check
When password is added then only confirmation will be called and presence will call on create action only
**validates_presence_of :password, :on =>:create**
**validates_confirmation_of :password**