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.