If you look at the documentation of Module#attr
, you will see that there are three "overloads" for using it:
attr(name, …)
→ nil
attr(name, true)
→ nil
attr(name, false)
→ nil
The documentation then further explains the differences between the three:
The first form is equivalent to attr_reader
. The second form is equivalent to attr_accessor(name)
but deprecated. The last form is equivalent to attr_reader(name)
but deprecated.
So, the first and third forms only generate readers, not writers. The second form generates a reader and a writer, but it only takes a single name
formal parameter, you cannot use it to generate multiple attributes.
Also, forms two and three are deprecated and all three forms are non-idiomatic.
If you want to fix your code using the non-idiomatic Module#attr
, you need to do this:
attr :name , true
attr :age , true
attr :gender, true
You can simplify this a little using something like
%i[name age gender].each do |attribute|
attr attribute, true
end
However, as mentioned in the documentation, this form is deprecated, and as I mentioned earlier, Module#attr
is non-idiomatic.
The idiomatic way is to use the Module#attr_*
family of methods, i.e. Module#attr_reader
, Module#attr_writer
, and Module#attr_accessor
:
attr_accessor :name, :age, :gender
Note that since your Person
class doesn't actually have any behavior, you could simply replace it with a Struct
:
Person = Struct.new(:name, :age, :gender)
However, note that accessors in general and writers in particular are not good object-oriented design. Objects should do something, they should have behavior. Your Person
objects don't have any behavior, they just contain data. Also, an object should be valid after it is created, but your Person
objects are empty when they are created. And lastly, if at all possible, objects should be immutable.
We can make sure that the object is valid after creation by adding an initializer:
class Person
attr_accessor :name, :age, :gender
def initialize(name, age, gender)
self.name, self.age, self.gender = name, age, gender
end
end
person = Person.new('John', 55, 'male')
Note: Struct
will create an initializer for us.
If we want to make the object immutable, we need to remove the writers (or make them private
). Unfortunately, Struct
won't do that for us:
class Person
attr_reader :name, :age, :gender
private
attr_writer :name, :age, :gender
def initialize(name, age, gender)
self.name, self.age, self.gender = name, age, gender
end
end
person = Person.new('John', 55, 'male')
Now, person
is fully initialized after creating it, and it can no longer be changed.