Is there a way to validate that one text_field does not equal another before saving record? I have two text_fields with integers in them and they cannot be identical for record to be valid.
You can add a custom validation:
class Something
validate :fields_a_and_b_are_different
def fields_a_and_b_are_different
if self.a == self.b
errors.add(:a, 'must be different to b')
errors.add(:b, 'must be different to a')
end
end
That will be called every time your object is validated (either explicitly or when you save with validation) and will add an error to both of the fields. You might want an error on both fields to render them differently in the form.
Otherwise you could just add a base error:
errors.add(:base, 'a must be different to b')
In your model:
validate :text_fields_are_not_equal
def text_fields_are_not_equal
self.errors.add(:base, 'Text_field1 and text_field2 cannot be equal.') if self.text_field1 == self.text_field2
end
more fun:
validates :a, exclusion: { in: ->(thing) { [thing.b] } }
Though of course this isn't terribly readable, but it's elegant - we're leveraging the exclusion validation with a proc to prevent the values from being equal. A more verbose approach might be preferred by some, but I'm a fan of brevity - code that doesn't exist can't have bugs. Plus, this will get its error message the default rails location, which may be convenient for i18n purposes.
(better?)
Related
I'm using Rail's ActiveRecord Validation Confirmation to validate an id. I have two form fields, cbiz_id and cbiz_id_confirmation, so that the fields receive exactly the same content.
cbiz_id is an integer and ActiveRecord makes cbiz_id_confirmation a string.
Is there a way to validate cbiz_id_confirmation as an integer?
validates :cbiz_id, confirmation: true, presence: { if: :has_any_qapps_role?, notice: I18n.t('messages.updated') }
I've attempted a number of things such as:
before_validation :change_cbiz_id_confirmation_to_integer
def change_cbiz_id_confirmation_to_integer
:cbiz_id_confirmation.to_i
end
I'm new to Ruby and Rails, so foundational explanations are appreciated!
Rails converts cbiz_id because it looks at the DB can sees that the column is an integer. Since there's no column for cbiz_id_confirmation, Rails doesn't have any type data, and the default type for any form submission (since form encoding and query params have no types) is a string.
Your change_cbiz_id_confirmation_to_string should work, but you're calling to_i on a symbol. You want just cbiz_id_confirmation.to_i (no leading :). Also, perhaps that method should end in to_integer.
If you want to ensure cbiz_id_confirmation is an integer even outside of validations, you can write a setter method that does the conversion for you.
attr_reader :cbiz_id_confirmation
def cbiz_id_confirmation=(value)
#cbiz_id_confirmation = Integer(value)
rescue TypeError
#cbiz_id_confirmation = nil
end
Integer(value) will raise an exception if the value cannot be converted (eg. it is nil or the word "ham"), so the rescue block makes this act like Rails and treat invalid data as nil. You can also use value.to_i, which that will coerce nil and "ham" to 0, or just let the exception happen and handle the problem elsewhere.
I solved the issue with the following code:
attr_accessor :current_user_qapps_role, :cbiz_id_confirmation
validates :cbiz_id, confirmation: true, presence:true, if: :has_any_qapps_role?
def cbiz_id_confirmation=(val)
#cbiz_id_confirmation = val.to_i
end
You can easily create a custom validation rule like this, but there's probably some more elegant solution to your problem if I'd understand the context:
validate :cbiz_matches
def cbiz_matches
errors.add(:cbiz_id, "ID's don't match") unless self.cbiz_id.to_i == self.cbiz_id_confirmaton.to_i
end
I have a rails project, using active record and was wondering if there was a validation helper for max number of individual entries. For example, if you had a form submission and only wanted, say 2, and you just wanted the first 2 to be persisted to the table, how would you do this?
I have read the manual and had a look at numericality etc but it's not really what I'm looking for. I have tried to write my own validation method in the model, but I am assuming there is a validation helper that makes this easier:
def validatePassengerNumber
if self.passengers.length > 2
# unsure on how to refuse new data access to database
end
end
Add an error to base after check return true, and it will prohibit to save into database.
def validate_passenger_number
self.errors.add(:base, "exceed maximum entry") if self.passengers.length > 2
end
Call this custom validation in respective model.
validate :validate_passenger_number, on: :create
There's no built-in validation; at least I haven't come across any. But following is a way to impose this type of validation:
def max_passengers
if self.passengers.count > 2
errors.add_to_base("There should not be more than 2 passengers.")
end
end
And then, you can use this validation to impose a check on the number of passengers.
You can add callback and validate the record.
before_validation :validate_passanger_number
private
def validate_passanger_number
errors.add("Sorry", "you have added maximum passengers.") if self.passengers.count > 2
end
Disclaimer:The model name and attributes are changed due to security issues!
class Human < ActiveRecord::Base
...
attr_accessible :ssn
validates :ssn, uniqueness: { scope: ... }
validate :ssn_validator
def ssn_validator
#some regexp for checking
...
end
end
And the FactoryGirl factory:
FactoryGirl.define do
factory :human do
ssn '123456789'
end
end
And in my test I test my ssn_validator, i.e. I create some Human instances with wrong ssn values.
class SisIdentifiableTest < ActiveSupport::TestCase
context 'human ssn' do
should 'be valid' do
assert FactoryGirl.build(:human).valid?
end
should 'be invalid if containing a & symbol' do
assert !FactoryGirl.build(:human, ssn:'12345678&').valid?
end
should 'be invalid if sorter than 9 symbols' do
assert !FactoryGirl.build(:human, ssn:'12345678').valid?
end
end
end
The test results:
HumanTest
human ssn be valid #pass
human ssn be invalid if containing a & symbol #fail
human ssn be invalid if sorter than 9 symbols #fail
First, I thought that my ssn_validator is somehow faulty. But that was not the case.
All human instances were valid, because somehow the ssn in valid? call was cached.
I mean that valid? method tests the human instance against the default '123456789' valid value(provided by the :human factory), not against supposed ssn:'12345678' or ssn:'12345678&'
In a sample:
human = FactoryGirl.build(:human, ssn:'there be dragons')
human.ssn #'there be dragons'
human.ssn_validator #false - because the sting is not valid
human.valid? #true
My question: How to test validation with FactoryGirl? Is there other method like valid? working not against cached attributes? Or I'm creating the tested instances using the wrong way?
FactoryGirl is not really for testing validations. It's a convenience to help you set up your test data. The build method will not cause any validation on the model because it's really just doing Human.new and then setting the attributes from your factory. Validation won't happen until you actually try to save the model.
(The create method of FactoryGirl would validate the object as it's the same as Human.create, and so has to save the object. Again it sets the attributes from the factory.)
Is it possible you can post the code of your ssn_validator method? For example, does it do something like errors.add(:ssn, ...) if validation fails? This would be needed for the valid? method to then realise there has been a validation failure.
The way I read your question, it does imply the error is in the validator. When you say:
human ssn be invalid if containing a & symbol #fail
that implies your validation of not working. This is an example of an invalid ssn, and if the validator correctly picks it up your test would be expected to pass, not fail.
Apologies if I've misunderstood your question.
Lets say I have the following model:
class Car < ActiveRecord::Base
attr_accessible :wheels,
:engine_cylinders
validates :wheels, :engine_cylinders, presence: true, numericality: true
end
Lets say I then have the following controller action:
#car = Car.find(params[:id])
#car.wheels = "foo"
#car.engine_cylinders = 4
#car.save
This save will fail as wheels will fail the numericality condition.
Is there any way to persist the succesful attributes (in this case engine_cylinders), while adding the invalid attributes to the errors array? E.g. is there a "soft" validation in Rails?
You want to write a Custom Validator.
class Car < ActiveRecord::Base
validate :wheel_range,
:engine_cylinder_range
def engine_cylinder_range
flash[:notice] = "engine_cylinder was not saved because it wasn't a number" unless engine_cylinder.is_a? Fixnum
# set engine_cylinder back to old value
end
def wheel_range
flash[:notice] = "wheels was not saved because it wasn't a number" unless wheels.is_a? Fixnum
# set wheels back to old value
end
end
You don't have to use flash here, you could use any variable for internal processing or re-display.
You may also want to put this custom validation check on the :before_save hook. Use the _was magic method to get the old value.
If you're looking to bypass validations you can always do so with:
if #car.save
# ...
else
#car.save(validate: false)
end
You may want to have a different conditional on that or whatever... but this is how you bypass validations on a one-off basis.
This may, however, destroy the errors array so you could rebuild it after the save(validate: false) with:
#car.valid?
You can also bypass validations one-at-a-time using #car.update_attribute(:attribute, <value>).
If you just want to know if the model is valid or not without saving it #car.valid? does exactly that. It also adds invalid attributes to errors array. And #pdobb already pointed out how to bypass validation when saving.
hi all what i am trying to do is create a "soft" validation in other words instead of my failing the validation and not saving the data to the DB, id like to have the validation give a warning to the user and allow the user to save faulty data if they so choose. but the validator will give them a warning before.
i want to do somehting like the following:
class MyModel < ActiveRecord::Base
warnings do
validate :warnings_validation
end
def warnings_validation
warnings.add(:name_of_element, "warning message") unless x == x
end
end
my model uses alot of inheritance and so gems like validations_scope dont work any ideas what i can do/use ?
I believe you could inspire yourself from the ActiveModel::Error example to implement the warning feature.
Explanation :
If you look at how the data is validated in Active Record, simply take a look at the valid? method :
def valid?(context = nil)
context ||= default_validation_context
output = super(context)
errors.empty? && output
end
The context and the output is not important, what matter is that the valid? method check if the errors instance variable is empty or not.
In other words, in the previous link I gave you, simply renaming the errors instance variable into warnings should do the trick. You'd have to create a custom validator using rails built-in and then simply call warnings.add(:name, "error") when needed. It should save the records while populating the warnings variable.
see this - custom validation
try this
validate :warnings_validation
def warnings_validation
self.name_of_element.blank?
errors.add(:name_of_element, "warning message") unless x == x
end
end