user19812359
user19812359

Reputation: 21

Is it possible to make a class_attribute private or protected in Rails?

I have a situation where I need a variable (e.g. foo) which is used internally within a class (e.g. Parent) and sub-classes (e.g. Child). In my case, I'm using Single Table Inheritance. Child should inherit the variable foo from Parent if Child does not set foo. If Child does set foo, that value should only change foo for Child but not in Parent. This seems like a clear cut case for class_attribute.

The problem is that class_attribute is not respecting a private or protected keyword, so foo can be accessed from outside of Parent and Child, e.g. from the console:

# Now
Parent.foo
=> "Foo"
Child.foo
=> "Foo"/"Bar"

# What I want
Parent.foo
=> 'method_missing': protected method 'foo' called for Parent:Class (NoMethodError)
Child.foo
=> 'method_missing': protected method 'foo' called for Child:Class (NoMethodError)

I'm wondering how to achieve the setup I've described above, including solutions other that class_attribute. Note, setting instance_accessor, instance_writer, and instance_reader had no effect. Also note that a class instance variable will not work since Child will not inherit a value for foo from Parent.

class Parent < ApplicationRecord
  private
  class_attribute :foo, default: "Foo" 
end

class Child < Parent
  # Child may or may not set :foo. If not, the value should be inherited from Parent.
  # Setting :foo in Child should NOT change the value of Parent, hence using class_attribute.
  # class_attribute :foo, default: "Bar"
end

Upvotes: 2

Views: 325

Answers (1)

Les Nightingill
Les Nightingill

Reputation: 6156

I think this meets your needs, if I have understood them correctly! Take a look:

class Parent
  @@foo = "parent foo"
  class << self
    private
    def foo
      @@foo
    end
  end
end

class Child < Parent
  @@foo = "child foo"
end

class AnotherChild < Parent
  @@foo = "another child foo"
  class << self
    def foo
      @@foo
    end
  end
end

puts Parent.foo # private
puts Parent.send(:foo) # private, but accessible via .send
puts Child.send(:foo) # inherits from Parent, => "parent foo"
puts Child.foo # private method
puts AnotherChild.foo # defined in subclass => "child foo"

The class attributes (@@...) are accessed via getters def foo..., and the getters are constrained by the private keyword to control the access as you expressed.

Upvotes: 0

Related Questions