Ruby on Rails: errors.add_to_base vs. errors.add - ruby-on-rails

I have read that errors.add_to_base should be used for errors associated with the object and not a specific attribute. I am having trouble conceptualizing what this means. Could someone provide an example of when I would want to use each?
For example, I have a Band model and each Band has a Genre. When I validate the presence of a genre, if the genre is missing should the error be added to the base?
The more examples the better
Thank you!

It's worth noting (since this shows up in the search engines, which is how I found it) that this is deprecated. The Rails 3 way of doing it is below but is no longer valid as of Rails 7 (see the comment from April 2022)
errors[:base] << "Error message"
the preferred way of doing it is
errors.add(:base, "Error message")
http://apidock.com/rails/ActiveRecord/Errors/add_to_base
http://apidock.com/rails/v3.2.8/ActiveModel/Errors/add

A missing genre would be a field error. A base error would be something like an exact duplicate of an existing record, where the problem wasn't tied to any specific field but rather to the record as a whole (or at lest to some combination of fields).

In this example, you can see field validation (team must be chosen). And you can see a class/base level validation. For example, you required at least one method of contact, a phone or an email:
class Registrant
include MongoMapper::Document
# Attributes ::::::::::::::::::::::::::::::::::::::::::::::::::::::
key :name, String, :required => true
key :email, String
key :phone, String
# Associations :::::::::::::::::::::::::::::::::::::::::::::::::::::
key :team_id, ObjectId
belongs_to :team
...
# Validations :::::::::::::::::::::::::::::::::::::::::::::::::::::
validate :validate_team_selection
validate :validate_contact_method
...
private
def validate_contact_method
# one or the other must be provided
if phone.empty? and email.empty?
errors.add_to_base("At least one form of contact must be entered: phone or email" )
end
end
def validate_team_selection
if registration_setup.require_team_at_signup
if team_id.nil?
errors.add(:team, "must be selected" )
end
end
end
end

Related

How can I use a custom predicate to validate multiple fields with dry-validation?

I have an address form that I want to validate as a whole rather than validating each input on its own. I can only tell if the address is valid by passing the line1, city, state, zip to the custom predicate method so that it can check them as a unit.
How can I do this? I only see how to validate individual fields.
It seems that this "High-level Rules" example could help you :
schema = Dry::Validation.Schema do
required(:barcode).maybe(:str?)
required(:job_number).maybe(:int?)
required(:sample_number).maybe(:int?)
rule(barcode_only: [:barcode, :job_number, :sample_number]) do |barcode, job_num, sample_num|
barcode.filled? > (job_num.none? & sample_num.none?)
end
end
barcode_only checks 3 attributes at a time.
So your code could be :
rule(valid_address: [:line1, :city, :state, :zip]) do |line, city, state, zip|
# some boolean logic based on line, city, state and zip
end
Update—this is for ActiveRecords rather than dry-validation gem.
See this tutorial, http://guides.rubyonrails.org/active_record_validations.html
Quoting from the tutorial,
You can also create methods that verify the state of your models and add messages to the errors collection when they are invalid. You must then register these methods by using the validate (API) class method, passing in the symbols for the validation methods' names.
class Invoice < ApplicationRecord
validate :discount_cannot_be_greater_than_total_value
def discount_cannot_be_greater_than_total_value
if discount > total_value
errors.add(:discount, "can't be greater than total value")
end
end
end

Validating uniqueness of an attribute in two tables/models

