Write inherited attribute versus primary purpose in Rails

Just wondering what the difference is between the two in Rails stone:

write_inheritable_attribute(:sample, "sample") self.sample = "sample" 

I could not find good documentation on write_inheritable_attribute and just read through some source of gems and found that the former was used several times. Thanks!

+7
ruby ruby-on-rails
source share
3 answers

There won't be a difference for a simple class or module, but with more complex modules that can be loaded with several other modules, methods like write_inheritable_attribute can help you easily and reliably modify objects without worrying about scope, private / protected methods and all kinds of interference from metaprogramming magic to ruby, like method_missing .

In short, when you write foo.sample = "sample" , there are all kinds of things that can happen before, after, or instead of setting an attribute, especially if the object uses ActiveModel or ORM. When you use foo.write_inheritable_attribute(:sample, "sample") , you have much more control over what happens.

+5
source share

Subclasses do not inherit instance variables:

 >> class B ; @candy = 1 ; end >> B.instance_variable_get :@candy # => 1 >> class C < B ; end >> C.instance_variable_get :@candy # => nil 

In rails, inherited attributes provide a solution:

 >> class B ; end >> B.write_inheritable_attribute(:candy, 7) # => 7 >> class C < B ; end >> C.read_inheritable_attribute(:candy) # => 7 
+13
source share

The inherited attribute was mainly implemented to solve the problem in which the ruby ​​class variable is common for class inheritance. Consider this example

 class Counter @@count = 0 def self.count @@count end def self.increment puts "==> #{self} increment" @@count += 1 end end class DogCounter < Counter end puts "Counter.count: #{Counter.count}" puts "DogCounter.count: #{DogCounter.count} -> nice, DogCounter inherits @@count from Counter" DogCounter.increment puts "DogCounter.count: #{DogCounter.count} -> as expected" puts "Counter.count: #{Counter.count} -> but Counter.count is also changed!" Counter.increment puts "Counter.count: #{Counter.count}" puts "DogCounter.count: #{DogCounter.count} -> @@count is shared with all the descendants of Counter" 

This will create this output.

 Counter.count: 0 DogCounter.count: 0 -> nice, DogCounter inherits @@count from Counter ==> DogCounter increment DogCounter.count: 1 -> as expected Counter.count: 1 -> but Counter.count is also changed! ==> Counter increment Counter.count: 2 DogCounter.count: 2 -> @@count is shared with all the descendants of Counter 

Please note that since Rails 3.2 write_inheritable_attribute has been removed. See http://dev.mensfeld.pl/2012/01/upgrading-to-rails-3-2-0-from-rails-3-1-3/

With the class attribute (an attribute that was inherited) we can implement something like this:

 class Counter class_attribute :count self.count = 0 def self.increment puts "==> #{self} increment" self.count += 1 end end class DogCounter < Counter end puts "Counter.count: #{Counter.count}" puts "DogCounter.count: #{DogCounter.count} -> nice, DogCounter inherits count from Counter" DogCounter.increment puts "DogCounter.count: #{DogCounter.count} -> as expected" puts "Counter.count: #{Counter.count} -> nice, it doesn't change count for Counter" Counter.increment puts "Counter.count: #{Counter.count}" puts "DogCounter.count: #{DogCounter.count} -> now each subclass can have their own class attribute that inherits default value from the superclass" 

This will create this output.

 Counter.count: 0 DogCounter.count: 0 -> nice, DogCounter inherits count from Counter ==> DogCounter increment DogCounter.count: 1 -> as expected Counter.count: 0 -> nice, it doesn't change count for Counter ==> Counter increment Counter.count: 1 DogCounter.count: 1 -> now each subclass can have their own class attribute that inherits default value from the superclass 
+2
source share

All Articles