What is the difference between class_eval, class_exec, module_eval and module_exec?
Asked Answered
K

3

54

I am reading the Module documentation but can't seem to understand their differences and which should be used where.

How is the eval different than exec?

Kimberli answered 29/1, 2012 at 23:20 Comment(0)
A
96

I'm going to answer a bit more than your question by including instance_{eval|exec} in your question.

All variations of {instance|module|class}_{eval|exec} change the current context, i.e. the value for self:

class Array
  p self                     # prints "Array"
  43.instance_eval{ p self } # prints "43"
end

Now for the differences. The eval versions accepts a string or a block, while the exec versions only accept a block but allow you to pass parameters to it:

def example(&block)
  42.instance_exec("Hello", &block)
end
example{|mess| p mess, self } # Prints "Hello" then "42"

The eval version does not allow to pass parameters. It provides self as the first parameter, although I can't think of a use for this.

Finally, module_{eval|exec} is the same as the corresponding class_{eval|exec}, but they are slightly different from instance_{eval|exec} as they change what is the current opened class (i.e. what will be affected by def) in different ways:

String.instance_eval{ def foo; end }
Integer.class_eval  { def bar; end }

String.method_defined?(:foo)            # => false
String.singleton_methods.include?(:foo) # => true
Integer.method_defined?(:bar)           # => true

So obj.instance_{eval|exec} opens the singleton class of obj, while mod.{class|module}_{eval|exec} opens mod itself.

Of course, instance_{eval|exec} are available on any Ruby object (including modules), while {class|module}_* are only available on Module (and thus Classes)

Atiana answered 30/1, 2012 at 0:3 Comment(5)
Note: when you module_eval lambda, it passes the module itself to lambda as the first parameter. module_exec doesn't.Hyperon
I didn't realize that. I can't think of a single case where that would be of any use (use self instead), but I've edited my answer for completeness.Country
Thank you @Marc-AndréLafortune, but in the above example, where is the mess parameter coming from?Dimmick
@BKSpurgeon the second reference to &block, where "Hello" is passed as the argument for messGratian
I've just discovered that on, for example, ::Oga::XML::Element I can call both instance_ and module_. Wanted to know the difference but answers here don't tell it. This article says "In effect, class_eval is an alias for module_eval." medium.com/rubycademy/…Hyperon
T
5

To answer your last question first, eval (in all its variations) is completely different from exec. exec $command will start a new process to run the command you specify and then exit when that finishes.

class_eval and module_eval have the power to redefine classes and modules -- even those that you yourself did not write. For example, you might use class eval to add a new method that did not exist.

Fixnum.class_eval { def number; self; end }
7.number # returns '7'

class_eval can be used to add instance methods, and instance_eval can be used to add class methods (yes, that part is very confusing). A class method would be something like Thing.foo -- you're literally calling the foo method on the Thing class. An instance method is like the example above, using class_eval I've added a number method to every instance of Fixnum.

Okay, so that's the *_eval class of methods. The exec methods are similar, but they allow you to look inside a class and execute a block of code as though it was defined as a method on that class. Perhaps you have a class that looks like this:

class Foo
  @@secret = 'secret key'
  @@protected = 'some secret value'
  def protected(key)
    if key == @@secret
       return @@protected
    end
  end
end

The class Foo is just a wrapper around some secret value, if you know the correct key. However, you could trick the class into giving you its secrets by executing a block inside the context of the class like so:

Foo.class_exec { @@secret = 'i'm a hacker' }
Foo.protected('i'm a hacker') #returns the value of @@protected because we overwrote @@secret

In general, with a lot of the tools in ruby, you could use any of these to solve a lot of problems. A lot of the time you probably won't even need to unless you want to monkey patch a class some library you use has defined (although that opens up a whole can of worms). Try playing around with them in irb and see which you find easier. I personally don't use the *_exec methods as much as the *_eval methods, but that's a personal preference of mine.

Tapis answered 29/1, 2012 at 23:34 Comment(3)
I think you're confusing exec and instance_execCountry
Not really? I mentioned exec explicitly at the beginning. exec doesn't really have much in common with the eval family of methods, so much as {instance|module|class}_exec do.Tapis
Oh, I see. I think the OP meant *_eval vs *_exec, not eval vs exec, but I might be wrong.Country
F
1

To avoid ambiguity I'm going to call a method that belongs to (owned by) a singleton class a singleton method. The rest are instance methods. Although one might say that a singleton method of an object is an instance method of its singleton class.

tl;dr Use class_eval/module_eval on a class/module to define instance methods, and instance_eval on a class/module to define class methods (or to be more precise, use instance_eval to define singleton methods). Additionally you can use instance_eval to access instance variables.

A terminology is a bit lacking in this case. ruby maintains a stack of class references (cref for short). When you open/reopen a class, the corresponding class reference is pushed to the stack. And the current class refernece affects where def defines methods (to which class/module they're added).

Now, class_eval/module_eval and class_exec/module_exec are aliases.

The *_exec() variants don't accept strings, and allow to pass arguments to the block. Since the *_eval() variants are mainly used I'll focus on them.

class_eval/module_eval changes cref and self to the receiver (Thing in Thing.module_eval(...)):

instance_eval changes cref to the singleton class of the receiver, and self to the receiver.

Let's see them in action:

class A
  p self          #=> A
  @a = 1
  def initialize
    @b = 2
  end
end
p A.instance_variables      #=> [:@a]
p A.new.instance_variables  #=> [:@b]

@a on a class level adds an instance variable to the class A as an object. I'm adding it here for completeness. But that's not how you add a class variable.

A.instance_eval do
  p self                #=> A
  p @a                  #=> 1
  def m() puts 'm' end
end
sclass = A.singleton_class
p sclass.instance_methods(false).include? :m  #=> true
A.m                                           #=> m

a = A.new
a.instance_eval do
  p self                  #=> #<A:0x00007fc497661be8 @b=2>
  p @b                    #=> 2
  def m2() puts 'm2' end
end
sclass = a.singleton_class
p sclass.instance_methods(false).include? :m2  #=> true
a.m2                                           #=> m2

So, inside instance_eval def adds a singleton method to the receiver (an instance method to the singleton class of the receiver). For a class/module that means a class/module method. For other objects, a method that is available for that particular object.

A.class_eval do
  p self                #=> A
  p @a                  #=> 1
  def m() puts 'm' end
end
p A.instance_methods(false).include? :m  #=> true
A.new.m                                  #=> m

And, inside class_eval def adds an instance method to the receiver itself (the class/module). class_eval is only available for classes/modules.

Also, when class_eval is passed a block, constant/class variable lookup is not affected:

module A
  C = 1
  @@c = 1
  class B
    C = 2
    @@c = 2
  end
  A::B.class_eval { p [C, @@c] }  #=> [1, 1]
  A::B.class_eval 'p [C, @@c]'    #=> [2, 2]
end

The naming is confusing. I might guess that instance in instance_eval suggests that receiver is treated as an instance (allows to change things for a particular instance), and class in class_eval as a class (allows to change things for a class of objects).

Fayfayal answered 15/1, 2023 at 14:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.