虽然完全有可能,但正如 Wiktor 的回答所示,我的建议是不要在单个正则表达式中定义它,因为:
- 除非您非常了解正则表达式,否则该解决方案很难理解。
- 同样,解决方案很难根据新要求进行更新,除非您非常了解正则表达式。
- 通过一次性执行整个检查,如果验证失败,您将不可避免地收到一条通用错误消息,例如
"Invalid Format",它没有解释为什么它是无效的。然后让用户重新阅读非平凡的格式规则并理解原因。
相反,我建议定义一个custom validation class,它可以分别执行这些检查(通过易于理解的方法),并在每次检查失败时添加不同的错误消息。
类似的东西:
# app/models/user.rb
class User < ApplicationRecord
validates :username, presence: true, username: true
end
# app/validators/username_validator.rb
class UsernameValidator < ActiveModel::EachValidator
def validate(record, attribute, value)
validate_length(record, attribute, value)
validate_allowed_chars(record, attribute, value)
validate_sequential_chars(record, attribute, value)
validate_first_and_last_chars(record, attribute, value)
end
private
def validate_length(record, attribute, value)
unless value.length >= 3 && value.length <= 28
record.errors[attribute] << "must be between 3 and 28 characters long"
end
end
def validate_allowed_chars(record, attribute, value)
unless value =~ /\A[._a-zA-Z0-9]*\z/
record.errors[attribute] << "must only contain periods, underscores, a-z, A-Z or 0-9"
end
end
def validate_sequential_chars(record, attribute, value)
if value =~ /[._]{2}/
record.errors[attribute] << "cannot contain two consecutive periods or underscores"
end
end
def validate_first_and_last_chars(record, attribute, value)
if value =~ /\A[._]/ || value =~ /[._]\z/
record.errors[attribute] << "cannot start/end with a period or underscore"
end
end
end
例如,您在上面问:“如果我需要扩展它以仅允许小写字母怎么办?”我认为现在很明显如何更新代码以适应这种行为,但要清楚 - 你需要做的就是:
def validate_allowed_chars(record, attribute, value)
unless value =~ /\A[._a-z0-9]*\z/
record.errors[attribute] << "must only contain periods, underscores, a-z or 0-9"
end
end
您现在还可以非常轻松地为这些验证检查编写测试,并通过针对错误消息的内容进行验证来断言正在执行正确的验证 ;当所有验证失败都导致相同的错误时,这是不可能的,
这种方法的另一个好处是代码可以很容易地共享(可能有一些细微的行为差异)。您可以对多个属性或多个模型执行相同的验证,可能具有不同的允许长度或格式。