Ruby - Checkbox Validation - ruby-on-rails

I'm trying to get my checkbox validation working for my app. Basically, I enter a vehicle id number (VIN) and the system checks if there is a duplicate in the database. We want to allow the duplicate if the user clicks a checkbox to allow it. Here is my current code:
View:
.field-row
%label VIN
= f.text_field :vin, class:'mono-field'
.field-row
%label Allow Dup VIN
= f.check_box :vincheck
Model:
class Vehicle < ApplicationRecord
attr_accessor :vincheck
#this checks if the checkbox is checked
validates_acceptance_of :vincheck, message: "Must check 'Allow Dup Vin' to save a duplicate VIN."
#this checks for the duplicate VIN in the database
validates :vin, uniqueness: true, :if => :vincheck
If I enter a duplicate VIN and don't check the checkbox I get the following errors:
Vincheck Must check 'Allow Dup Vin' to save a duplicate VIN.
Vin has already been taken
If I enter a valid VIN I get:
Vincheck Must check 'Allow Dup Vin' to save a duplicate VIN.
If I enter a valid VIN and click the checkbox I don't get any errors. I need to be able to select the checkbox and leave a duplicate VIN in there.
I feel like I'm close but must be missing something. Its as if the
:if => :vincheck
isn't actually doing anything. I thought that would have allowed me to skip out on having the validates_acceptance_of.
Any thoughts?
thanks!

You're close, just use a Proc in your "if" clause:
validates :vin, uniqueness: true, :unless => Proc.new { |vehicle| vehicle.vincheck? }
The current record is passed to the Proc, so you can check any of the attributes.
You also don't need the acceptance validation for vincheck. That will force the box to be checked. You should use a custom message on the vin validation that explains the usage of the check box.

Here is how I finally got it working. I added vincheck into the database and preset the values to 1. Here is the code that does the check:
validates :vin, uniqueness: true, :unless => Proc.new { |vehicle| vehicle.vincheck == '1' }
Thanks to #Michael Chaney for the Proc and db suggestions.

You have added acceptance validation, due to which acceptance is get check each time, irrespective of vin number,
please try following solution, this will show acceptance error message only if vincheck id failed.
class Vehicle < ApplicationRecord
attr_accessor :vincheck
validates_acceptance_of :vincheck,
message: "Must check 'Allow Dup Vin' to save a duplicate VIN.",
:if => :vincheck_fail?
validates :vin, uniqueness: true, :unless => :vincheck
def vincheck_fail?
self.vin.present? && Vehicle.where('vin = ? AND id != ?', self.vin, self.id).present?
end
end
I hope this will work for you.

Related

How can I implement dynamic validation in activerecord

