In Ruby, getters and setters are typically defined by using the class method `attr_accessor`. Normally you see this at the top of the class and it sort of defines what properties that instances of the class will have. I feel like this method causes some confusion for Ruby beginners and it is something I had trouble with myself when I was first starting out. So let’s take a look:
class Foo attr_accessor :bar, :bazend
foo = Foo.new=> #<Foo:0x007fada99c2d00>foo.bar=> nilfoo.bar = 'jelly'=> "jelly"foo.bar=> "jelly"foo.baz=> nilfoo.baz = 'time'=> "time"foo.baz=> "time"
The first thing to note is that
attr_accessor is different than Rails’
attr_accessor is a RUBY method, it has nothing to do with Rails.
attr_accessor automatically sets up getters and setters for those instance variables. That’s it. So in the above example, Foo will gain these methods:
def bar @barenddef bar=(value) @bar = valueenddef baz @bazenddef baz=(value) @baz = valueend
So this is somewhat similar to Objective-C’s
synthesize method. Instance variables are private to the class so you expose them with these automatically generated getters and setters. Also note that since it’s Ruby, you are not restricted to the true definition of “private” and you can still retrieve instance variables with
instance_variable_set("@bar", value) if you have to do some hacks. It has some interesting documentation.
If you only need to generate the getters or setters there are also two other methods.
attr_reader will define only the getter methods.
attr_writer will defined only the setter methods. So all
attr_accessor really does is combined those two methods into one call.
Rails and ActiveRecord
ActiveRecord defines getters and setters for you as methods. When you call
current_user.name you are calling the
name method on
current_user. There are not properties in Ruby like you might see on other languages. There are only getter methods that provide access to the instance variables inside of them.
So how is
name defined? Luckily in Ruby you can do whatever you want, so ActiveRecord will read your database schema and generate methods on the fly when your Rails application starts. You can do this in a number of ways if you ever need to on your own, for example:
# Re-opening the User class and sticking the method into itclass User def name; "Whatever"; endend# Using class_eval to add arbitrary blocks of code to the classUser.class_eval do def name; "Whatever"; endend# Using define_methodclass User define_method(:name) do "Whatever" endend
module_eval (same as
class_eval) to add the getters to your model. This is done in rails/activerecord/lib/active_record/attribute_methods/read.rb (in ActiveRecord 4.1).
Read is one of many modules included into your model.
Overriding ActiveRecord getters is sometimes very useful. If you do need to override something in your model then you will definitely have to call
super because ActiveRecord does so much shit behind the scenes that it’s useful to gain all of that for free. One nice use is when you want to use a Null Object when an attribute is nil:
def address super || NullAddress.newend
Maybe you need to override a setter so you can parse a string date into a DateTime so ActiveRecord can read it and store it in a datetime column:
def started_at=(date) super DateTime.parse(date) # or the Chronic gemend