代码之家  ›  专栏  ›  技术社区  ›  Tomer Aberbach

如何加载不直接从Rails 5中的Rails验证器类继承的自定义验证器?

  •  3
  • Tomer Aberbach  · 技术社区  · 7 年前

    我正在为一个我一直在工作的Rail5项目编写一些自定义验证器。例如:

    class EmailValidator < ActiveModel::EachValidator
      def validate_each(record, attribute, value)
        if value.present? && !/\A[^\p{Z}]+@[^\p{Z}]+\.[^\p{Z}]+\z/.match?(value)
          record.errors.add(attribute, "must be an email")
        end
      end
    end
    

    这种方法工作得很好,但是我的许多自定义验证器都在类似地检查regex匹配,因此我决定创建一个抽象的 RegexValidator 班级:

    class RegexValidator < ActiveModel::EachValidator
      self.abstract_class = true
    
      def regex
        raise "Children of this class must implement the 'regex' method."
      end
    
      def message(record, attribute, value)
        raise "Children of this class must implement the 'message(record, attribute, value)' method."
      end
    
      def validate_each(record, attribute, value)
        if value.present? && !regex.match?(value)
          record.errors.add(attribute, message(record, attribute, value))
        end
      end
    end
    

    所以现在 EmailValidator 实现方式如下:

    class EmailValidator < RegexValidator
      def regex
        /\A[^\p{Z}]+@[^\p{Z}]+\.[^\p{Z}]+\z/
      end
    
      def message(record, attribute, value)
        "must be an email"
      end
    end
    

    但是,由于这一改变Rails 5不再自动加载我的 电子邮件验证程序 即使两者都是 正则表达式 电子邮件验证程序 放在适当命名的文件中, regex_validator.rb email_validator.rb ,在 app/validators 以前正在自动加载的文件夹(这使得此问题不同于 similar question )我怀疑这是因为 电子邮件验证程序 不再直接继承自 ActiveModel::EachValidator 但这不重要。

    作为参考,当我尝试生成迁移文件时发生以下错误:

    /var/lib/gems/2.5.0/gems/activemodel-5.2.2/lib/active_model/validations/validates.rb:121:in `rescue in block in validates': Unknown validator: 'EmailValidator' (ArgumentError)
            from /var/lib/gems/2.5.0/gems/activemodel-5.2.2/lib/active_model/validations/validates.rb:118:in `block in validates'
            from /var/lib/gems/2.5.0/gems/activemodel-5.2.2/lib/active_model/validations/validates.rb:114:in `each'
            from /var/lib/gems/2.5.0/gems/activemodel-5.2.2/lib/active_model/validations/validates.rb:114:in `validates'
            from /home/tomeraberbach/Desktop/msf/src/app/models/user.rb:7:in `<class:User>'
            from /home/tomeraberbach/Desktop/msf/src/app/models/user.rb:4:in `<main>'
            from /var/lib/gems/2.5.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:50:in `load'
            from /var/lib/gems/2.5.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:50:in `load'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:476:in `block in load_file'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:661:in `new_constants_in'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:475:in `load_file'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:373:in `block in require_or_load'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:37:in `block in load_interlock'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/dependencies/interlock.rb:14:in `block in loading'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/concurrency/share_lock.rb:151:in `exclusive'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/dependencies/interlock.rb:13:in `loading'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:37:in `load_interlock'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:356:in `require_or_load'
            from /var/lib/gems/2.5.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/active_support.rb:46:in `block in require_or_load'
            from /var/lib/gems/2.5.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/active_support.rb:16:in `allow_bootsnap_retry'
            from /var/lib/gems/2.5.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/active_support.rb:45:in `require_or_load'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:510:in `load_missing_constant'
            from /var/lib/gems/2.5.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/active_support.rb:58:in `block in load_missing_constant'
            from /var/lib/gems/2.5.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/active_support.rb:16:in `allow_bootsnap_retry'
            from /var/lib/gems/2.5.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/active_support.rb:57:in `load_missing_constant'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:195:in `const_missing'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/inflector/methods.rb:283:in `const_get'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/inflector/methods.rb:283:in `block in constantize'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/inflector/methods.rb:281:in `each'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/inflector/methods.rb:281:in `inject'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/inflector/methods.rb:281:in `constantize'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:582:in `get'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:613:in `constantize'
            from /var/lib/gems/2.5.0/gems/devise-4.5.0/lib/devise.rb:316:in `get'
            from /var/lib/gems/2.5.0/gems/devise-4.5.0/lib/devise/mapping.rb:83:in `to'
            from /var/lib/gems/2.5.0/gems/devise-4.5.0/lib/devise/mapping.rb:78:in `modules'
            from /var/lib/gems/2.5.0/gems/devise-4.5.0/lib/devise/mapping.rb:95:in `routes'
            from /var/lib/gems/2.5.0/gems/devise-4.5.0/lib/devise/mapping.rb:162:in `default_used_route'
            from /var/lib/gems/2.5.0/gems/devise-4.5.0/lib/devise/mapping.rb:72:in `initialize'
            from /var/lib/gems/2.5.0/gems/devise-4.5.0/lib/devise.rb:346:in `new'
            from /var/lib/gems/2.5.0/gems/devise-4.5.0/lib/devise.rb:346:in `add_mapping'
            from /var/lib/gems/2.5.0/gems/devise-4.5.0/lib/devise/rails/routes.rb:243:in `block in devise_for'
            from /var/lib/gems/2.5.0/gems/devise-4.5.0/lib/devise/rails/routes.rb:242:in `each'
            from /var/lib/gems/2.5.0/gems/devise-4.5.0/lib/devise/rails/routes.rb:242:in `devise_for'
            from /home/tomeraberbach/Desktop/msf/src/config/routes.rb:3:in `block (2 levels) in <main>'
            from /var/lib/gems/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/routing/mapper.rb:879:in `scope'
            from /home/tomeraberbach/Desktop/msf/src/config/routes.rb:2:in `block in <main>'
            from /var/lib/gems/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/routing/route_set.rb:432:in `instance_exec'
            from /var/lib/gems/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/routing/route_set.rb:432:in `eval_block'
            from /var/lib/gems/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/routing/route_set.rb:414:in `draw'
            from /home/tomeraberbach/Desktop/msf/src/config/routes.rb:1:in `<main>'
            from /var/lib/gems/2.5.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:50:in `load'
            from /var/lib/gems/2.5.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:50:in `load'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:285:in `block in load'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:257:in `load_dependency'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:285:in `load'
            from /var/lib/gems/2.5.0/gems/railties-5.2.2/lib/rails/application/routes_reloader.rb:41:in `block in load_paths'
            from /var/lib/gems/2.5.0/gems/railties-5.2.2/lib/rails/application/routes_reloader.rb:41:in `each'
            from /var/lib/gems/2.5.0/gems/railties-5.2.2/lib/rails/application/routes_reloader.rb:41:in `load_paths'
            from /var/lib/gems/2.5.0/gems/railties-5.2.2/lib/rails/application/routes_reloader.rb:20:in `reload!'
            from /var/lib/gems/2.5.0/gems/railties-5.2.2/lib/rails/application/routes_reloader.rb:30:in `block in updater'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/file_update_checker.rb:83:in `execute'
            from /var/lib/gems/2.5.0/gems/railties-5.2.2/lib/rails/application/routes_reloader.rb:10:in `execute'                                                      
            from /var/lib/gems/2.5.0/gems/railties-5.2.2/lib/rails/application/finisher.rb:130:in `block in <module:Finisher>'
            from /var/lib/gems/2.5.0/gems/railties-5.2.2/lib/rails/initializable.rb:32:in `instance_exec'
            from /var/lib/gems/2.5.0/gems/railties-5.2.2/lib/rails/initializable.rb:32:in `run'
            from /var/lib/gems/2.5.0/gems/railties-5.2.2/lib/rails/initializable.rb:61:in `block in run_initializers'
            from /usr/lib/ruby/2.5.0/tsort.rb:228:in `block in tsort_each'
            from /usr/lib/ruby/2.5.0/tsort.rb:350:in `block (2 levels) in each_strongly_connected_component'
            from /usr/lib/ruby/2.5.0/tsort.rb:431:in `each_strongly_connected_component_from'
            from /usr/lib/ruby/2.5.0/tsort.rb:349:in `block in each_strongly_connected_component'
            from /usr/lib/ruby/2.5.0/tsort.rb:347:in `each'
            from /usr/lib/ruby/2.5.0/tsort.rb:347:in `call'
            from /usr/lib/ruby/2.5.0/tsort.rb:347:in `each_strongly_connected_component'
            from /usr/lib/ruby/2.5.0/tsort.rb:226:in `tsort_each'
            from /usr/lib/ruby/2.5.0/tsort.rb:205:in `tsort_each'
            from /var/lib/gems/2.5.0/gems/railties-5.2.2/lib/rails/initializable.rb:60:in `run_initializers'
            from /var/lib/gems/2.5.0/gems/railties-5.2.2/lib/rails/application.rb:361:in `initialize!'
            from /home/tomeraberbach/Desktop/msf/src/config/environment.rb:5:in `<main>'
            from /var/lib/gems/2.5.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:21:in `require'
            from /var/lib/gems/2.5.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:21:in `block in require_with_bootsnap_lfi'
            from /var/lib/gems/2.5.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/loaded_features_index.rb:65:in `register'
            from /var/lib/gems/2.5.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:20:in `require_with_bootsnap_lfi'
            from /var/lib/gems/2.5.0/gems/bootsnap-1.3.2/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:29:in `require'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:291:in `block in require'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:257:in `load_dependency'
            from /var/lib/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/dependencies.rb:291:in `require'
            from /var/lib/gems/2.5.0/gems/spring-2.0.2/lib/spring/application.rb:102:in `preload'
            from /var/lib/gems/2.5.0/gems/spring-2.0.2/lib/spring/application.rb:153:in `serve'
            from /var/lib/gems/2.5.0/gems/spring-2.0.2/lib/spring/application.rb:141:in `block in run'
            from /var/lib/gems/2.5.0/gems/spring-2.0.2/lib/spring/application.rb:135:in `loop'
            from /var/lib/gems/2.5.0/gems/spring-2.0.2/lib/spring/application.rb:135:in `run'
            from /var/lib/gems/2.5.0/gems/spring-2.0.2/lib/spring/application/boot.rb:19:in `<top (required)>'
            from /usr/local/lib/site_ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'
            from /usr/local/lib/site_ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'
            from -e:1:in `<main>'
    

    我的 User 班级:

    ##
    # A class representing a user account for this web application
    # Instances of this class represent rows in the +users+ table on the database.
    class User < ApplicationRecord
      # Validation
      validates_presence_of :email, :encrypted_password
      validates :email, email: true
    
      # Associations
      devise :database_authenticatable, :registerable, :recoverable,
             :rememberable, :trackable, :validatable
      has_and_belongs_to_many :roles
    
      # Events
      before_validation do
        # Removes unnecessary whitespace
        self.email = email.strip
      end
    end
    

    有什么想法吗?

    1 回复  |  直到 7 年前
        1
  •  1
  •   Tomer Aberbach    7 年前

    我解决了问题。这个 self.abstract_class = true 行在 RegexValidator 因为没有 self.abstract_class 类中的方法。我以为是因为我在 ApplicationRecord 但是我现在意识到这是在 ActiveRecord::Base 班级。

    我通过添加 require_relative "../validators/email_validator" 最上面的 user.rb 文件。在这样做之后,我能够看到以前沉默的错误:

    /home/tomeraberbach/Desktop/msf/src/app/validators/regex_validator.rb:4:in `<class:RegexValidator>': undefined method `abstract_class=' for RegexValidator:Class (NoMethodError)
    

    在移除 self.abstract_class=真 线与 需要“相对”../validator/email“验证程序” 行,问题已解决。

    推荐文章
    Robin  ·  自定义验证代码
    12 年前