Home > Blockchain >  Ruby: Working with instance variables within a case statement
Ruby: Working with instance variables within a case statement

Time:12-08

I'm perplexed as to why I need to selectively refer to an instance variable with "self" inside a case statement inside a class method:

I have a class with an instance method @working_dir:

class FileSystem
  attr_accessor :sizes, :working_dir
  attr_reader :input

  def initialize(input)
    @input = input.split("\n")
    @sizes = Hash.new(0)
    @working_dir = []
  end
...
end

I've defined a method parse_cmd that performs an operation on @working_dir depending on the outcome of a case statement:

...
  def parse_cmd(str)
    cmd_arr = str.split(' ')
    return unless cmd_arr[1] == 'cd'

    case cmd_arr[2]
    when '..'
      working_dir.pop
    when '/'
      self.working_dir = ['/']
    else
      working_dir << cmd_arr[2]
    end
  end
...

Rubocop/the interpreter yells at me if I exclude the self on self.working_dir = ['/']. Why is this? Why do I need to include it here, but not on other references to @working_dir within the case statement?

CodePudding user response:

Consider a simple example:

irb(main):001:0> class A
irb(main):002:1>   attr_accessor :b
irb(main):003:1>   def initialize(b) 
irb(main):004:2>     @b = b
irb(main):005:2>   end
irb(main):006:1>   def c
irb(main):007:2>     b = 42
irb(main):008:2>   end
irb(main):009:1> end
=> :c
irb(main):010:0> a = A.new(27)
=> #<A:0x00007f7999088bc0 @b=27>
irb(main):011:0> a.c
=> 42
irb(main):012:0> a.b
=> 27

Calling a.c is assigning 42 to a local variable b, and is not modifying the instance variable @b.

I'd either need to use self.b = 42 or @b = 42 to ensure I am modifying the instance variable.

In your case, you don't need to use self.working_dir elsewhere because those uses cannot be construed as assigning to a local variable. Because no local variable working_dir exists, the accessor method is used.

CodePudding user response:

In your case statement, you are NOT directly refering to the @working_dir instance variable. Instead, you are using the accessor methods defined by attr_accessor :working_dir at the top of your class.

When calling attr_accessor, it will effectively define two methods on your class:

def working_dir
  @working_dir
end

def working_dir=(value)
  @working_dir = value
end

This allows you to access the value of the instance variable via the method call as if it were a local variable (but it's not, it's always a method call).

Now, in order to call the setter method working_dir=, Ruby requires that you call it with an explicit receiver (self in your case).

This is because without an explicit receiver, if you assign some value, Ruby will always assign to local variable. With working_dir = 'value', you are thus always creating a local variable named working_dir and assign a value to it.

If you use an explicit receiver however, e.g. self.working_dir = 'value', Ruby knows that this can not be a variable assignment anymore and will thus call your setter method.

  • Related