简体   繁体   English

是否可以在 Rails 中将 class_attribute 设为私有或受保护?

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

I have a situation where I need a variable (eg foo ) which is used internally within a class (eg Parent ) and sub-classes (eg Child ).我有一种情况,我需要一个在 class (例如Parent )和子类(例如Child )内部使用的变量(例如foo )。 In my case, I'm using Single Table Inheritance.就我而言,我使用的是单表 Inheritance。 Child should inherit the variable foo from Parent if Child does not set foo .如果Child未设置foo ,则Child应从Parent继承变量foo If Child does set foo , that value should only change foo for Child but not in Parent .如果Child确实设置了foo ,则该值应该只更改Childfoo而不是Parent This seems like a clear cut case for class_attribute .这似乎是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 , eg from the console:问题是class_attribute不尊重privateprotected关键字,因此foo可以从ParentChild外部访问,例如从控制台:

# 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 .我想知道如何实现我上面描述的设置,包括class_attribute以外的解决方案。 Note, setting instance_accessor , instance_writer , and instance_reader had no effect.请注意,设置instance_accessorinstance_writerinstance_reader无效。 Also note that a class instance variable will not work since Child will not inherit a value for foo from Parent .另请注意, class 实例变量将不起作用,因为Child不会从Parent继承foo的值。

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

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. class 属性(@@...)通过 getter def foo...访问,并且 getter 受private关键字的约束,以控制您表达的访问。

The following should work.以下应该工作。 It uses class instance variables instead of class variables, which can be a bit tricky to implement .它使用 class 实例变量而不是 class 变量,这可能有点难以实现

class Parent < ApplicationRecord  
  @foo = "Parent"

  class << self
    private

    # This will try to get the class instance variable @foo on the current class,
    # trying each parent until base_class is reached.
    def foo
      if self == base_class
        @foo
      else
        @foo || superclass.send(:foo)
      end
    end
  end
end

class Child < Parent
  @foo = "Child"
end

class DeepChild < Child
end

puts Parent.foo # private method error
puts Parent.send(:foo) # private, but accessible via .send
puts Child.foo # private methode error
puts Child.send(:foo) # private, but accessible via .send. Child's @foo || Parent's @foo
puts DeepChild.foo # private method error
puts DeepChild.send(:foo) # private, but accessible via .send. DeepChild's @foo || Child's @foo || Parent's @foo

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM