rails 3 validation of a string - ruby-on-rails

hiho
Is there any way to tell rails that my string may not be 'something'?
I am searching for something like
validates :string, :not => 'something'
thanks
klump

Either of these will do the job (click on the methods for documentation):
Probably the best and fastest way, easy to extend for other words:
validates_exclusion_of :string, :in => %w[something]
This has a benefit of using a regexp, so you can generalise easier:
validates_format_of :string, :without => /\A(something)\Z/
You can extend to other words with /\A(something|somethingelse|somemore)\Z/
This is the general case with which you can achieve any validation:
validate :cant_be_something
def cant_be_something
errors.add(:string, "can't be something") if self.string == "something"
end
To get exactly the syntax you proposed (validates :string, :not => "something") you can use this code (a warning though, I discovered this while reading the master branch of the rails source and it should work, but it doesn't work on my ~ 3 months old install). Add this somewhere in your path:
class NotValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors[attribute] << "must not be #{options{:with}}" if value == options[:with]
end
end

A couple of ways. If you have exact list of what it can't be:
validates_exclusion_of :string, :in => ["something", "something else"]
If you want to ensure that it doesn't exist as a substring at all:
validates_format_of :string, :with => /\A(?!something)\Z/
If it is more complicated and you want to hide the messy details:
validate :not_something
def not_something
errors.add(:string, "Can't be something") if string =~ /something/
end

Related

Rails : Validates_format_of for float not working

I am new at Ruby on Rails.
I was trying to validate format of one of the attribute to enter only float.
validates :price, :format => { :with => /^[0-9]{1,5}((\.[0-9]{1,5})?)$/, :message => "should be float" }
but when I enter only character in price, it accepts it and show 0.0 value for price.
can anybody tell, what is wrong in this or why this happens?
This is my solution,
validates :price,presence:true, numericality: {only_float: true}
when you fill in for example 7 it automatically transfer the value to 7.0
For rails 3:
validates :price, :format => { :with => /^\d+??(?:\.\d{0,2})?$/ },
:numericality =>{:greater_than => 0}
A float is a number and regular expressions are for strings.
It appears that when you enter a string for the float, it gets converted as 0.0 automatically by Rails.
Do you have a default (0.0) on the column? If yes, then you may try removing it and use validates_presence_of :price only.
Something to try: instead of putting the string directly into the price column, put it into a price_string attr and use a before_save callback to try to convert the string to price. Something like that:
attr_accessor :price_string
before_save :convert_price_string
protected
def convert_price_string
if price_string
begin
self.price = Kernel.Float(price_string)
rescue ArgumentError, TypeError
errors.add(ActiveRecord::Errors.default_error_messages[:not_a_number])
end
end
And in your form, change the name of the text_field to :price_string.

Rails: How to set different ":message" for each one of the possible errors?

I use this validation:
validates_numericality_of :price, :greater_than_or_equal_to => 0, :less_than => 1000000
How could I set a different :message for each one of the following cases ?
price < 0
price >= 1000000
Assuming you're using Rails 3, another option you have is to create a custom validator:
# You can put this in lib/better_numericality_validator.rb
class BetterNumericalityValidator < ActiveModel::EachValidator
def validate_each(record,attribute,value)
if value < 0
record.errors[attribute] << "must be greater than or equal to 0"
elsif value >= 1000000
record.errors[attribute] << "must be less than 1000000")
end
end
end
Then you can use your custom validator in your model:
# In your model.rb
validates :price, :better_numericality => true
This method is very similar to Anubhaw's answer. But pulling the logic out into the a custom validator makes it so that you can reuse the validation elsewhere easily, you can easily unit test the validator in isolation, and I personally think that validates :price, :better_numericality => true leaves your model looking cleaner than the alternative.
You can use following in model.rb:-
def validate
if self.price < 0
errors.add(:price, "custom message")
elsif self.price > 1000000
errors.add(:price, "custom message")
end
end
Thanks....
How about:
validates_numericality_of :price, :greater_than_or_equal_to => 0, :message => "Foo"
validates_numericality_of :price, :less_than => 1000000, :message => "Bar"
I've not tested it, but it should work?
Alternatively, Anubhaw's question is a good fallback.
At some point, you should probably ask yourself whether it isn't time to apply some convention over configuration.
In my opinion, an error message such as "Please enter a valid price greater than 0 and less than 1 million" (or similar) is a perfectly valid solution to the problem. It prevents you from adding unnecessary complexity to your application and allows you to move on to other (presumably more important) features.

