views:

187

answers:

2

In this example from The Ruby Programming Language (p.270), I'm confused why the instance_eval method on the last line of the sample code defines a class method called String.empty.

Don't you use class_eval to define a class method and instance_eval when you want to define an instance method?

o.instance_eval("@x") # Return the value of o's instance variable @x

# Define an instance method len of String to return string length
String.class_eval("def len; size; end")

# Here's another way to do that
# The quoted code behaves just as if it was inside "class String" and "end"
String.class_eval("alias len size")

# Use instance_eval to define class method String.empty
# Note that quotes within quotes get a little tricky...
String.instance_eval("def empty; ''; end")
+2  A: 

Don't you use class_eval to define a class method and instance_eval when you want to define an instance method?

Unfortunately, it is not as straightforward as that.

First take a closer look at what the examples of class_eval are doing. class_eval is a method which comes from Ruby's module class so can be called on any class or module. When you use String.class_eval you are evaluating the given code in the context of the class. i.e. when you write String.class_eval("def len; size; end") it's exactly like you reopened the class and typed the code passed to class_eval e.g.

class String
  def len
    size
  end
end

Thus to add a class method using class_eval you would write String.class_eval("def self.empty; ''; end") which has the same effect as:

class String
  def self.empty
    ''
  end
end

instance_eval is defined in Ruby's Object class so is available on any Ruby object. In the general case it can be used to add a method to a specific instance. e.g. if we have a String str and say:

str.instance_eval("def special; size; end")

Then this will alias special to size just for str but not for any other String object:

irb(main):019:0> "other".special
NoMethodError: undefined method `special' for "other":String
        from (irb):19

To understand what is going on with String.instance_eval remember that the class String is itself an object (an instance of the class Class) and that there is such a singleton instance object of every class defined. When you use String.instance_eval you are evaluating the given code in the context of the String instance object. i.e. it is equivalent to reopening String's metaclass and typing the code passed e.g.

class String
  class << self
    def empty
      ''
    end
  end
end
mikej
A: 

This is the general theme:

Use ClassName.instance_eval to define singleton methods.

Use ClassName.class_eval to define instance methods.

This post has a very neat explanation, give it a shot...

khelll