Forum: Ruby initializing instance variables in a module

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Jeff R. (Guest)
on 2006-05-22 19:53
(Received via mailing list)
Have a module:

Module Foo
   def add_stuff(key, stuff)
     @foo_the_stuff[key] = stuff
   end
end

class Bar
   include Foo
end

Is there a standard or correct way to initialize @foo_the_stuff in this
example?  Should the add_stuff method check whether the variable has
been initialized every time, or should this be done by implementing a
callback like append_features or included?  (That's how I'd want to do
it, but it's not clear which or how...)

Thanks,
Jeff
Ryan L. (Guest)
on 2006-05-22 19:59
(Received via mailing list)
module Foo
   def add_stuff(key, stuff)
     (@foo_the_stuff ||= {})[key] = stuff
   end
end

If @foo_the_stuff is null, it will be initialized to an empty hash,
otherwise the existing value will be used. I consider this use of ||=
to be a Ruby idiom.

Ryan
Jeff R. (Guest)
on 2006-05-22 20:15
(Received via mailing list)
That's what I was afraid of.  In this example case it's no big deal, but
in a module where you are accessing a more complex variable many times
it's a real pain, let alone inefficient.  We are working on an event
system, so the hash is a hash of arrays or possibly a hash of hashes.
Any of 4 methods could be called, which would require an initialized
value.  Having this in every method is sorta lame...

   handlers = (@event_handlers ||= Hash.new {|k,v| k[v] = []})[key]

Of course we can pull it out to a method:

def handlers(key)
   @event_handlers ||= Hash.new {|k,v| k[v] = []})[key]
end

  but just initializing on inclusion seems to be the most reasonable
way, doesn't it?

-Jeff
Tim H. (Guest)
on 2006-05-22 20:20
Jeff R. wrote:
>   but just initializing on inclusion seems to be the most reasonable
> way, doesn't it?
>

Put the initialization code in the module's initialize method and have
every class that includes the module call super from its initialize
method:

module Foo
   def initialize(*args, &block)
       # whatever...
   end
end

class Bar
   include Foo
   def initialize(*args, &block)
       super  # calls Foo's initialize
       # more stuff
   end
end
Francis C. (Guest)
on 2006-05-22 20:25
(Received via mailing list)
Jeff, I tried a few combinations of class_eval and module_eval and I
couldn't figure it out. What seems to be desired is for the mixed-in
module code to create an instance variable in the class which is doing
the mixin. I'll bet there's a way to do it.
unknown (Guest)
on 2006-05-22 22:53
(Received via mailing list)
Hi --

On Tue, 23 May 2006, Jeff R. wrote:

>>>
>>> Thanks,
>> to be a Ruby idiom.
>  handlers = (@event_handlers ||= Hash.new {|k,v| k[v] = []})[key]
>
> Of course we can pull it out to a method:
>
> def handlers(key)
>  @event_handlers ||= Hash.new {|k,v| k[v] = []})[key]
> end
>
> but just initializing on inclusion seems to be the most reasonable way,
> doesn't it?

Not if it leads to code you strongly dislike :-)  It seems OK to wrap
it in a method, perhaps like this:

   def event_handlers
     @event_handlers ||= Hash.new {|k,v| k[v] = []}
   end

   def whatever ...
     handlers = event_handlers[key]
   end


David
Jeff R. (Guest)
on 2006-05-22 23:06
(Received via mailing list)
Well, it took a while but I found the answer.  This is done in
ActiveRecord to wrap a possible setup method in the unit tests for
auto-instrumentation of fixtures.  Once I knew what to look for,
however, I found an old post by Florian G. that lays out the exact
answer nicely:

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/...

For educational purposes though, I'll explain a few things I found...

1) The append_features method is deprecated, and the included method is
now the preferred callback.  This method is called when a module is
included, and it is passed the module that has done the including.

2) There is another callback, method_added, which is a meta-class method
that is called every time a new method is defined for its class, and the
symbol for the method name is passed.

