Initialization via a Module

I have a module that needs to set a few instance variables on the instances it is used with, during initialization.

I wrote a library that did this a while ago, and I really thought it was working fine when I left it. It had code like this:

module Foo
   attr_reader :foo
   def initialize
     @foo = 'foo'
     super
   end
end

class Bar
   include Foo
   attr_reader :bar
   def initialize
     @bar = 'bar'
   end
end

b = Bar.new
p b.foo

...but Foo#initialize is not being called. Am I wrong? Did I leave my library in a broken state, despite calling it 1.0.3 and releasing it and using it? Or did the above used to work? (In 1.8.1 or 2 or so?)

Anyhow, I cobbled the following together, and it works, but...is there a better/cleaner way to do the following:

module Foo
   attr_reader :foo
   def self.included( klass )
     klass.instance_eval{
       alias_method :_pre_foo_initialize, :initialize
       define_method( :initialize ){ |*args|
         _setup_foo
         _pre_foo_initialize( *args )
       }
     }
   end

   def self.extended( obj )
     obj.instance_eval{ _setup_foo }
   end

   private
     def _setup_foo
       @foo = 'foo'
     end
end

class Bar
   attr_reader :bar
   def initialize( bar )
     @bar = bar
   end
   include Foo
end

class Cow
   attr_reader :cow
   def initialize
     @cow = 'cow'
   end
end

b = Bar.new( 'bar' )
p b.bar
p b.foo

c = Cow.new
c.extend( Foo )
p c.cow
p c.foo

···

