2

I have a model eclub, which has many invites, and I want to limit the number of invites that can be added. My model looks like this

class Eclub < ActiveRecord::Base
    has_many :invites, dependent: :destroy
    validates :invites, length: {maximum: 50, message: 'The maximum number of invites have been sent' }

which according to this SO answer should work provided I am not concerned about invites marked for destruction. My rspec test first creates an eclub and successfully adds 50 invites to it. Then

  invite = Invite.new(name: 'Too Many', email: 'extra@gmail.com')
  eclub.invites << invite
  expect(eclub).to be_invalid
  expect(eclub.errors[:invites].first).to include 'maximum number'
  expect(eclub.reload.invites.size).to eq 50

The first two expectations pass, but the last fails with

Failure/Error: expect(eclub.reload.invites.size).to eq 50

       expected: 50
            got: 51

How do I prevent the extra invite from being added to the collection?

Community
  • 1
  • 1
Obromios
  • 15,408
  • 15
  • 72
  • 127

3 Answers3

4

The best place to add this validation is not in Eclub but Invite model. Try this:

class Invite < ActiveRecord::Base
  belongs_to :eclub
  validate :check_invite_count!, on: :create

  private

  def check_invite_count!
    return if eclub.nil?
    if eclub.invites.count > 50
      errors.add(:base, 'Cannot add more than 50 invites for an Eclub')
    end
  end
end

Also, this code validates :invites, length: {maximum: 50, message: 'The maximum number of invites have been sent' } won't work because it assumes invites to be a string.

Zim-Zam O'Pootertoot
  • 17,888
  • 4
  • 41
  • 69
Jagdeep Singh
  • 4,880
  • 2
  • 17
  • 22
  • That is the best idea I have seen so far, will give it a try. – Obromios Aug 17 '16 at 05:55
  • Made an edit to the answer related to when this validation should run. – Jagdeep Singh Aug 17 '16 at 06:32
  • Thank you, I have accepted your answer and upvoted it. I also edited it to make it cleaner, added a check for a nil eclub, and changed the test to be >=. Using == should be fine if everything works as expected, but I am used to things not working. – Obromios Aug 17 '16 at 09:49
-1

validates length is intended to measure the length of a string, and probably doesn't work well with integers.

What you probably want to try is validates :numericality => {:maximum => 50}

EDIT yeah, what I have here probably wont' work either as it's the count of invites (not the invites association itself) that is what we need to validate, so here's a custom validator that should do what you want:

validate :invite_count_is_ok

private

def invite_count_is_ok
  return unless invites.present?
  errors.add(:base, "You may only have up to 50 invites at a time") unless invites.count <= 50
end
Taryn East
  • 27,486
  • 9
  • 86
  • 108
  • I tried ```validates :invites, numericality: {maximum: 50 }``` and received a validation error upon Factory(:eclub), i.e. creating an eclub without any invites. The message was ```ActiveRecord::RecordInvalid: Validation failed: Invites is not a number```. I also tried ```validates :invites, size: {maximum: 50}``` but this was a non-starter with a ```Unknown validator: SizeValidator``` error. – Obromios Aug 17 '16 at 05:10
  • yes, you will probably need `:allow_blank => true` in order for it not to explode when the invite count is zero... though actually what you want it to check that he count of invites is <= 50 so probably you need a custom validator... there isn't really a validator that does what you actually want by itself. I'll have a go at adding another idea for you – Taryn East Aug 17 '16 at 05:16
  • I have tried several like this, the problem is that it does mark the eclub as invalid, it does add the error message, but it does not prevent the invite from being added to the collection. The only one that works so far is Jagdeep's suggestion, but I am surprised I cannot do this directly as a validator on the eclub. I am also surprised the original SO post that I referred to did not see this problem. – Obromios Aug 17 '16 at 06:01
-2
validates(
  :invites,
  numericality: {
    greater_than_or_equal_to: 0,
    less_than_or_equal_to: 50
  }
)
bk chovatiya
  • 343
  • 2
  • 8
  • I tried this an received the same error as when I implemented the suggestion by Taryn East. – Obromios Aug 17 '16 at 05:13
  • try this one validates_length_of :invites, maximum: 50 – bk chovatiya Aug 17 '16 at 05:17
  • It has the same trouble as my original validator above, it does mark the eclub as invalid, but does not prevent the saving of the extra eclub. – Obromios Aug 17 '16 at 05:39
  • ohk than do one thing use before_validation in that count current eclub invites. if it is greater than your condition that return false and add errors.add(:your_filed, "message") otherwise return true might this one work... :) – bk chovatiya Aug 17 '16 at 05:48
  • Hi bk chovatiya : If you are recommending changes to your answer, it is best to edit the answer to make those changes. That way, both the original questioner and any other reader can easily find what they are looking for without having to also scan through every comment. – Vince Bowdren Aug 18 '16 at 10:43