So I create a class in ruby:
class User
def initialize
end
end
Now say I want to create a attribute that is a hash with a getter/setter, I'm confused as to the options I have doing this.
If I do this:
class User
attr_accessor :some_hash
end
But I don't want this hash to be nil ever, always an empty hash.
I'm confused when I should do:
def some_hash
self.some_hash ||= {}
end
and do:
def some_hash
@some_hash ||= {}
end
What is the difference?
If I don't use the attr_accessor, I have to create both the reader and writer (or getter/setter):
def some_hash=()
end
def some_hash
end
I'm hoping someone can clear up the options I would have for creating a some_hash attribute which is a hash, and that never returns a nil if it is empty.
i.e. using attr_accessor, manually creating the methods, and finally when to use @some_hash and self.some_hash
attr_accessor :some_hash
defines reader and writer methods for the given attribute. It is equivalent to this:
class User
def some_hash
@some_hash
end
def some_hash=(some_hash)
@some_hash = some_hash
end
end
@some_hash
refers to an instance variable of the object, while some_hash
and some_hash=
are methods. The former returns the value of the variable, the latter sets it.
The idiom self.some_hash ||= {}
is equivalent to self.some_hash || self.some_hash = {}
.
Boolean operators in Ruby short circuit, which means the second expression (self.some_hash = {}
) will not be executed at all if the first expression (self.some_hash
) returns a true value.
The method:
def some_hash
self.some_hash ||= {}
end
Is actually recursive, since it expands to some_hash || self.some_hash = {}
. It will keep calling itself until you get a stack overflow. Use the second form:
def some_hash
@some_hash ||= {}
end
Since it sets the instance variable directly, you will not have problems with recursion, nor will you have to call a writer method. some_hash
can never return nil
, because if @some_hash
is nil
, it will be assigned an empty hash before the method returns.
By the way, this is also called lazy initialization, because the variable is initialized when it is first accessed, not when the User
instance is created.
Apart from Matheus's answer, a little more about attributes:
Attributes are methods. They define how you do with some symbols. attr_reader
defines the getter, attr_writer
defines the setter and attr_accessor
defines both. While attributes only take symbols, it does not "declare" any instance variables, as there is no such a thing in Ruby as declaring a variable. It is always declared and initialized when the first time used.
So after you say, attr_accessor :some_hash
, the runtime will know the way to access a variable called some_hash
, but this variable does not exist till you use it. You can try:
class User
attr_accessor :some_hash
attr_reader :some_other_hash
end
usr = User.new
p usr.some_hash
usr.some_hash = {}
p usr.some_hash
p usr.some_other_hash
usr.some_other_hash = {}
You will get nil,{},nil, and an error
. The error is because only getter method is defined but no setter. If you want to define a setter yourself, you can still write a method called some_other_hash=()
My suggestion for instance variable is (which I and maybe many others use):
class User
attr_accessor :some_hash
def initialize
@some_hash = {}
end
end
usr = User.new
p usr.some_hash
This way does a "psudo" declaration when you create an object for the class, and the instance variable will also be initialized.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With