【问题标题】:how to create Proc to prevent DRY in rails model? Rails 5.2.1, rails_admin如何在 Rails 模型中创建 Proc 以防止 DRY? Rails 5.2.1,rails_admin
【发布时间】:2018-08-21 20:45:56
【问题描述】:
class Client < ApplicationRecord
  has_many :projects
  validates :name, presence: true

  validates :phone,
    presence: {
      message: "Phone or Email can not be blank",
      if: Proc.new { |a| a.email.blank? }
    },
    length: {
      minimum: 10,
      unless: Proc.new { |a| a.phone.blank? }
    }

  validates :email,
    uniqueness: {
      unless: Proc.new { |a| a.email.blank? }
    },
    presence: {
      message: "Phone/Email can't both be blank",
      if: Proc.new { |a| a.phone.blank? }
    },
    format: {
      with: URI::MailTo::EMAIL_REGEXP,
      unless: Proc.new { |a| a.email.blank? }
    }

    def phone_blank?
      Proc.new { |a| a.phone.blank? }
    end
end

如何创建一个方法来替换所有 Proc? 我刚刚了解了 Proc,但我还不太熟悉。我尝试在 if:/unless: 之后使用 :phone_blank 替换所有 proc,但它无法正常工作。有人可以告诉我如何制作phone_blank吗?方法可以替换代码中嵌入的所有过程吗?谢谢~

已编辑: 我忘了提到我正在使用 rails_admin 作为管理界面。如果我在 if:/unless: 中调用方法,管理面板将显示 找不到模型“客户端”,然后该模型将从管理面板中消失。我不确定这是 rails_admin 的事情还是 Rails 5 的行为方式。我对 RoR 很陌生,仍然对所有不同版本的 Rails 感到困惑......

【问题讨论】:

    标签: ruby-on-rails proc


    【解决方案1】:

    Proc wrapper 中不需要使用方法。

    例如

    class Client < ApplicationRecord
      has_many :projects
      validates :name, presence: true
    
      validates :phone,
        presence: {
          message: "Phone or Email can not be blank",
          if: email_blank?
        },
        length: {
          minimum: 10,
          unless: phone_blank?
        }
    
      validates :email,
        uniqueness: {
          unless: email_blank?
        },
        presence: {
          message: "Phone/Email can't both be blank",
          if: phone_blank?
        },
        format: {
          with: URI::MailTo::EMAIL_REGEXP,
          unless: email_blank?
        }
    
      def phone_blank?
        phone.blank?
      end
    
      def email_blank?
        email.blank?
      end
    end
    

    您也可以在验证中直接指定此条件,无需方法或Proc 作为字符串。

    例如

    class Client < ApplicationRecord
      has_many :projects
      validates :name, presence: true
    
      validates :phone,
        presence: {
          message: "Phone or Email can not be blank",
          if: 'email.blank?'
        },
        length: {
          minimum: 10,
          if: 'phone.present?'
        }
    
      validates :email,
        uniqueness: {
          if: 'email.present?'
        },
        presence: {
          message: "Phone/Email can't both be blank",
          if: 'phone.blank?'
        },
        format: {
          with: URI::MailTo::EMAIL_REGEXP,
          if: 'email.present?'
        }
    end
    

    【讨论】:

    • 你确定if: 'email.blank?'的事情吗?
    • rails ~5 不接受字符串 if: 和 unless: 我想。
    • 我已经尝试过这种方式,但我认为它在 Rails 5.2.1 中不起作用。我正在使用 rails_admin 作为管理界面。当我声明一个方法然后在 if:/unless: 中调用该方法时,客户端面板将消失。我不知道这是 rails_admin 的问题还是 Rails 5 的工作原理
    • 是的,正确,rails >5 不接受用于条件验证的字符串。
    【解决方案2】:

    您可以编写一个返回 lambda 的类方法,例如:

    def self.blank_field?(field)
      ->(m) { m.send(field).blank? }
    end
    

    然后说:

    validates :phone,
      presence: {
        message: "Phone or Email can not be blank",
        if: blank_field?(:email)
      },
      length: {
        minimum: 10,
        unless: blank_field?(:phone)
      }
    

    请注意,我们使用blank_field? 而不是blank?,因为blank? 已被占用,我们不想覆盖它。而且由于这是一种“内部”方法,我们不必担心public_sendsend

    【讨论】:

    • 我建议将 send 替换为 [] 以读取模型上的属性。例如m[field].blank?
    • 我忘了说我正在使用 rails_admin。我开始怀疑由于 rails_admin,您提到的解决方案不再起作用。我真的不知道。太菜鸟....感谢您的解决方案。我会去挖掘更多关于 lambda 的内容
    • sfate,当我用 [] 替换 send 时,我的代码会抛出错误
    • @sfate 但是使用#[] 会绕过访问器方法(如果有的话),这可能会导致意外行为。
    • @MorboRe' 您是否在控制台或日志中收到 NameError? blank_field?在你尝试使用之前你定义了吗?
    【解决方案3】:

    不是直接的答案,而是干燥事物的另一种方法是使用with_options

    with_options if: -> { email.blank? } do
      validates :phone, presence: { message: "Phone or Email can not be blank" }
    end
    
    with_options if: -> { phone.blank? } do
      validates :email, presence: { message: "Phone/Email can't both be blank" }
    end
    
    with_options if: -> { email.present? } do
      validates :phone, length: { minimum: 10 }
      validates :email, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP }
    end
    

    当验证的条件取决于不同的...例如类别(如果您有 category 列)时,这尤其有用,您可以简单地将这些验证分组 with_options

    琐事:

    您可以将-&gt; { ... } 想象成您已经熟悉的Proc.new { ... }(虽然准确地说它是lambda ......它就像Proc 的一种特殊类型。如果您有更多兴趣,请参阅这些 SO 帖子:HEREHERE

    【讨论】:

      猜你喜欢
      • 2016-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-18
      • 1970-01-01
      • 2016-12-07
      相关资源
      最近更新 更多