So, the trick is to add a custom method_added method to the meta-class
of the class that does the including by extending it in the included
callback.  The custom method_added needs to redefine initialize after it
is added so that it does the custom setup, then calls the original.

Phew...  Too bad we can't just have another callback that lets us do
this easily.  Maybe that feels too much like multiple inheritance or
something?

module Foo
   def module_initialize
     @myvar = Hash.new {|k,v| k[v] = []}
   end

   def bar(key)
     @mybar[key]
   end
end

Isn't this a common thing to want?

-Jeff
unknown (Guest)
on 2006-05-23 00:27
(Received via mailing list)
Hi --

On Tue, 23 May 2006, Jeff R. wrote:

> the preferred callback.  This method is called when a module is included, and
>
>  end
> end
>
> Isn't this a common thing to want?

I'm probably being thick but what's wrong with just writing a method
that wraps @myvar and initializes it if necessary, and then returns
it?


David
Francis C. (Guest)
on 2006-05-23 00:34
(Received via mailing list)
You still have to call it every time you use the variable.
Simon Kröger (Guest)
on 2006-05-23 00:49
(Received via mailing list)
Francis C. schrieb:
> You still have to call it every time you use the variable.
>

Do you realy need to use the variable directly?
My scripts tend to have very little use of '@'.

module Foo
 def bar(key)
   @myvar ||= Hash.new {|k,v| k[v] = []}
   @myvar[key]
 end
end

class Test
  include Foo
end

t = Test.new
t.bar('baz') << 'eeek'
p t.bar('baz')


I didn't followed the whole thread so please ignore me
if this was suggested before.

cheers

Simon
unknown (Guest)
on 2006-05-23 00:59
(Received via mailing list)
On Tue, 23 May 2006, Jeff R. wrote:

> the preferred callback.  This method is called when a module is included, and
>
>  end
> end
>
> Isn't this a common thing to want?

and precisely one of the reasons i wrote traits - with traits you can
simply
ignore the issue altogether:

   harp:~ > gem install traits

   harp:~ > cat a.rb
   require 'rubygems'
   require 'traits'

   module Foo
     trait 'myvar' => Hash.new{|k,v| k[v] = []}
     def bar(key) myvar[key] end
   end

   class Bar
     include Foo
   end

   b = Bar.new

   p b.bar(:k)

   p b.myvar


   harp:~ > ruby a.rb
   []
   {:k=>[]}



;-)

-a
Francis C. (Guest)
on 2006-05-23 01:31
(Received via mailing list)
Looks promising but what if you have to assign to the variable? This
code
throws an exception:

module Mixin
  def myvar
    @myvar ||= 100
    @myvar
  end

  def inc_myvar
    myvar += 1
  end
end

class Klass
  include Mixin
end

Klass.new.inc_myvar
Joel VanderWerf (Guest)
on 2006-05-23 01:40
(Received via mailing list)
Francis C. wrote:
>    myvar += 1
self.myvar += 1
unknown (Guest)
on 2006-05-23 01:41
(Received via mailing list)
On Tue, 23 May 2006, Francis C. wrote:

>   myvar += 1
> end
> end
>
> class Klass
> include Mixin
> end
>
> Klass.new.inc_myvar

   harp:~ > cat a.rb
   require 'traits'

   module Mixin
     trait 'myvar' => 100

     def inc_myvar() myvar( myvar + 1) end
   end

   class Klass
     include Mixin
   end

   p Klass.new.inc_myvar


   harp:~ > ruby a.rb
   101




-a
Simon Kröger (Guest)
on 2006-05-23 01:44
(Received via mailing list)
Francis C. wrote:
> Looks promising but what if you have to assign to the variable? This code
> throws an exception:

[...]

>  def inc_myvar
>    myvar += 1
>  end
> end

this is equivalent to

myvar = myvar + 1

which tries to initialize a *local* variable, thats not what you want.
try self.myvar += 1

this of course does only work if you define a setter for myvar.

