You have to create a custom validation, like so:
validate :has_categories
def has_categories
unless categories.size > 0
errors.add(:base, "There are no categories")
end
end
This shows you the general idea, you can adapt this to your needs.
UPDATE
This post has come up once more, and I found a way to fill in the blanks.
The validations can remain as above. All I have to add to that, is the case of direct assignment of an empty set of categories. So, how do I do that?
The idea is simple: override the setter method to not accept the empty array:
def categories=(value)
if value.empty?
puts "Categories cannot be blank"
else
super(value)
end
end
This will work for every assignment, except when assigning an empty set. Then, simply nothing will happen. No error will be recorded and no action will be performed.
If you want to also add an error message, you will have to improvise. Add an attribute to the class which will be populated when the bell rings.
So, to cut a long story short, this model worked for me:
class Event < ActiveRecord::Base
has_many :event_categories
has_many :categories, through: :event_categories
attr_accessor :categories_validator # The bell
validates :categories, presence: true
validate :check_for_categories_validator # Has the bell rung?
def categories=(value)
if value.empty?
self.categories_validator = true # Ring that bell!!!
else
super(value) # No bell, just do what you have to do
end
end
private
def check_for_categories_validator
self.errors.add(:categories, "can't be blank") if self.categories_validator == true
end
end
Having added this last validation, the instance will be invalid if you do:
event.categories = []
Although, no action will have been fulfilled (the update is skipped).