I have two different kind of user (devise) models in my Rails application. Doctor and Patient. It was my fault I haven't defined an upper class User and inherit attributes from it. They have a mutual attribute - a personal identification number and I want two check uniqueness of this attribute in both of tables. I've searched a bit and saw this answer.
I've applied things as it written there but it had no effect.
#patient.rb
class Patient < ActiveRecord::Base
...
validates_uniqueness_of :pin
validate :pin_not_on_doctors
private
def pin_not_on_doctors
Doctor.where(:pin => self.pin).first.nil?
end
end
#doctor.rb
class Doctor < ActiveRecord::Base
...
validates_uniqueness_of :pin
validate :pin_not_on_patients
private
def pin_not_on_patients
Patient.where(:pin => self.pin).first.nil?
end
end
First I created a patient instance, then a doctor instance with the same pin I used in my first (patient) case. Rails unexpectedly didn't spit out an error message and created doctor instance, and more interestingly devise also turned a blind eye for duplicate email.
How can I overcome this problem?
You should add the error on the validation function:
http://api.rubyonrails.org/classes/ActiveModel/Errors.html
def pin_not_on_doctors
errors.add :pin, "already exists" if Doctor.exists?(:pin => self.pin)
end
In addition to adding the error, try adding a line to return true/false,
Something like,
def pin_not_on_doctors
errors.add :pin, "already exits" if Doctor.exists?(:pin => self.pin)
Doctor.exists?(:pin => self.pin)
end
I don't know the details of your app, but in depending on how you're actually creating the object in this case, it might need that.
EDIT: Misread something in the original, looks like your current version is to just return true/false, so this probably doesn't help. Sorry.

Is there way to validate emails on signup based on the domain in Rails?

Example:
Accepted extensions: "#blogsllc.org"
A user signs up with the email "joe#blogsllc.org" would be able to create an account.
Wondering what would be the best way to do this in Rails and how others would approach this? I imagined trying to check the format of the email address against a bunch of regular expressions but this could be tedious as the list of supported extensions grow.
The other way to do this would be to have a database of the supported extensions and check the created email address against the database to see if the extension is accepted but I'm not sure what would be the best way to implement this in Rails.
I'm looking to implement something similar to what Facebook did in it's early days.
Any help appreciated.
EDIT for misunderstanding:
If you don't need anything more fancy than a straight-up match of the domain (files have extensions, emails have domains), just splitting on # and matching the second part with a database column is the easiest way.
You can add the following code
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless value =~ /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
record.errors[attribute] << (options[:message] || "is not an email")
end
end
end
class Person < ActiveRecord::Base
validates :email, :presence => true, :email => true
end
This Link will be useful

Adding custom fields to a class at runtime, in Ruby with mongoid

In a project came across a requirement wherein a logged in user should be asked specific data based on his company. This specific data would be company specific, and could be mandatory or unique. This is the approach I took.
1. Defined a model to with three fields: Label (string), Mandatory(boolean), Unique(boolean).
2. The admin of the company could then enter the required fields for. e.g: Label => "Employee number", Mandatory => true, Unique => false using a simple form.
3. This data should be asked at the time of creating another record of model Redeemed Coupon for logged in user.
4. So during initialize of the Redeemed Coupon model, reopening the class, and checking the logged in user's company.
class RedeemedCoupon
def initialize(attrs = nil, options = nil)
super
if Merchant.current #this is set in the application controller as thread specific variable
coupon_custom_field = CouponCustomField.where(:merchant_id => Merchant.current).first
if coupon_custom_field and coupon_custom_field.custom_fields.size > 0
coupon_custom_field.custom_fields.each do |custom_field|
class_eval do
field custom_field.label.to_sym, :type => String
attr_accessible custom_field.label.to_sym
end
if custom_field.unique
class_eval do
index custom_field.label.to_sym
#validates_uniqueness_of custom_field.label.to_sym, :case_sensitive => false
end
end
if custom_field.mandatory
class_eval do
#validates_presence_of custom_field.label.to_sym
end
end
end
end
end
end
However the validations validates presence of and uniqueness does not work, with a failure message being given : callback not defined. this is thrown before save, when is_valid? is called object.
TO work around that
Put in custom validation
validate :custom_mandatory_unique
def custom_mandatory_unique
if Merchant.current
coupon_custom_field = CouponCustomField.where(:ira_merchant_id => Merchant.current).first
if coupon_custom_field and coupon_custom_field.custom_fields.size > 0
coupon_custom_field.custom_fields.each do |custom_field|
field_value = self.send(custom_field.label.to_sym)
self.errors.add(custom_field.label.to_sym, "cannot be blank") if !field_value.present? and custom_field.mandatory
if field_value.present? and custom_field.unique
if RedeemedCoupon.where(custom_field.label.to_sym => field_value, :merchant_id => Merchant.current).size > 0
self.errors.add(custom_field.label.to_sym, "already taken")
end
end
end
end
end
end
My questions:
1. Is this the best way of doing it.
2. Are there any gems already present (have searched, however couldnt get any)?
3. How can i use the validation helpers here instead of defining a seperate validate block?
I would define a model that stores the set of attribute mappings that correspond to a company, and an attribute model that holds its values and is associated with your coupon model. Then create a custom validation method in coupon that makes sure all of the require attributes are present based on the company id, and a method that builds them per the company association.