module Mixin
 attr_writer :myvar

 def myvar
   @myvar ||= 100
   @myvar
 end

 def inc_myvar
   self.myvar += 1
 end
end

class Klass
 include Mixin
end

Klass.new.inc_myvar

cheers

Simon
Joel VanderWerf (Guest)
on 2006-05-23 01:47
(Received via mailing list)
Joel VanderWerf wrote:
> Francis C. wrote:
>> Looks promising but what if you have to assign to the variable? This code
>> throws an exception:
>>
>> module Mixin
>>  def myvar
>>    @myvar ||= 100
>>    @myvar
>>  end

Oops... forgot this, obviously (attr_writer is an alternative):

    def myvar=(val)
      @myvar = val
    end
Francis C. (Guest)
on 2006-05-23 02:00
(Received via mailing list)
We've definitely wandered into one of Ruby's darker, mustier corners.
None
of these solutions really sing.
Yukihiro M. (Guest)
on 2006-05-23 04:34
(Received via mailing list)
Hi,

In message "Re: initializing instance variables in a module"
    on Tue, 23 May 2006 00:51:35 +0900, Jeff R. 
<removed_email_address@domain.invalid>
writes:

|Is there a standard or correct way to initialize @foo_the_stuff in this
|example?

This is not the answer, but I have been aware of this issue.  I'm
planning to add method combination a la CLOS in the future to support
this issue.  It is another option that adding a new hook like
'module_initialize' which is called at instantiation time for all
modules.

							matz.
unknown (Guest)
on 2006-05-23 05:10
(Received via mailing list)
On Tue, 23 May 2006, Yukihiro M. wrote:

> this issue.  It is another option that adding a new hook like
> 'module_initialize' which is called at instantiation time for all
> modules.
>
> 							matz.

hi matz-

(rcr ahead)

the other day i realized that one could detect class creation using

   class Class
     def inherited
       ...
     end
   end

but that no such hook existed for class Module.  could one be added such
as

   class Module
     def instantiated
       ...
     end
   end

to hook into rb_define_module/rb_define_module_under?


regards.

-a
Bob H. (Guest)
on 2006-05-23 05:41
(Received via mailing list)
Hi,

On May 22, 2006, at 12:20 PM, Tim H. wrote:

>    def initialize(*args, &block)
> end
What is wrong with what Tim suggests? I must be missing something.
Here is a specific example:


module Foo
   def initialize
     super
     @foo_the_stuff = {}
   end

   def add_stuff(key, stuff)
     @foo_the_stuff[key] = stuff
   end
end

class Bar
   include Foo

   def show_stuff
     puts "stuff: #{@foo_the_stuff.inspect}"
   end
end

b1 = Bar.new
b2 = Bar.new
b1.add_stuff("one", 1)
b2.add_stuff("two", 2)
b1.add_stuff("three", 3)
b2.add_stuff("four", 4)

b1.show_stuff
b2.show_stuff


Cheers,
Bob

----
Bob H.                  -- blogs at <http://www.recursive.ca/
hutch/>
Recursive Design Inc.          -- <http://www.recursive.ca/>
Raconteur                      -- <http://www.raconteur.info/>
xampl for Ruby                 -- <http://rubyforge.org/projects/xampl/>
Yukihiro M. (Guest)
on 2006-05-23 08:27
(Received via mailing list)
Hi,

In message "Re: [OT] Re: initializing instance variables in a module"

|the other day i realized that one could detect class creation using
|
|   class Class
|     def inherited
|       ...
|     end
|   end
|
|but that no such hook existed for class Module.  could one be added such as
|
|   class Module
|     def instantiated
|       ...
|     end
|   end
|
|to hook into rb_define_module/rb_define_module_under?

I am not sure what situation that kind of method is useful.  But in
any case I don't feel the word "instantiated" is the right word for
the method.  Note that I am not opposing the method itself.

							matz.
Francis C. (Guest)
on 2006-05-23 10:14
(Received via mailing list)
How about "included" instead of "instantiated"?
unknown (Guest)
on 2006-05-23 10:36
(Received via mailing list)
On Tue, 23 May 2006, Yukihiro M. wrote:

