Forum: Ruby Mixin Syntax for Newbies

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.
7863582496e914f1ffce56f98d468f72?d=identicon&s=25 Nathan Olberding (nko)
on 2006-05-02 18:40
I'm trying to start using mixins and I'm having a little syntactual
trouble, if that's a word.

Here's my test code:

---------------
module One
        @one = "One!"
        attr_reader :one
end

class Two
        include One
        def initialize
                puts @one
        end
end

this = Two.new
----------------

I have tried several variations on this, but Two.one always seems to
come out as "nil". Is it possible to set it to "One!" by default?
F2d901ea9430646c6dd35a629bb3f119?d=identicon&s=25 Simen (Guest)
on 2006-05-02 18:53
Nathan Olberding wrote:
> I'm trying to start using mixins and I'm having a little syntactual
> trouble, if that's a word.
>
> Here's my test code:
>
> ---------------
> module One
>         @one = "One!"
>         attr_reader :one
> end
>
> class Two
>         include One
>         def initialize
>                 puts @one
>         end
> end
>
> this = Two.new
> ----------------
>
> I have tried several variations on this, but Two.one always seems to
> come out as "nil". Is it possible to set it to "One!" by default?

In the above code, @one is actually an instance variable of module One:

  One.instance_variable_get "@one"
  # => "One!"
7863582496e914f1ffce56f98d468f72?d=identicon&s=25 Nathan Olberding (nko)
on 2006-05-02 19:37
Simen wrote:
> In the above code, @one is actually an instance variable of module One:
>
>   One.instance_variable_get "@one"
>   # => "One!"

Okay, that makes sense. In that case, is it possible to have instance
variables for classes defined in modules which they mix in?
F2d901ea9430646c6dd35a629bb3f119?d=identicon&s=25 Simen (Guest)
on 2006-05-02 20:31
Nathan Olberding wrote:
> Simen wrote:
>> In the above code, @one is actually an instance variable of module One:
>>
>>   One.instance_variable_get "@one"
>>   # => "One!"
>
> Okay, that makes sense. In that case, is it possible to have instance
> variables for classes defined in modules which they mix in?

If you make it a method, you can call it from initialize:

  module One
    def one; @one = "One!"; end
  end
  class Two
    def initialize
      one
      puts @one
    end
  end
  # Outputs "One!"
  two = Two.new
F2d901ea9430646c6dd35a629bb3f119?d=identicon&s=25 Simen (Guest)
on 2006-05-02 20:33
Simen wrote:
> Nathan Olberding wrote:
>> Simen wrote:
>>> In the above code, @one is actually an instance variable of module One:
>>>
>>>   One.instance_variable_get "@one"
>>>   # => "One!"
>>
>> Okay, that makes sense. In that case, is it possible to have instance
>> variables for classes defined in modules which they mix in?
>
> If you make it a method, you can call it from initialize:
>
>   module One
>     def one; @one = "One!"; end
>   end
>   class Two
>     def initialize
>       one
>       puts @one
>     end
>   end
>   # Outputs "One!"
>   two = Two.new

Whoops, you need to include One first:

  class Two
    include One
    ...
  end

( I gotta stop using the ruby-forum gateway. It's so easy it makes me
sloppy ).
7863582496e914f1ffce56f98d468f72?d=identicon&s=25 Nathan Olberding (nko)
on 2006-05-02 23:04
Simen wrote:
>> If you make it a method, you can call it from initialize:
>>
>>   module One
>>     def one; @one = "One!"; end
>>   end
>>   class Two
>>     def initialize
>>       one
>>       puts @one
>>     end
>>   end
>>   # Outputs "One!"
>>   two = Two.new
>
> Whoops, you need to include One first:
>
>   class Two
>     include One
>     ...
>   end

Is there a way to do this without having to re-initialize variables in
each inheriting class?

> ( I gotta stop using the ruby-forum gateway. It's so easy it makes me
> sloppy ).

Fast answers mean fast self-checking! It's a tradeoff I'm willing to
live with.
E34b5cae57e0dd170114dba444e37852?d=identicon&s=25 Logan Capaldo (Guest)
on 2006-05-03 18:59
(Received via mailing list)
On May 2, 2006, at 12:40 PM, Nathan Olberding wrote:

>
> I have tried several variations on this, but Two.one always seems to
> come out as "nil". Is it possible to set it to "One!" by default?

