How do I validate a value entry for Integer (), Float (), or Rational ()?

This is free based on How to Convert String to Integer or Float .

If I wanted to convert the input of a numeric string to my "most suitable type" using Ruby's built-in conversion mechanics, I could do something like this:

def convert(input) value = Integer(input) rescue nil value ||= Float(input) rescue nil value ||= Rational(input) rescue nil value end convert('1') #=> 1 convert('1_000') #=> 1000 convert('0xff') #=> 255 convert('0.5') #=> 0.5 convert('1e2') #=> 100.0 convert('1/2') #=> (1/2) convert('foo') #=> nil 

But this call to brute force looks dirty. Is there a more elegant way to get closer to this? Can I check if the value is a valid input for Integer() , Float() or Rational() so that I can more effectively control these methods?

+6
source share
3 answers

Using trailing rescue makes me cringe because it can hide problems with the underlying code because it is an Exception trap and not an ArgumentError, which can lead to unsuccessful conversion attempts. This is not so concise, but it should handle the corresponding exception:

 def convert(input) value = begin Integer(input) rescue ArgumentError nil end value ||= begin Float(input) rescue ArgumentError nil end value ||= begin Rational(input) rescue ArgumentError nil end value end convert('1') # => 1 convert('1_000') # => 1000 convert('0xff') # => 255 convert('0.5') # => 0.5 convert('1e2') # => 100.0 convert('1/2') # => (1/2) convert('foo') # => nil 

Thinking about it, it looks like it could be DRY'd before:

 def convert(input) [:Integer, :Float, :Rational].each do |m| begin return Kernel.method(m).call(input) rescue ArgumentError end end nil end convert('1') # => 1 convert('1_000') # => 1000 convert('0xff') # => 255 convert('0.5') # => 0.5 convert('1e2') # => 100.0 convert('1/2') # => (1/2) convert('foo') # => nil 

As Jorn pointed out, the above was not a good example. I used Kernel to access Integer() , Float() and Rational , because where they are defined, but in fact Object was the place to search, since it inherits from the kernel.

And this was one of those days when I knew that there was a good way to name a method indirectly, but call was in my mind, not send , as Stefan pointed out. So here is a cleaner way to do this, starting with:

  return Object.send(m, input) 

But this can be reduced to:

  return send(m, input) 

as a result of:

 def convert(input) [:Integer, :Float, :Rational].each do |m| begin return send(m, input) rescue ArgumentError end end nil end convert('1') # => 1 convert('1_000') # => 1000 convert('0xff') # => 255 convert('0.5') # => 0.5 convert('1e2') # => 100.0 convert('1/2') # => (1/2) convert('foo') # => nil 
+2
source

Since for some reason you prefer "0.3" convert to 3e-1 rather than 3/10 , this can be done in a more explicit way. In the end, under the hood in the ruby ​​parser there is the same recognition mechanism:

 def convert input raise unless String === input && input[/\A_|_\z|__/].nil? input = input.strip.delete('_') case input when /\A-?\d+\z/ then Integer(input) when /\A-?0x[\da-f]+\z/i then Integer(input) when /\A-?(\d*\.)?\d+(e-?\d+)?\z/i then Float(input) when /\A-?(\d*\.)?\d+(e-?\d+)?\/\d+\z/i then Rational(input) end end 

And it works as expected :)

+1
source

Piggybacking on Tin Man's answer , you can use the module to override Kernel 's default behavior:

 module SafeConvert def Integer(*) ; super ; rescue ArgumentError ; end def Float(*) ; super ; rescue ArgumentError ; end def Rational(*) ; super ; rescue ArgumentError ; end end 

This will shorten the code to:

 class Helper include SafeConvert def convert(input) Integer(input) || Float(input) || Rational(input) end end 
+1
source

Source: https://habr.com/ru/post/1012530/


All Articles