What is the best/easy way to validate an email address in Ruby?

What is the best/easy way to validate an email address in ruby (on the server side)?
You could look whether or not it matches a regexp like the one used in this Rails validator:
validates_format_of :email,:with => /\A[^#\s]+#([^#\s]+\.)+[^#\s]+\z/
But if you use Devise, simply do:
validates_format_of :email,:with => Devise::email_regexp
Source: http://lindsaar.net/2008/4/14/tip-4-detecting-a-valid-email-address
Edit 1:
useful website for tests: http://www.rubular.com/
In Ruby? The same way as in any language.
Send a confirmation email to the address with a link that the recipient has to click before the email address is considered fully validated.
There are any number of reasons why a perfectly formatted address may still be invalid (no actual user at that address, blocked by spam filters, and so on). The only way to know for sure is a successfully completed end-to-end transaction of some description.
validates :email, presence: true, format: /\w+#\w+\.{1}[a-zA-Z]{2,}/
checks that email field is not blank and that one or more characters are both preceding the '#' and following it
Added specificity, any 1 or more word characters before an the #and any 1 or more word character after and in between specifically 1 . and at least 2 letters after
I know that this is a old question but I was looking for a simple to way to do this. I came across a email_validator gem this is really simple to set up and use.
as a validator
validates :my_email_attribute, :email => true
Validation outside a model
EmailValidator.valid?('narf#example.com') # boolean
I hope that this help everyone.
Happy Codding
Shortcut Form:
validates :email, :format => /#/
Normal Form (Regex) :
validates :email, :format => { :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/ }
Source: Validator Class
You can use
<%=email_field_tag 'to[]','' ,:placeholder=>"Type an email address",:pattern=>"^([\w+-.%]+#[\w-.]+\.[A-Za-z]{2,4},*[\W]*)+$",:multiple => true%>
Since the main answer's blog site was down, here is the snippet of code from that site via nice cacher or gist:
# http://my.rails-royce.org/2010/07/21/email-validation-in-ruby-on-rails-without-regexp/
class EmailValidator < ActiveModel::EachValidator
# Domain must be present and have two or more parts.
def validate_each(record, attribute, value)
address = Mail::Address.new value
record.errors[attribute] << (options[:message] || 'is invalid') unless (address.address == value && address.domain && address.__send__(:tree).domain.dot_atom_text.elements.size > 1 rescue false)
end
end
You can take reference from https://apidock.com/rails/ActiveModel/Validations/HelperMethods/validates_format_of
validates_format_of :email, with: /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
If you are using Rails/Devise - addition to #apneadiving`s answer -
validates_format_of :email,:with => Devise::email_regexp
Devise::email_regexp is taken from config/initializers/devise.rb
config.email_regexp = /\A[^#\s]+#([^#\s]+\.)+[^#\s]+\z/
Send a confirmation mail , and I will usualy use this validator ... D.R.Y.
# lib/email_validator.rb
class EmailValidator < ActiveModel::EachValidator
EmailAddress = begin
qtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]'
dtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]'
atom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-' +
'\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+'
quoted_pair = '\\x5c[\\x00-\\x7f]'
domain_literal = "\\x5b(?:#{dtext}|#{quoted_pair})*\\x5d"
quoted_string = "\\x22(?:#{qtext}|#{quoted_pair})*\\x22"
domain_ref = atom
sub_domain = "(?:#{domain_ref}|#{domain_literal})"
word = "(?:#{atom}|#{quoted_string})"
domain = "#{sub_domain}(?:\\x2e#{sub_domain})*"
local_part = "#{word}(?:\\x2e#{word})*"
addr_spec = "#{local_part}\\x40#{domain}"
pattern = /\A#{addr_spec}\z/
end
def validate_each(record, attribute, value)
unless value =~ EmailAddress
record.errors[attribute] << (options[:message] || "is not valid")
end
end
end
in your model
validates :email , :email => true
or
validates :email, :presence => true,
:length => {:minimum => 3, :maximum => 254},
:uniqueness => true,
:email => true

