1

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).

Community
  • 1
  • 1
Daniel Viglione
  • 8,014
  • 9
  • 67
  • 101

2 Answers2

3

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.

Cody Poll
  • 7,690
  • 3
  • 27
  • 22
2

You just forgot self. when you assigned to the attribute. Ruby thought you were assigning to a local. Fix it with self.:

class A < ActiveRecord::Base
  class_attribute :filter_by
end

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

B.filter_by => nil
=> ["user_id_is", "status_id_is", "task_category_id_is"]
Dave Schweisguth
  • 36,475
  • 10
  • 98
  • 121