Rails 3, Unknown validator: 'EmailValidator' - ruby-on-rails

I try to add an email-validator in my rails app. I created the following file /lib/validators/email_validator.rb
class 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
In the application.rb I added this line:
config.autoload_paths << "#{config.root}/lib/validators"
And here is my User model:
class User < ActiveRecord::Base
attr_accessible :email, :password,:name
validates :email, :presence => true, :uniqueness => true, :email => true
end
If i want to start the server I got an error:
Unknown validator: 'EmailValidator' (ArgumentError)
Has anybody an idea how I can fix this problem?

If you place your custom validators in app/validators they will be
automatically loaded without needing to alter your
config/application.rb file.
Resource: Where should Rails 3 custom validators be stored? (second answer)

This error occures, because rails loads model file before your validation file
Try to require your validation file manually at the start of your model file
require_dependency 'validators/email_validator.rb'

Try the modified User model;
class User < ActiveRecord::Base
attr_accessible :email, :password,:name
validates :email, :presence => true, :uniqueness => true
end

Related

How to use default and custom validators together? How to define their order?

This is my model class:
class Availability < ActiveRecord::Base
attr_accessible :beginning_date, :end_date
validates :beginning_date, :end_date :presence => true
# custom validators
validate :dates_cant_be_in_the_past
def dates_cant_be_in_the_past
if Date.parse(beginning_date) < Date.today
errors.add(:beginning_date, "cant be in the past")
end
if Date.parse(end_date) < Date.today
errors.add(:end_date, "cant be in the past")
end
end
end
Now two things should happen: At first validate the presence of the beginning_date and end_date attributes and than run my dates_cant_be_in_the_pastvalidator.
Sadly this approach doesn't work. If I leave a field empty the Date.parsemethod throws an exception, because the argument is obviously empty.
Is it possible to define the order of default and custom validations? Or do I have to implement the presence validator myself, so I would do something like:
validate :dates_cant_be_blank, :dates_cant_be_in_the_past
The guide at least says:
You can pass more than one symbol for each class method and the respective validations will be run in the same order as they were registered.
Thank in advance
It's much simpler if you create a validator for that:
class DateValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if Date.parse(value) < Date.today
record.errors.add(attribute, "cant be in the past")
end
end
end
And at your model you would use it like this:
class Availability < ActiveRecord::Base
attr_accessible :beginning_date, :end_date
validates :beginning_date, :end_date :presence => true
validates :beginning_date, :end_date, :date => true, :allow_blank => true
end
The :allow_blank piece if the one prevents the validation from running if the value is empty. Using a real validator object also removes the code form your model making it much simpler and removing the duplication you currently have.
You could try something like this
class Availability < ActiveRecord::Base
attr_accessible :beginning_date, :end_date
validates :beginning_date, :end_date : presence => true
# custom validators
validate :valid_dates
def valid_dates
if valid_string(beginning_date)
errors.add(:beginning_date, "Can't be in the past") unless Date.parse(beginning_date) > Date.today
end
if valid_string(end_date)
errors.add(:end_date, "Can't be in the past") unless Date.parse(end_date) > Date.today
end
end
def valid_string(test_value)
test.value.is_a? String
end
end

