calling custom validation methods in Rails - ruby-on-rails

I just upgraded my rails to 2.3.4 and I noticed this with validations:
Lets say I have a simple model Company which has a name. nothing to it.
I want to run my own validation:
class Company < ActiveRecord::Base
validate :something
def something
false
end
end
saving the model actually works in this case.
The same thing happens if i override validate() and return false.
I noticed this in a more complex model where my validation was returning false, but the object was still saving...I tried it out in an essentially empty model and the same thing applied. Is there a new practice I am missing? This doesn't seem to be the case in some of my older rails code.

Your validations are executed when you use the validate method. However rails doesn't relies on the returned value.
It relies on if there are validations errors or not. So you should add errors when your model doesn't validates.
def something
errors.add(:field, 'error message')
end
Or, if the error is not related to a field :
def something
errors.add(:base, 'error message')
end
Then your model won't be saved because there are errors.

You're getting confused between validations and callbacks.
Validations are supposed to fail if there are any errors on the object, doesn't matter what the validation returns. Callbacks fail if they return false, regardless if they add any errors to object.
Rails uses calls valid? from save calls which does not check the result of any validations.
Edit: Rails treats validate :method as a callback, but valid? still doesn't check for their results, only for errors they added to the object.
I don't think this behaviour changed at all but I could be wrong. I don't think I've ever written a validation to return false before.

Just FYI errors.add_to_base('error message') has been deprecated in rails 3 and got replaced by
errors[:base] << "Error message"
Or
errors.add(:base, "Error message")

Related

How can I raise an error if my factory fails validation?

If I use FactoryBot to create a record, but it fails validation, it only returns an unsaved instance. Is there a way to make FactoryBot throw an error to help me debug issues early?
FactoryBot.create(:house, some_options)
=> #<House id: nil, ...>
The test will continue and will ultimately fail somewhere else because my object was not valid.
My first thought would be to create a trait and somehow default it to all factories, but I'm not seeing an easy way to do that.
trait :ensure_validity do
before(:create) do |obj|
raise StandardError unless obj.valid?
end
end
Try the "bang" version of methods.
house = FactoryBot.build(:house, options_hash)
house.save!
Unfortunately, FactoryBot doesn't come with a create! method, but ActiveRecord does.
In Rails, you can cause ActiveRecord to throw errors when a record is invalid by using the :create! method instead of just the :create method.
You could do
House.create!(FactoryBot.attributes_for(:house, options_hash))
factory_bot uses save! as the default for creating instances (here is the configuration on Github), so you should get an error right away if there are any validation errors. Are you by any chance using a custom to_create in your factory? Are you able to provide the complete factory?
Could you use the callbacks to validate before persisting?
after(:build) { |record| raise 'foo' unless record.valid? }

rails prevent object creation in before_create callback

I want to check some attributes of the new record, and if certain condition is true, prevent the object from creation:
before_create :check_if_exists
def check_if_exists
if condition
#logic for not creating the object here
end
end
I am also open for better solutions!
I need this to prevent occasional repeated API calls.
before_create :check_if_exists
def check_if_exists
errors[:base] << "Add your validation message here"
return false if condition_fails
end
Better approach:
Instead of choosing callbacks, you should consider using validation here.Validation will definitely prevent object creation if condition fails. Hope it helps.
validate :save_object?
private:
def save_object?
unless condition_satisifed
errors[:attribute] << "Your validation message here"
return false
end
end
You can use a uniqueness validator too... in fact is a better approach as they are meant for those situations.
Another thing with the callback is that you have to be sure that it returns true (or a truthy value) if everything is fine, because if the callback returns false or nil, it will not be saved (and if your if condition evaluates to false and nothing else is run after that, as you have written as example, your method will return nil causing your record to not be saved)
The docs and The guide

Rails, warnings on Validations

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

Soft db error handling for duplicate entries? -- Rails 3.1 newbie

If a user tries to enter a duplicate entry into a table, they get a full page nasty error
ActiveRecord::RecordNotUnique in Admin::MerchantsController#add_alias
Mysql2::Error: Duplicate entry '2-a' for key 'merchant_id': ..
Is there a way to give a flash message instead like "This record already exists"?
This thread from railsforum can help you
I wouldn't recommend checking for the uniqueness and specifically responding to this validation rule via a flash message, this conventionally should be handled by the form's error messages instead.
Nonetheless, in the controller actions for creating and updated you could wrap the conditional block which decides where to send the user, with a check for uniqueness first:
def create
#merchant = Merchant.new params[:merchant]
if Merchant.where(:merchant_id => #merchant.id).count > 0
flash[:error] = "The merchant id #{#merchant.id} already exists"
render :create # amend this to :update when applying to the update action
else
# your normal generated save and render block
end
end
This isn't the cleanest way of achieving your goal, but I think it'll be easiest to understand.
Would really recommend the model validations and form error messages instead, which if you are usung the generated scaffolding, all you need to do is add a model validation and the form throw out the error messages to the user for you:
# app/models/merchant.rb
class Merchant < ActiveRecord::Base
validates_uniqueness_of :merchant_id
end

In rails, how can I find out what caused a .save() to fail, other than validation errors?

I have an ActiveRecord model which is returning true from valid? (and .errors is empty), but is returning false from save(). If the model instance is valid, how can I find out what's causing the save to fail?
If #user.save (for example) returns false, then just run this to get all the errors:
#user.errors.full_messages
Try using the bang version save! (with an exclamation mark at the end) and inspecting the resulting error.
Check all your callbacks.
I had a problem like this where I had and "after_validate" method that was failing after I had made a bunch of changes to the model. The model was valid but the "after_validate" was returning false, so if I used model.valid it said true, but then if I saved it gave me validation errors (passed through from the after_validate callback). It was weird.
Look at the application trace and you should be able to see what line of code is raising the exception.
Yea, I fixed this issue by making sure I return true in all my before_* callbacks then it starts working :)
Make sure you aren't trying to save a deleted record.
I had the same issue. But unlike the selected answer - my issue wasn't callbacks related.
In my case I had tried to save to a deleted record (deleted from DB).
#user = User.new
#user.save! # user saved to DB
#user.persisted? # true
#user.destroy # user deleted from DB
#user.persisted? # false, user still has its id
#user.valid? # return true
#user.errors # empty
#user.save # return false
#user.save! # raise ActiveRecord::RecordNotSaved
The problem I had was that I had forgotten to add the validation to the model.
class ContactGroup < ActiveRecord::Base
validates_presence_of :name
end

Resources