Got completely stuck with using 'class <<' feature

Hello!

I'm more than a bit confused with module inclusion and 'class <<' syntax for
class definition, so I'm asking for some help.
Here's a short program that produces an effect I cannot understand:

module One
  def self.included mod
    mod.extend Two
  end

  def one
    'one'
  end
end

module Two
  def two
    'two'
  end
end

module Three
  def self.included mod
    class << mod
      include One

      def three
        'three'
      end
    end
  end
end

class Klass1
  include Three
end

class Klass2
  include One
end

I get the following results after loading the .rb file:

irb(main):003:0> Klass1.methods.include? 'one'
=> true
irb(main):004:0> Klass1.instance_methods.include? 'one'
=> false
irb(main):005:0> Klass2.instance_methods.include? 'one'
=> true
irb(main):006:0> Klass2.methods.include? 'one'
=> false
irb(main):007:0> Klass1.methods.include? 'three'
=> true
irb(main):008:0> Klass1.methods.include? 'two'
=> false
irb(main):009:0> Klass1.instance_methods.include? 'two'
=> false
irb(main):010:0> Klass2.methods.include? 'two'
=> true
irb(main):011:0> Klass2.instance_methods.include? 'two'
=> false

What I am asking for is a step by step explanation of what happens here, as
I'm completely confused.
What I am trying to achieve is to make #two a class method of Klass1 - I
have no idea, why it doesn't show up in Klass1.methods.

The complexity of this array of inclusions is actually explained by the fact
that this sample is an oversimplified pattern extracted
from a project I'm working on now with modules One and Two being used by
client classes in my script. One uses extend to append
Two's methods to a class' list of class methods, thus extending both its
class and instance methods. However to work properly
methods form One and Two require some additional methods to be defined in
classes that include them. I want my
classes (e.g. Klass1) to include both those necessary methods and module One
by specifying just one include statement.
With that statement a utility module would be included (module Three) that
both defines additional methods I mentioned
before and mixes One into a class it gets included into. Unfortunately I
didn't succeed with this approach as methods
from Two wouldn't mix as client class' class methods. So I created a small
model of this situation to trace possible problems
with inclusions, and the results got me completely confused, so I decided to
post the code for small model in this mailing list
and ask for help and explanation.

Best regards,
Maksim Bartenev

Maksim Bartenev schrieb:

Here's a short program that produces an effect I cannot understand:

module Two
def two
   'two'
end
end

I show the effect of your program with boxes:

Two

···

+-----+
   > two |
   +-----+

This means, when you include module Two, you get an instance method #two.

module One
def self.included mod
   mod.extend Two
end

def one
   'one'
end
end

When you include module One, you get an instance method #one. Additionally, you extend the including module/class with module Two, which is the same as including module Two into the singleton class:

One
   +---------+
   > +-----+ |
   > > two | |
   > +-----+ |
   > one |
   +---------+

This means, when you include module One, you get an instance method #one plus an instance method in the singleton class, or a class method .two.

module Three
def self.included mod
   class << mod
     include One

     def three
       'three'
     end
   end
end
end

Here, all you are doing is altering the singleton class of the including module/class:

Three
   +-------------+
   > +---------+ |
   > > +-----+ | |
   > > > two | | |
   > > +-----+ | |
   > > one | |
   > > three | |
   > +---------+ |
   +-------------+

You include module One into the singleton class, and you add an additional class method .three. Note that "two" now is a method of the singleton class' singleton class. (You can nest them as long as you want.)

class Klass1
include Three
end

class Klass2
include One
end

After this, the following results are correct:

irb(main):003:0> Klass1.methods.include? 'one'
=> true
irb(main):004:0> Klass1.instance_methods.include? 'one'
=> false
irb(main):005:0> Klass2.instance_methods.include? 'one'
=> true
irb(main):006:0> Klass2.methods.include? 'one'
=> false
irb(main):007:0> Klass1.methods.include? 'three'
=> true
irb(main):008:0> Klass1.methods.include? 'two'
=> false
irb(main):009:0> Klass1.instance_methods.include? 'two'
=> false
irb(main):010:0> Klass2.methods.include? 'two'
=> true
irb(main):011:0> Klass2.instance_methods.include? 'two'
=> false

What I am asking for is a step by step explanation of what happens here, as
I'm completely confused.
What I am trying to achieve is to make #two a class method of Klass1 - I
have no idea, why it doesn't show up in Klass1.methods.

The complexity of this array of inclusions is actually explained by the fact
that this sample is an oversimplified pattern extracted
from a project I'm working on now with modules One and Two being used by
client classes in my script. One uses extend to append
Two's methods to a class' list of class methods, thus extending both its
class and instance methods. However to work properly
methods form One and Two require some additional methods to be defined in
classes that include them. I want my
classes (e.g. Klass1) to include both those necessary methods and module One
by specifying just one include statement.
With that statement a utility module would be included (module Three) that
both defines additional methods I mentioned
before and mixes One into a class it gets included into. Unfortunately I
didn't succeed with this approach as methods
from Two wouldn't mix as client class' class methods. So I created a small
model of this situation to trace possible problems
with inclusions, and the results got me completely confused, so I decided to
post the code for small model in this mailing list
and ask for help and explanation.

I'm not sure I understand you, but I think what you want is:

Three
   +---------+
   > +-----+ |
   > > two | |
   > +-----+ |
   > one |
   > three |
   +---------+

For this you should try the following code:

   module Three
     def self.included mod
       mod.instance_eval { include One }
     end

     def three
       'three'
     end
   end

Then you get:

Klass1.instance_methods - Object.instance_methods # => ["three", "one"]
Klass1.methods - Object.methods # => ["two"]

which I think is what you want. Note that you can't write

   mod.include One

because #include is a private method, so you have to use #instance_eval.

Regards,
Pit