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.