> I am not sure what situation that kind of method is useful.

   harp:~ > cat a.rb
   class Class
     WRAPPER = []
     def inherited other
       wrapper = WRAPPER.first
       wrapper.const_set other.name.split(%r/::/).last, other if wrapper
     end
     def load file, m
       wrapping(m){ ::Kernel.load file, wrap = true }
     end
     def wrapping m
       WRAPPER.push m
       yield
     ensure
       WRAPPER.pop
     end
   end

   m = Module.new

   Class.load 'b.rb', m

   p m::C
   p C


   harp:~ > cat b.rb
   class C; end


   harp:~ > ruby a.rb
   #<Module:0xb75cb50c>::C
   a.rb:24: uninitialized constant C (NameError)


i'd like to do the same for modules.


> But in > any case I don't feel the word "instantiated" is the right word for
> the method.  Note that I am not opposing the method itself.

   how about #inherited then?

     harp:~ > ruby -e'  p Module.new.is_a?(Module)  '
     true

   or perhaps #defined

   or perhaps #created


regards.

-a
Joel VanderWerf (Guest)
on 2006-05-23 10:45
(Received via mailing list)
Francis C. wrote:
> How about "included" instead of "instantiated"?

Defining SomeModule.included already does something quite different, so
it would be confusing to define Module#included.

Anyway, I think what he wanted was a callback that fires not when a
module is included, but when ruby evals this code:

module Foo
end

You can get this behavior for classes rather than modules: you get a
callback that fires for

class Bar
end

by defining the Class#inherited method (a global definition, with all
the inherent dangers), since Bar inherits (implicitly) from Object.
Yukihiro M. (Guest)
on 2006-05-23 11:26
(Received via mailing list)
Hi,

In message "Re: module creation hook method (Re: [OT] Re: initializing
instance variables in a module)"
    on Tue, 23 May 2006 15:35:30 +0900, removed_email_address@domain.invalid 
writes:

|   harp:~ > cat a.rb
|   class Class
|     WRAPPER = []
|     def inherited other
|       wrapper = WRAPPER.first
|       wrapper.const_set other.name.split(%r/::/).last, other if wrapper
|     end
|     def load file, m
|       wrapping(m){ ::Kernel.load file, wrap = true }
|     end
|     def wrapping m
|       WRAPPER.push m
|       yield
|     ensure
|       WRAPPER.pop
|     end
|   end
|
|   m = Module.new
|
|   Class.load 'b.rb', m
|
|   p m::C
|   p C

|i'd like to do the same for modules.

Thank you for the example.  I am still not sure this is a right tool
for the right purpose, but I do understand what you wanted to do in
the code.  Hidetoshi NAGAI is proposing load_to(file, module) method
to do the thing you wanted.  In my opinion, this is better way to
implement.

|   how about #inherited then?
|
|     harp:~ > ruby -e'  p Module.new.is_a?(Module)  '
|     true

I am not positive for this one.  Since this is misleading people to
feel like modules are inherited from something.

|   or perhaps #defined
|   or perhaps #created

Maybe.  Much better than inherited.  Or maybe invoking initialize at
the module creation is even better.

							matz.
Yukihiro M. (Guest)
on 2006-05-23 11:57
(Received via mailing list)
Hi,

In message "Re: module creation hook method (Re: [OT] Re: initializing
instance variables in a module)"
    on Tue, 23 May 2006 16:23:23 +0900, Yukihiro M.
<removed_email_address@domain.invalid> writes:

|Or maybe invoking initialize at the module creation is even better.

Forget it.  Module (and Class) already has its own initialize method
to call with Module.new (Class.new).

							matz.
Florian GroÃ? (Guest)
on 2006-05-23 15:09
(Received via mailing list)
Yukihiro M. wrote:

> Forget it.  Module (and Class) already has its own initialize method
> to call with Module.new (Class.new).

But it's not called for the module Foo; end syntax:

