Using Attributes With Ruby

Look at any object oriented code and it all more or less follows the same pattern. Create an object, call some methods on that object and access attributes of that object. There's not much else you can do with an object except pass it as a parameter to another object's method. But what we're concerned with here is attributes.

Attributes are like instance variables you can access via the object dot notation. For example, person.name would access a person's name. Similarly, you can often assign to attributes like person.name = "Alice". This is a similar feature to member variables (such as in C++), but not quite the same. There's nothing special going on here, attributes are implemented in most languages using "getters" and "setters," or methods that retrieve and set the attributes from instance variables.

Ruby doesn't make a distinction between attribute getters and setters and normal methods. Because of Ruby's flexible method calling syntax, no distinction needs to be made. For example, person.name and person.name() are the same thing, you're calling the name method with zero parameters. One looks like a method call and the other looks like an attribute, but they're really both the same thing. They're both just calling the name method. Similarly, any method name that ends in an equals sign (=) can be used in an assignment. The statement person.name = "Alice" is really the same thing as person.name=(alice), even though there is a space in between the attribute name and the equals sign, it's still just calling the name= method.

01
of 03

Implementing Attributes Yourself

Close up of woman's hands using laptop at home
Andreas Larsson/Folio Images/Getty Images

You can easily implement attributes yourself. By defining setter and getter methods, you can implement any attribute you wish. Here's some example code implementing the name attribute for a person class. It stores the name in a @name instance variable, but the name doesn't have to be the same. Remember, there's nothing special about these methods.

 #!/usr/bin/env ruby class Person def initialize(name) @name = name end def name @name end def name=(name) @name = name end def say_hello puts "Hello, #{@name}" end end 

One thing you'll notice right away is that this is a lot of work. It's a lot of typing just to say that you want an attribute named name that accesses the @name instance variable. Luckily, Ruby provides some convenience methods that will define these methods for you.

02
of 03

Using attr_reader, attr_writer and attr_accessor

There are three methods in the Module class that you can use inside of your class declarations. Remember that Ruby makes no distinction between runtime and "compile time," and any code inside of class declarations can not only define methods but call methods as well. Calling the attr_reader, attr_writer and attr_accessor methods will, in turn, define the setters and getters we were defining ourselves in the previous section.

The attr_reader method does just like what it sounds like it will do. It takes any number of symbol parameters and, for each parameter, defines a "getter" method that returns the instance variable of the same name. So, we can replace our name method in the previous example with attr_reader :name.

Similarly, the attr_writer method defines a "setter" method for each symbol passed to it. Note that the equals sign need not be part of the symbol, only the attribute name. We can replace the name= method from the previous example with a call to attr_writier :name.

And, as expected, attr_accessor does the job of both attr_writer and attr_reader. If you need both a setter and getter for an attribute, it's common practice not to call the two methods separately, and instead call attr_accessor. We could replace both the name and name= methods from the previous example with a single call to attr_accessor :name.

#!/usr/bin/env ruby def person attr_accessor :name def initialize(name) @name = name end def say_hello puts "Hello, #{@name}" end end
03
of 03

Why Define Setters and Getters Manually?

Why should you define setters manually? Why not use the attr_* methods every time? Because they break encapsulation. Encapsulation is the principal that states no outside entity should have unrestricted access to the internal state of your objects. Everything should be accessed using an interface that prevents the user from corrupting the internal state of the object. Using the methods above, we've punched a big hole in our encapsulation wall and allowed absolutely anything to be set for a name, even obviously invalid names.

One thing you'll often see is that attr_reader will be used to quickly define a getter, but a custom setter will be defined since the internal state of the object often wants to be read directly from the internal state. The setter is then defined manually and does checks to ensure that the value being set makes sense. Or, perhaps more commonly, no setter is defined at all. The other methods in the class function set the instance variable behind the getter in some other way.

We can now add an age and properly implement a name attribute. The age attribute can be set in the constructor method, read using the age getter but only manipulated using the have_birthday method, which will increment the age. The name attribute has a normal getter, but the setter makes sure the name is capitalized and is in the form of Firstname Lastname.

#!/usr/bin/env ruby class Person def initialize(name, age) self.name = name @age = age end attr_reader :name, :age def name=(new_name) if new_name =~ /^[A-Z][a-z]+ [A-Z][a-z]+$/ @name = new_name else puts "'#{new_name}' is not a valid name!" end end def have_birthday puts "Happy birthday #{@name}!" @age += 1 end def whoami puts "You are #{@name}, age #{@age}" end end p = Person.new("Alice Smith", 23) # Who am I? p.whoami # She got married p.name = "Alice Brown" # She tried to become an eccentric musician p.name = "A" # But failed # She got a bit older p.have_birthday # Who am I again? p.whoami
Format
mla apa chicago
Your Citation
Morin, Michael. "Using Attributes With Ruby." ThoughtCo, Aug. 26, 2020, thoughtco.com/using-attributes-2908103. Morin, Michael. (2020, August 26). Using Attributes With Ruby. Retrieved from https://www.thoughtco.com/using-attributes-2908103 Morin, Michael. "Using Attributes With Ruby." ThoughtCo. https://www.thoughtco.com/using-attributes-2908103 (accessed March 28, 2024).