Initializing instance variables in Mixins
module Example
def self.included(base)
base.instance_variable_set :@example_ivar, :foo
end
end
Edit: Note that this is setting a class instance variable. Instance variables on the instance can't be created when the module is mixed into the class, since those instances haven't been created yet. You can, though, create an initialize method in the mixin, e.g.:
module Example
def self.included(base)
base.class_exec do
def initialize
@example_ivar = :foo
end
end
end
end
There may be a way to do this while calling the including class's initialize method (anybody?). Not sure. But here's an alternative:
class Foo
include Example
def initialize
@foo = :bar
after_initialize
end
end
module Example
def after_initialize
@example_ivar = :foo
end
end
Initialize instance variable in a mixin
I would propose to put that in an __init__()
of the Mixin. What do you think is the disadvantage?
class Mixin(object):
def __init__(self, *args, **kwargs):
super(Mixin, self).__init__(*args, **kwargs)
self.items = []
I think this is right way to do it; all other (maybe working) solutions look like a hack to me.
Ruby mixin - accessing module instance variables in class instances
Instance variables belong to instances (which is why they are called "instance variables"). You have three objects of interest here, every single one of which has its own @b
which has nothing to do with any of the other @b
s of the other objects: ModuleA
has its own @b
, as does ModuleB
, and instA
also has its own @b
.
Here is a more sensible implementation of what you are trying to do:
module ModuleB
def initialize(browser)
puts "initialize from ModuleB"
@browser = browser
@b = 5
end
end
module ModuleA
include ModuleB
def initialize(browser)
super
puts "initialize from ModuleA"
@a = @b
end
def action_1
@a = @b + 1
end
end
class ClassA
include ModuleA
def initialize(browser)
super
puts 'initialize - method in ClassA'
@c = @a
@d = @b
puts "a = #@a"
puts "b = #@b"
puts "c = #@c"
puts "d = #@d"
end
end
s = 'hello'
instA = ClassA.new(s)
# initialize from ModuleB
# initialize from ModuleA
# initialize - method in ClassA
# a = 5
# b = 5
# c = 5
# d = 5
#=> #<ClassA:0x007f8b5f12e110 @a=5, @b=5, @browser="hello", @c=5, @d=5>
puts instA.action_1
# 6
Javascript mixin pattern setting instance specific this variables
You could inherit all mixable objects from a base object that ensures proper initialization. This is a clean way of achieving your goal.
The following code demonstrates this principle:
//------------ framework
var inherits = function(childCtor, parentCtor) {
function tempCtor() {};
tempCtor.prototype = parentCtor.prototype;
childCtor.superClass_ = parentCtor.prototype;
childCtor.prototype = new tempCtor();
childCtor.prototype.constructor = childCtor;
};
var mixIn=function(target,source){
for(fn in source){
if(source.hasOwnProperty(fn) && fn.name != 'init'){
target.prototype[fn]=source[fn];
}
}
if (typeof source.init == 'function') {
if (target.prototype._mixInits === undefined) {
target.prototype._mixInits = [];
}
target.prototype._mixInits.push(source.init);
}
};
// all objects that can be mixin's should inherit from
// this object in order to ensure proper initialization
var Mixable = function() {
var mixInits = this.__proto__._mixInits;
if (mixInits !== undefined) {
for (var i = 0; i < mixInits.length; i++) {
mixInits[i].call(this);
}
}
};
//------------ testcode
var SpeakEnable = {
say:function(){
console.log(this.message);
},
init:function(){
console.log('say init called');
this.message="Saying Hello World Mixed in!";
this.object=[];
}
};
var WalkEnable = {
walk:function(){
console.log(this.walk_message);
},
init:function(){
console.log('walk init called');
this.walk_message="Walking step 1.2.3.";
}
};
var Person=function() {
Mixable.call(this);
};
inherits(Person, Mixable);
mixIn(Person,SpeakEnable);
mixIn(Person,WalkEnable);
var lulu=new Person();
lulu.say();
lulu.walk();
How do you access an instance variable within a mixin method?
In general, avoid having mixins access member variables: It's a very tight form of coupling that can make future refactoring unnecessarily difficult.
One useful strategy is for the Mixin to always access variables via accessors. So, instead of:
#!/usr/bin/ruby1.8
module Mixin
def do_something
p @text
end
end
class Foo
include Mixin
def initialize
@text = 'foo'
end
end
Foo.new.do_something # => "foo"
the mixin accesses the "text" accessor, which is defined by the including class:
module Mixin
def do_something
p text
end
end
class Foo
attr_accessor :text
include Mixin
def initialize
@text = 'foo'
end
end
Foo.new.do_something # => "foo"
What if you need to include the Mixin in this class?
class Foo
def initialize
@text = "Text that has nothing to do with the mixin"
end
end
Using generic and common data names in mixins can lead to conflicts when the including class uses the same name. In that case, have the mixin look for data with a less common name:
module Mixin
def do_something
p mixin_text
end
end
and let the including class define the appropriate accessor:
class Foo
include Mixin
def initialize
@text = 'text that has nothing to do with the mixin'
@something = 'text for the mixin'
end
def mixin_text
@something
end
end
Foo.new.do_something # => "text for the mixin"
In this way, the accessor acts as sort of "impedance matcher" or "translator" between the mix-in's data and the including class's data.
Does Instance Variables of a module shared between class with the mixin?
In Ruby modules and classes are objects, so it's possible to set instance variables for them.
module Test
@test = 'red'
def self.print_test
puts @test
end
end
Test.print_test #=> red
Your mistake is thinking that the variable @color is the same for:
module SharedVar
@color
end
and
module SharedVar
def show_color
@color
end
end
which is not.
In the first example, the instance variable belongs to the SharedVar
object and in the second example the instance variable belongs to the object you include the module in.
Another explanation by self pointer. In the first example the self pointer is set to the module object SharedVar
, so typing @color
will refer to the object SharedVar
and there's no connection with another object. In the second example, the method show_color
can be called only on some object, i.e. ex1.show_color
, so the self pointer will point to ex1
object. So in this case the instance variable will refer to ex1
object.
Related Topics
Stubbing Chained Methods with Rspec
How to Timeout Flash Messages in Rails
Ruby on Rails - Paperclip and Dynamic Parameters
How to Select the Longest String from a Ruby Array
Stream and Unzip Large CSV File with Ruby
How to Get Words Frequency in Efficient Way with Ruby
How to Read a File's Modification Date with Ruby
Installing MySQL2 Gem for Ruby on Rails 3.1.0
Does Rails Come with a "Not Authorized" Exception
How to Select Array Elements in a Given Range in Ruby
Rails Nested With_Option :If Used in Validation
Begin Rescue Not Catching Error
How to Make a Non-Blocking Request for an Exclusive Lock Using File#Flock
What Is the Most Elegant Way in Ruby to Remove a Parameter from a Url
How to Pass <Arguments> to Irb If I Don't Specify <Programfile>