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
Posting Large Amounts of Data with Httparty
How to Make :Level Change Based on :Committed Days
How to Globally Configure Rspec to Keep the '--Color' and '--Format Specdoc' Options Turned On
Tutorials or Screencasts on Building a Rest Web Service on Rails
How to List the Currently Available Objects in the Current Scope in Ruby
Should I Define a Main Method in My Ruby Scripts
Raise Custom Exception with Arguments
Split Ruby Regex Over Multiple Lines
Rails-Like Database Migrations
When to Call a "Require" in Rails
How to Create a Delete Link for a Related Object in Ruby on Rails
Modifying Database Ids from Rails Console
How to Turn Off Automatic Stylesheet/JavaScript Generation on Rails 3.1
Aws-Sdk for Ruby Access Folder Within Bucket
What Is the Argument Against Using Before, Let and Subject in Rspec Tests