Just curious what the difference is between these two in a Rails gem:
write_inheritable_attribute(:sample, "sample")
self.sample = "sample"
I couldn't find any good documentation on write_inheritable_attribute
, and was just reading through some gem source and found the former used a few times. Thanks!
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, inheritable attributes provide a solution:
>> class B ; end
>> B.write_inheritable_attribute(:candy, 7) # => 7
>> class C < B ; end
>> C.read_inheritable_attribute(:candy) # => 7
For a simple class or module, there wouldn't be a difference, but with more complex modules that may be loaded with multiple other modules, methods like write_inheritable_attribute
can help you modify objects easily and reliably without having to worry about scope, private/protected methods and all kinds of interference from ruby metaprogramming magic like method_missing
.
In short, when you write foo.sample = "sample"
there are all kinds of things that may happen before, after, or instead of setting the attribute, especially if the object uses ActiveModel or an ORM. When you use foo.write_inheritable_attribute(:sample, "sample")
you have much greater control over what happens.
Inheritable attribute was implemented mainly to address the problem where ruby class variable is shared across the 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 produce 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
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 class attribute (what used to be inheritable attribute) 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 produce 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With