Home > Blockchain >  Why is the instance attribute modified when << shoveling a value into the instance method call
Why is the instance attribute modified when << shoveling a value into the instance method call

Time:03-27

Why does the line, restaurant.menu << "pizza" , modify @menu attribute for our restaurant instance? The way I understand it is restaurant.menu is called and finishes evaluating to ["donuts", "cake"]. At this point the method call is done. So why is << pizza affecting the @menu attribute?

class Restaurant
    def initialize(name)
        @name = name
    end

    def menu
        @menu = @menu || ["donuts", "cake"]
    end
end

restaurant = Restaurant.new("Mcdonalds")

p restaurant.menu             #["donuts", "cake"]

restaurant.menu << "pizza"    #???    

p restaurant.menu             #["donuts", "cake", "pizza"]>

CodePudding user response:

As @red_menace suggests, it might be worthwhile trying something like:

class Restaurant
    def initialize(name)
        @name = name
        @menu = ["donuts", "cake"]
    end

    def menu
        @menu.dup
    end
end

CodePudding user response:

Although @red_menace already answered what root cause is, but I will elaborate what that means.

First you need to understand Ruby objects mutability. Here you can find some good article.

Basically in your example menu instance method of the class returns reference to instance variable @menu of the class' instance, which is mutable array (you can modify that through the reference).

If you really want to make it immutable, you can either freeze the variable or clone it as it was suggested by @Jad.

Here you can find a great discussion on similar problem.

Here you can see an program trace that all the time you modify menu method's returned result you modified instance variable @menu.

require 'pp'

class Restaurant
    def initialize(name)
        @name = name
    end

    def menu
        @menu = @menu || ["donuts", "cake"]
        puts "@menu object_id = #{@menu.object_id}"
        @menu
    end
end

restaurant = Restaurant.new("Mcdonalds")


p restaurant.menu             #["donuts", "cake"]

pp "object instance variables are #{restaurant.instance_variables}"


restaurant.menu << "pizza"    #???    

p restaurant.menu             #["donuts", "cake", "pizza"]>

p "restaurand.menu.object_id = #{restaurant.menu.object_id}"

Here is the program's output:

$ruby main.rb
@menu object_id = 47245049576960
["donuts", "cake"]
"object instance variables are [:@name, :@menu]"
@menu object_id = 47245049576960
@menu object_id = 47245049576960
["donuts", "cake", "pizza"]
@menu object_id = 47245049576960
"restaurand.menu.object_id = 47245049576960"
  •  Tags:  
  • ruby
  • Related