How to add custom errors to the User errors collection? - ruby-on-rails

How can I add errors to the User model manually?
Is it just #user.errors << "some text goes here" ?

#user.errors.add(:email, "Not valid")
If you don't want to use any attributes, then in your model
#user.errors[:base] << "This person is invalid because ..."
For details: link

WARNING
If you just add errors in a separate method (not in a validation), then by default when you call .valid? or .save those errors will be automatically cleared. So you may want to use validation contexts instead.

Use the errors.add method
Example:
#user.errors.add(:name, "wasn't filled in")

try this:
errors.add(:base, "#{user.full_name} has errors here!")

Expanding on Jason Axelson's answer:
Manually added errors (e.g. errors.add(:email, 'invalid')) will not affect .save or .valid?.
You can use validation contexts to run conditional validations:
class User < ActiveRecord::Base
validate :ensure_something, on: :special_context
private
def ensure_something
errors.add(:email, 'invalid') if special_condition
end
end
user.save!(context: :special_context)
user.valid?(context: :special_context)

Related

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

Validation message helper or retrieving validation error

P.S: I've been working with Ruby for less than 48 hours, so expect me to be dumb!
I have set up validation on a model which all works fine. However I want to modify the message retuned by the uniqueness constraint. The message needs to be returned by a method which does some additional processing.
So for example if i try to create a new user model which the "name" attribute set to "bob", and the bob named account already exists, i want to display a message like "Bob isn't available, click here to view the current bob's account".
So the helper method will lookup the current bob account, get the localised string, and do the usual string placement of the localised string, placing the name, link to account and any other information i want into the message.
Essentially i want something like:
validates_uniqueness_of :text, :message => self.uniquefound()
def uniquefound()
return "some string i can make up myself with #{self.name}'s info in it"
end
No doubt that's completely wrong...
If this isn't possible, i've found i can use users.errors.added? to detect if the name attribute has a unique error added, from there i can probably do some hash manipulation to remove the unique error, and place my own in there either in an "after_validation" callback or in the controller... haven't worked out exactly how to do that, but that's my fallback plan.
So, is there a way of providing a class method callback for the message, AND either passing the current model to that method (if its a static method) or calling it so self inside that method is the model being validated.
Update
Trawling through the rails sourcecode, i have found this private method that is called during adding an error to the error class
private
def normalize_message(attribute, message, options)
case message
when Symbol
generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
when Proc
message.call
else
message
end
end
end
So if i pass a method as the message, i presume that this method is used to call my function/method and get teh return data. However it appears it's not called in the scope of the current object, nor does it pass in the object for which the error pertains...
So, if my digging is on the right path, it appears that calling a method on the current object to the message, or calling a static method passing the object, is not possible.
You could do something like this if you really need this functionality.
Class User < ActiveRecord::Base
include Rails.application.routes.url_helpers
....
validate :unique_name
def unique_name
original_user = User.where("LOWER(name) = ?", name.downcase)
if original_user
message = "#{name} is not available "
message << ActionController::Base.helpers.link_to("Click Here",user_path(original_user))
message << "to view the current #{name}'s account."
errors.add(:name,message)
end
end
end
EDIT
with a Proc Object instead
Class User < ActiveRecord::Base
include Rails.application.routes.url_helpers
....
validates_uniqueness_of :name, message: Proc.new{|error,attributes| User.non_unique(attributes[:value]) }
def self.non_unique(name)
original_user = User.where("LOWER(name) = ? ", name.downcase)
message = "#{name} is not available "
message << ActionController::Base.helpers.link_to("Click Here",Rails.application.routes.url_helpers.user_path(original_user))
message << "to view the current #{name}'s account."
end
end
These will both add the following error message to :name
"Name Bob is not available Click Hereto view the current Bob's account."

Save Model with a partially failed validation

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.

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

calling custom validation methods in 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")

Resources