First of all when you say
module One
    @one = "One!"
end

That sets the instance variable of the module One.

Second, this:

def initialize
   puts @one
end

defeats the whole purpose of using attr_reader

Finally, you may want to do something like:

module One
   def one
      @one ||= "One!" # sets @one to "One!" only if it's not already set
                      # or is nil or false
   end
end

class Two
   include One
   def initialize
     puts one
   end
end
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2006-05-03 18:59
(Received via mailing list)
On May 2, 2006, at 11:40 AM, Nathan Olberding wrote:

>
> I have tried several variations on this, but Two.one always seems to
> come out as "nil". Is it possible to set it to "One!" by default?

 >> module One
 >>   attr_writer :one
 >>   def one
 >>     @one || "One!"
 >>   end
 >> end
=> nil
 >> class Two
 >>   include One
 >>   def initialize
 >>     puts one
 >>   end
 >> end
=> nil
 >> Two.new
One!
=> #<Two:0x328384>

Hope that helps.

James Edward Gray II
0b561a629b87f0bbf71b45ee5a48febb?d=identicon&s=25 Dave Burt (Guest)
on 2006-05-03 18:59
(Received via mailing list)
Nathan Olberding wrote:
>
> I have tried several variations on this, but Two.one always seems to
> come out as "nil". Is it possible to set it to "One!" by default?

Consider this simple alternative:

module One
  def one
    @one or "One!"
  end
end

If that's not good enough, and you don't mind using a library rather
than bending Ruby's modules to your will yourself, try Ara T. Howard's
Traits library; it deals with this completely.

If you do want to delve into the how of it, check out Ruby Quiz 67:
metakoans.rb and the submitted solutions.

http://rubyquiz.com/quiz67.html

Cheers,
Dave
E34b5cae57e0dd170114dba444e37852?d=identicon&s=25 Logan Capaldo (Guest)
on 2006-05-03 19:03
(Received via mailing list)
On May 2, 2006, at 5:04 PM, Nathan Olberding wrote:

> Is there a way to do this without having to re-initialize variables in
> each inheriting class?

This question is confusing me. Are you looking for class variables or
instance variables? You initialize instance variables every time an
instance is created. @one in this case is an instance variable.
7863582496e914f1ffce56f98d468f72?d=identicon&s=25 Nathan Olberding (nko)
on 2006-05-03 19:08
Dave Burt wrote:
> Consider this simple alternative:
>
> module One
>   def one
>     @one or "One!"
>   end
> end

That might work... I theoretically have to define each variable as a
method? There's no way to initialize variables in modules and have those
values appear in inheriting classes?

> If that's not good enough, and you don't mind using a library rather
> than bending Ruby's modules to your will yourself, try Ara T. Howard's
> Traits library; it deals with this completely.

I think I might have to.
7863582496e914f1ffce56f98d468f72?d=identicon&s=25 Nathan Olberding (nko)
on 2006-05-03 19:10
Nathan Olberding wrote:
> Dave Burt wrote:
>> Consider this simple alternative:
>>
>> module One
>>   def one
>>     @one or "One!"
>>   end
>> end
>
> That might work... I theoretically have to define each variable as a
> method? There's no way to initialize variables in modules and have those
> values appear in inheriting classes?

> I think I might have to.

Better yet, should I be trying to use superclasses for this?
10e3653e8f411fa2d140129b947cdaea?d=identicon&s=25 polypus (Guest)
on 2006-05-03 21:24
> Better yet, should I be trying to use superclasses for this?

maybe, i don't think there are any hard and fast rules for deciding when
stuff should be in a module and when it should be in a class, but off
the top of my head, and imho, a few rules of thumb might be:

1) avoid defining instance instance variables (as opposed to module
instance variable) in mixin modules because they can potentialy clash
with instance variables of instances of the class which included the
module. of course it can be argued that method names will also clash,
but instance variables are a little more intractable i think, and also
why double the chances of bugs.

2) use module mixins when you want to be able to enhance classes with
functionality regardless of their class structure.

3) note that in 2), i said functionality and not state. if you want
state a better bet is to go with either inheritance or object
composition.

4) break above rules freely if breaking them makes your life easier. for
instance you might find yorself with two unrelated class hierarchies
which have a common problem of state. if your module is not meant for
widespread use like the Enumerable module, but only within your project
and for your two class hierarchies then it would be convenient and also
DRY to do it via a module mixin, but it should still be considered
carefully as your requirements might change.

