Is There a Way in Ruby/Rails to Execute Code That Is in a String

Is there a way in Ruby/Rails to execute code that is in a string?

You can use eval:

code = '@var.reverse'
@var = 'Hello'
@result = eval(code) # => "olleH"

But be very careful in doing so; you're giving that code full access to your system. Try out eval('exit()') and see what happens.

Execute code from string in Ruby?

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.

How to execute view code stored in string in run time with eval in Rails 3.2 erb?

eval works by evaluating ruby code.

irb(main):001:0> eval('puts "Hello World"')
Hello World
=> nil

while Converting erb template should be done with ERB class

irb(main):002:0> require 'erb'
irb(main):003:0> ERB.new('<%= "Hello World" %>').result
=> "Hello World"

My guess is that the code is actually a string that contain ERB template rather than ruby code.

Nonetheless I don't think that this is a good approach. task.project.name are probably from database that possibly came from user input. doing eval on that seems not like a good idea.

Probably you could solve your problem with normal partials

How to execute code from string Rails and render it?

So, as said it is a very wrong idea to store code in your database. There is very little exception to that and it's very complicated to implement, bug prone, and present security issues. In short: don't do it.

So you need an alternative design. There is basically 3 situations, pick-up the one that correspond to your application's goal:

A- If your method parameters and output will never change in the future, then you execute your code and store the output in a cached columns (type string or text) of your model before saving. When retrieving this cached output later you can just use it as it

post.cached_output = my_method(...)
post.save
# In the future, in your view:
<%= post.cached_output %>

B- If the parameter of your method will never change in the future but the method output differs according to external elements, then just store that one parameter's value and call the method each time you need its output

post.cached_parameter = 17
post.save
# In the future, in your view:
<%= my_method(params: post.cached_parameter) %>

C- If your method's parameter change over time and your method output also changes overtime, then you don't need to cache anything in your database

How do I associate a Ruby script with an object and execute it on demand (in Rails)?

You can use eval, which will run a Ruby script, but be very, very, careful!

For a detailed explanation see "Code is data, data is code".

Example:

eval(@model_instance.script)

eval is very dangerous. If you give a user the ability to upload and run an unchecked script, it may create a huge problem. For example, someone can upload a script like this:

User.delete_all

This will delete all users from the users table using a SQL query without invoking any Active Record callbacks.

You can use Ruby's taint method to add some additional safety, but it is not 100% foolproof.

For a detailed example, see "Locking Ruby in the Safe".

Is there some validation in Rails that edits a string before it commits to the database?

The issue here was in respect to the serialisation of an array into a string as it is rendered on the DOM and then back into an array as it is passed as a paramater to the controller.

Update the controller as follows:

    def create
blast = @account.blasts.create(blast_params)
if blast
if params[:blast][:to].present?
blast_to_params = params[:blast][:to].gsub('[', '').gsub(']', '').split(',').map{|i| i.to_i}
blast.update(to: blast_to_params)
end
redirect_to blasts_step_1_path(blast)
else
redirect_to blasts_path, alert: "Failed to create this blast."
end
end

How to execute code stored in DB?

While it would probably be best to keep application logic in the application itself, this would be a simple matter of using eval().

eval("1000 * %i" % min_level)


Related Topics



Leave a reply



Submit