Search code examples
rubyclassmethodsselfruby-2.7

Ruby: Change Instances from Within Class Method


I have two instances of a class that I want to swap. Both instances are arrays. I want to swap them using a class method. How do I change/access the instances from within the class method self.collide?

class MagicBus < Array

  attr_writer :seating

  def self.collide(bus1, bus2)
    stored_arr1 = bus1
    stored_arr2 = bus2
    bus1 = stored_arr2
    bus2 = stored_arr1
    return bus1, bus2
  end

end

def test_two_magic_buses_collide_and_swap_their_passengers
    bus1 = MagicBus.new(["Mark","Dale","Peter"])
    bus1_object_id = bus1.object_id

    bus2 = MagicBus.new(["James","Patrick","Bardoe"])
    bus2_object_id = bus2.object_id

    MagicBus.collide(bus1, bus2)

    assert_equal ["James","Patrick","Bardoe"], bus1
    assert_equal bus1_object_id, bus1.object_id

    assert_equal ["Mark","Dale","Peter"], bus2
    assert_equal bus2_object_id, bus2.object_id
  end

I've tried the code below, doesn't work, but should illustrate what I am trying to do.

  def self.collide(bus1, bus2)
    stored_arr1 = bus1
    stored_arr2 = bus2
    bus1 = stored_arr2
    bus2 = stored_arr1
    self.bus1 = bus2
    self.bus2 = bus1
  end

Test results are...

....E

Error:
TestMagicBus#test_two_magic_buses_collide_and_swap_their_passengers:
NoMethodError: undefined method `bus1=' for MagicBus:Class
    magic_bus.rb:56:in `collide'
    magic_bus.rb:126:in `test_two_magic_buses_collide_and_swap_their_passengers'

Solution

  • What you are trying to do isn't possible in Ruby, because Ruby does not have "pass by reference" parameters, but always passes by pointer. This means when you make an assignment in a method to a parameter then this doesn't change the value of the variable on the outside:

    a = "Hello"
    
    def make_message(param)
      param = "Hello, my Friend"
    end
    
    make_message(a)
    
    a => Still has the value "Hello"
    

    Next, in your code example you refer to self.bus1, but you haven't declared such a member field. This is why you get the error.

    To achieve what you are trying to do, you need to use methods of the Array class, which change the provided instances:

    a = [1,2,3]
    b = [4,5,6]
    
    def swap_array_content(x, y)
    
      temp = y.dup # Need to make a temporary copy here
      y.replace(x)
      x.replace(temp)
    
    end
    
    swap_array_content(a, b)
    
    p a
    p b