I have a model called Log that has relations to two models ButtonPress and LinkClick. I need to validate the presence of either (for analytics reasons, I'm required to do this without using polymorphic relations)
My Spec file is as follows:
RSpec.describe RedirectLog, type: :model do
it { is_expected.to belong_to(:button_press).optional }
it { is_expected.to belong_to(:link_click).optional }
it "has either a button_press or a link_click" do
log = build(:log, button_press: nil, link_click: nil)
expect(log).not_to be_valid
end
end
To achieve this I created the model as follows:
class Log < ApplicationRecord
belongs_to :button_press, optional: true
belongs_to :link_click, optional: true
validates :button_press, presence: true, unless: :link_click
end
When I run this, I get the following error:
Failure/Error: it { is_expected.to belong_to(:button_press).optional }
Expected Log to have a belongs_to association called button_press (and for the record not to fail validation if :button_press is unset; i.e., either the association should have been defined with `optional: true`, or there should not be a presence validation on :button_press)
So in short, I cant have the belongs_to association if I want to validate its presence, even conditionally. My question is, what is the Rails way to have a validation on one of two belongs_to columns without using polymorphism?
CodePudding user response:
One way to do it is with a custom validator. I'd make it completely formal with its own file and such, but for a proof-of-concept you could do this:
class Log < ApplicationRecord
###################################################################
#
# Associations
#
###################################################################
belongs_to :button_press, optional: true
belongs_to :link_click, optional: true
###################################################################
#
# Validations
#
###################################################################
validate :button_press_or_link_click_presence
###################################################################
#
# In-Model Custom Validators (Private)
#
###################################################################
def button_press_or_link_click_presence
self.errors.add(:base, 'Either a Button Press or a Link Click is required, but both are missing.') if button_press.blank? && link_click.blank?
end
private :button_press_or_link_click_presence
end