Why can you call instance_eval (as opposed to class_eval) inside 'initialize'?

  class Observer
    def initialize(&block)
      instance_eval(&block) if block_given?
    end
  end 

      

I'm wondering what an assumption is made here about the block type that is used with 'initialize'.

Since instance_eval is called, it means the block is being evaluated in the context of the Observer class.

Why do that, as opposed to, say, class_eval and what might result from evaluating a block in the context of a class?

Also how will this be called?

+2


source to share


4 answers


First of all, you cannot do something like this:

class Observer
  def initialize(&block)
    class_eval(&block) if block_given?
  end
end

      

Because it is class_eval

not defined for an instance Observer

. It is defined in Module

(from which it Class

descends). We'll come back to class_eval

later.

The reason for using the above idiom often allows you to initialize a block:

x = Observer.new do
  add_event(foo)
  some_other_instance_method_on_observer
  self.some_attribute = something
end

      

Alternatively, you can add methods to a given instance of the class:

foo = Observer.new do
  def foo
    'foo'
  end
end

foo.foo  # => "foo"

      

You can do roughly the same without instance_eval

:

class Foo
  def initialize
    yield self if block_given?
  end
end

foo = Foo.new do |x|
  x.add_event(foo)
  x.some_other_instance_method_on_observer
  x.self.some_attribute = something
end

      



But this prevents you from adding methods. If you did this:

foo = Foo.new do
  def foo
    'foo'
  end
end

foo.foo  # => "foo"

      

Seems to work, doesn't it? But what you actually did is add a method foo

to everything because it is self

set to the "main" object. This is equivalent to simply defining a method outside the block. They are added as instance methods in Object

, so they work for everything.

Now, as promised, a quick return to class_eval

. You can do something like this:

class Observer
  def initialize(&block)
    class.class_eval(&block) if block_given?
  end
end

      

But then you open up the whole class:

x = Observer.new { def foo; 'foo'; end }
x.foo                                      # => "foo"
y = Observer.new
y.foo                                      # => "foo"

      

This is usually not what we want to do. Also, it self

will be a class, not an instance. This makes it useless for initializing a block as shown above.

+9


source


One use case would be to set the state of the observer in its context.

Can



o = Observer.new do
      listen_to(<some object>)
      report_to(<something>)
    end

      

This usage will not work with class_eval, which could only access the class state.

+2


source


Initialization is an instance method. It is executed in the context of the instance, not the class itself. Thus, it instance_eval

causes the block to be executed in the context of the instance.

There class_eval

is no regular object in an object - it is only defined for classes.

0


source


Just because the method to initialize is an instance method and class_eval is defined for objects of type Class , which means that they can only be executed in a class or within the body of a class.

Thus, the following piece of code will throw an error:

". class_eval {methods} # => NoMethodError: undefined method`class_eval 'for" ": String

While this will work:

s.class.class_eval{methods} #=> ["methods", "respond_to?", "module_eval", "class_variables", "dup", "instance_variables", "protected_instance_methods", "__id__", "public_method_defined?", "eql?", "object_id", "const_set", "id", "singleton_methods", "send", "class_eval", "taint", "include?", "private_instance_methods", "frozen?", "instance_variable_get", "private_method_defined?", "__send__", "instance_of?", "name", "to_a", "autoload", "type", "new", "protected_methods", "instance_eval", "display", "instance_method", "instance_variable_set", "kind_of?", "protected_method_defined?", "extend", "const_defined?", "to_s", "ancestors", "public_class_method", "allocate", "class", "<=>", "hash", "<", "tainted?", "private_methods", "==", "instance_methods", "===", "class_variable_defined?", ">", "nil?", "untaint", "constants", ">=", "is_a?", "autoload?", "<=", "inspect", "private_class_method", "const_missing", "method", "clone", "=~", "public_instance_methods", "public_methods", "method_defined?", "superclass", "instance_variable_defined?", "equal?", "freeze", "included_modules", "const_get"]

      

0


source







All Articles