What a mess. You have undoubtedly run across these methods sprinkled throughout the Ruby and Rails world. If you've been working with Rails for even a short time, you've probably read a little about security and attr_accessible
. But do you really understand what each of them do and when to use them?
I'm not a fan of whoever made these methods so closely named, especially because they serve very different purposes: two are specific to Rails (or more accurately, ActiveRecord), while the other three are Ruby core methods. When I have a need for any of them, I still have to really think about which one I actually want to use. Often I still have to glance at the rdocs to be reassured my choice is the right one. So let's dive in and figure out what the heck these are supposed to do, and when to use them.
Forget Rails, Let's Talk Ruby Attributes
I've noticed a fair number of folks try to write Rails apps without much knowledge of Ruby. That's a noble thing to try, but to be a truly effective Rails developer, you'll need to be intimately knowledgeable about the Ruby language.
For this reason, and to get our feet wet, we'll ignore the Rails methods for a moment and focus on the Ruby, since that's the source of three important attr_*
methods: attr_accessor
, attr_reader
, and attr_writer
. Put these three methods together in a bucket inside your head, as they are closely related.
Say we have a simple little Ruby class (remember we're ignoring Rails at the moment, so this is just a basic Ruby Object
class, not an ActiveRecord model).
In address.rb:
Our class doesn't do much yet, but we can create an Address
instance and print it as a string using the to_s
method:
What if we want to be able to get each of those attributes separately from the full address, say to print out only the zip code? Trying to print the zip attribute with our current code won't work:
In order to call address.zip
, we need an attribute getter to make the @zip
instance variable visible outside the instance. If you're from the world of Java you're probably intimately familiar with these so-called "getter" methods. So, you'd go and write your "getter" method for @zip
:
Okay, only four more methods to go and you can head over to the water cooler for a well-deserved break! (You get paid by the line, after all!) It feels kinda silly, though, since all these methods follow an identical pattern. Since this is such a common pattern, Ruby gives us a little shortcut: anytime you would add an attribute getter as above, you can and should use the handy attr_reader
method instead, which effectively creates the attribute getter methods for us behind the scenes, without us having to explicitly define each method in detail. Our new class looks like this:
Now we can grab all of our attributes apart from the full address:
But what about setting the zip code?
Nope, doesn't work. Maybe that's what you want: if an attribute should not be settable after the object is instantiated, you wouldn't want the ability to set the value of an attribute like this. (If you ever hear the term "immutable", that is what we're talking about here: Address instances are currently immutable because they can not be "mutated," or changed, after instantiation.) But if you do want to set your attribute values, you'll need to create a way for code outside the instance to change the value of that somewhat elusive @zip
instance variable. Again, if you are coming from Java programming, you'd attempt to write a "setter" method here:
But, if you're starting to hear harp music right now, you might be thinking that Ruby might give us an equally simple, declarative way to make attribute setter methods as it did for getters. Lo and behold, attr_writer
comes to the rescue:
And now we can set or get any attribute value:
Now, you might think we're in good shape: we've consolidated what would have been ten different method definitions into a two lines of declarative, readable code. Not so fast! There's a bit of redundancy in those two lines, isn't there? Specifying all your fields in two places is clearly less maintainable than if we could tell Ruby "we want getters and setters for this list of attributes." It might not be a big deal for our little Address class, but when you have hundreds or thousands of classes that you haven't looked at in a year, every extraneous line matters.
Again, this is such a common scenario in programming that Ruby offers the third attribute method, attr_accessor
, which does exactly what two separate calls to attr_reader
and attr_writer
would do.
So, let's get rid of that repetition now:
And this works exactly like the previous example:
Hopefully this clarifies what these three methods are for. Next time, we'll explore the similarly-named-but-entirely-different-purposed attr_protected
and attr_accessible
methods brought to us by Rails' ActiveRecord models.
Further Reading
The attr_accessor
, attr_reader
, and attr_writer
methods are fairly well documented in the Ruby core rdocs.
Feedback and Article Ideas
Want to see a topic explored here? Send Me a Message.