2

I would like to implement a method that converts any given String value into its proper representation. Here are some examples of what I am referring to:

  • "TRUE" should become true (TrueClass)
  • "1000" should become 1000 (Integer)
  • "text" should simply be returned (String)

I believe this has been solved by the code that I have provided, although I am not very pleased with how it looks. I think this could be reworked into something more sophisticated, but my creativity has reached its limit.

def convert(value)
    return true if value =~ /^true$/i
    return false if value =~ /^false$/i
    return value unless value =~ /^[0-9]*$/

    begin
        Integer(value)
    rescue ArgumentError
        nil
    end
end

My question is essentially: Is there a way to provide the same functionality but with fewer return statements and overall less code?

  • https://stackoverflow.com/questions/6803647/how-to-write-a-ruby-switch-statement-case-when-with-regex-and-backreferences – baseballlover723 Jan 04 '19 at 23:05
  • @baseballlover723, are you expecting readers to go to that link and try to figure out why you posted it? Please provide context when you do that. – Cary Swoveland Jan 05 '19 at 01:12
  • 1
    What about `"1.3"`, `"{ 1=>2, e\3=>4 }"`, etc. Are they to be treated as strings? That is, does the string represent an integer, `true` or `false`, and everything else is text? – Cary Swoveland Jan 05 '19 at 01:16
  • 1
    I am not totally sure that I understand your question, Cary. I think you are asking me how I would deal with floating point numbers, in which case I would answer that I treat them as String objects. I basically only want to distinguish between integers, booleans, and strings - anything else is irrelevant in this context. –  Jan 05 '19 at 17:24

2 Answers2

1

In general, case is a decent replacement for multiple ifs.

def convert(str)
  case str.downcase
  when /\A[+-]?[0-9]+\z/
    str.to_i
  when 'true'
    true
  when 'false'
    false
  when ''
    nil
  else
    str.clone
  end
end

Note: only integers in decimal are currently supported (the code in the question also supports octal, but it is unknown whether that is intentional or not). Change the regular expression as needed (and switch back to Integer(str)) if other bases indicated by the 0, 0x, and/or 0b prefixes are desired.

Arkku
  • 41,011
  • 10
  • 62
  • 84
  • This looks way better than what I had produced. Thank you! –  Jan 04 '19 at 23:26
  • 2
    What about octal, hex, and binary numbers? `Integer('0b11')` does the right thing but that will slip through your regex. And are you sure you want `^` and `$`? You almost always want `\A` and `\z` in Ruby regexes. – mu is too short Jan 04 '19 at 23:43
  • @muistooshort Hex and binary are intentionally omitted since they are also omitted by the OP, and I understood the point of the question to be how to rewrite the same code. Admittedly the inclusion of signs strays from this. Likewise they also use `^` and `$`, but I admit that it's probably a bug and I should have corrected it. (Edited now to use `\A` and `\z` and to also exclude octal for consistency.) – Arkku Jan 05 '19 at 10:31
  • Oh, I see! I will rewrite my expressions to use `\A` and `\z` instead, as the String object may in fact include newline characters. –  Jan 05 '19 at 17:29
0

YAML.load may work for your use case, but it is more powerful than you need, and can introduce security holes if you are parsing untrusted data.

It will parse numbers, booleans, and strings just fine, but also has syntax for arrays and hashes, and can load arbitrary object types which can cause arbitrary code execution...

Garrett Motzner
  • 3,021
  • 1
  • 13
  • 30