richbai90
richbai90

Reputation: 5204

Ruby modify original variable

In C programming I believe they call this pass by reference. What I want to do is this.

class A
  attr_accessor :foo
  def initialize
    @foo = 'foo'
  end
  def get_b
    _b = Object.new
    _b.extend B
    _b.foo = @foo
  end
  module B
    attr_accessor :foo
    def change_foo
      @foo = 'bar'
    end
  end
end

a = A.new
puts a.foo # 'foo'
b = a.get_b
puts b.foo # 'foo'
b.change_foo
puts b.foo # 'bar'
puts a.foo # This should also be 'bar' but is instead still 'foo'

After b.change_foo I would like the value of a.foo to be modified. Is there a way of passing the reference of @foo from class A to module B instead of the value?

Upvotes: 1

Views: 1765

Answers (2)

mrodrigues
mrodrigues

Reputation: 1082

Objects are passed as reference in Ruby, but not pointers to variables. This means that, although you can modify any object, if you change the variable's reference, this change will occur only in the current scope. I'd recommend you to actually change your architecture and use more object-oriented techniques, instead of trying to rely on error-prone language features like this. For example, you can use composition and delegation to implement what you're trying to accomplish:

class A
  attr_accessor :foo
  def initialize
    @foo = 'foo'
  end

  def get_b
    _b = Object.new
    _b.extend B
    _b.a = self
  end

  module B
    attr_accessor :a

    def foo
      a.foo
    end

    def foo=(value)
      a.foo = value
    end

    def change_foo
      a.foo = 'bar'
    end
  end
end

I don't know exactly your purpose, but I'd probably not dynamically extend modules in a factory method like this. I prefer to create classes whose purpose is clear, without depending on context.

Upvotes: 1

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230481

With this concrete example of strings, you can make it work.

module B
  attr_accessor :foo
  def change_foo
    # @foo = 'bar' # this won't work
    foo.replace('bar')
  end
end

When you do a @foo = 'bar', you're completely breaking any connection between foo of B and foo of A. They're now two separate objects.

What the code above does is, instead of making another object, use the reference to the object to call a method on it (which will change its state). Will work equally well with other mutable objects (arrays, hashes, instances of your custom classes). But not with immutable primitives like integers.

Is there a way of passing the reference of @foo from class A to module B instead of the value?

A reference (or a "pointer", in C-speak) is actually passed here. You then overwrite it.

Upvotes: 4

Related Questions