Custom validation rails - ruby-on-rails

I have EmailValidator class inside module like:
module ActiveModel
module Validations
class EmailValidator < EachValidator
def validate_each(record, attribute, value)
if value.presence && (value =~ /\A[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]+\z/).nil?
record.errors[attribute] << (options[:message] || "is invalid")
end
rescue => e
record.errors[attribute] << (options[:message] || "is invalid")
end
end
end
end
I am trying to use this inside my model but facing load error when I try to start rails server => email_validator.rb to define EmailValidator (LoadError)
Can anyone help me with this?

I recommend using the valid_email gem instead, because validating email adresses is a real pain!
class User < ActiveRecord::Base
validates :email, :presence => true, :email => true
# ...
end
Also see this humongous regular expression which actually validates email addresses according to RFC 822.

validates_format_of :email, :with =>~ /\A[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+.[A-Za-z]+\z/

You can try this kind of syntax and implement your code according this blog that should be very helpful:-
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if value.presence && (value =~ /\A[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]+\z/).nil?
record.errors[attribute] << (options[:message] || "is invalid")
end
rescue => e
record.errors[attribute] << (options[:message] || "is invalid")
end
end
just put this file in app/validators/email_validator.rb
for rails 3 custom validation please read this blog.
In Rails 4
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 foo < ActiveRecord::Base
validates :email, presence: true, email: true
end
At last very simple and dry use reg. exp.:-
/\A[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]+\z/
That was adapted from http://www.regular-expressions.info/email.html

Related

confusion over validates_with custom validations syntax shown in rails guide

I am attempting to use the validates_with custom validations helper with Rails 4.
The following code is working in my application:
class Photo
validates_with CleanValidator
include ActiveModel::Validations
end
class CleanValidator < ActiveModel::Validator
def validate(record)
if record.title.include? "foo"
record.errors[:title] << "Photo failed! restricted word"
end
end
end
However I want to pass this helper to multiple attributes in multiple models, not just :title.
There is an example in validates_with section of guide that contains the following example:
class GoodnessValidator < ActiveModel::Validator
def validate(record)
if options[:fields].any?{|field| record.send(field) == "Evil" }
record.errors[:base] << "This person is evil"
end
end
end
class Person < ApplicationRecord
validates_with GoodnessValidator, fields: [:first_name, :last_name]
end
This is what I want to achieve, substituting [:fields] for [:title] in my code example so that I can use CleanValidator for multiple models and multiple attributes (User.name, Photo.title etc).
I think you want the other example from the guides, each validator. You should be able to do
class CleanValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless ["Evil", "Other", "Restricted", "Words"].include?(value)
record.errors[attribute] << (options[:message] || "is a restricted word")
end
end
end
class Photo
include ActiveModel::Validations
attr_accessor :title
validates :title, clean: true
end

Rails plug and play validations

Here is my example
class User < ActiveRecord::Base
validates_with EmailValidator
end
class EmailValidator < ActiveModel::Validator
def validate(record)
if record != someregex
record.errors.add(:email, 'invalid email')
end
end
end
Now I can use this EmailValidator for any model. But my requirement is to validate its uniqueness and presence also for that particular model.
If I can achieve this I can use this EmailValidator for any model email validation with functionalities unique, presence.
Then I can achieve more reusable Validator.
You can do this by rewriting your validator to be EachValidator:
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if attribute !~ someregex
record.errors.add attribute, (options[:message] || 'invalid email')
end
end
end
Then in your model:
validates :email, email: true, uniqueness: true, presence: true
Hope that helps.

ActiveModel custom EachValidator not found

My EachValidator cannot work on Rails 4.1.5.
My Product model:
class Product < ActiveRecord::Base
has_many :tags
validates :tags, tags_size: {minimum: 1, maximum: 10}
end
My Validator, I put it in app/validators/tags_size_validator.rb
class TagsSizeValidator < ActiveModel::Validator
def validate_each(record, attribute, value)
if value.size < options[:maximum]
record.errors[attribute] << (options[:message] || "must have at most #{options[:maximum]} tags.")
end
if value.size > options[:minimum]
record.errors[attribute] << (options[:message] || "must have at lease #{options[:minimum]} tags.")
end
end
end
end
I have made it autoload in application.rb
config.autoload_paths += %W["#{Rails.root}/app/validators/"]
When I put the validator at the same file as the Product model, it worked perfectly. But in separated file it failed. Are there any steps I have missed? Please advise. Thanks.
Try inheriting from ActiveModel::EachValidator rather than ActiveModel::Validator:
class TagsSizeValidator < ActiveModel::EachValidator
...
Also you shouldn't have to add it to your autoload_paths in Rails 4.1.

Rails 3: Uniqueness validation for nested fields_for - Part2

I am new to coding - and have not enough reputation to comment this answer:
Rails 3: Uniqueness validation for nested fields_for
So I am creating this question as "Part 2" :)
I am a web designer but curious to learn coding, held with this from my days.
# app/validators/nested_attributes_uniqueness_validator.rb
class NestedAttributesUniquenessValidator < ActiveModel::EachValidator
record.errors[attribute] << "Products names must be unique" unless value.map(&:name).uniq.size == value.size
end
end
above code with "ActiveModel::EachValidator" throw this error:
"undefined method `map' for "Area 1":String"
# app/validators/nested_attributes_uniqueness_validator.rb
class NestedAttributesUniquenessValidator < ActiveModel::Validator
record.errors[attribute] << "Products names must be unique" unless value.map(&:name).uniq.size == value.size
end
end
above code with "ActiveModel::Validator" throw this error:
"Subclasses must implement a validate(record) method. "
this is model file:
class Area < ActiveRecord::Base
validates :name,
:presence => true,
:uniqueness => {:scope => :city_id},
:nested_attributes_uniqueness => {:field => :name}
belongs_to :city
end
You can find complete code over here:
https://github.com/syed-haroon/rose
#Syed: I think you are trying to do this. else reply to my comment.
# app/models/city.rb
class City < ActiveRecord::Base
has_many :areas
validates :areas, :area_name_uniqueness => true
end
# app/models/area.rb
class Area < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name
end
# config/initializers/area_name_uniqueness_validator.rb
class AreaNameUniquenessValidator < ActiveModel::Validator
def validate_each(record, attribute, value)
record.errors[attribute] << "Area names must be unique" unless value.map(&:name).uniq.size == value.size
end
end
I found the answer over here :
https://rails.lighthouseapp.com/projects/8994/tickets/2160-nested_attributes-validates_uniqueness_of-fails
&
validates_uniqueness_of in destroyed nested model rails
This is for rails 2, one line need to me modified over here:
add_to_base has been deprecated and is unavailable in 3.1. Use self.errors.add(:base, message)