>> Is there a way to do this without having to re-initialize variables in
>> each inheriting class?

ok i think this question and a few others reveal a fundamental confusion
that you have, so i'll take it from the beginning. forget modules for a
minute.

class X
  @foo = 'mama'
  def imethod
    puts @foo
  end
  def self.cmethod
    puts @foo
  end
end

in the above example 'class X' signals to the ruby runtime that a class
is being built. from then on instance variables can be initialized (@foo
= 'mama'), methods can be called and class and instance methods can be
defined. note that the instance variable @foo is an instance variable of
class X which is an object like any other.

X.cmethod

prints 'mama'

x = X.new
x.imethod

prints nil or nothing because @foo has not yet been initialized in the
instance of class X, x.

in other words class instance variables can be initialized and used
either in the body of the class or in class methods, while instance
variables are used and initialized in instance methods.

module M
  @foo = 'papa'
end
class X
  include M
  def self.cmethod
    puts @foo
  end
  def imethod
    puts @foo
  end
end

in the above example @foo is initialized in the module body and not by a
method so it is an instance variable of the module. so when you write

X.cmethod

nothing is printed because in cmethod @foo references an instance
variable of X which was never initialized. and when you write

x  = X.new
x.imethod

nothing is printed because @foo in this case references an instance
variable of an instance of X, x in which again @foo was never
initialized. note that if i had defined imethod in the module instead,
it would have made absolutely no difference, it would still 'look for'
an instance variable in x.
i'd recommend getting your head firmly around the above before using a
fancy metaprogramming library. hope my examples clear it up for you.
7863582496e914f1ffce56f98d468f72?d=identicon&s=25 Nathan Olberding (nko)
on 2006-05-03 21:45
polypus wrote:
> ok i think this question and a few others reveal a fundamental confusion
> that you have, so i'll take it from the beginning. forget modules for a
> minute.

I'm not confused. Confusion would suggest I have some flawed knowledge,
which would suggest that I have some knowledge at all, which isn't the
case. This is a case where I'm downright *ignorant* :-)

> class X
>   @foo = 'mama'
>   def imethod
>     puts @foo
>   end
>   def self.cmethod
>     puts @foo
>   end
> end
... <snip>

I think I understand this much. I know that the specific implementations
I've outlined above fail because the initialize method(s) aren't being
called, and therefore nothing is being initialized. In a (tight!)
nutshell, that seems to be the basic flaw.

The thing I'm trying to implement is pretty basic, so I'm very confident
that variable names won't clash. In fact, method names *would* clash in
a lot of cases as I find myself doing similar operations on different
kinds of data, so I'm really trying to implement only instance variables
in modules (or super classes, though I'm having a little trouble with my
first googlings about how to use inheritance) and not methods.

So, is there a way to mix-in or inherit variables and have them
initialize to a default state? I'm thinking this sounds like a job for
super classes. I originally started doing this by googling for "multiple
inheritance ruby", and that's when I found mixins, so that's why I
started by asking about mixins.

Sorry, I just emanate noobiness :-)
10e3653e8f411fa2d140129b947cdaea?d=identicon&s=25 polypus (Guest)
on 2006-05-03 22:53
>...super classes, though I'm having a little trouble with my first googlings about how to 
use inheritance...

something like?

class X
  attr_reader :foo, :bar
  def initialize
    @foo, @bar = :mama, :papa
  end
end

class Y < X
end

y = Y.new
puts y.foo, y.bar
7863582496e914f1ffce56f98d468f72?d=identicon&s=25 Nathan Olberding (nko)
on 2006-05-03 22:58
polypus wrote:
>>...super classes, though I'm having a little trouble with my first googlings about how 
to use inheritance...
>
> something like?
> ...

Exactly like that :-) I got way too sidetracked by the idea of modules.
I'm off to start implementing something cool now. After that, I plan on
studying up on the different uses of inheritance and mixins.

Thanks everyone for the boundless patience!
42172acdf3c6046f84d644cb0b94642c?d=identicon&s=25 Pat Maddox (pergesu)
on 2006-05-04 01:51
(Received via mailing list)
Hey Nathan,

You should check out pp122-123 of Programming Ruby.
http://www.rubycentral.com/book/tut_modules.html#UA will take you to
the exact section that you should read.

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