Reputation: 1044
I have 2 models:
class PollAnswer < ActiveRecord::Base
belongs_to :poll, inverse_of: :poll_answers
# Validations
validates :answer, presence: true, uniqueness: { scope: :poll_id, case_sensitive: false }
validates :votes_number, presence: true
validates :poll, presence: true
end
class Poll < ActiveRecord::Base
# Associations
has_many :poll_answers, inverse_of: :poll, dependent: :destroy
# Attributes
accepts_nested_attributes_for :poll_answers, reject_if: :all_blank, allow_destroy: true
# Validations
validates :question, presence: true
validate :has_minimum_2_poll_answers
private
def has_minimum_2_poll_answers
remaining_poll_answers = poll_answers.reject(&:marked_for_destruction?)
if remaining_poll_answers.size < 2
errors.add :poll_answers, I18n.t(:minimum_2_poll_answers, scope: "activerecord.errors.models.polls")
end
end
end
and a simple test:
let(:new_poll_answer) { build(:poll_answer) }
let(:new_poll_answer1) { build(:poll_answer) }
let(:new_poll) { create(:poll) }
it "validates the uniqueness of an answer scoped to poll_id" do
new_poll_answer.answer = "andre"
new_poll_answer.poll = new_poll
new_poll_answer1.answer = "andre"
new_poll_answer1.poll = new_poll
expect(new_poll_answer.valid?).to eq(false)
end
and it fails:
1) PollAnswer validations validates the uniqueness of an answer scoped to poll_id
Failure/Error: expect(new_poll_answer.valid?).to eq(false)
expected: false
got: true
(compared using ==)
# ./spec/models/poll_answer_spec.rb:22:in `block (3 levels) in <top (required)>'
# -e:1:in `<main>'
Any ideas why?
UPDATE:
After Marek Lipka comment I can see that's exactly my problem because this is how accepts_nested_attributes_for works. so it does not validate the uniqueness.
I tried with this test:
it "validates the uniqueness of an answer scoped to poll_id" do
new_poll_answer.answer = "andre"
new_poll_answer.poll = new_poll
new_poll_answer1.answer = "andre"
new_poll_answer1.poll = new_poll
new_poll.save
puts "#{new_poll.inspect}"
puts "#{new_poll_answer.inspect}"
puts "#{new_poll_answer1.inspect}"
expect(new_poll_answer1.valid?).to eq(false)
end
and I get this:
#<Poll id: 62, question: "Question", created_at: "2014-04-08 12:31:06", updated_at: "2014-04-08 12:31:06", active: true, published_at: "2014-04-08 11:31:06">
#<PollAnswer id: nil, answer: "andre", votes_number: 0, poll_id: 62, created_at: nil, updated_at: nil>
#<PollAnswer id: nil, answer: "andre", votes_number: 0, poll_id: 62, created_at: nil, updated_at: nil>
Failures:
1) PollAnswer validations validates the uniqueness of an answer scoped to poll_id
Failure/Error: expect(new_poll_answer1.valid?).to eq(false)
expected: false
got: true
(compared using ==)
Which for me is still weird if you look at my custom validation for the poll class called has_minimum_2_poll_answers.
How could I validate correctly that a poll should only be create if there is no poll_answers with the same answer?
Upvotes: 0
Views: 969
Reputation: 1
Also note that beyond the requirement for one of the records to have been previously saved, it is not possible to guarantee uniqueness validation in the application layer. Uniqueness validation can only be guaranteed using database layer constraints (i.e unique indexes).
Excerpt from the Rails docs:
Using this validation method in conjunction with ActiveRecord::Validations#save does not guarantee the absence of duplicate record insertions, because uniqueness checks on the application level are inherently prone to race conditions. For example, suppose that two users try to post a Comment at the same time, and a Comment's title must be unique. At the database-level, the actions performed by these users could be interleaved in the following manner:
Upvotes: 0
Reputation: 51151
You didn't save your first new_poll_answer
, uniqueness validation doesn't work against unsaved records. You need to do:
new_poll_answer.save
before testing new_poll_answer1
for validity.
Upvotes: 1