
Comparison of class level accessors in Ruby On Rails
attr_accessor is very useful macro that provides a getter and setter for an instance variable. Sometimes we would like to achieve the same effect, yet for class variable — useful way to make our class configurable. There are at least three ways we can approach this problem: attr_accessor, cattr_accessor and class_attribute. All of those practically work the same way if we do not use inheritance, however when inheritance comes into play, there are some significant differences.
attr_accessor — when we do not want value to be inherited
In this scenario we take advantage of regular attr_accessor, but on the class level. Using attr_accessor allows us to define variables for each of child classes, but values set in parent classes are not inherited. Each child class has attribute value set to nil initially.
class Parent
class << self
attr_accessor :foo
end
endclass Child < Parent
endParent.foo = 100
Child.foo #=> nil // Value was not inherited
Child.foo = 200
Child.foo #=> 200
Parent.foo #=> 100 // Changing value in child class does not affect parent class
cattr_accessor — when we do want value to be shared across all classes
In this case we use cattr_accessor to inherit value of parent class variable in child classes. It is not necessary to wrap cattr_accessor declaration in class << self block. The important thing to be aware of is that redefining class variable value in child class will change it in both child and parent classes.
class Parent
cattr_accessor :foo
endclass Child < Parent
endclass Grandchild < Child
endParent.foo = 100
Child.foo #=> 100
Child.foo = 200
Child.foo #=> 200
Parent.foo #=> 200 // Value propagated up in inheritance tree
Grandchild.foo #=> 200 // Value propagated down in inheritance tree
class_attribute — when we want value to be inherited and overwritable without affecting parent classes
In my opinion the most expected scenario. Using class_attribute causes variable value from parent class to be inherited, yet we can safely redefine it in child class without affecting parent class.
class Parent
class_attribute :foo
endclass Child < Parent
endclass Grandchild < Child
endParent.foo = 100
Child.foo #=> 100 // Value is inherited
Child.foo = 200
Child.foo #=> 200
Parent.foo #=> 100 // Redefined value does not affect parent class
Grandchild.foo #=> 200 // Redefined value is inherited in child class
