Kernel#eval
eval(string[, binding, filename, lineno]) -> object The eval method executes a string as Ruby code within a specified or current context. It provides dynamic code execution capabilities, useful for metaprogramming and runtime code generation.
Basic Usage
# Simple evaluation
eval("1 + 1") # => 2
# With local variables
x = 10
eval("x + 5") # => 15
With Binding
The binding object captures the execution context—local variables, instance variables, and self—at the point where it is created. Passing a binding to eval lets you run code inside that captured scope instead of the current one. This is particularly useful when you need to evaluate expressions against an object’s internals without exposing accessor methods for every piece of state you want to reference.
# Evaluate in a specific context
class Calculator
def initialize
@value = 100
end
end
calc = Calculator.new
eval("@value * 2", calc.binding) # => 200
Practical Examples
Dynamic method definition
eval can generate entire method bodies at runtime, which is helpful when you need to create several similarly shaped methods from a list of names or attributes. The approach below uses the method name to derive behavior—each generated method performs a different calculation based on the digit embedded in the name. This pattern appears in libraries that wrap external APIs or generate accessors for dynamic column sets.
# Define methods dynamically
methods = [:double, :triple]
methods.each do |name|
eval("def #{name}; @value * #{name.to_s[-1].to_i}; end")
end
Configuration scripts
Ruby’s syntax is close enough to a configuration language that small scripts can double as config files. Using eval on the raw file content reads each top-level assignment as a Ruby statement, which sets local variables in the current scope. This approach is concise but ties the config format directly to Ruby’s parser—if you need a format that non-Ruby tools can read, YAML or JSON are safer bets.
# config.rb
# database = "production"
# cache_ttl = 3600
eval(File.read("config.rb"))
puts database # => production
Template engines
A minimal template engine can be built by scanning for <%= ... %> placeholders and evaluating each expression in a binding that holds the template variables. The gsub block form passes each match to the block, where the captured expression replaces the placeholder in the string to produce the final rendered output. For production use, ERB handles edge cases like escaping and multiline expressions that a quick regex-based approach would miss.
# Simple template
template = "Hello, <%= name %>!"
context = { name: "World" }
result = template.gsub(/<%=(.*?)%>/) { eval($1, context.binding) }
User input as code
Passing user-supplied strings directly to eval turns your program into an open interpreter—the caller can run any Ruby code, including file deletion, network access, or shell commands. The calculator example below works for trusted input in a controlled environment, like a REPL or a developer tool, but should never face a public endpoint without strict allowlisting or sandboxing. For arithmetic specifically, a parser that only handles numbers and operators is a safer design.
# Calculator example
def calculate(expression)
# WARNING: Security risk!
eval(expression)
end
calculate("2 + 2") # => 4
calculate("10 / 2") # => 5
Security warning
Even a single unguarded eval call can compromise the entire process. The comment in this example hints at the worst case: a string containing system("rm -rf /") would execute that shell command with the same permissions as the Ruby process. Wrapping eval in a $SAFE level or a separate process with restricted capabilities can reduce the risk, but the most reliable protection is keeping untrusted input out of eval altogether.
# DANGEROUS - Never eval untrusted input!
# eval(user_input) # Could run: `system("rm -rf /")`
Safer alternatives
Ruby provides lower-risk options that stop short of full code execution. The RubyVM::InstructionSequence.compile method only parses and compiles the code—it checks syntax without running anything, which is enough for validation or static analysis workflows. For read-only code inspection, RubyVM::AbstractSyntaxTree offers structured access to the parse tree in Ruby 2.6 and later.
# Use RubyVM for safer evaluation (Ruby 2.7+)
require 'rubyvm'
# Or parse-only approach
RubyVM::InstructionSequence.compile("puts 'hi'")
With filename and line number
Passing a filename and line number to eval improves error messages when the evaluated code raises an exception. Instead of a generic (eval) source location, the backtrace shows the filename you supplied and the line offset you specified. This is essential when the Ruby code comes from a file on disk or a template that has a known name—without it, debugging a stack trace full of anonymous eval frames can be painful.
# For better error messages
code = "undefined_var"
eval(code, binding, "script.rb", 10)
# Error points to script.rb:10
instance_eval vs eval
While eval runs code in a given binding, instance_eval runs the block in the context of a specific object, setting self to that object for the duration of the block. This means instance_eval can access private methods and instance variables directly, without needing to capture a binding first. The example below shows how instance_eval reaches @secret on an object that has no public accessor for it.
class MyClass
def initialize
@secret = 42
end
end
obj = MyClass.new
# eval in object's context
obj.instance_eval { @secret } # => 42
DSL implementation
A simple DSL can be built by combining method_missing with eval in a binding that points to a builder object. When eval processes the DSL string, any unknown method call triggers method_missing on the builder, which can log, store, or act on it. This pattern powers many Ruby testing and configuration libraries, though the same effect can be achieved more cleanly with instance_eval and blocks.
# Simple DSL
class Builder
def method_missing(name, *args)
puts "Building: #{name} with #{args.inspect}"
end
end
builder = Builder.new
eval("button 'Submit'", builder.binding)
eval is powerful but should be used sparingly due to security concerns. Prefer safer alternatives when possible.
Safer dynamic evaluation
eval should be reserved for code that you fully trust or code that you generated yourself. Once the input can come from a user or another external system, the risks rise quickly and the surrounding code has to do much more work to stay safe. When you do use it, pass a binding only when the extra context is needed, and give the code a filename and line number if that will make errors easier to read. In many cases, a small parser or a dedicated Ruby API will be easier to reason about.
If the goal is to interpret a small expression, it is often better to parse or dispatch explicitly than to execute arbitrary text. That keeps the surface area smaller and makes the supported syntax obvious. eval can still be useful in controlled DSLs or generated code, but the calling code should make that trust boundary very clear. The fewer places that depend on it, the easier the program is to audit later.
Another reason to keep the evaluated string short is that smaller expressions are easier to test and easier to replace later. If the code starts to look like a full program, it usually deserves a real method or a parser instead. eval is strongest when it handles a narrow, well defined job and leaves the rest of the application in normal Ruby code.
That narrower use also makes the security boundary more obvious to the next reader. When the call site says exactly what is being evaluated and why, it is much easier to notice when the trust level is too high. Clear boundaries matter even more than clever syntax here.