Client side validations undefined method `validates_email'

I copied the code from client_side_validations page. And i had error
undefined method validates_email for #<Class:0x007ff3382428a8>
When i put includes ::Validations in my model this error disappeares but validation doesn't work at all.
app/validators/email_validator.rb
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attr_name, value)
unless value =~ /^([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
record.errors.add(attr_name, :email, options.merge(:value => value))
end
end
end
# This allows us to assign the validator in the model
module ActiveModel::Validations::HelperMethods
def validates_email(*attr_names)
validates_with EmailValidator, _merge_attributes(attr_names)
end
end
config/locales/en.yml
# config/locales/en.yml
en:
errors:
messages:
email: "Not an email address"
app/assets/javascripts/rails.validations.customValidators.js
// The validator variable is a JSON Object
// The selector variable is a jQuery Object
window.ClientSideValidations.validators.local['email'] = function(element, options) {
// Your validator code goes in here
if (!/^([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})$/i.test(element.val())) {
// When the value fails to pass validation you need to return the error message.
// It can be derived from validator.message
return options.message;
}
}
models/comment.rb
class Comment < ActiveRecord::Base
attr_accessible :content, :email, :ip, :name, :post_id
belongs_to :post
validates_presence_of :content, :email, :post_id, :name
validates_email :email
end
I don't see you including your module. Also, depending on where you put your module, you will sometimes need to "require" it. And also, try:
include <Module name>
instead of
includes <Module name>
hope this will help

FriendlyId triggers BEFORE model validations. How do I get around this?

Ideally i want urls that look like:
/users/john-s
/users/foo-b
/users/brad-p
I have a user model that looks like this:
class User < ActiveRecord::Base
extend FriendlyId
friendly_id :name, :use => :slugged
validates :first_name, :presence => true
validates :last_name, :presence => true
# "John Smith" becomes "John S."
def name
"#{self.first_name.capitalize} #{self.last_name[0].capitalize}."
end
end
The bad behavior is best explained with this console output:
[15] pry(main)> User.new(first_name: nil, last_name: nil).save!
(0.2ms) BEGIN
(0.1ms) ROLLBACK
NoMethodError: undefined method `capitalize' for nil:NilClass
The Issue (finally! :) )
It appears what happens is that FriendlyId is called BEFORE my validations for first_name and last_name are triggered. This results in the name method pooping when capitalize is called on a nil value.
What can I do so that my validations are triggered before FriendlyId is called? And actually taking it a bit further... Why is FriendlyId involved at all prior to any validity being established?
Thank you!!
It is invoked because the slug is generated prior to validation on save:
https://github.com/FriendlyId/friendly_id/issues/280
I am not quite sure what it would take to monkeypatch it.
The way I wound up fixing mine was like this:
def name
"#{self.first_name.capitalize} #{self.last_name[0].capitalize}." if first_name.present? && last_name[0].present?
end
I think the way to go is to set the user name in a before_validation on create that has is prepended to the friendly_id's own before_validation callback of setting the slug:
class User < ActiveRecord::Base
extend FriendlyId
friendly_id :name, :use => :slugged
# Make sure to prepend it so that it runs before Friendly_id's own callback
# http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
before_validation :set_name, on: :create, prepend: true
validates :first_name, :presence => true
validates :last_name, :presence => true
# To control when new slugs should be generated
def should_generate_new_friendly_id?
new_record? || first_name_changed? || last_name_changed?
end
private
def set_name
"#{self.first_name.capitalize} #{self.last_name[0].capitalize}."
end
end
Hope this helps!

Rails: sending emails, getting undefined method `model_name' for Mail::Message:Class

My Rails skills is (to be kind) rusty so this is probably a newbie question. I'm trying to create a simple email sending form, but I keep getting:
NoMethodError in Mail#create
undefined method `model_name' for Mail::Message:Class
I'm pretty sure that my problem is in my controller, the relevant method looks like this:
def create
#mail = Mail.new(params[:mail])
MailMailer.send_mail(#mail).deliver
end
Thinks this line is causing the error #mail = Mail.new(params[:mail]). My Mail model class looks like this:
class Mail < ActiveRecord::Base
include ActiveModel::Validations
validates :password, :presence => true
attr_accessor :password, :to, :cc, :bcc, :from, :subject, :message
end
And my mailer looks like this:
class MailMailer < ActionMailer::Base
def send_mail(mail)
mail(:to => mail.to, :subject => mail.subject)
end
end
Can anyone spot what I'm doing wrong?
Your problem is probably right here:
class Mail < ActiveRecord::Base
# ---------^^^^^^^^^^^^^^^^^^
include ActiveModel::Validations
validates :password, :presence => true
attr_accessor :password, :to, :cc, :bcc, :from, :subject, :message
end
Subclassing ActiveRecord::Base and including ActiveModel::Validations is a bit odd as AR already includes all the validation stuff. Mixing AR and attr_accessor is another sign that something strange is going on.
In your comments you note that you created this model with:
$ rails g model mail
And that tries to create a database-backed ActiveRecord model as that's almost always what people want. You might also run into trouble because Mail is already in use so maybe you want to use a different name.
If you just want a model that is just a temporary bag of data then you can do this:
class SomeEmail
attr_accessor :password, :to, :cc, :bcc, :from, :subject, :message
end
You probably don't need the validations here but you can add them:
class SomeEmail
include ActiveModel::Validations
validates :password, :presence => true
attr_accessor :password, :to, :cc, :bcc, :from, :subject, :message
end
but the validations won't be triggered unless you manually call valid? so there's no much point.
Finally, just adding attr_accessor doesn't give you a useful constructor so with all of the above changes, this:
#mail = SomeMail.new(params[:mail])
still won't do what you want as nothing in params[:mail] will get saved anywhere. So add an initialize implementation to your email class and a call to valid? to your controller.

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