Minimal example to change an instance variable on a class on inclusion

Hi.

I don’t know if the following is possible, but it would be great.

I have:

module Foo; end
class Bar; end

class Bar has one instance variable, let’s call it @test.

class Bar
def initialize
@test = ‘test 1’
end
end

Now I want the module be able to change @test variable
to ‘test 2’ instead. I am fine by using any way possible
but so far I failed.

Is there any way to have a module change a class instance
variables upon inclusion OR extend-functionality?

Thanks.

Hello,

On 17.09.2013 01:53, Marc H. wrote:

Is there any way to have a module change a class instance
variables upon inclusion OR extend-functionality?

I think the easiest way for you would be to define an initialize method
on Foo in which you change the values as you want, and then call super
at the end of Bar#initialize. That way Foo#initialize will be called
sooner or later, provided you included Foo in Bar. If you don’t include
it, Foo#initialize will never be called and you’re fine.

If you want some finer control I think you will have to do some magic
around the Module#included hook and a hash of standard values.

Regards,
Calvin

I think the easiest way for you would be to define an initialize
method on Foo in which you change the values as you want, and
then call super at the end of Bar#initialize.

Hmm. But how does this work?

Foo is a module, can it have an initialize method?

By the way, I found a way via extend:

require ‘pp’

module Foo
def initialize
@test = ‘test 2’
end
def self.extended(i)
i.instance_variable_set :@test,‘test 2’
end
end

class Bar

include Foo

def initialize
@test = ‘test 1’
end

end

bar = Bar.new
pp bar
bar.extend Foo
pp bar

#<Bar:0x8e70308 @test=“test 1”>
#<Bar:0x8e70308 @test=“test 2”>

But ideally I want a way with include too. :frowning:

Thanks for the help so far!

On 17.09.2013 02:42, Marc H. wrote:

I think the easiest way for you would be to define an initialize
method on Foo in which you change the values as you want, and
then call super at the end of Bar#initialize.
Hmm. But how does this work?

Foo is a module, can it have an initialize method?

Yes, it can. The reason modules are able to have an initialize method is
that it is a regular method which just happens to be called after object
creation. And since you are able to propagate a method call up the chain
of ancestors with super, this will work in #initialize as well.

On Tue, Sep 17, 2013 at 3:00 AM, Calvin Bornhofen
[email protected] wrote:

Yes, it can. The reason modules are able to have an initialize method is
that it is a regular method which just happens to be called after object
creation. And since you are able to propagate a method call up the chain of
ancestors with super, this will work in #initialize as well.

The approach does have a bit of a downside: usually you invoke super
class initialization before you do initialization in the current
class. For this approach to work you need to invoke super after
initialization in this class or make it lazy.

$ cat -n x.rb
1 module Foo
2 def initialize
3 @test = ‘test 2’
4 end
5 end
6
7 class Bar
8 attr_reader :test
9
10 include Foo
11
12 def initialize
13 super
14 @test = ‘test 1’
15 end
16 end
17
18 p Bar.new.test
19
20 class Bar
21 def initialize
22 @test = ‘test 1’
23 super
24 end
25 end
26
27 p Bar.new.test
28
29 class Bar
30 def initialize
31 super
32 @test ||= ‘test 1’
33 end
34 end
35
36 p Bar.new.test
$ ruby x.rb
“test 1”
“test 2”
“test 2”

Cheers

robert

Yes, it can. The reason modules are able to have an initialize method
is that it is a regular method which just happens to be called
after object creation.

Interesting, I never heard that before!

Thanks to you two by the way!

That way you can include it in arbitrary class hierarchies and still
keep #initialize call chain intact - even if super class #initialize
have different argument lists.

Yes, it is strange… I actually looked it up, and the latest
Pickaxe book also mentions that. I kind of did not read the new
pickaxe book too closely, most of it I seem to know already,
but this part I did not know.

The pickaxe also mentions the initialize method as part of a
module, but I never saw this method in a module before.

In fact, I never had the idea that initialize could be used
in a module, because in other parts of the Pickaxe, it was
always said that a module is not a class, and we should use
subclasses rather than think about a module.

But this brings me to another question, actually …

Why do we really use subclasses in Ruby when there are almost
completely legit alternative uses to do the same with modules?

That is almost as if there are two different ways to do (almost)
the same and I am unsure whether this is really what matz always
intended? But perhaps I am wrong … it just feels as if there
are two ways to do quite the same…

On Tue, Sep 17, 2013 at 12:28 PM, Marc H. [email protected]
wrote:

Yes, it can. The reason modules are able to have an initialize method
is that it is a regular method which just happens to be called
after object creation.

Interesting, I never heard that before!

If a module needs an initialize method I would typically do this

module Foo
def initialize(*a, &b)
super
# init for module
end
end

That way you can include it in arbitrary class hierarchies and still
keep #initialize call chain intact - even if super class #initialize
have different argument lists.

Thanks to you two by the way!

You’re welcome!

Kind regards

Hobbes