开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