How to add a instance variable through a mixin?

Hi,

I would like to have a mixin which adds an instance variable to a
class. I ve tried it the following way:

I ve defined a little module:
module MyApp
module MyMixin
def self.included(base)
@should_be_an_instance_variable = nil
end
end
end

which is included by my little class:
class MyClass
include MyApp::MyMixin
end

However, it seems that @should_be_an_instance_variable is currently
defined per mixin, but I would like to have a unique one for each
object of MyClass.

Maybe anybody can help me?
Thanks a lot in advance.

base.instance_variable_set(‘@new_ivar’,nil)
.included is run bound to MyMixin, not the object in base. Alternately
there
is usage of the *eval methods.

On Feb 12, 2008 10:55 AM, [email protected]
[email protected]

On Feb 12, 11:54 am, “[email protected]
[email protected] wrote:

class MyClass
include MyApp::MyMixin
end

However, it seems that @should_be_an_instance_variable is currently
defined per mixin, but I would like to have a unique one for each
object of MyClass.

If you mix the module in after some instances of MyClass have already
been created, do you want it to dynamically create the instance
variables in those instances?

If you mix the module in before some instances of MyClass have been
created, do you want it to run code each time a new instance is
created?

If you mix the module in after some instances of MyClass have already
been created, do you want it to dynamically create the instance
variables in those instances?

If you mix the module in before some instances of MyClass have been
created, do you want it to run code each time a new instance is
created?

I would be interested in both cases, but for my particularly case it s
the second one.

On Feb 12, 12:54 pm, “[email protected]
[email protected] wrote:

If you mix the module in after some instances of MyClass have already
been created, do you want it to dynamically create the instance
variables in those instances?

If you mix the module in before some instances of MyClass have been
created, do you want it to run code each time a new instance is
created?

I would be interested in both cases, but for my particularly case it s
the second one.

Here’s a way to do it:

module Rands
def self.included( klass )
# Modify existing instances
ObjectSpace.each_object( klass ){ |inst|
inst.initialize_rands
}

# Replace the initialization with your own
klass.class_eval{
  # Beware name clashes
  alias_method :init_pre_rand, :initialize
  def initialize( *args )
    init_pre_rand( *args )
    initialize_rands
  end
}

end

def initialize_rands
@foo = “%.2f” % rand
end
end

class Foo
def initialize( id )
@id = id
end
end

f1 = Foo.new( 1 )
f2 = Foo.new( 2 )
class Foo
include Rands
end
f3 = Foo.new( 3 )
f4 = Foo.new( 4 )

p f1, f2, f3, f4
#=> #<Foo:0x7ffa696c @id=1, @foo=“0.92”>
#=> #<Foo:0x7ffa691c @id=2, @foo=“0.57”>
#=> #<Foo:0x7ffa6908 @id=3, @foo=“0.40”>
#=> #<Foo:0x7ffa68cc @id=4, @foo=“0.41”>

On 12.02.2008 20:54, [email protected] wrote:

If you mix the module in after some instances of MyClass have already
been created, do you want it to dynamically create the instance
variables in those instances?

If you mix the module in before some instances of MyClass have been
created, do you want it to run code each time a new instance is
created?

I would be interested in both cases, but for my particularly case it s
the second one.

You can do

module Foo
attr_accessor :var

def initialize(*a,&b)
@var = 10
end
end

And then

irb(main):015:0> class Bar
irb(main):016:1> include Foo
irb(main):017:1> end
=> Bar
irb(main):018:0> Bar.new.var
=> 10

Or

irb(main):026:0> class Oink
irb(main):027:1> include Foo
irb(main):028:1> def initialize
irb(main):029:2> super
irb(main):030:2> @x = 10
irb(main):031:2> end
irb(main):032:1> end
=> nil
irb(main):033:0> Oink.new.var
=> 10

Kind regards

robert

On 2/12/08, [email protected] [email protected] wrote:

end

object of MyClass.
You don’t really add instance variables for the instances of classes
to classes in Ruby.

http://talklikeaduck.denhaven2.com/articles/2008/02/08/whose-variable-is-it-anyway

If you add a method to a class through a mixin which refers to an
instance variable, the variable will be created dynamically in the
instance as needed when the method is run. if you want to initialize
it to something other than nil, you can use techniques like lazy
initialization.

module M
def iv
@iv ||= 0 # or whatever you want the default value to be
end
end


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

On 2/12/08, Robert K. [email protected] wrote:

And then
irb(main):026:0> class Oink
irb(main):027:1> include Foo
irb(main):028:1> def initialize
irb(main):029:2> super
irb(main):030:2> @x = 10
irb(main):031:2> end
irb(main):032:1> end
=> nil
irb(main):033:0> Oink.new.var
=> 10

You have to be careful here, since it relies on invoking super in the
initialize method, and since the initialize method in the module
doesn’t do this, it can break under the right conditions:

class A
attr_reader :a_var

def initialize
@a_var = :a_var
end

end

module M
attr_reader :m_var
def initialize
@m_var = :m_var
end
end

class B < A
include M
end

B.new.instance_variables # => [“@m_var”]

Note that the A instance didn’t get an @a_var instance variable.

The solution here is to invoke the superclass’ initialize, which also
works for subclasses which don’t have an initialize method themselves:

class A
attr_reader :a_var

def initialize
@a_var = :a_var
end

end

module M
attr_reader :m_var
def initialize
super
@m_var = :m_var
end
end

class B < A
include M
end

class C
include M
end

B.new.instance_variables # => [“@a_var”, “@m_var”]
C.new.instance_variables # => [“@m_var”]

But this is hard to do in general when the initialize methods take
different parameters.

module M
attr_reader :m_var
def initialize(*a, &b)
super
@m_var = :m_var
end
end

class D
include M
def initialize(d_val)
@d_var = d_val
super
end
end

D.new(10).instance_variables # =>

~> -:13:in `initialize’: wrong number of arguments (1 for 0)

(ArgumentError)

~> from -:13:in `initialize’

~> from -:30:in `initialize’

~> from -:35:in `new’

~> from -:35


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

On 13.02.2008 14:37, Rick DeNatale wrote:

You have to be careful here, since it relies on invoking super in the
initialize method, and since the initialize method in the module
doesn’t do this, it can break under the right conditions:

Absolutely!

attr_reader :m_var

@a_var = :a_var

end
C.new.instance_variables # => ["@m_var"]

But this is hard to do in general when the initialize methods take
different parameters.

Well, as long as you make it a convention to do this in modules

def initialize(*a,&b)
super

more

end

or have no #initialize in modules and make any class pass the proper
super according to its superclass. I for my part would even opt to
apply the following changes to the language:

  1. ignore all arguments to a module’s initialize

  2. automatically do a super in module constructors which passes on the
    arguments passed by the class’s constructor.

In other words: keep invocation of a module’s initialize in the call
chain but automate it to an extend that only class initialize pass on
data to their superclasses. At least it seems the language has some
room for improvements in this area. But then again, people do seem to
rarely stumble across this, or do they?

def initialize(d_val)

~> from -:35

But this will also break without the module because Object does not
accept arguments to #initialize.

Kind regards

robert