--
(-, /\ \/ / /\/

Call `super' from `initialize'.

···

--
Daniel Brockman <daniel@brockman.se>

Errm.. isn't that what he's doing?

module Foo
  attr_reader :foo
  def initialize
    @foo = 'foo'
    super
  end
end

···

On 7/12/05, Daniel Brockman <daniel@brockman.se> wrote:

Call `super' from `initialize'.

Gleah, of course. My library was fine, my use of it was broken. Thanks :slight_smile:

I did discover this, problem, however:

module Foo
   attr_reader :foo
   def initialize( *args )
     super
     @foo = 'foo'
   end
end

class Bar
   include Foo
   def initialize
     super
     @bar = 'bar'
   end
end

b = Bar.new
p b.foo #=> "foo"

class Bar2
   include Foo
   def initialize( bar_value )
     super
     @bar = bar_value
   end
end

b2 = Bar2.new( 'bar2' )

/Users/gavinkistner/Desktop/tmp.rb:5:in `initialize': wrong number of arguments (1 for 0) (ArgumentError)
     from /Users/gavinkistner/Desktop/tmp.rb:5:in `initialize'
     from /Users/gavinkistner/Desktop/tmp.rb:23:in `initialize'
     from /Users/gavinkistner/Desktop/tmp.rb:28:in `new'
     from /Users/gavinkistner/Desktop/tmp.rb:28

Obviously I can leave the call to #super out of my module, and leave it up to the consumer of my module to know that including mine means that any inherited class's initialization won't be called, unless the user explicitly looks for that ancestor and binds and so on. But that seems gross.

Is there a good solution for writing a module's initialize so that it can properly pass initialization to the parent class first? Something longwinded involving arity checking?

···

On Jul 12, 2005, at 10:20 PM, Daniel Brockman wrote:

Call `super' from `initialize'.

Joe Van Dyk wrote:

···

On 7/12/05, Daniel Brockman <daniel@brockman.se> wrote:

Call `super' from `initialize'.

Errm.. isn't that what he's doing?

module Foo
  attr_reader :foo
  def initialize
    @foo = 'foo'
    super
  end
end

Not in Bar as far as I can see.
*That's* the place where it should be.
:slight_smile:

Sorry for the rhyme...

    robert

module Foo
   attr_reader :foo
   def initialize( *args )
     super
     @foo = 'foo'
   end
end

I think that what you want is
  super(*args)

Sylvain

I have zero understanding of this stuff, but I thought #super called
the initialize function on the class' parent. So you'd use it for
inheritance-type stuff only and not for modules. But I'm most likely
mistaken.

···

On 7/13/05, Robert Klemme <bob.news@gmx.net> wrote:

Joe Van Dyk wrote:
> On 7/12/05, Daniel Brockman <daniel@brockman.se> wrote:
>> Call `super' from `initialize'.
>
> Errm.. isn't that what he's doing?
>
> module Foo
> attr_reader :foo
> def initialize
> @foo = 'foo'
> super
> end
> end

Not in Bar as far as I can see.
*That's* the place where it should be.

Calling super without any parentheses is a special case where it passing along the argument list to the current function automatically.

···

On Jul 13, 2005, at 6:52 AM, Sylvain Joyeux wrote:

module Foo
   attr_reader :foo
   def initialize( *args )
     super
     @foo = 'foo'
   end
end

I think that what you want is
    super(*args)

Joe Van Dyk wrote:

Joe Van Dyk wrote:

Call `super' from `initialize'.

Errm.. isn't that what he's doing?

module Foo
  attr_reader :foo
  def initialize
    @foo = 'foo'
    super
  end
end

Not in Bar as far as I can see.
*That's* the place where it should be.

I have zero understanding of this stuff, but I thought #super called
the initialize function on the class' parent. So you'd use it for
inheritance-type stuff only and not for modules. But I'm most likely
mistaken.

This might help clarify:

11:40:36 [robert.klemme]: irbs

module Mod;end

=> nil

class Foo; end

=> nil

class Bar<Foo;include Mod; end

=> Bar

Bar.ancestors

=> [Bar, Mod, Foo, Object, Kernel]

class Zap; include Mod end

=> Zap

Zap.ancestors

=> [Zap, Mod, Object, Kernel]

That's exactly the chain along which initialize super calls occur.

We had a discussion a while ago about automatically generating super class
in constructors under certain circumstances. I don't remember the subject
thogh...

Kind regards

    robert

···

On 7/13/05, Robert Klemme <bob.news@gmx.net> wrote:

On 7/12/05, Daniel Brockman <daniel@brockman.se> wrote:

Hi Joe,

Joe Van Dyk <joevandyk@gmail.com> writes:

I have zero understanding of this stuff, but I thought #super called
the initialize function on the class' parent.

That's right.

So you'd use it for inheritance-type stuff only and not for modules.

But modules are inheritance-type stuff. You should think of modules
as uninstantiable classes, and `mixin' as a warm and fuzzy euphemism
for multiple inheritance. Then it will all start to make sense.

(There are people who feel that truly unifying modules and classes to
just one essential concept would reduce this kind of confusion.)

But I'm most likely mistaken.

I don't think you realize that when you include a module `Foo' into a
module or class `Bar', then `Foo' becomes the parent of `Bar'.

The following example should illustrate this nicely:

   module Foo
     def initialize
       puts "Foo#initialize not calling super"
     end
   end

   class Bar
     def initialize
       puts "Bar#initialize is the topmost initializer"
     end
   end

   class Baz < Bar
     def initialize
       puts "Baz#initialize calling super..."
       super
     end
   end

   Baz.ancestors #=> [Baz, Bar, Object, Kernel]
   Baz.superclass #=> Bar

   Baz.new
   # Baz#initialize calling super...
   # Bar#initialize is the topmost initializer

   class Baz
     include Foo
   end

   Baz.ancestors #=> [Baz, Foo, Bar, Object, Kernel]
   Baz.superclass #=> Bar

   Baz.new
   # Baz#initialize calling super...
   # Foo#initialize not calling super

Note in particular how failing to call `super' from an `initialize'
method in a module prevents the rest of the initializers from running.

That's because modules are really just emasculated classes, and method
calls work just the same across modules and classes.

···

--
Daniel Brockman <daniel@brockman.se>

    So really, we all have to ask ourselves:
    Am I waiting for RMS to do this? --TTN.

Gavin Kistner wrote:

module Foo
   attr_reader :foo
   def initialize( *args )
     super
     @foo = 'foo'
   end
end

I think that what you want is
    super(*args)

Calling super without any parentheses is a special case where it
passing along the argument list to the current function automatically.

Answering your earlier question: I'd do

module X
  def initialize(*a,&b)
    ...
    super
  end
end

The problem with this (and you see it in your example) is that the super
class initialize does not take any arguments. So you should rather have
done:

class Bar2
   include Foo
   def initialize( bar_value )
     super()
     @bar = bar_value
   end
end

(Explicitely use an empty arg list on super)

The whole issue is so complicated (because of dynamism, because of all
possible combinations of initialize with and without arguments and with
and without block...) that sometimes I think the mechanism should be
changed for modules. For example by allowing only initialize(*a,&b) in
modules (or redifining it to this) to avoid breaking the inheritance
initialization chain. Another option is to completely forbit initialize
in modules (which I don't like because there are cases where you rather
want it). etc.

Kind regards

    robert

···

On Jul 13, 2005, at 6:52 AM, Sylvain Joyeux wrote:

Perfect explanation. I thought that the methods and constants in a
module were just inserted into a class.

Thanks,
Joe

···

On 7/13/05, Daniel Brockman <daniel@brockman.se> wrote:

Hi Joe,

Joe Van Dyk <joevandyk@gmail.com> writes:

> I have zero understanding of this stuff, but I thought #super called
> the initialize function on the class' parent.

That's right.

> So you'd use it for inheritance-type stuff only and not for modules.

But modules are inheritance-type stuff. You should think of modules
as uninstantiable classes, and `mixin' as a warm and fuzzy euphemism
for multiple inheritance. Then it will all start to make sense.

Joe Van Dyk <joevandyk@gmail.com> writes:

But modules are inheritance-type stuff. You should think of modules
as uninstantiable classes, and `mixin' as a warm and fuzzy euphemism
for multiple inheritance. Then it will all start to make sense.

Perfect explanation.

Thanks. :slight_smile:

I thought that the methods and constants in a module were just
inserted into a class.

Yet another reason why modules and classes should be unified.

···

On 7/13/05, Daniel Brockman <daniel@brockman.se> wrote:

--
Daniel Brockman <daniel@brockman.se>

I rather like the Object > Module > Class hierarchy
but I'd like to unify subclassing and including. They just
don't seem (in practice) to be very different from one-another.
In particular, method resolution (implicit and via super)
doesn't seem to be any different with respect to an
included module or a declared superclass. Other than
method resolution, what language features depend on the
ancestors list?

Gary Wright

···

On Jul 15, 2005, at 12:40 AM, Daniel Brockman wrote:

I thought that the methods and constants in a module were just
inserted into a class.

Yet another reason why modules and classes should be unified.

I don't see it, personally. I really don't see why they should be
unified in the least.

-austin

···

On 7/15/05, Daniel Brockman <daniel@brockman.se> wrote:

> I thought that the methods and constants in a module were just
> inserted into a class.
Yet another reason why modules and classes should be unified.

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

SomeClass === an_object || an_object.is_a?(Array)

...

   class Class
     def singleton?
       not ancestors.include?(self)
     end
   end

i seem to end up using it alot...

-a

···

On Fri, 15 Jul 2005 gwtmp01@mac.com wrote:

Other than method resolution, what language features depend on the ancestors
list?

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================

I wouldn't really call these language features. Can't they
just be implemented as standard library calls if you assume
the ancestors array has been constructed? And aren't they
oblivious to whether a entry in the ancestor's array arrived
there via subclassing or via including?

You certainly need the concept of an ordered list of
ancestors but do you need to have two ways (subclassing
and including) of extending that list?

Gary Wright

···

On Jul 15, 2005, at 10:47 AM, Ara.T.Howard wrote:

On Fri, 15 Jul 2005 gwtmp01@mac.com wrote:

Other than method resolution, what language features depend on the ancestors
list?

  SomeClass === an_object || an_object.is_a?(Array)

  class Class
    def singleton?
      not ancestors.include?(self)
    end
  end