Virtual Attributes & Rails

I have a Phone model for phone numbers in my application thats built as follows:
t.column :number, :string
t.references :phoneable, :polymorphic => true
I want to restrict the number to be of the format 317.555.5555x234, so I'm creating a form with four boxes (area code, 3 digits, 4 digits, ext):
- form_for #user do |user_form|
-user_form.fields_for :phones do |phone|
= phone.text_field :area_code
= phone.text_field :first_three_digits
etc...
I'm assuming a virtual attribute would be the route to go (a la railscasts ep16), but not sure how to assemble the "number" from the 4 separate text_fields.
I think I would have to do something like this:
def full_number=(phone)
self.number = area_code+"."+first_three_digits+"."+second_four_digits+"."+extension
end
But I'm unsure of how to approach this in assembling the number from form inputs. Any thoughts?
I normally do this as a before_save:
before_save :update_phone_number
def update_phone_number
self.phone_number = [area_code, first_three_digits, second_four_digits, extension].reject(&:blank?).join('.')
end
First I would have some validations:
validates_presence_of :area_code, :first_three_digits, :second_four_digits
validates_format_of :area_code, :with => /\d{3}/
validates_format_of :first_three_digits, :with => /\d{3}/
validates_format_of :second_four_digits, :with => /\d{4}/
validates_format_of :extension, :with => /\d{0,6}/, :allow_blank => true
This is just to make sure that you get valid data in your phone number and your before save doesn't throw any errors. I also assumed that you would allow the extension to be blank, but is easily changed.
EDIT: you will want to have attr_accessors for the different segments of the phone number:
attr_accessor :area_code, :first_three_digits, :second_four_digits, :extension

How do you validate the presence of one field from many

I'm answering my own questions - just putting this up here for google-fu in case it helps someone else. This code allows you to validate the presence of one field in a list. See comments in code for usage. Just paste this into lib/custom_validations.rb and add require 'custom_validations' to your environment.rb
#good post on how to do stuff like this http://www.marklunds.com/articles/one/312
module ActiveRecord
module Validations
module ClassMethods
# Use to check for this, that or those was entered... example:
# :validates_presence_of_at_least_one_field :last_name, :company_name - would require either last_name or company_name to be filled in
# also works with arrays
# :validates_presence_of_at_least_one_field :email, [:name, :address, :city, :state] - would require email or a mailing type address
def validates_presence_of_at_least_one_field(*attr_names)
msg = attr_names.collect {|a| a.is_a?(Array) ? " ( #{a.join(", ")} ) " : a.to_s}.join(", ") +
"can't all be blank. At least one field (set) must be filled in."
configuration = {
:on => :save,
:message => msg }
configuration.update(attr_names.extract_options!)
send(validation_method(configuration[:on]), configuration) do |record|
found = false
attr_names.each do |a|
a = [a] unless a.is_a?(Array)
found = true
a.each do |attr|
value = record.respond_to?(attr.to_s) ? record.send(attr.to_s) : record[attr.to_s]
found = !value.blank?
end
break if found
end
record.errors.add_to_base(configuration[:message]) unless found
end
end
end
end
end
This works for me in Rails 3, although I'm only validating whether one or the other field is present:
validates :last_name, :presence => {unless => Proc.new { |a| a.company_name.present? }, :message => "You must enter a last name, company name, or both"}
That will only validate presence of last_name if company name is blank. You only need the one because both will be blank in the error condition, so to have a validator on company_name as well is redundant. The only annoying thing is that it spits out the column name before the message, and I used the answer from this question regarding Humanized Attributes to get around it (just setting the last_name humanized attribute to ""

Resources