why use include module when class_eval by itself would suffice
In the following code include module 开发者_如何学编程is used. The way I see it if include module is removed then also an instance method would be created. Then why user include module ?
http://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations.rb#L1416
include Module.new {
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def destroy # def destroy
super # super
#{reflection.name}.clear # posts.clear
end # end
RUBY
}
First of all let's make one thing clear. When they call super
inside the class_eval
— it has absolutely nothing to do with why they used include Module.new {}
thing. In fact the super
which was called inside the destroy
method is completely irrelevant to answering your question. There could be any arbitrary code inside that destroy method.
Now that we got it out of the way, here's what's going on.
In ruby, if you simply define a class method, and then define it again in the same class, you will not be able to call super
to access the previous method.
For example:
class Foo
def foo
'foo'
end
def foo
super + 'bar'
end
end
Foo.new.foo # => NoMethodError: super: no superclass method `foo' for #<Foo:0x101358098>
This makes sense, because the first foo
was not defined in some superclass, or anywhere up the lookup chain (which is where super
points). However, you can define the first foo
in such a way that when you later overwrite it — it will be available by calling super
. This is exactly what they wanted to achieve with doing module include.
class Foo
include Module.new { class_eval "def foo; 'foo' end" }
def foo
super + 'bar'
end
end
Foo.new.foo # => "foobar"
This works, because when you include a module, ruby inserts it into the lookup chain. This way you can subsequently call super
in the second method, and expect the included method to be called. Great.
However, you may wonder, why not simply include a module without all the tricks? Why are they using block syntax? We know that my above example is exactly equivalent to the following:
module A
def foo
'foo'
end
end
class Foo
include A
def foo
super + 'bar'
end
end
Foo.new.foo # => "foobar"
So why didn't they do that? The answer is — the call to reflection
. They needed to capture the variable (or method) which was available in the current context, which is reflection
.
Since they are defining the new module using block syntax, all the variables outside of the block are available for usage inside the block. Convenient.
Just to illustrate.
class Foo
def self.add_foo_to_lookup_chain_which_returns(something)
# notice how I can use variable something in the class_eval string
include Module.new { class_eval "def foo; '#{something}' end" }
end
end
# so somewhere else I can do
class Foo
add_foo_to_lookup_chain_which_returns("hello")
def foo
super + " world"
end
end
Foo.new.foo # => "hello world"
Neat, huh?
Now let me stress it again. The call to super
inside of the destroy
method in your example has nothing to do with any of the above. They called it for their own reasons, because maybe the class where this is happening is subclassing another class which already defined destroy
.
I hope this made it clear.
I'm guessing but... they don't want to overwrite the "destroy" method, and want to leave it available to be overloaded by some end-user (you or me), without it removing this "reflection.clear" functionality.
So - by including it as a module, they can call "super" which will call the original destroy or the overloaded version (written by the end-user).
Thanks to include
, destroy
method is not overwritten. It lands in ghost class that actual class derives from. That way, when one will call destroy
on AR object, original one will be called, and super
will call one from anonymous module (which will later call original destroy
from class that it derived from).
A bit tricky, indeed.
精彩评论