A few questions about a Rails model for a simple addressbook app

I have a Rails application that lists information about local services. My objectives for this model are as follows: 1. Require the name and tag_list fields. 2. Require one or more of the tollfreephone, phone, phone2, mobile, fax, email or website fields. 3. If the paddress field has a value, then encode it with the Geokit plugin. Here is my entry.rb model:
class Entry < ActiveRecord::Base
validates_presence_of :name, :tag_list
validates_presence_of :tollfreephone or :phone or :phone2 or :mobile or :fax or :email or :website
acts_as_taggable_on :tags
acts_as_mappable :auto_geocode=>{:field=>:paddress, :error_message=>'Could not geocode physical address'}
before_save :geocode_paddress
validate :required_info
def required_info
unless phone or phone2 or tollfreephone or mobile or fax or email or website
errors.add_to_base "Please have at least one form of contact information."
end
end
private
def geocode_paddress
#if paddress_changed?
geo=Geokit::Geocoders::MultiGeocoder.geocode (paddress)
errors.add(:paddress, "Could not Geocode address") if !
geo.success
self.lat, self.lng = geo.lat,geo.lng if geo.success
#end
end
end
Requiring name and tag_list work, but requiring one (or more) of the tollfreephone, phone, phone2, mobile, fax, email or website fields does not.
As for encoding with Geokit, in order to save a record with the model I have to enter an address. Which is not the behavior I want. I would like it to not require the paddress field, but if the paddress field does have a value, it should encode the geocode. As it stands, it always tries to geocode the incoming entry. The commented out "if paddress_changed?" was not working and I could not find something like "if paddress_exists?" that would work.
Help with any of these issues would be greatly appreciated. I posted three questions pertaining to my model because I'm not sure if they are preventing each other from working. Thank you for reading my questions.
I see following problems in your code:
1) Duplicate presence checks
2) Auto and manual geo coding at the same time.
Here is a version of your code that might work:
class Entry < ActiveRecord::Base
acts_as_mappable
acts_as_taggable_on :tags
validates_presence_of :name, :tag_list
validate :required_info
before_save :geocode_paddress
private
def required_info
if( phone.empty? and phone2.empty? and tollfreephone.empty? and
mobile.empty? and fax.empty? and email.empty? and
website.empty?
)
errors.add_to_base "Please have at least one form of contact information."
end
end
def geocode_paddress
# if paddress is nil or empty set the old values to nil and return
((self.lat = self.lng = nil); return true) if paddress.empty?
g=Geokit::Geocoders::MultiGeocoder.geocode(paddress)
(errors.add(:paddress,"Could not Geocode address");
return false) unless g.success
self.lat, self.lng = g.lat, g.lng
end
end
Edit
The required_info validation fails as the input data submitted by the form contains empty strings for missing fields rather than null values. Hence the phone or phone2 etc. check always returned true. I have changed the validation code to address this edge case. I am quite sure it will work now.
PS:
This is a typical scenario where you should be using a debugger. Download and play with any free IDE like Aptana Radrails OR Netbeans. Once you are familiar with the tool you will be able to easily debug such issues.
I don't have ruby on here, but I'm pretty sure you can't do that with validates_presence_of.
You'll need to do something like:
validates_presence_of :tollfreephone, :unless => some_method_to_tell_if_other_methods_are_there
As for the geocoder method, maybe just check if it's nil? Something like:
def geocode_paddress
if paddress != nil
geo = Geokit::Geocoders::MultiGeocoder.geocode (paddress)
if geo.success
self.lat, self.lng = geo.lat,geo.lng
else
errors.add(:paddress, "Could not Geocode address")
end
end
end

Resources