Topic: Simple formulas - alternative to eval?

We have a Rails 2.2 app that collects responses to a set of questions. Each set of responses receives a score in several different categories, and we have a formula to calculate the value for each one.

The formula typically looks like something like this:

(
(42 - ((questions[12345].response == 44444 ? 2 : 0)
+ (questions[12346].response == 55555 ? 2 : 0).to_f / 14)).to_f
) / 2

We build up the formulas into a massive string, and then evaluate it using instance_eval:

formulas.each do |formula|
  eval_string += "results[#{formula.id.to_s}] = (#{formula.formula})\n"
end 

instance_eval(eval_string)


This works, but is slow and memory intensive. Can anyone think of another way we could do this?

Last edited by cdr (2009-02-24 08:03:40)

Re: Simple formulas - alternative to eval?

I'm not sure about the performance of to_f but if you use 14.0 instead of 14 and 2.0 instead of 2 it works without the to_f in the formula.

what's your reason against setting the results directly and not use instance_eval at all? You could pass self if you are using it in some method.

Ruby on Rails developer from Vienna looking for freelance gigs. My Blog. If I answered your questions I'd be happy if you recommend me at working-with-rails

Re: Simple formulas - alternative to eval?

Thanks for your reply Tom.

The formulas are stored in the database, so unless I'm missing something obvious I don't think we can set the results directly?

Re: Simple formulas - alternative to eval?

I'm not sure, but I think Tom means something like this:

formulas.each do |formula|
  results[formula.id.to_s] = eval( formula.formula )
end

Be sure to check the content of questions[x].response carefully! You are executing user input as code in your application.

It's the nature of a forum to contain lots of information. That is because old threads don't get deleted:
Tutorials: http://railsforum.com/viewforum.php?id=20
Extremely Powerful Tool: http://railsforum.com/search.php

Re: Simple formulas - alternative to eval?

Thanks wups. We evaluate all of the formulas in one block because each call to eval() is expensive, and this way we only have to make one call. It's much quicker.

We were very careful not to directly use user input in the formula. It is not possible for a user to insert an arbitrary value for questions[x].response.

Re: Simple formulas - alternative to eval?

If you only have a limited number of different formula-structures, maybe this approach is useful:

Just store the formula type and the numbers used in this particular formula in your database.
Make a class containing the formulas and just insert the numbers loaded from the database.

((n1 - ((response == n2 ? n3 : n4) + (response == n5 ? n6 : n7).to_f / n8)).to_f) / n9
with all the n's loaded from the db

If this does not work due to too many different formulas and you need to use your version posted above, use << instead of += to concatenate the stings. += can become a performace issue if it is called really often, because it always creates a new object. << just expands the old object. Should not be problem at all, if it is called only a few times, though.

It's the nature of a forum to contain lots of information. That is because old threads don't get deleted:
Tutorials: http://railsforum.com/viewforum.php?id=20
Extremely Powerful Tool: http://railsforum.com/search.php

Re: Simple formulas - alternative to eval?

I did consider this approach but unfortunately our formulas vary too much (in complexity and length) to substitute the numbers in that way.

Thanks for the concatenation tip - I didn't know that << was quicker.