Number Validation with Regex in Ruby

tg
Launch School
Published in
3 min readJun 15, 2016

--

Background

Proper implementation of input validation is one of the most fundamental aspects of any web application. While we could rely on some of the existing methods to achieve many types of input validation, some methods might not be as flexible as we would want them to be.

For instance, the Object#is_a? method allows us to check if its calling object is of certain type:

>> 12.is_a? Integer
=> true
>> 'foo'.is_a? String
=> true
>> [1, 2, 3].is_a? Array
=> false

This method works well until we introduce objects that look like a number, an array, etc:

>> '12'.is_a? Integer
=> false
>> '[1, 2, 3]'.is_a? Array
=> false

The reason they return false , of course, is that these calling objects are actually strings rather than an integer or an array.

Here, we want to avoid these inputs being treated as strings since we are interested in validating what’s “inside” the strings.

Wouldn’t it be nice if we had a method that could have any types of arguments as an input, check the “inside value” of the input, and verify if it is of a certain type we specify?

Implementation

The implementation described below focuses on number validation in Ruby. More precisely, it will check if the “content” of a given input of an arbitrary type belongs to one of the following types (classes): Integer, Float.

def number?(obj)
obj = obj.to_s unless obj.is_a? String
/\A[+-]?\d+(\.[\d]+)?\z/.match(obj)
end

Let’s start by analysing the regex:

/\A[+-]?\d+(\.\d+)?\z/

Here is the list of the elements contained in the regex.

/         start of the regex
\A start of the string to be matched
[+-]? zero or one of '+' or'-'
\d+ one or more of digit
(\.\d+)? zero or one of 'one dot and 'one or more of digit''
\z end of the string to be matched
/ end of the regex

(For more details on regex syntax in general, you might want to check this regex quick reference.)

Let’s hop into irb to verify the regex actually works:

(irb)
REGEX = /\A[+-]?\d+(\.[\d]+)?\z/
REGEX.match '13'
=> #<MatchData "13" 1:nil>
!!REGEX.match '13'
=> true
REGEX.match '3.14'
=> #<MatchData "3.14" 1:".14">
!!REGEX.match '3.14'
=> true
REGEX.match 'not a number'
=> nil
!!REGEX.match 'not a number'
=> false

The Regexp#match method in the above example returns a MatchData object if the inspected string represents a number. Otherwise, it will return nil. The double exclamation notation (!!) is used here to convert the returned value into its corresponding boolean value.

Note that the match method will throw an error when passed a non-string object as an argument:

(irb)
REGEX = /\A[+-]?\d+(\.[\d]+)?\z/
REGEX.match 13
=> TypeError: no implicit conversion of Integer into String ...
REGEX.match 3.14
=> TypeError: no implicit conversion of Float into String ...

In order to avoid this we will add the following code (*):

obj.to_s unless obj.is_a? String                                 (*)

(“obj” is the object we want to test against the regex) This line of code transforms an object into a String object if the object is not of type String and ensures that we can use this String object to test against the regex.

This completes the explanation as to how the above method:

def number?(obj)
obj = obj.to_s unless obj.is_a? String
/\A[+-]?\d+(\.[\d]+)?\z/.match obj
end

works.

Here is an example of how we can use this method (in this example, the code(*) is optional as ‘number’ will always be of type String after being assigned a value via gets.chomp , a method which returns a string:

puts "Enter a number:"
number = nil
loop do
number = gets.chomp
break if number?(number)
puts "That is not a number."
end
puts "#{number} is indeed a number."

Running this code prints out the following:

Enter a number:
foo
That is not a number.
bar
That is not a number.
‘12.34’
That is not a number.
12.3.4
That is not a number.
12.34
12.34 is indeed a number.

References

You might also find the following resources on this subject helpful:

http://stackoverflow.com/questions/1235863/test-if-a-string-is-basically-an-integer-in-quotes-using-ruby

http://rubular.com/ (Interactive Ruby regex editor)

If there are areas which you think can be improved, please let me know in the comment section below. Any feedback will be greatly appreciated!

--

--