What is the best way to adjust your validation of a model based on a parameter or action? Say I am entering a lead into a system, so all that is required is basic contact info. But then later I may enter a new contract for that user at which point I need more detailed information. So I may need phone number when just entering a lead but once they are entering into a contract I might need their birthdate and alternate phone number.
I considered using a state machine but the user could actually enter into two contracts at the same time so state doesn't really work for this scenario.
I also considered storing the extra info with the contract but since the user can have more than one contract, it needs to live with the user so it is not redundant.
So basically when saving a contract, it would tell me that the attached user is invalid if said user doesn't have the extra fields.
Check out conditional validations:
class Person
validates_presence_of :given_name, family_name
validates_presence_of :phone_number, :email_address, :if => :registered
with_options :if => :registered do |person|
# validations in this block are scoped to a registered user
person.validates_presence_of :dob
end
end
The :if option can take:
a symbol that corresponds to a method on the class that evaluates to true or false
a proc or lambda that returns a value that evaluates to true or false
a string containing ruby code (god knows why you'd want to do that)
You also have access to an :unless option which works in a similar fashion.
You can also encapsulate the logic to determine the current state of the user and use that to determine what validation steps you can take:
class Person
validates_presence_of :email_address, :if => ->(p) { p.state == :pending_confirmation }
# I actually prefer validations in this format
validate do # stricter validations when user is confirming registration
if confirming_membership && (given_name.blank? || family_name.blank?
errors.add(:base, 'A full name is required')
end
end
def state
# your logic could be anything, this is just an example
if self.confirmed_email
:registered
elsif confirming_membership
:pending_confirmation
else
:new_user
end
end
def confirming_membership
# some logic
end
end
You can use conditional validation for example:
validates_presence_of :phone, :if => Proc.new { |p| p.lead? }
In whatever action the lead posts to, you could just do this:
#object.save(validate: false)
Then, when they need to enter the contract, leave off that validate: false option to ensure that those validations run.
Also, see this post if you want to skip only certain validations.

Rails range validation with data from another model

I have a Ticket and Event model in which the relationship is that an Event has many Tickets. The Ticket model has columns serial starting value and serial ending value which denotes the range of the valid serial nubers for that event.
I want to validate the Ticket on creation so that if a Ticket is created with a serial number beyond that range, the system will spew out errors saying that the range in invalid.
What I currently have in my ticket model validation is another validation to show a valid serial number for all events which is between 140000 and 149999
validates :serial_number,
presence: true,
numericality: {
only_integer: true,
:greater_than_or_equal_to => 140000,
:less_than => 149999,
:message => "is invalid" },
:uniqueness => { :message => "%{value} has already been taken" }
I need to get data from the Event model and place it into the Ticket validation. Does Rails allow something like this in the model? Should I do it in the controller?
Definitely do this in your model. Use custom validations for it:
validate :serial_number_valid
def serial_number_valid
unless event.serial_number_range.include?(serial_number)
errors.add(:serial_number, "must be within range")
end
end
One way to do this is by using rails validation helper of inclusion. This helper validates that the attributes' values are included in a given set. For this particular case you can use:
validates :serial_number, inclusion: { in: 140000..149999 }, presence: true, uniqueness: true
If you want to use the same method in rails 4 way here's a link to that.

Custom Validations Rails Model

I have a contact form and would like to show individual messages depending on what has failed.I would like to use flash messages. So from what i have read so far i can create a custom method (or i think it just overrides the one in place?)
So for example, i want to validate the presence of the name field
Class Message
attr_accessor :name
validates :name, :presence => true
def validate
if self.name < 0
errors.add(:name, "Name cannot be blank")
end
end
end
Within my controller i normally use a generic message
flash.now.alert = "Please Ensure all Fields are filled in"
Is there a way to call the particular message that failed validation?
Thanks
There is a plugin available, u can follow the below url
https://github.com/jeremydurham/custom-err-msg
Check the method validates because you can pass a message argument with the desired message.
validates :name, :presence => {:message => 'The name can't be blank.'}

Validation of fields from some box on the view - Rails

I have an User model. It has next fields:
attr_accessible :user_name, :first_name, :last_name, :email ....
There is a profile view for the User with 6 blocks. Each of them associated with the various fields. Box 1 - first_name and last_name, Box 2 - user_name and email, etc.
I need to validate all the fields (presence, format, etc). But validators must trigger only for those fields, that has came from a particular block (Box 1 or Box 2, for example).
If I write something like next:
validates :user_name, :presence => true
and I will not edit the block with the *user_name*, I will see the error "user Name can't be blank". I can't use *:allow_blank => true* or nil because it can't(!) be blank!
In two words: I must validate only those fields, that was past from the resquest.
What I can do to solve my problem? Thx
You can add if or unless option to skip of particular condition.
validates :user_name, :presence => true, :if => "first_name.blank? and last_name.blank?"
You can pull the specific fields out of your model and create a model for each block, then you add one_to_one relationships to your User model.

Validating a existence of a beta code before creating a User

model: User
has_one :beta_invite
before_save :beta_code_must_exist
def beta_code_must_exist
if beta_invite_id == beta_invite.find_by_name(beta_invite.id)
user
else
nil
end
end
model: BetaInvite
has_many :users
What I`m trying to do is check for the existence of a beta invite in DB, before allowing the user to be saved.
Since the User will be passing in the BetaInvite name into the field, I would like to check if it matches any existing Codes in the DB.
Hope I didn`t mix things up too much.
Would appreciate any help with this problem.
Add a text field to the form for :beta_code
Add an attr_accessor for that field: attr_accessor :beta_code
Then add the following line to the model (Assumes you only want to do this check on user creation):
validate :beta_code_must_exist, :on => :create
Change beta_code_must_exist to add an error to the form. Also be sure to properly cast :beta_code into the correct type.
Warning untested code below
def beta_code_must_exist
#invite = BetaInvite.find_by_name(beta_code)
if #invite.empty?
errors.add(:beta_code, "is not a valid invite code")
else
beta_invite_id = #invite.id
end
end
Use :inclusion with the :in option. You can supply :in with any enumerable:
validates :beta_invite, :inclusion => { :in => BetaInvite.all,
:message => "%{value} is not a valid beta invite code" }
Source: Rails Active Record Validation

Resources