I have 3 fields in my Model and i need at least one of them to be present - how can do this using inbuilt validations?
Create a custom validation:
validate :one_of_three
def one_of_three
errors.add(:base, 'Must have one of foo, bar or wee') unless foo || bar || wee
end
Try the following.
validate :attributes_presence
def attributes_presence
errors.add(:base, 'At least one attribute must be present') if attr1.blank? && attr2.blank? && attr3.blank?
end
There is a way to conditionally validate the presence of one attribute depending on presence of another:
validates :attr_1,
presence: true,
if: ->(model) { model.attr_2.blank? && model.attr_3.blank? }
validates :attr_2,
presence: true,
if: ->(model) { model.attr_1.blank? && model.attr_3.blank? }
validates :attr_3,
presence: true,
if: ->(model) { model.attr_1.blank? && model.attr_2.blank? }
Related
let say that I have set in model for validation like this
validates :tel, presence: true , length: { minimum: 10, maximum: 11 }, numericality: { only_integer: true }
how do I can display a custom message in view for each validate.
when I set this in views page.
<% if #diary.errors.include?(:tel) %>
<div class="err"><p><%= #diary.errors.full_messages_for(:tel).join("") %></p></div>
<% end %>
it directly displays all error message. I want to make a display in view like this
if(error_require)
echo "tel is needed"
else if(error_length)
echo "tel is to long"
else
echo "tel must numeric"
end
can I make like that?
You can pass message in separate hashes for each validator:
validates :tel,
presence: { message: 'is needed' },
length: { minimum: 10, maximum: 11, too_long: 'is too long' },
numericality: { only_integer: true, message: 'must be numeric' }
Read more about presence, length, and numericality validators.
One way to do this is to define methods for each type of validation (in your model) like this:
validate :chech_length
def chech_length
if tel.length < 10 || tel.length > 11
errors.add(:base, "tel is too long!")
end
end
validate :check_if_present
def check_if_present
if tel.blank?
errors.add(:base, "tel must be present!")
end
end
etc...
Hope this helps.
How can I create a validation where committed presence needs to be true only if the challenge's category is habit?
class Challenge < ActiveRecord::Base
CATEGORY = ['goal', 'habit']
serialize :committed, Array
validates :committed, presence: true, if: :habit # I also tried with 'habit' & 'Habit'
end
Since your category is called 'habit' (note, it is not 'Habit'), the validation would look as follows:
validates :committed, presence: true, if: ->(c) { c.category == 'habit' }
As a sidenote: I do not think your scopes will work, unless you have a column called categories in your challenges table.
Thus, if your intention was to select challenges, which have category 'habit', the scope would look as follows:
scope :habit, -> { where(category: 'habit') }
EDIT
As per discussion in comments, if you want committed to be nil instead of [""] when nothing is there, add custom validation:
validate :committed_content
private
def committed_content
self.committed = nil if committed.empty? || committed.all?(&:blank?)
true
end
validates :committed, presence: true, :if => lambda { |c| c.category == 'Habit' }
You can have a method and use it like this:
validates :committed, presence: true, if: :habit?
def habit?
self.category == 'habit'
end
I have been unable to wrap my head around how to get these tests back to green.
Model
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: true
validates :zip, presence: true, format: { with: VALID_ZIP_REGEX }
validates_numericality_of :puzzle_pieces, only_integer: true
Spec
it { should validate_presence_of(:email) }
it { should validate_uniqueness_of(:email) }
it { should allow_value('john.doe#example.com', 'alice#yahoo.ca').for(:email) }
it { should_not allow_value('john2example.com', 'john#examplecom').for(:email) }
it { should validate_presence_of(:zip) }
it { should allow_value('35124', '35124-1234').for(:zip) }
it { should_not allow_value('5124', '35124-12345').for(:zip) }
it { should validate_numericality_of(:puzzle_pieces).only_integer }
The above tests pass until I add this custom validation.
Custom Validator
class PiecesValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless value > 0 && value <= Puzzle.remaining
record.errors[attribute] << (options[:message] || "Puzzle pieces must be between 1 and #{Puzzle.remaining}")
end
end
end
Model
validates :puzzle_pieces, pieces: true
Spec
it "does not allow a negative number of puzzle pieces to be saved" do
order = build(:order, puzzle_pieces: -1)
expect(order).to be_invalid
end
That last test passes, but all of my shoulda tests then fail with the same error
NoMethodError:
undefined method `>' for nil:NilClass
I am not understanding how to fix this. It seems that the shoulda tests operate in isolation just fine. But then they all blow up when the custom validation is added in.
Any help pushing me towards understanding this would be greatly appreciated!
Your problem is that your validation is not expecting value to be nil. Change your method to:
class PiecesValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless value && value > 0 && value <= Puzzle.remaining
record.errors[attribute] << (options[:message] || "Puzzle pieces must be between 1 and #{Puzzle.remaining}")
end
end
end
This will not add an error if validated field is blank. However, what you are trying to do can be achieved using standard rails validators:
validates :puzzle_pieces, numericality: { only_integer: true, less_then: Puzzle.remaining, greater_then: 0 }
So I'm looking at this link but I'm wondering how to do an 'OR' instead of an 'XOR'. Does anyone know how to do this? I am trying to do this using Rails 3.
There is short option for Rails 4 (not sure about Rails 3) :
validates :email, presence: { if: -> { username.blank? } }
validates :username, presence: { if: -> { email.blank? } }
I figured it out. all you have to change is the ^ to &&
private
def time_or_money
if time.blank? && money.blank?
errors[:base] << "Specify Time, Money or Both."
end
end
How can I write validation for barcode to be unique for all users where is_deleted is false and same chain?
validates :barcode, uniqueness: { conditions: -> { |record| where(is_deleted: false, chain_id: record.chain_id) } }, if: proc { |u| u.barcode.present? }
what is wrong here?
Thanks.
upd. There can be two users with same barcode with same chain_id, if one of them or both have :is_deleted => true
Rails validation have if and unless parameters which allow you to add conditions, you used it to check barcode presence properly, but you can extend it for is_deleted as well.
As to chain id, I understand that you are interested in scoping.
In your case that would be
validates :barcode, uniqueness: { scope: [:chain_id] }, if: proc { |u| u.barcode.present? && w.is_deleted.false? }
validates :barcode, uniqueness: { scope: :chain_id, conditions: ->{ where(is_deleted: false) } }, if: proc { |u| u.barcode.present? && u.active? }
I came up with a solution.
Thanks and sorry, its late here and head is not working as expected :)