Reputation: 676
I have a model that has a long list of attributes. Not all attributes need to be present to save, but, if they are present I want to validate them.
Currently I am adding an if:
proc to the end of each validation as such:
# app/models/bio.rb
class Bio < ApplicationRecord
...
validates :age, numericality: { only_integer: true }, if: Proc.new { |a| a.age.present? }
validates :height, numericality: { only_integer: true }, if: Proc.new { |a| a.height.present? }
validates :weight, numericality: { only_integer: true }, if: Proc.new { |a| a.weight.present? }
...
end
This doesn't seem very DRY, so, I wanted to move this proc to a class method and call it via it's symbol as:
...
validates :age, numericality: { only_integer: true }, if: :has_attr(:age)
...
def has_attr(a)
#some_code.a.present?
end
I'm just unsure what the a
in the proc refers to. I am assuming it's self
or something similar.
How do I need to modify the method to get it to work the same as the proc? And do I need to add a ?
to the method name, such as def has_attr?(a)
or def has_attr(a)?
? And, honestly, I'm not even sure if you can pass an argument to a symbol. Any light you can shed on this is greatly appreciated.
Upvotes: 0
Views: 764
Reputation: 434785
You can't do it that way, the :if
option wants something callable (such as a Proc
or lambda) or a symbol which names an instance method. There's no way to tunnel arguments down to the instance method unless you want to wrap it as in your first example.
However, in this case, you can say allow_nil: true
which will have the same effect:
validates :age, numericality: { only_integer: true }, allow_nil: true
# ----------------------------------------------------^^^^^^^^^^^^^^^
Adding that will skip the validation if the age
is nil
and that should have the same effect as your current logic.
You could do something close to what you were trying to do with a class method that returns lambdas. Something like:
def self.has_attr(a)
->(obj) { obj.public_send(a).present? }
end
and then:
validates :age, ..., if: has_attr(:age)
You'd need to arrange for self.has_attr
to be defined before your validates
calls but that's not too hard, you'd probably put has_attr
in a concern and include it at the top of your model class to deal with that problem and make has_attr
easily available in other models as well.
Upvotes: 2