drew.cuthbert
drew.cuthbert

Reputation: 1015

Ruby dynamic module mix-in

Suppose I have two modules:

module Test1

  attr_accessor :a, :b

  @a = 0.0
  @b = 0.0

end

module Test2

  attr_accessor :c, :d

  @c = 0.0
  @d = 0.0

end

Now, I want to conditionally mix these modules into a class. This is what I've tried:

require './Test1.rb'
require './Test2.rb'

class MyClass

  def initialize(mode)
    if mode == 0
      (class << self; include Test1; end)
    elsif mode == 1
      (class << self; include Test2; end)
    else
      class << self
        include Test1
        include Test2
      end
    end
  end

end

This is the behavior I am seeing:

obj = MyClass.new(0)
obj.a  #=> nil

Also @a is nil in instance methods within the class. I feel that I am not understanding something important here. I would like to understand why what I'm doing isn't working and also what the correct way to achieve my desired functionality is.

Upvotes: 7

Views: 3040

Answers (2)

drew.cuthbert
drew.cuthbert

Reputation: 1015

I thought of a way to work around this problem, so I thought I'd share it. I'd still like to see if anyone knows of a better way to achieve what I was trying to.

Module Test1
  attr_accessor :a, :b

  def init1
    @a = 0.0
    @b = 0.0
  end
end

class MyClass
  def initialize
    if mode == 0
      (class << self; include Test1; end)
      init1
    elsif mode == 1
      ...
  end
end

Upvotes: 2

Marek Lipka
Marek Lipka

Reputation: 51151

You have this behaviour because these instance variables you set in modules belong to modules themselves instead of belonging to MyClass instances. Consider this code:

Test1.instance_variable_get(:@a)
# => 0.0

To solve this issue, you could use extend instead of include:

module Test1

  attr_accessor :a, :b

  def self.extended(object)
    object.a, object.b = 0.0, 0.0
  end

end

module Test2

  attr_accessor :c, :d

  def self.extended(object)
    object.c, object.d = 0.0, 0.0
  end

end

And in your class:

require './Test1.rb'
require './Test2.rb'

class MyClass

  def initialize(mode)
    if mode == 0
      extend Test1
    elsif mode == 1
      extend Test2
    else
      extend Test1
      extend Test2
    end
  end

end

Upvotes: 11

Related Questions