class Observer
def initialize(&block)
instance_eval(&block) if block_given?
end
end
I'm wondering what assumption is made here about the type of block that is being used with 'initialize'.
Since instance_eval is called, this means the block is evaluated in the context of the class Observer.
Why would it do that, as opposed to, say, class_eval and what is likely to be the outcome of evaluating a block in the context of the class?
Also, how would this be invoked?
First of all, you can't do something like this:
class Observer
def initialize(&block)
class_eval(&block) if block_given?
end
end
Because class_eval
isn't defined for an instance of Observer
. It is defined in Module
(which Class
descends from). We'll come back to class_eval
later.
The reason to use the above idiom is often to allow block initialization:
x = Observer.new do
add_event(foo)
some_other_instance_method_on_observer
self.some_attribute = something
end
Plus, 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 accomplish roughly the same thing 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 that doesn't give you the ability to add methods. If you were to do this:
foo = Foo.new do
def foo
'foo'
end
end
foo.foo # => "foo"
It seems to work, right? But what you've actually done is to add the foo
method to everything, because self
is set to the "main" object. It's equivalent to simply defining the method outside of the block. They get added as instance methods to Object
, so they work on everything.
Now, as promised, a brief return to class_eval
. You could do something like this:
class Observer
def initialize(&block)
class.class_eval(&block) if block_given?
end
end
But then you open up the entire class:
x = Observer.new { def foo; 'foo'; end }
x.foo # => "foo"
y = Observer.new
y.foo # => "foo"
This isn't typically what we want to do. Plus, self
will be the class, not the instance. This makes it useless for the block initialization as demonstrated above.
One use-case would be setting the state of the observer within its context.
Perhaps
o = Observer.new do
listen_to(<some object>)
report_to(<something>)
end
Such use would not work with a class_eval, which could only access class state
Initialize is an instance method. It's executed in the context of an instance, not the class itself. Thus instance_eval
causes the block to execute in the context of the instance as well.
There is no such thing as class_eval
in a normal object instance — it's only defined for classes.
Simply because the initialize method is an instance method and class_eval is defined for objects of type Class which means they can be executed only in class methods or inside the class body.
So the following snippet of code will raise an error:
"".class_eval{methods} #=> NoMethodError: undefined method `class_eval' for "":String
While this one will just 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"]
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.