Ruby: Parse, replace, and evaluate a string formula
- by Swartz
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:
Is there a better way to do this?
I'm open to any suggestions.
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?
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.
I do not expect this, but how to
best protect eval() from abuse (e.g.
bad formula, manipulated values
coming in)?
Thank you!