liveBook · Manning

Class_eval

As explained in The Well-Grounded Rubyist, Fourth Edition

class_eval is a Ruby method that evaluates a string or block in the context of a class or module; module_eval is its alias.

class_eval evaluates a string or block in a class-definition context; module_eval is its alias. It provides an ad hoc way to enter an existing class or module rather than opening it with the class keyword. section 14.4 section 14.4.4

Use class_eval when ordinary class syntax is insufficient:

  • Evaluate a string as code in a class-definition context. section 14.4
  • Open an anonymous class such as Class.new and define instance methods on it. section 14.4.4
  • Preserve access to surrounding local variables inside the class_eval block. section 14.4.4

Block scope versus method scope

var = "initialized variable" C.class_eval { puts var } # initialized variable C.class_eval { def talk; puts var; end } C.new.talk # NameError The class_eval block can see var, but def starts a new scope. To retain the variable, define the method with define_method: C.class_eval { define_method("talk") { puts var } } C.new.talk # initialized variable section 14.4.4

Listing 15.7 MicroTest, a testing class that emulates some MiniTest functionality
require_relative 'callertools'
class MicroTest
  def self.inherited(c)
    c.class_eval do
      def self.method_added(m)
        if m =~ /^test/             1
          obj = self.new                2
          if obj.methods.include?(:setup)   3
            obj.setup
          end
          obj.send(m)
        end
      end
    end
  end
  def assert(assertion)       4
    if assertion
      puts "Assertion passed"
      true
    else
      puts "Assertion failed:"
      stack = CallerTools::Stack.new         5
      failure = stack.find {|call| call.meth !~ /assert/ } 5
      puts failure
      false
    end
  end
  def assert_equal(expected, actual)      6
    result = assert(expected == actual)
    puts "(#{actual} is not #{expected})" unless result 7
    result
  end
end

A compact MicroTest implementation uses class_eval inside inherited to install method_added on each subclass, then runs newly defined test methods with setup and assertions. section 15.5.2

How The Well-Grounded Rubyist, Fourth Edition covers it

How deep it goes
Explains strings, blocks, anonymous classes, lexical scope, and define_method, but stops short of exhaustive API reference.
Examples
Uses anonymous classes, captured local variables, define_method, and a MicroTest method_added implementation.
What it assumes
Assumes Ruby classes, blocks, methods, local-variable scope, and callbacks while clarifying class_eval behavior from concrete examples.
What you can do after
Build it from scratch by extending classes dynamically and preserving selected outer variables with define_method.
How it connects
Connects class_eval with eval, instance_eval, module_eval, define_method, local-variable scope, and callbacks.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of The Well-Grounded Rubyist, Fourth Edition. The section links point to the parts of the book it draws on.