From: Pit Capitain Date: 2007-01-27T23:39:57+09:00 Subject: Re: Got completely stuck with using 'class <<' feature. 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