class Parent
VALID_COLORS = ["blue"]
MyStruct = Struct.new(:color) do
def initialize(color:)
raise "invalid color" unless self.class::VALID_COLORS.include?(color)
super(color: color)
end
end
end
class Subclass < Parent
VALID_COLORS = ["red"]
def call
MyStruct.new(color: "red")
end
end
> Subclass.new.call
=> uninitialized constant Parent::MyStruct::VALID_COLORS
如何VALID_COLORS在我的結構初始化中訪問正確的常量?通常self.class::CONSTANT會允許您在父類中訪問子類的常量,但看起來self.class會在結構內部顯示結構。我想到了一些解決方法,例如 1)向結構添加一個新引數以采用有效顏色;或 2) 設定VALID_COLORS為實體變數而不是類變數。但我很好奇是否有辦法讓我當前的實作按預期作業?
uj5u.com熱心網友回復:
我很好奇是否有辦法使我當前的實作按預期作業?
既然你要求,讓我們試一試。
首先,您Parent::MyStruct已經無法正常作業:
Parent::MyStruct.new(color: 'blue')
#=> NameError: uninitialized constant Parent::MyStruct::VALID_COLORS
那是因為VALID_COLORS定義在 中Parent,而不是在 中Parent::MyStruct。為了動態參考父命名空間(在嵌套方面),Rails 有module_parent:
Parent::MyStruct.module_parent #=> Parent
應用于您的代碼:(我還添加keyword_init: true了使結構采用實際關鍵字引數,請參閱檔案Struct.new)
class Parent
VALID_COLORS = ["blue"]
MyStruct = Struct.new(:color, keyword_init: true) do
def initialize(color:)
raise "invalid color" unless self.class.module_parent::VALID_COLORS.include?(color)
# ^^^^^^^^^^^^^
super
end
end
end
這使:
Parent::MyStruct.new(color: 'blue')
#=> #<struct Parent::MyStruct color="blue">
現在我們已經修復了那個,讓我們嘗試通過子類創建一個結構:
Subclass::MyStruct.new(color: 'red')
#=> RuntimeError: invalid color
發生此錯誤,因為Subclass::MyStruct實際上是Parent::MyStruct:
Subclass::MyStruct #=> Parent::MyStruct
要解決這個問題,Subclass需要它自己的MyStruct. 我們可以通過創建一個繼承自的新類來解決它Parent::MyStruct:
class Subclass < Parent
VALID_COLORS = ["red"]
MyStruct = Class.new(Parent::MyStruct)
end
Parent::前綴實際上是可選的,但看起來MyStruct = Class.new(MyStruct)太混亂了。
以上給出:
struct = Subclass::MyStruct.new(color: 'red')
#=> #<struct Subclass::MyStruct color="red">
struct.is_a?(Subclass:MyStruct) #=> true
struct.is_a?(Parent::MyStruct) #=> true
如果您真的想使用這種方法,您甚至可以將動態類創建移到Parent使用inherited回呼中:
class Parent
def self.inherited(subclass)
subclass.const_set(:MyStruct, Class.new(self::MyStruct))
end
# ...
end
uj5u.com熱心網友回復:
AVALID_COLORS 在內部Parent和內部定義Subclass(此處無關緊要)。的初始化程式Parent::MyStruct也嘗試訪問此名稱的常量,但MyStruct沒有VALID_COLORS.
我不知道你到底想達到什么目標,但根據你想要的,你可以做一個
... unless Parent::VALID_COLORS.include?(color)
或一個
... unless Subclass::VALID_COLORS.include?(color)
或為 提供另一個VALID_COLORS常數MyStruct。
uj5u.com熱心網友回復:
這里的問題是MyStruct 常量實際上是對定義在Parent. 當Parent被子類化時,MyStruct物件沒有被重新定義,參考只是被復制。我們可以通過以下方式驗證這一點:
irb(main):014:0> Parent::MyStruct.object_id
=> 70326616692980
irb(main):015:0> Subclass::MyStruct.object_id
=> 70326616692980
為了解決這個問題,您可以創建一個動態初始化類物件的方法:
class Parent
private
def my_struct
@my_struct ||= Struct.new(:color) do
def initialize(color:)
raise "invalid color" unless self.class::VALID_COLORS.include?(color)
super(color: color)
end
end
end
end
如果您愿意做更多的作業,那么將顏色作為自己的類而不是結構會更加健壯:
class Color
attr_accessor :color
def initialize(color: color, valid_colors: Parent:: VALID_COLORS)
raise "invalid color" unless valid_colors.include?(color)
@color = color
end
end
class Parent
def self.color(color)
Color.new(color: color, valid_colors: self.class::VALID_COLORS)
end
end
或者您可以嘗試調查呼叫者以加載正確的常量(我強烈建議不要這樣做):
MyStruct = Struct.new(:color) do
def initialize(color:)
caller_path = caller_locations(1,1)[0].absolute_path
valid_color_lib = {
"parent.rb" => Parent::VALID_COLORS,
"subclass.rb" => Subclass::VALID_COLORS
}[caller_path]
raise "invalid color" unless valid_color_lib.include?(color)
super(color: color)
end
end
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/490668.html
上一篇:只有最后一個物件被推送到物件陣列(JavaScript)
下一篇:如何將資料輸入新陣列
