0

Let's say I have a class called Person and person has an attribute called partner. When I call partner= on one of the Person objects, I want to set the @partner instance variable of both objects. Here's an example with invalid syntax:

class Person

  attr_reader :partner

  def partner=(person)

    # reset the old partner instance variable if it exists
    partner.@partner = nil if partner

    # set the partner attributes
    @partner = person
    person.@partner = self
  end
end

5 Answers 5

2

Change the attr_reader to an attr_accessor and add a helper method:

class Person

  attr_accessor :partner

  def link_partners(person)
    @partner = person
    person.partner = self
  end
end

Update for visibility. Based on suggestion from Frederick below. This is a little more verbose, but will prevent partner from being set directly:

class Person
  protected
  attr_writer :partner

  public
  attr_reader :partner

  def link_partners(person)
    @partner = person
    person.partner = self
  end
end

Both implementations works like this:

p1, p2 = Person.new, Person.new
p1.link_partners(p2) 
# p2.link_partners(p1)
Sign up to request clarification or add additional context in comments.

4 Comments

I'm not really sure how that solves my problem. Could you please elaborate?
Sorry, I was elaborating, but didn't get there before you found the setter.
The main problem with my solution is that anyone could call person.partner directly and that would only set the person's partner.
You could make the setter protected.
1

You could provide a protected helper method which gets called by your partner= method to do the actual work. Since it can't be called by "outsiders", all of your check and balances can be maintained in your implementation of partner=:

class Person
  attr_reader :partner

  def partner=(person)
    @partner.set_partner(nil) if @partner
    set_partner(person)
    person.set_partner(self) if person
  end

  def set_partner(person)
    @partner = person
  end

  protected :set_partner
end

Comments

0

Never mind, I just discovered instance_variable_set.

class Person

  attr_reader :partner

  def partner=(person)

    # reset the old partner instance variable if it exists
    partner.instance_variable_set(:@partner, nil) if partner

    # set the partner attributes
    @partner = person
    person.instance_variable_set(:@partner, self)
  end
end

1 Comment

You can definitely access instances variable getters and setters that way, especially if you want the setter to be privately accessible for instance. In your case, there is probably a better way to achieve what you want. I think I need a little bit more clarity on the issue.
0

In theory, you could do this as follows:

def partner=(person)
  @partner = person
  person.instance_variable_set(:@partner, self)
end

However, I would consider this to be magic. (That's not a good thing.) Instead, make the attr_reader into an attr_accessor and write a different method to set two persons' partners to each other.

1 Comment

I guess I don't feel like it's magic, since it's the behavior I would expect. Documentation would definitely help :)
0

This will solve your issue with recursive set and looks better than your solution:

class Partner
  attr_reader :partner
  def set_partner(person, recursive = true)
    # reset previous partner
    @partner.set_partner(nil, false) if recursive && @partner

    # set new partner
    @partner = person
    @partner.set_partner(self, false) if recursive
  end
  alias_method :partner=, :set_partner
end

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.