Ruby: class_eval vs module_eval

Adding methods or attributes to a class/module “on-the-fly” is a pretty common pattern in Ruby (activerecord, activesupport, rake, rack, etc..).

To do so, we can use the Module#class_eval and Module#module_eval methods.

What are the main differences between these 2 methods ?

class_eval

In Ruby, when we want to add a method to a class we can use the Module#class_eval method. This method accepts a String or a block as argument

array_second = <<-RUBY
def second
self[1]
end
RUBY
Array.class_eval(array_second)
String.class_eval do
def /(delimiter)
split(delimiter)
end
end
$> [1,2,3].second
=> 2
$> "1,2,3" / ','
=> ["1", "2", "3"]

The call to Array.class_eval(array_second) adds the method second to any instance of Array by passing a String that will be evaluated in the context of the class Array .

The call to String.class_eval with a block will evaluate the content of the block in the context of the class String. Here it’ll add a String#/(delimiter) method — that we can use as operator — to any instance of String.

NB: feel free to read this article about heredocs if the <<-RUBY syntax is unfamiliar to you

module_eval

The Module#module_eval is the equivalent of Module#class_eval for modules

module Commentable
def add_comment(comment)
self.comments << comment
end
  def comments
@comments ||= []
end
end
Commentable.module_eval do
def comment_count
comments.count
end
end
class Post
include Commentable
end
$> post = Post.new
=> #<Post:0x00007fd9ac0238b0>
$> post.add_comment("Very nice !")
=> ["Very nice !"]
$> post.comment_count
=> 1

two skins for one core

We said that class_eval is used for adding methods and attributes to an existing class.

And module_eval is used for adding methods and attributes to an existing modules.

But this is just a convention..

In effect, class_eval is an alias to module_eval.

Let’s have a look to the Ruby code source to confirm the previous affirmation.

in ruby/vm_eval.c

rb_define_method(rb_cModule, "module_eval", rb_mod_module_eval, -1);    rb_define_method(rb_cModule, "class_eval",  rb_mod_module_eval, -1);

As we can see the module_eval and class_eval share the same C function named rb_mod_module_eval().

As this C function designation includes module_eval we can say that class_eval is an alias to module_eval.

Voilà !

May I have your attention please 🎤🎤

Feel free to subscribe here: www.rubycademy.com


Thank you for taking the time to read this post :-)

Feel free to 👏 and share this Medium post if it has been useful for you.

Here is a link to my last medium post: The yield keyword.