0

I'm using a metadata approach to process some code. Let's say the following is a string:

if $bmi >= 18.5 && $bmi <= 24.9 then return 'healthy' elsif $bmi >= 25 && $bmi <= 29.9 then return 'overweight' elsif $bmi > 30 then return 'obese' end

$bmi gets replaced properly, but I then want the code to execute.

6
  • Why do you feel that your metadata approach requires raw strings? What is the benefit of doing it this way? There's some context here that's missing that would get you a better answer (the obvious solution is to do regex replace on your symbols then throw it through eval, but I would strongly caution against that right now). Commented Jul 20, 2015 at 20:25
  • Find an article that explains Ruby's different eval methods and choose the one that fits your use case. Here's one: infoq.com/articles/eval-options-in-ruby (Of course, make sure you really want to do this at all first...) Commented Jul 20, 2015 at 20:26
  • Thank you. Eval is actually what I was looking for. That fits, thank you :) Commented Jul 20, 2015 at 20:27
  • 2
    WAIT, NO! Don't just blindly use eval! Commented Jul 20, 2015 at 20:27
  • The metadata is coming from my side, and is on the server. It is used to evaluate values that come in from the DB and compare them using the code snippet I provided. Seems to work with Eval. Anything wrong with that here? Commented Jul 20, 2015 at 20:29

1 Answer 1

6

The eval method will evaluate your string as Ruby code. BUT...

eval is fragile

Your string contains the return keyword, which always returns from a method call. If you eval that string inside a method, any code that comes after it will never run because the method will return. If you eval that outside of a method, you'll get a LocalJumpError exception because return is only valid inside a method. I can imagine this causing all sorts of mysterious bugs in your code.

eval is dangerous

You say the metadata comes from your side, but you're still opening up a huge failure point in your code when you eval because it could do literally anything. That one call to eval could wipe the entire disk, it could download and run an exploit, it could even rewrite your script into a malicious AI with a hatred for humanity. There is really no point in using such a powerful method when you just want to do some numerical comparison.

eval is confusing

Realistically, you're probably fine using eval. Your code will work and the world won't end. But even then it's terrible because it creates completely unreadable code. Any programmer reading your script (including yourself, six months later when you come back to fix something) will reach that eval and think "Uh... what exactly does this do?" In cases like this, you should always store as little logic in the DB as possible, and do the rest of the calculation in the code. How would you do that in this case? I can think of a few options.

Store only the intervals

With a DB schema like

table BMIIntervals (
  MinValue    Double,
  Description String
)

You could store the rows

18.5, 'healthy'
25,   'overweight'
30,   'obese'

Then you can easily do the calculation in Ruby:

intervals = db.query("SELECT * FROM BMIIntervals ORDER BY MinValue DESC")
intervals.each { |row| return row[1] if $bmi >= row[0] }
return nil

Store only the calculation name

Or if the BMI intervals are fixed, and the metadata says what kind of calculation to do, you could simply define the calculation in Ruby:

module MetadataFuncs
  def self.bmi
    return 'obese' if $bmi > 30
    return 'overweight' if $bmi >= 25
    return 'healthy' if $bmi >= 18.5
  end
end

Then your metadata string would be just "bmi" and instead of eval(metadata) you could do MetadataFuncs.send(metadata). This way you know exactly what code the metadata might call, even without seeing what data are in the database.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you, great answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.