rails3 doesn't load my validators in lib

I put EmailValidator in lib/validators/email_validator and it's not workings (I put root/lib in the load_path)
here is the code.. I put the class in module validators as the parent folder name
class Validators::EmailValidator < ActiveModel::EachValidator
def validate_each(object, attribute, value)
unless value =~ /^([^#\s]+)#([a-z0-9]+\.)+[a-z]{2,}$/i
object.errors[attribute] << (options[:message] || "is not formatted properly")
end
end
end
I get the error Unknown validator: 'email'
You have two options:
Either put your custom validator under config/initializers.
Or add lib/validators to the autoload path in config/application.rb.
config.autoload_paths << "#{config.root}/lib/validators"
Personally I would go with the second option as lib/validators makes for good encapsulation.
Since you put your custom validator in the Validators:: in the lib/validators, you have to reference it with that namespace also.
validates :email, presence: true, :'validators/email' => true
UPDATE: You need this:
module Validators
class EmailValidator < ActiveModel::EachValidator
def validate(object, attribute, value)
unless value =~ /^([^#\s]+)#([a-z0-9]+\.)+[a-z]{2,}$/i
object.errors[attribute] << (options[:message] || "is not formatted properly")
end
end
end
end
class YourModel < ActiveRecord::Base
include Validators
validates :email, :presence => true, :email => true
end
Otherwise, you need to put your validator class under the ActiveModel::Validations namespace. When you namespace a class, ActiveRecord isn't going to see it, if that namespace isn't a namespace it has already included.

Resources