【问题标题】:Rails - Usernames that cannot start or end with charactersRails - 不能以字符开头或结尾的用户名
【发布时间】:2019-01-26 22:54:57
【问题描述】:

最初问这个关于用户名正则表达式的问题:Usernames that cannot start or end with characters

如何使用正确的 Ruby on Rails 语法来实现这一点?

这是我当前的 User.rb 验证:

validates_format_of :username, with: /\A[\w\._]{3,28}\z/i

此验证允许下划线和句点,但目标是不允许它们出现在用户名的开头或结尾。

我正在尝试使用 Rails 正则表达式来实现这些规则:

  • 可以包含大小写字母和数字
  • 可以包含下划线和句点
  • 不能连续包含 2 个下划线
  • 不能连续包含 2 个句点
  • 不能以下划线或句点开头或结尾
  • 不能包含带重音符号的字母
  • 长度必须在 3 到 28 个字母之间

有效:

Spicy_Pizza
97Indigos
Infinity.Beyond

无效:

_yahoo
powerup.
un__real
no..way

【问题讨论】:

    标签: ruby-on-rails ruby regex validation ruby-on-rails-5


    【解决方案1】:

    虽然完全有可能,但正如 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
    

    您现在还可以非常轻松地为这些验证检查编写测试,并通过针对错误消息的内容进行验证来断言正在执行正确的验证 ;当所有验证失败都导致相同的错误时,这是​​不可能的,

    这种方法的另一个好处是代码可以很容易地共享(可能有一些细微的行为差异)。您可以对多个属性或多个模型执行相同的验证,可能具有不同的允许长度或格式。

    【讨论】:

    • 这也使得编写相关的测试用例变得更加容易。在这里,测试覆盖率实际上会显示您是否遇到了所有情况。
    • 真的很感激 - 让我大开眼界。在测试方面肯定会给我们更大的灵活性,很高兴看到这两种方法相互比较。
    【解决方案2】:

    你可以使用

    /\A(?=.{3,28}\z)[a-zA-Z0-9]+(?:[._][a-zA-Z0-9]+)*\z/
    

    请参阅Rubular demo

    详情

    • \A - 字符串开头
    • (?=.{3,28}\z) - 允许/需要 3 到 28 个字符,而不是直到字符串末尾的换行符
    • [a-zA-Z0-9]+ - 一个或多个 ASCII 字母/数字
    • (?:[._][a-zA-Z0-9]+)* - 0+ 个序列:
      • [._] - ._
      • [a-zA-Z0-9]+ - 一个或多个 ASCII 字母/数字
    • \z - 字符串结束。

    【讨论】:

    • 这看起来很棒!请问,如果我需要扩展它以仅允许小写字母,我是否只需删除“A-Z”?
    • @Kobius 是的,这就是你需要做的所有事情。
    • 这个答案很好,但我必须补充一点......定义自定义验证类并将这些检查中的每一个作为更小、更简单的方法执行可能更明智/*有用*/实用.
    • 例如,可能需要为每个失败条件显示不同的错误消息,以解释为什么验证失败。更不用说如果你对正则表达式不太满意,如果检查不是像这样一次性执行,那么修改这段代码会容易得多。
    • @TomLord 我只是在回答这个问题。我认为该评论应低于原始问题。
    猜你喜欢
    • 1970-01-01
    • 2022-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-29
    • 1970-01-01
    • 2020-10-08
    • 2018-02-18
    相关资源
    最近更新 更多