Ruby: Parse, replace, and evaluate a string formula

Posted by Swartz on Stack Overflow See other posts from Stack Overflow or by Swartz
Published on 2011-02-02T22:53:55Z Indexed on 2011/02/02 23:25 UTC
Read the original article Hit count: 237

Filed under:
|
|
|
|

I'm creating a simple Ruby on Rails survey application for a friend's psychological survey project. So we have surveys, each survey has a bunch of questions, and each question has one of the options participants can choose from. Nothing exciting.

One of the interesting aspects is that each answer option has a score value associated with it. And so for each survey a total score needs to be calculated based on these values.

Now my idea is instead of hard-coding calculations is to allow user add a formula by which the total survey score will be calculated. Example formulas:

"Q1 + Q2 + Q3"
"(Q1 + Q2 + Q3) / 3"
"(10 - Q1) + Q2 + (Q3 * 2)"

So just basic math (with some extra parenthesis for clarity). The idea is to keep the formulas very simple such that anyone with basic math can enter them without resolving to some fancy syntax.

My idea is to take any given formula and replace placeholders such as Q1, Q2, etc with the score values based on what the participant chooses. And then eval() the newly formed string. Something like this:

f = "(Q1 + Q2 + Q3) / 2"  # some crazy formula for this survey
values = {:Q1 => 1, :Q2 => 2, :Q3 => 2}  # values for substitution 
result = f.gsub(/(Q\d+)/) {|m| values[$1.to_sym] }   # string to be eval()-ed
eval(result)

So my questions are:

  1. Is there a better way to do this? I'm open to any suggestions.

  2. How to handle formulas where not all placeholders were successfully replaced (e.g. one question wasn't answered)? Ex: {:Q3 => 2} wasn't in values hash? My idea is to rescue eval()... any thoughts?

  3. How to get proper result? Should be 2.5, but due to integer arithmetic, it will truncate to 2. I can't expect people who provide the correct formula (e.g. / 2.0 ) to understand this nuance.

  4. I do not expect this, but how to best protect eval() from abuse (e.g. bad formula, manipulated values coming in)?

Thank you!

© Stack Overflow or respective owner

Related posts about ruby-on-rails

Related posts about ruby