I have two customs validations :
def validate_email
regexp = "[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]+"
if sleep_email.present? && !sleep_email.match(regexp)
errors.add(:sleep_email, "l'email indiqué semble ne pas avoir le bon format")
end
end
def validate_website
regexp = "(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?"
if website.present? && !website.match(regexp)
errors.add(:website, "l'url de votre site web doit avoir la forme de http://votresite.com")
end
end
But yo#yo and http://website are valids. What's wrong ?
You're building regexes using strings. Strings and regexes have different quoting. You're effectively double escaping. Things like \. are turned into a plain ..
# This results in the regex /a.c/
p "abc".match?("a\.c") # true
# This results in the desired regex /a\.c/
p "abc".match?("a\\.c") # true
# This avoids the string escaping entirely.
p "abc".match?(%r{a\.c}) # false
To avoid this double escaping, use /.../ or %r{...} to create regexes.
Don't try to validate email with a regex. Instead, use the validates_email_format_of gem which provides a proper validator you can use on any attribute.
validates :sleep_email, presence: true, email_format: true
If you want to see how to fully validate an email address, look at the source.
Your URL regex does work.
regexp = "(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?"
p "http://website".match?(regexp) # true
http://website is valid URL syntax. It's not URL's job to check the validity of the host.
If you also want to validate parts of the URL your regex will get increasingly complex. Instead, parse the URL with URI and then check its individual pieces as you like.
Here's a custom validator I whipped up which parse the URI, checks it's an allowed scheme, and does a very rudimentary check on the host.
class UrlValidator < ActiveModel::EachValidator
ALLOWED_SCHEMES = ['http', 'https']
private def allowed_schemes
options[:allowed_schemes] || ALLOWED_SCHEMES
end
def validates_each(record, attribute, value)
uri = URI(value)
if !allowed_schemes.include?(uri.scheme)
record.errors.add(attribute, :scheme_not_allowed, message: "Scheme #{uri.scheme} is not allowed")
end
# Has to have at least xxx.yyy
# This is a pretty sloppy host check.
if !uri.host.match?(/\w+\.\w+/)
record.errors.add(attribute, :host_not_allowed, message: "Host #{uri.host} is not allowed")
end
rescue URI::Error
record.errors.add(attribute, :not_a_uri)
end
end
validates :website, url: true
If you wanted to allow other schemes, like ftp...
validates :website, url: { allowed_schemes: ['http', 'https', 'ftp'] }
If you wanted true domain validation, you could add a DNS lookup.
begin
Resolv::DNS.open do |dns|
dns.getaddress(uri.host) }
end
rescue Resolv::ResolvError
record.errors.add(attribute, :invalid_host, { message: "#{uri.host} could not be resolved" }
end
However, this lookup has a performance impact.
The standard email regex (RFC 5322 Official Standard) to use is:
(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")#(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])
As for the website URL, use this one. The URL will only be valid if the TLD (.com, .net, etc.) is included.
^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$
Related
I have a field in a form, and I need it to be registered only if its format equals one of the two regex I am using.
I have tried to use methods, the helper structure validates_format_of with Proc.new, but may not be using the correct syntax or the correct data type
private def format_field
errors.add(
:field, "The field does not have a valid format."
)unless[
/[0-9]{3}\.?[0-9]{3}\.?[0-9]{3}\-?[0-9]{2}/,
/[0-9]{2}\.?[0-9]{3}\.?[0-9]{3}\/?[0-9]{4}\-?[0-9]{2}/
].include?(field.format)
end
A simple approach to this is the following:
FORMAT_REGEXES = [
/[0-9]{3}\.?[0-9]{3}\.?[0-9]{3}\-?[0-9]{2}/,
/[0-9]{2}\.?[0-9]{3}\.?[0-9]{3}\/?[0-9]{4}\-?[0-9]{2}/
].freeze
def validate_format
return unless FORMAT_REGEXES.find { |regex| field.format =~ regex }
errors.add(:field, :invalid, message: 'The field does not have a valid format')
end
I thought this would be easier to find, but I'm quite surprised that it isn't.
How on Earth do I test if a string is a number (including decimals) outside a Model?
e.g.
is_number("1") # true
is_number("1.234") # true
is_number("-1.45") # true
is_number("1.23aw") #false
In PHP, there was is_numeric, but I can't seem to find an equivalent in Ruby (or Rails).
So far, I've read the following answers, and haven't gotten any closer:
Ruby on Rails - Validate a Cost
Ruby/Rails - How can you validate against decimal scale?
invalid decimal becomes 0.0 in rails
You could borrow the idea from the NumericalityValidator Rails uses to validate numbers, it uses the Kernel.Float method:
def numeric?(string)
# `!!` converts parsed number to `true`
!!Kernel.Float(string)
rescue TypeError, ArgumentError
false
end
numeric?('1') # => true
numeric?('1.2') # => true
numeric?('.1') # => true
numeric?('a') # => false
It also handles signs, hex numbers, and numbers written in scientific notation:
numeric?('-10') # => true
numeric?('0xFF') # => true
numeric?('1.2e6') # => true
You could use Regular Expression.
!!("1" =~ /\A[-+]?[0-9]+(\.[0-9]+)?\z/) # true
!!("1.234" =~ /\A[-+]?[0-9]+(\.[0-9]+)?\z/) # true
!!("-1.45" =~ /\A[-+]?[0-9]+(\.[0-9]+)?\z/) # true
!!("1.23aw" =~ /\A[-+]?[0-9]+(\.[0-9]+)?\z/) # false
You can use it like this or make a method in a module or add this in the String class
class String
def is_number?
!!(self =~ /\A[-+]?[0-9]+(\.[0-9]+)?\z/)
end
end
You can use this site to test your expression : Rubular: a Ruby regular expression editor and tester
I can explain much more the expression if needed.
Hope this helps.
I want to write a validation for email filed .But some different way.
I will allow user to enter email in two format like "name 'email#example.com' " and simple 'email#example.com' .so basically i want to write a validation which will check that is valid email format in present in the value or not.
Just need a custom validation for check the valid email format is present in the input email value.
My model look like :
class Contact < ActiveRecord::Base
validates :email ,presence: true
validate :email_format
def email_format
??? what to write here ???
end
end
How can i write a validation for this.
You need to slightly modify the regular expression in your case.
validates :email, format: { with: /(\A([a-z]*\s*)*\<*([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\>*\Z)/i }
This will match the following formats.
soundar.rathinsamy#gmail.com
Soundar<soundar.rathinsamy#gmail.com>
Soundar <soundar.rathinsamy#gmail.com>
soundar<soundar.rathinsamy#gmail.com>
Soundar Rathinsamy<soundar.rathinsamy#gmail.com>
Soundar Rathinsamy <soundar.rathinsamy#gmail.com>
soundar rathinsamy <soundar.rathinsamy#gmail.com>
If you need some change continue editing this regular expression at rubular.com
validates :email, format: { with: /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i }
Taken from apidock.com: http://apidock.com/rails/ActiveModel/Validations/ClassMethods/validates
This code extracts from the email a string between "<" and ">" but if it doesn't find a match then it takes the last word in the email... in either case it then takes that string and tests for a valid email.
So it should work for "John john#example.com" and "John<john#example.com>"
def email_format
test_string = $1 if email =~ /\<([^\>]+)\>/
test_string = email.split(' ').last unless test_string
return if test_string =~ /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
errors.add(:email, "not a valid email")
end
I want to validate a form input so that http:// and www is not allowed. Which REGEX would work?
For example:
Allowed
google.com
NOT Allowed
www.google.com
http://google.com
http://www.google.com
MODEL
VALID_DOMAIN_REGEX = ???
validates :domain, format: { with: VALID_DOMAIN_REGEX }
Since http:// contains so many forward slashes, this would be a good use for Ruby's %r{} regex literal syntax.
def has_forbidden_prefix?(string)
string =~ %r{^(http://|www)}
end
This will return nil, falsy, if the string does not start with http:// or www.
It will return 0, truthy (the offset of the first match) if the string does.
You can use validate :some_method_name to call a custom validation method in the model, I would structure it as follows
model MyThing
validate :no_forbidden_prefix
private
def has_forbidden_prefix?(string)
string =~ %r{^(http://|www)}
end
def no_forbidden_prefix
if has_forbidden_prefix?(uri)
errors.add :domain, 'The URI cannot start with "http://" or "www"'
end
end
end
This should do it for you:
/^[http\:\/\/|www].*/
For example:
1.9.2p320 :007 > "http://www.google.de".match /^[http\:\/\/|www].*/
=> #<MatchData "http://www.google.de">
1.9.2p320 :008 > "www.google.de".match /^[http\:\/\/|www].*/
=> #<MatchData "www.google.de">
1.9.2p320 :009 > "google.de".match /^[http\:\/\/|www].*/
=> nil
So it doenst match if its valid for your purposes...
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 ""