Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
        }
like image 469
Nick Vanderbilt Avatar asked Aug 09 '10 22:08

Nick Vanderbilt


People also ask

When to use a class module instead of a module?

For example, when you have a module whose sole purpose is to basically do one single (probably large) task, and the coding is getting unruly (although it’s difficult to define “unruly”), the code itself may be simplified and easier maintained by going into a class module instead of a standard module.

What is the difference between class_Eval and module_Eval?

The Module#module_eval is the equivalent of Module#class_eval for modules $> post.add_comment ("Very nice !") => ["Very nice !"] We said that class_eval is used for adding methods and attributes to an existing class. And module_eval is used for adding methods and attributes to an existing modules. But this is just a convention..

What is the difference between module_ {Eval|Exec} and instance_{Eval| exec}?

Finally, module_ {eval|exec} is the same as the corresponding class_ {eval|exec}, but they are slightly different from instance_ {eval|exec} as they change what is the current opened class (i.e. what will be affected by def) in different ways:

When to use class modules in VBA?

When to use Class Modules in VBA. When you have one module that crunches a lot of code to do one particular task, likely the module only really has one main entry (one public sub/function that’s called), usually with a rather long list of arguments, most of which are probably optional, set with a default.


1 Answers

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.

like image 185
Max Chernyak Avatar answered Sep 28 '22 13:09

Max Chernyak