【问题标题】:How to move validating methods in separate class?如何在单独的类中移动验证方法?
【发布时间】:2013-12-05 12:28:37
【问题描述】:

模型代码如下:

class Place < ActiveRecord::Base
  attr_accessible :address, :name, :latitude, :longitude
  validates :name, :address, :latitude, :longitude, presence: true

  etc...
end

还有一些类具有相同的验证方法:

class PlaceClub
  include Virtus

  extend ActiveModel::Naming
  include ActiveModel::Conversion
  include ActiveModel::Validations

  attr_reader :club
  attr_reader :place

  attribute :name, String
  attribute :address, String
  attribute :latitude, Float
  attribute :longitude, Float

  validates :name, :address, :latitude, :longitude, presence: true

  etc ...
end

我可以将验证方法移到单独的类中以便在两个类中使用它吗?提前致谢。

【问题讨论】:

  • 可以为所欲为,但设计很差。如果您使用的是 Rails 4,请考虑使用关注点。
  • 我使用 Rails 3.2。此版本中没有问题,不是吗?
  • 不,它们不是,但您可以使用模块完成基本相同的事情。我会在一秒钟内发布答案,但我强烈建议不要这样做。说真的,不要这样做。
  • 但是您可以发表您对模块的想法吗?

标签: ruby-on-rails


【解决方案1】:

您可以使用ActiveSupport::Concern

更新您的代码,如:

class Place < ActiveRecord::Base
  include CustomPlaceClubValidation
  attr_accessible :address, :name, :latitude, :longitude
  validates :name, :address, :latitude, :longitude, presence: true

  etc...
end

并且(将其放入 /app/models/concerns/custom_place_club_validation):

module CustomPlaceClubValidation
  extend ActiveSupport::Concern

  include Virtus

  extend ActiveModel::Naming
  include ActiveModel::Conversion
  include ActiveModel::Validations

  included do
    attr_reader :club
    attr_reader :place

    attribute :name, String
    attribute :address, String
    attribute :latitude, Float
    attribute :longitude, Float

    validates :name, :address, :latitude, :longitude, presence: true

    etc ...
  end
end

【讨论】:

    【解决方案2】:

    这里是如何做你想做的事。警告其他读者:这样做只是自找麻烦。它非常脆弱,我想不出为什么你会故意在你的代码中添加一些如此脆弱的东西,尤其是当一个强大的替代品随时可用时。如果您不关心此警告,请继续阅读。

    要在 Rails 3 中实现这一点,您只需创建一个新模块来存储您的通用验证器,然后使用 include 回调在包含该模块的任何类上使用验证器。

    首先,如果您还没有创建一个模块目录。我更喜欢lib/modules。接下来,通过编辑config/application.rb将模块目录添加到您的加载路径

    # config/application.rb
    require File.expand_path('../boot', __FILE__)
    
    # Pick the frameworks you want:
    # ...
    
    # Require the gems listed in Gemfile, including any gems
    # you've limited to :test, :development, or :production.
    Bundler.require(:default, Rails.env)
    
    module AppName
      class Application < Rails::Application
        config.autoload_paths += %W(#{config.root}/lib) # <- Add this line
    
        # ...
      end
    end
    

    接下来,创建模块。对于此示例,我将其命名为文件common_validator.rb。记得把它放在lib/modules

    # lib/modules/common_validator.rb
    module CommonValidator
      def CommonValidator.included(mod)
        mod.validates :name, :what, :ever, :params
      end
    end
    

    最后,您只需将这个模块包含在您的模型中。像这样:

    class Place < ActiveRecord::Base
      attr_accessible :address, :name, :latitude, :longitude
      include CommonValidator # <- Add this
    
      etc...
    end
    

    你有它。只需确保重新启动 Rails 服务器以获取加载路径更改。每当您对模块进行更改时,您还必须重新启动服务器。 (编辑:阅读this question 了解如何在每次请求时重新加载模块)。 Rails 4 在ActiveSupport::Concern 中有更好的语法来做这种事情。如果您理解上面的示例,将其移植到关注点中是微不足道的。

    【讨论】:

    • 那么,如果您强烈不建议使用此代码并且我不能使用关注点 - 我该怎么办?
    • 你应该在两个地方使用相同的验证器。即使你有顾虑,我也不推荐他们。上面代码不好的原因是不能保证PlacePlaceClub 永远是一样的。事实上,它们很可能在某个时候出现分歧。所以通过合并验证器,你只会给自己带来很多痛苦。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-28
    • 1970-01-01
    • 2020-08-12
    相关资源
    最近更新 更多