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.

···

--
Posted via http://www.ruby-forum.com/.

Hello,

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

···

On 17.09.2013 01:53, Marc Heiler 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?

···

--
Posted via http://www.ruby-forum.com/\.

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!

···

--
Posted via http://www.ruby-forum.com/\.

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, but I never
saw this method in a module before.

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...

···

--
Posted via http://www.ruby-forum.com/\.

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 17.09.2013 02:42, Marc Heiler 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.

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

···

On Tue, Sep 17, 2013 at 12:28 PM, Marc Heiler <lists@ruby-forum.com> wrote:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

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

···

On Tue, Sep 17, 2013 at 3:00 AM, Calvin Bornhofen <calvin.bornhofen@web.de> wrote:

On 17.09.2013 02:42, Marc Heiler 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.

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/