irb(main):012:0> class Module; def initialize(*x) p x; end; end
=> nil
irb(main):013:0> Module.new
[]
=> #<Module:0x2c87558>
irb(main):014:0> module M; end
=> nil
Daniel S. (Guest)
on 2006-05-23 15:34
(Received via mailing list)
Simon Kröger wrote:
>
> t = Test.new
> t.bar('baz') << 'eeek'
> p t.bar('baz')

Why not just

   module Foo
     def bar
       @bar ||= Hash.new{|hsh, key| hsh[key] = []}
     end
   end

   class Test
     include Foo
   end

   test = Test.new
   test.bar[:baz] << 'bur'

`a ||= b' returns `a' or, if `a' is nil, `b'.


Daniel
Jeff R. (Guest)
on 2006-05-23 16:24
(Received via mailing list)
Yukihiro M. wrote:
> this issue.  It is another option that adding a new hook like
> 'module_initialize' which is called at instantiation time for all
> modules.
>
> 							matz.

That would do the trick, and it would probably make the AOP people very
happy.  But that is a bit different than an explicit module
initialization method.  Is there a reason you don't want
module_initialize or something like that?

CLOS does something like (defmethod rock :before ...), right?  Any ideas
on how the method combination would work in ruby?

# Maybe using methods...
module Rocker
   def pre_rock
     puts 'pre rock'
   end

   before :rock, :pre_rock
end

# Or with syntax...
module Rocker
   def before:rock
     puts 'pre rock'
   end
end

class Foo
   include Rocker

   def rock
     puts 'We are rocking'
   end
end

Foo.new.rock # => "pre rock\nWe are rocking\n"

-Jeff
unknown (Guest)
on 2006-05-23 17:18
(Received via mailing list)
On Tue, 23 May 2006, Yukihiro M. wrote:

> Thank you for the example.  I am still not sure this is a right tool for the
> right purpose, but I do understand what you wanted to do in the code.
> Hidetoshi NAGAI is proposing load_to(file, module) method to do the thing
> you wanted.  In my opinion, this is better way to implement.

indeed.  that's just one example though, a module/class creation hook
could be
used in all sorts of interesting ways:

   class Module
     def defined
       STDERR.puts "#{ self } defined @ #{ caller.first }"
     end
   end

for instance.

> |   or perhaps #defined
> |   or perhaps #created
>
> Maybe.  Much better than inherited.  Or maybe invoking initialize at
> the module creation is even better.

(after you reading your other note)

yes.  #defined would be really nice for both classes and modules -
called via
rb_define_xxx.

cheers.

-a
Robert K. (Guest)
on 2006-05-23 18:43
(Received via mailing list)
2006/5/22, Jeff R. <removed_email_address@domain.invalid>:
> end
>
> Is there a standard or correct way to initialize @foo_the_stuff in this
> example?  Should the add_stuff method check whether the variable has
> been initialized every time, or should this be done by implementing a
> callback like append_features or included?  (That's how I'd want to do
> it, but it's not clear which or how...)

This thread seems to have gone astray a bit but I didn't see this
solution which I regard the standard way to handle this. The important
part is to use initialize(*a,&b) in the module. I've put this on the
wiki for easier reference:

http://wiki.rubygarden.org/Ruby/page/show/Initiali...

Kind regards

robert
Jim W. (Guest)
on 2006-05-23 20:30
Robert K. wrote:
> This thread seems to have gone astray a bit but I didn't see this
> solution which I regard the standard way to handle this. The important
> part is to use initialize(*a,&b) in the module. I've put this on the
> wiki for easier reference:
>
> http://wiki.rubygarden.org/Ruby/page/show/Initiali...

Nice writeup.  I especially like that you captured the need to
tranparently pass arguments thru the module initializer.  That's easy to
miss at first glance.

--
-- Jim W.
Joel VanderWerf (Guest)
on 2006-05-23 22:01
(Received via mailing list)
removed_email_address@domain.invalid wrote:
...
> yes.  #defined would be really nice for both classes and modules -
> called via
> rb_define_xxx.

This is sort of a tangent, but what about a const_added hook? That
wouldn't fire when Module.new is called, though. And it would mean
having to ignore non-class/module constants.
Jeff R. (Guest)
on 2006-05-23 22:10
(Received via mailing list)
> This thread seems to have gone astray a bit but I didn't see this
> solution which I regard the standard way to handle this. The important
> part is to use initialize(*a,&b) in the module. I've put this on the
> wiki for easier reference:
>
> http://wiki.rubygarden.org/Ruby/page/show/Initiali...
>
> Kind regards
>
> robert

This doesn't really solve the problem.  The idea is that you don't want
the including class to have to do anything related to initialization of
the module(s) being included.  In your example any including class would
have to call super in order to make it work.  If Derived implements an
initialize method and doesn't call super then the module isn't
initialized.  But why should it call super, it isn't deriving from
anything, it is mixing it in.

The two options Matz mentioned are the way to go.  Either a callback
(module_initialize), which will act as an initialization method for the
module, or the ability for the module to insert its own method(s)
before, after, or around the class that includes it.  (CLOS method
combinations...  AOP)

-Jeff
unknown (Guest)
on 2006-05-23 23:20
(Received via mailing list)
On Wed, 24 May 2006, Joel VanderWerf wrote:

> removed_email_address@domain.invalid wrote:
> ...
>> yes.  #defined would be really nice for both classes and modules -
>> called via
>> rb_define_xxx.
>
> This is sort of a tangent, but what about a const_added hook? That
> wouldn't fire when Module.new is called, though. And it would mean
> having to ignore non-class/module constants.

hmmm.  i like this - are you sure that's the hook name?

   harp:~ > cat a.rb
   class Object
     def const_added *a, &b
       p a, b
     end
   end

   load 'b.rb', anon = true


   harp:~ > cat b.rb
   class C; end


   harp:~ > ruby a.rb


i also tried in Module and Class - no joy.

thoughts?

-a
Joel VanderWerf (Guest)
on 2006-05-24 00:01
(Received via mailing list)
removed_email_address@domain.invalid wrote:
> On Wed, 24 May 2006, Joel VanderWerf wrote:
...
>> This is sort of a tangent, but what about a const_added hook? That
>> wouldn't fire when Module.new is called, though. And it would mean
>> having to ignore non-class/module constants.
>
> hmmm.  i like this - are you sure that's the hook name?

Sorry ... it was just a wild suggestion. There's nothing like that now,
AFAIK.

It's too bad set_trace_func slows down the interpreter, but you could
use that to catch "class" events: "start a class or module definition"
according to the docs.
Robert K. (Guest)
on 2006-05-24 11:15
(Received via mailing list)
2006/5/23, Jeff R. <removed_email_address@domain.invalid>:
>
> This doesn't really solve the problem.  The idea is that you don't want
> the including class to have to do anything related to initialization of
> the module(s) being included.

It doesn't have to.

>  In your example any including class would
> have to call super in order to make it work.

But this is true for classes inheriting anyway. You could even make it
yourself a habit to even call super if you do not inherit specifically
(i.e. inherit Obect).

>  If Derived implements an
> initialize method and doesn't call super then the module isn't
> initialized.  But why should it call super, it isn't deriving from
> anything, it is mixing it in.

I don't fully agree: Derived actually inherits from Base so it's
standard procedure to invoke the super class initialize.  If you then
write your module initialize with signature (*a,&b) you can insert it
into *any* inheritance chain that obeys the basic behavior without
doing any harm or losing initialization code.

> The two options Matz mentioned are the way to go.  Either a callback
> (module_initialize), which will act as an initialization method for the
> module, or the ability for the module to insert its own method(s)
> before, after, or around the class that includes it.  (CLOS method
> combinations...  AOP)

Well, yes, if a change to the language / lib can be considered these
are probably better.

Kind regards

robert
This topic is locked and can not be replied to.