2

Idiosyncratic Ruby: idiosyncratic_eval

 3 years ago
source link: https://idiosyncratic-ruby.com/63-instance-eval.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

idiosyncratic_eval

When you get farther upwards the steep hill that is Ruby mastery, you will come across some powerful, yet slightly evil methods: instance_eval and class_eval¹. They allow you to execute code and define methods tied to a specific class, at the same time giving you access to outer scope variables through the Ruby block syntax. Their exact behavior varies, depending on the context they are used in. So what is the difference between all the evals?

¹ Also aliased as module_eval

Eval / Method Definition Comparison

In the following tables, you will find all combinations of defining a method and executing it in a different class context:

Class Scope

Definition Method No eval class_eval instance_eval def instance instance class define_method instance instance instance def self. class class class define_singleton_method class class class

Class-Class Scope (class << self)

Observations

While class_eval behaves exactly as if it was in no eval-context at all, instance_eval features a notable difference: def inside instance_eval will define methods one class-level higher. So when instance_eval is executed on instances, def will create instance methods instead of singleton methods. And when it is run on classes, def will create class methods instead of instance methods.

Another difference is that while class_eval is defined on Module, instance_eval lives in BasicObject allowing you to use it on any object, not only modules and classes. However, there is a simple way to use class_eval for instances, too. You can explicitly use the object's singleton class (class << self), which is a module:

o = Object.new # => #<Object:0x000055b6fdabf1f8>
o.singleton_class.class_eval do
  def m
    p self
  end
end

o.m # => #<Object:0x000055b6fdabf1f8>

Best Practice

Overall, the behavior of instance_eval is rather confusing and my recommendation is to avoid it and always use class_eval. If you do not need closure access, consider using no eval at all.

Reference / Examples: Class-Level Scope

For reference, what follows is a list of snippets illustrating each eval-define combination.

Class / def

Defines method on instance-level

class C
  def m
    p self
  end
end

C.new.m # => #<C:0x0000556efd3eb1a8>

Class / class_eval + def

Defines method on instance-level

class C
  class_eval{
    def m
      p self
    end
  }
end

C.new.m # => #<C:0x0000556efd3eb1a8>

Class / instance_eval + def

Defines method on class-level

class C
  instance_eval{
    def m
      p self
    end
  }
end

C.m # => C

Class / define_method

Defines method on instance-level

class C
  define_method(:m){
    p self
  }
end

C.new.m # => #<C:0x0000556efd3eb1a8>

Class / class_eval + define_method

Defines method on instance-level

class C
  class_eval{
    define_method(:m){
      p self
    }
  }
end

C.new.m # => #<C:0x0000556efd3eb1a8>

Class / instance_eval + define_method

Defines method on instance-level

class C
  instance_eval{
    define_method(:m){
      p self
    }
  }
end

C.new.m # => #<C:0x0000556efd3eb1a8>

Class / def self.

Defines method on class-level

class C
  def self.m
    p self
  end
end

C.m # => C

Class / class_eval + def self.

Defines method on class-level

class C
  class_eval{
    def self.m
      p self
    end
  }
end

C.m # => C

Class / instance_eval + def self.

Defines method on class-level

class C
  instance_eval{
    def self.m
      p self
    end
  }
end

C.m # => C

Class / define_singleton_method

Defines method on class-level

class C
  define_singleton_method(:m){
    p self
  }
end

C.m # => C

Class / class_eval + define_singleton_method

Defines method on class-level

class C
  class_eval{
    define_singleton_method(:m){
      p self
    }
  }
end

C.m # => C

Class / instance_eval + define_singleton_method

Defines method on class-level

class C
  instance_eval{
    define_singleton_method(:m){
      p self
    }
  }
end

C.m # => C

Reference / Examples: Class-Class-Level Scope

Class-Class / def

Defines method on class-level

class C
  class << self
    def m
      p self
    end
  end
end

C.m # => C

Class-Class / class_eval + def

Defines method on class-level

class C
  class << self
    class_eval{
      def m
        p self
      end
    }
  end
end

C.m # => C

Class-Class / instance_eval + def

Defines method on class-class-level

class C
  class << self
    instance_eval{
      def m
        p self
      end
    }
  end
end

C.singleton_class.m #=> #<Class:C>

Class-Class / define_method

Defines method on class-level

class C
  class << self
    define_method(:m){
      p self
    }
  end
end

C.m # => C

Class-Class / class_eval + define_method

Defines method on class-level

class C
  class << self
    class_eval{
      define_method(:m){
        p self
      }
    }
  end
end

C.m # => C

Class-Class / instance_eval + define_method

Defines method on class-level

class C
  class << self
    instance_eval{
      define_method(:m){
        p self
      }
    }
  end
end

C.m # => C

Class-Class / def self.

Defines method on class-class-level

class C
  class << self
    def self.m
      p self
    end
  end
end

C.singleton_class.m # => C

Class-Class / class_eval + def self.

Defines method on class-class-level

class C
  class << self
    class_eval{
      def self.m
        p self
      end
    end
  }
end

C.singleton_class.m #=> #<Class:C>

Class-Class / instance_eval + def self.

Defines method on class-class-level

class C
  class << self
    instance_eval{
      def self.m
        p self
      end
    }
  end
end

C.singleton_class.m #=> #<Class:C>

Class-Class / define_singleton_method

Defines method on class-class-level

class C
  class << self
    define_singleton_method(:m){
      p self
    }
  end
end

C.singleton_class.m # => C

Class-Class / class_eval + define_singleton_method

Defines method on class-class-level

class C
  class << self
    class_eval{
      define_singleton_method(:m){
        p self
      }
    }
  end
end

C.singleton_class.m #=> #<Class:C>

Class-Class / instance_eval + define_singleton_method

Defines method on class-class-level

class C
  class << self
    instance_eval{
      define_singleton_method(:m){
        p self
      }
    }
  end
end

C.singleton_class.m #=> #<Class:C>

Also See

More Idiosyncratic Ruby


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK