提问人:PKP 提问时间:1/6/2023 更新时间:1/7/2023 访问量:298
Ruby:如何进行类/结构输入验证
Ruby: How to do class/struct input validation
问:
我正在编写一个基于结构的类,但我正在努力寻找一种进行输入验证的好方法。
结构定义了许多成员。有些成员是必需的 (),有些是可选的 ()。创建类的实例时,必须至少提供所有必需的输入,除非您不提供任何输入,在这种情况下,它们将初始化为 nil。如果提供的参数太少或太多,则应引发异常。此外,您应该能够使用哈希来初始化实例,其中所有键都与结构的成员匹配。如果有任何键与成员不匹配,则应引发异常。m
o
请考虑以下代码:
MyClass = Struct.new(:m1, :m2, :o1) do
# Write class content...
end
# Should be allowed and initialize m1, m2 and o1 to nil - works out of the box
instance1 = MyClass.new
# Should be allowed - works out of the box
instance2 = MyClass.new("m1", "m2") # o1 automatically initialized to nil
instance3 = MyClass.new("m1", "m2", "o1")
# Should be allowed and should map hash values and keys to struct members
instance4 = MyClass.new({m1: "m1", m2: "m2"}) # o1 automatically initialized to nil
instance5 = MyClass.new({m1: "m1", m2: "m2", o1: "o1"})
# Should raise exception saying too many arguments
instance6 = MyClass.new({m1: "m1", m2: "m2", o1: "o1", extra: "extra"})
instance7 = MyClass.new("m1", "m2", "o1", "extra")
# Should raise exception saying "m2" is missing
instance8 = MyClass.new({m1: "m1"})
instance9 = MyClass.new("m1")
# Should raise exception saying "other" key isn't allowed
instance10 = MyClass.new({m1: "m1", other: "other"})
我试图在类中定义常量,以说明哪些输入是必需的和可选的,然后循环访问它们。但这似乎是错误和繁琐的。我还认为常量在我的类之外泄漏,因为如果我在另一个类中使用相同的常量名称,我会收到重新定义警告。
MyClass = Struct.new(:m1, :m2, :o1) do
MANDATORY_INPUT = [:m1, :m2]
OPTIONAL_INPUT = [:o1]
def initialize(args = nil)
if args != nil
# loop through arrays above and raise exception if necessary
end
# if input is a Hash assign members
end
end
请注意,Ruby 对我来说是很陌生的,所以我的问题可能会有明显的答案/问题。
答:
这里的第一个问题是这不是结构体的正确使用。
当您想要拥有一个简单的对象来封装一些数据但实际上没有太多逻辑时,结构体是合适的。否则,请使用类。
常量“泄漏”到外部作用域的原因是由于常量作用域和模块嵌套在 Ruby 中的工作方式。当前的模块嵌套仅由 and 关键字更改,而不是块:module
class
module Foo
BAR = 1
end
puts BAR # uninitialized constant BAR (NameError)
Baz = Module.new do
BAR = "ooops"
end
puts BAR # "ooops"
如果您使用它,它会将常量范围限定为 Struct,但这是一件愚蠢的事情。self::MANDATORY_INPUT = [:m1, :m2]
如果你真的需要验证,这也是值得怀疑的 - 你要做的大部分事情似乎都暗示你真正想要的只是关键字参数:
class MyClass
def initialize(required_arg_1:, required_arg_2:, optional_arg: nil, **kwargs)
# ...
@required_1 = required_arg_1
@required_2 = required_arg_2
@optional = optional_arg
@extra_options = kwargs
end
end
将哈希作为位置参数是一种非常过时的做法——关键字参数是在 Ruby 2.0 中引入的,那是很久以前的事了。
如果您以后确实需要复杂的规则来验证参数的格式、类型等,请稍后添加 - 但首先花一分钟时间思考一下您需要构建什么样的对象以及它们应该具有什么样的签名。它们可能比你想象的要灵活得多。
好吧,哈希格式的数据是通过专有的 json 协议来的。然后它被解析为 ruby 哈希。然后我想象使用哈希实例化这个类的实例。
这不应该在类的初始值设定项中全部处理。
相反,您可以使用工厂方法(返回实例的类方法),甚至可以更好地将输入数据规范化为您自己的表示形式。
class MyClass
def self.from_wonky_json(garbage_input)
# map the input from the API to your own class signature
new(
foo: garbage_input.dig("a", "b", "c"),
bar: garbage_input.dig("c", "d")&.frobnobize
)
end
end
如果遵循单一责任原则,则对象不应同时负责数据并对其进行规范化。
评论
ArgumentError
上一个:添加用于模型创建的验证
评论
dry-schema
dry-validation