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.
Related
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})?(\/.*)?$
I have a ruby variable #object which can have only one object inside it or multiple objects.
How to check that in Rails.
Tried checking with
.length
.size
.count
Michael's answer should work already, but another option is to check if it includes the Enumerable module (should support all "Array"-ish objects, unless they have their own custom implementation):
#object.is_a? Enumerable
# => returns true if Array-ish or false
Examples
# Array
[].is_a? Enumerable
# => true
# Hash
{}.is_a? Enumerable
# => true
# Set
[].to_set.is_a? Enumerable
# => true
# Subclass of any of the above
class MyArr < Array
end
MyArr.new.is_a? Enumerable
# => true
# ActiveRecord::Relation
User.all.is_a? Enumerable
# => true
# String
'somestring'.is_a? Enumerable
# => false
# Integer/Float
123.is_a? Enumerable
# => false
(123.45).is_a? Enumerable
# => false
# Time
Time.now.is_a? Enumerable
# => false
Gotcha
## Rails 4:
ActionController::Parameters.new.is_a? Enumerable
# => true
## Rails 5:
ActionController::Parameters.new.is_a? Enumerable
# => false
# in Rails 5, ActionController::Parameters no longer inherits from Hash
# ActionController::Parameters is the type of the variable `params` in your controllers
# Because practically speaking you can loop over it, so it should still be an "Array"
# Therefore, you might want to use the following instead of `.is_a? Enumerable`
#object.respond_to? :each
# => returns true if Array-ish or false
ActionController::Parameters.new.respond_to? :each
# => true
You can use respond_to? method
#object.respond_to? :size
It returns true if array of objects
I have a model wish contains the bellow method called by before_validation :
def set_to_false
self.confirme ||= false
self.deny ||= false
self.favoris ||= false
self.code_valid ||= false
end
When I run my tests, I got the deprecation message
DEPRECATION WARNING: You attempted to assign a value which is not
explicitly true or false to a boolean column. Currently this value
casts to false. This will change to match Ruby's semantics, and will
cast to true in Rails 5. If you would like to maintain the current
behavior, you should explicitly handle the values you would like cast
to false. (called from cast_value at
./Ruby2.1.0/lib/ruby/gems/2.1.0/gems/activerecord-4.2.1/lib/active_record/type/boolean.rb:17)
I understand I have to cast but I couldn't find a simple and smart way to do it. Any help to remove this deprecation would be great.
Here's a simple booleanification trick that I use often, double negation:
before_validation :booleanify
def booleanify
self.confirm = !!confirm
self.deny = !!deny
...
end
In case you are not familiar with this trick, it'll convert all values to their boolean equivalents, according to ruby rules (nil and false become false, everything else becomes true)
'foo' # => "foo"
!'foo' # => false
!!'foo' # => true
!nil # => true
!!nil # => false
I have a column which needs to hold 18 digits of value. Its defined in my rails file as
Schema.rb
t.decimal "revenue", precision: 23, scale: 5
Now when I try the following in console:
obj = Model.last
obj.revenue = 999999999999999999 ( 18 nines)
obj.save is returning false
( I have a validation that the maximum revenue should be less than 1000000000000000000 ( 1 followed by 18 zeros)
Validation on modal:
validates :revenue , numericality: {:greater_than => WBConstants::MIN_REVENUE_LIMIT, :less_than => WBConstants::MAX_REVENUE_LIMIT}, allow_blank: true
The error message says the value should be less than 100...(1 followed by 18 zeros)
I am unable to save.
The database I am using is sqlserver
The column data type is decmial(23,5)
This is a bug in Rails and there's already a pull request.
You could perform custom validation until the fix is merged.
What's causing this bug?
Rails internally converts the value to a float using Kernel.Float:
f = Kernel.Float(999_999_999_999_999_999)
#=> 1.0e+18
f < 1_000_000_000_000_000_000
#=> false
f == 1_000_000_000_000_000_000
#=> true
Why is this happening?
Because floats have limited precision and can only represent some integers exactly:
(999999999999999000..1000000000000001000).map { |i| i.to_f.to_i }.uniq
#=> [999999999999998976,
# 999999999999999104,
# 999999999999999232,
# 999999999999999360,
# 999999999999999488,
# 999999999999999616,
# 999999999999999744,
# 999999999999999872,
# 1000000000000000000,
# 1000000000000000128,
# 1000000000000000256,
# 1000000000000000384,
# 1000000000000000512,
# 1000000000000000640,
# 1000000000000000768,
# 1000000000000000896,
# 1000000000000001024]
See What Every Computer Scientist Should Know About Floating-Point Arithmetic for details.
You can use custom validation to solve this issue until now.
please check link below:
Custom Validators
Hope this help you.
Let's say I got a regular expression like this:
/\b[A-Z0-9._%a-zöäüÖÄÜ\-]+#(?:[A-Z0-9a-zöüäÖÜÄ\-]+\.)+[A-Za-z]{2,4}\z/
How can I check via Ruby/RoR if this string is a valid regular expression?
If it doesn't raise errors, it's a valid regex. :)
def valid_regex?(str)
Regexp.new(str)
true
rescue
false
end
valid_regex?('[a-b]') # => true
valid_regex?('[[a-b]') # => false