如何创建Proc以防止在Rails模型中发生DRY? Rails 5.2.1,rails_admin

时间:2018-08-21 20:45:56

标签: ruby-on-rails proc

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,但现在还不太熟悉。我尝试使用:phone_blank替换if:/ unless:之后的所有proc,但是它无法正常工作。有人可以告诉我如何制作phone_blank吗?方法工作可替换所有嵌入在代码中的proc?谢谢〜

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

3 个答案:

答案 0 :(得分:0)

对于使用方法,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,
      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

答案 1 :(得分:0)

您可以编写一个返回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的冲突。

答案 2 :(得分:0)

这不是直接的答案,但在DRY处理中的另一种方法是使用with_options

ComboBox

当验证的条件取决于不同的……类别(如果您有DISTINCT列)时,这非常有用,您可以简单地将这些验证分组{{1} }

琐事:

您可以像已经熟悉的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 ...,就像一种特殊的{{1} }。如果您进一步感兴趣,请参阅以下SO帖子:HEREHERE