Mixin Syntax for Newbies


#1

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?


#2

Nathan O. 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!”


#3

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?


#4

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


#5

Simen wrote:

Nathan O. 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 ).


#6

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.


#7

On May 2, 2006, at 11:40 AM, Nathan O. 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 G. II


#8

Nathan O. 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 Q. 67:
metakoans.rb and the submitted solutions.

http://rubyquiz.com/quiz67.html

Cheers,
Dave


#9

On May 2, 2006, at 12:40 PM, Nathan O. 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


#10

On May 2, 2006, at 5:04 PM, Nathan O. 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.


#11

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


#12

Nathan O. wrote:

Dave B. 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?


#13

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.


#14

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 :slight_smile:

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

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 :slight_smile:


#15

…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


#16

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


#17

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 :slight_smile: 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!