There are many many ways to evaluate any ruby code at runtime. Let’s look at them.

eval

The most basic and well-known in other dynamic languages method. Receives a string and run it as a Ruby code.

txt = 'hello'
eval "puts '#{txt}'"  #=> hello

Optionally you can pass a context which eval operates on with binding.

def get_binding
  binding
end

class Klass
  def initialize
    @var = 42
  end

  def self.class_func
    11
  end

  def get_binding
    binding
  end
end

puts self                                 #=> main
puts eval('self', get_binding)            #=> main
puts eval('self', Klass.new.get_binding)  #=> #<Klass:0x007f77d1161728>
puts eval('@var', Klass.new.get_binding)  #=> 42

Binding allows you to capture current execution context. For example, it’s used by closures.

instance_eval

Allows you to operate on object with self set to this object. It also can accept block instead of string to evaluate it. Somewhat similar to eval with binding.

# self is also passed as a block argument
Klass.new.instance_eval do |obj|
  puts @var             #=> 42
  puts self             #=> #<Klass:0x007f0c71f4d7e0>
  puts obj == self      #=> true
end

# Class itself in an instance of class `Class`
Klass.instance_eval do |obj|
  puts class_func       #=> 11
  puts self             #=> Klass
  puts obj == self      #=> true
end

# works with string too
Klass.new.instance_eval "puts @var" #=> 42

instance_exec

Similar to instance_eval, but also allows you to pass an argument to a block.

# instance_exec can pass an argument(s)
Klass.new.instance_exec(1, 2) do |obj1, obj2|
  puts @var             #=> 42
  puts self             #=> #<Klass:0x007f1136e57060>
  puts obj1             #=> 1
  puts obj2             #=> 2
end

# for example, you can define methods dynamically with instance_exec
instance = Klass.new
instance.instance_exec(3) do |obj|
  @_obj = obj
  def new_func
    @_obj
  end
end
puts instance.new_func  #=> 3 (or nil if no arguments passed to instance_exec)

# works only with block
Klass.new.instance_exec "puts 'hello'" #=> no block given (LocalJumpError)

class_eval

Similar to instance_eval, but operates on object itself. Available only on Module (Class).

Klass.class_eval do |obj|
  puts obj                  #=> Klass
  def another_func
    14
  end
end
puts Klass.new.another_func #=> 14

class_exec

Like instance_exec but for class_eval. Allows you to pass an argument to a block.

Klass.class_exec(1) do |obj|
  puts obj                  #=> 1 (or nil if no arguments passed to class_exec)
  def yet_another_func
    18
  end
end
puts Klass.new.yet_another_func #=> 18

# because class_{exec,eval} opens `Klass` itlelf any @variables are class instance variables (like instance_exec on a class)
# but any methods defined are instance methods
Klass.class_exec(1) do |obj|
  @_obj = obj
  puts @_obj                    #=> 1
  def yet_another_func1
    @_obj
  end
end
puts Klass.new.yet_another_func1 #=> nil
puts Klass.instance_variables    #=> @_obj

module_eval

Same as class_eval.

module_exec

Same as class_exec.

Conclusion

eval is the most basic and well-known method. You can evaluate any Ruby code with it. But if you’re using rubocop it will yell at eval as a security issue. True, if you run eval against user-provided string it can be exploited like SQL-injection.

Other methods are a little bit safer since they accept block, not a string (don’t use strings with {instance,class,module}_eval).

obj.instance_{eval,exec} operate on singleton class of an obj.

obj.{class,module}_{exec,eval} operate on obj itself and available only on Module (Class).

{instance,module,class}_exec allows you to pass an argument to eval’d block while {instance,module,class}_eval don’t.

{instance,module,class}_eval can also accept string (just like regular eval).