I just wrote a simple app to test this out. Here's what I ended up using in the model validation.
class Booking < ActiveRecord::Base
validate :uniqueness_of_date_range
private
def uniqueness_of_date_range
errors.add(:start_date, "is not available") unless Room.where("? >= start_date AND ? <= end_date",
start_date, start_date).count == 0
errors.add(:end_date, "is not available") unless Room.where("? >= start_date AND ? <= end_date",
end_date, end_date).count == 0
end
end
The first custom validation checks to see if the current record's start_date falls between the start_date and end_date of any record, and second one does the same for end_date. You might want to show a better error message (like what is the start/end date of the record(s) that is/are clashing with the current one by doing a similar query (using .all instead of .count, and adding all the details of matching rooms).
Here's the db migration I used
class CreateBookings < ActiveRecord::Migration
def self.up
create_table :bookings do |t|
t.date :start_date
t.date :end_date
t.string :title
t.timestamps
end
end
def self.down
drop_table :bookings
end
end