Customize error validation on rails - ruby-on-rails

How can add the error message into #errors on controller then show it on the view like:
<%= #question.errors[:tag][0] %>
with tag is not model element.

Take a look at this part of the Rails validation guide. They work by creating a custom validator, which just appends the desired error message to the desired hash entry.
In your case, this might look like:
class Question < ActiveRecord::Base
validates_with :tag_validator
end
class TagValidator < ActiveModel::Validator
def validate(question)
unless question.special?
question.errors[:tag] << 'Not special enough.'
end
end
end

Working with error messages in rails is done in 3 steps viz.
Validating the active record object using rails-validation-helpers OR creating a custom validator as specified by #chaleyc
Validating the ActiveRecord(AR) object using AR validation methods
Showing the errors on views, creating a view helper is the best practice notices and errors on rails3
Here's a nice rails-cast to get you started

Related

ActiveRecord::RecordNotUnique global handling

I am trying to figure out a graceful way to handle a ActiveRecord::RecordNotUnique exception globally for all my ActiveRecord code. I know about the validates_uniqueness_of validation but I want to rescue the exception directly as I have a constraint on the database in order to avoid bad data due to race conditions. I also don't want to create a bunch of custom methods that directly handle the exception every time I want to save or update an object where this constraint can be violated.
I would prefer not to monkey patch ActiveRecord methods like save() but I am beginning to think that achieving graceful exception handling for all ActiveRecord objects in my code might require that. Below is some code that demonstrates what a solution would look like:
class Photo < ActiveRecord::Base
belongs_to :post
def save(*args)
super
rescue ActiveRecord::RecordNotUnique => error
errors[:base] << error.message
false
end
end
While this works if I call save directly on a Photo object it won't work if I save the object through another model using accepts_nested_attributes_for with validates_associated.
Any help would be greatly apprecaited.
Thanks
Update
The desired outcome is to handle the exception and just add a key/value pair to the object's errors hash and then display form errors back to the user telling them that the email has been taken.
This is covered in the Action Controller Overview Rails Guide. In short, you can use the rescue_from method to register a handler for exceptions. If you use it in ApplicationController then it'll be inherited by all other controllers.
Here's the example from the Guide:
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
private
def record_not_found
render plain: "404 Not Found", status: 404
end
end
Go take a look for more information and an additional example.
What I was looking for was the inverse_of option when defining the association on each of the models. What inverse_of does is it causes rails to use the in memory instance of the associated object as opposed to going to the db to fetch the record. I created a save method in the Photo model that looks like this:
def save(*args)
super
rescue ActiveRecord::RecordNotUnique => error
post.errors[:base] << "You can only have one photo be your header photo"
false
end
In the rescue block, when I call post.errors I am getting the unsaved, associated post object rather than rails looking for one in the db based on photo.post_id which at this point is nil because the photo object is invalid which caused the post not to be persisted to the db.
Here are the docs for inverse_of
http://guides.rubyonrails.org/association_basics.html#bi-directional-associations
My gem activerecord-transactionable allows you to do all of the following (a la carte):
rescue
retry with alternate logic (switch from create to find, for example)
add errors to the record that failed to save
use locking
use nested transactions
handle different kinds of database errors in different ways

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

Better error handling than what I am currently doing?

I needed an error object to pass errors between controllers and js views for ajax responses. I wanted to use ActiveModel::Errors since then I could easily merge errors from model validation to non-model errors. But Ive been having some problems using this approach.
Using ActiveModel::Errors.new(nil)
If I try and call to_a() like so:
#errors = ActiveModel::Errors.new(nil)
#errors[:test] = "is a required field."
#errors.to_a().join("<br />")
I get the error:
undefined method `human_attribute_name' for NilClass:Class
Its trying to call the human_attribute_name for the nil I passed into Errors.new which is suppose to be a ActiveRecord Model.
I am wondering if anyone knows of a better way to handle errors or if the approach I am taken has already been made into some gem.
Chances are your validations are related to "something" that could be encapsulated in a model - it just doesn't need to be an ActiveRecord model.
You can use validations in any plain ruby object by doing something like this:
require 'active_model'
class Band
include ActiveModel::Validations
validates_presence_of :name
attr_accessor :name
end
Then, the usual suspects would work just fine:
b = Band.new
b.valid?
#false
b.name = "Machine Head"
b.valid?
#true

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

Rails 3 - Form Validation - Moving Logic to an Callback or Observer?

Hey all, I'm having issues with validation that I've been working on the last couple of days with no luck. What I have is that I have a model that requires the user to input a URL. I've done this with the following in my model:
validates :url, :presence => true
When a user submits their form, I take their URL and open it with Nokogiri to pull basic things like the webpage title. I'm currently doing this with my Create method in the controller. Code looks like so:
def create
require 'open-uri'
#page = Page.new(params[:page])
doc = Nokogiri::HTML(open(#page.url))
The problem I run in to, is that if the user enters a blank form, Nokogiri will error out as it runs even though I've tried to validate the form.
My question is, should I be moving this sort of logic to a callback or an observer? I'm pretty new to rails but would there be a way for me to work with my data/instance variables from a callback/observer? I've failed simply using #page, but was wondering if there was a way I'm supposed to pass it into the callback/observer if this is where this sort of logic is supposed to be placed?
Thanks
Would be better to put this in the model.
Controller method does something like
def create
#page = Page.new(params[:page])
respond_with #page
end
and in the model you have
class Page < ActiveRecord::Base
...
before_save :pull_info_from_url
def pull_info_from_url
doc = Nokogiri::HTML(open(self.url))
...
end
end
the before_save callback is run after validations, so if the presence-check fails, this code doesn't get executed, and the form with errors is shown instead.

Resources