Donato Donato - 6 months ago 9
Ruby Question

Why can't this inherited class attribute be assigned to?

I know in Ruby, unlike other languages, there is class method inheritance.

class A
def self.filter_by
"filter"
end
end

class B < A
end

B.filter_by => "filter"


B's singleton class inherits methods from A's singleton class, and it appears to inherit the instance variables of those class methods:

class A
class << self
attr_accessor :filter_by
end
end

class B < A
end

B.filter_by = "filter"
A.filter_by = "no_filter"
=> "no_filter"
B.filter_by # B's value is not changed
=> "filter"


But it does not inherit the values:

class A
class << self
attr_accessor :filter_by
end
end

class B < A
end

A.filter_by = "filter"
B.filter_by => nil


B did not inherit the value set to A's @filter_by instance variable.

It can be resolved this way using the built-in inherited hook, or in Rails you can use
class_attribute
:

class A < ActiveRecord::Base
class_attribute :filter_by
end
class B < A
end
A.filter_by = "filter"
=> "filter"
B.filter_by # Now B's @filter_by inherited the value set in A's @filter_by
=> "filter"


But what I don't understand is why I cannot do this:

class A < ActiveRecord::Base
class_attribute :filter_by
end

class B < A
filter_by= %w(user_id_is status_id_is task_category_id_is)
end
B.filter_by => nil


Why does it return nil in this situation and not in the other situation? In this case as well, I set a value on self when I declared B (which is the singleton object).

Answer

Keep in mind that in your last example, there is

  1. A class attribute named filter_by(with corresponding filter_by and filter_by= methods (inherited from class A)
  2. A locally scoped variable inside of class B called filter_by.

The variable filter_by is not available outside of the scoping, and is also not available inside class B methods of any kind. In class B, you've set this variable to the list of strings, but when you do B.filter_by, the method inherited from class A is called. This value has not yet been set, so nil is returned.

Had you done self.filter_by = %w(...) inside the class, then it would have accessed the method filter_by= and set the class attribute as you intended.

Comments