Search code examples
rubymetaprogrammingactivesupport

Alternatives to "eval" for ActiveSupport date parsing


My script currently accepts ActiveSupport date string as a command line argument:

my_script --mindate 1.day

Inside my script I am using eval to store it into my config

MyScript.configuration.min_date = eval(min_date_string)

I understand that this is extremely dodgy and insecure as anything can be passed to eval, but what are my alternatives?


Solution

  • You want time durations? I suppose you could use chronic_duration.

     my_script --mindate "1 day"
     MyScript.configuration.min_date = ChronicDuration.parse(min_date_string)
    

    But since it's natural language heuristics, it becomes not entirely well defined exactly what sorts of strings it will recognize. But it will do fancy things like "1 day and four hours".

    Or you could write your own very simple parser/interpreter for the argument. Just split on a space (for "1 day" type of input) or a period (for "1.day") type of input. Recognize a few words in the second position ("hour", "minute" "day", "month", "year"), translate them to seconds, multiply the number by the translated-to-seconds word. A dozen or so lines of ruby probably.

    Or you could even take advantage of the ActiveSupport feature that supports things like "1.day" to make it even easier.

      str = "11 hours"
      number, unit = str.split(' ')
      number.to_i.send(unit)
    

    That would let the command line user send any method they want to a number. I'm not sure it matters. For that matter, I'm not sure if the original eval really matters or not -- but I agree with you it's bad practice. For that matter, probably so is send on user input, although not quite as bad.

    Or you could just make them send in the raw number of seconds and calulcate it themselves.

    my_script --mindate 86400
    

    You realize 1.day just ends up being converted to the number of seconds in a standard day, right? I'm not sure why you're calling a number of seconds "mindate", but that's your business!

    edit Or yet another alternative, make them do:

    my_script --mindays 2 --minhours 4 --minminutes 3
    

    or something.