Build Status

ValidatesOverlap

This project rocks and uses MIT-LICENSE.

This gem is compatible with Rails 3, 4, 5.

When this gem should be helpful for you?

Ideal solution for booking applications where you want to make sure, that one place can be booked only once in specific time period.

Using

Add to your gemfile

gem 'validates_overlap'

In your model

without scope

validates :starts_at, :ends_at, :overlap => true

with scope

validates :starts_at, :ends_at, :overlap => {:scope => "user_id"}

exclude edge(s)

validates :starts_at, :ends_at, :overlap => {:exclude_edges => "starts_at"}
validates :starts_at, :ends_at, :overlap => {:exclude_edges => ["starts_at", "ends_at"]}

shift edges

validates :starts_at, :ends_at, :overlap => {:start_shift => -1.day, :end_shift => 1.day}

define custom validation key(s) and message

validates :starts_at, :ends_at, :overlap => {:message_title => "Some validation title", :message_content => "Some validation message"}
validates :starts_at, :ends_at, :overlap => {:message_title => [:start_at, :end_at], :message_content => "Some validation message"}

with complicated relations

Example describes valildatation of user, positions and time slots. User can't be assigned 2 times on position which is under time slot with time overlap.

class Position < ActiveRecord::Base
  belongs_to :time_slot
  belongs_to :user
  validates "time_slots.starts_at", "time_slots.ends_at",
    :overlap => {
      :query_options => {:includes => :time_slot},
      :scope => { "positions.user_id" => proc{|position| position.user_id} }
    }
end

apply named scopes

class ActiveMeeting < ActiveRecord::Base
  validates :starts_at, :ends_at, :overlap => {:query_options => {:active => nil}}
  scope :active, where(:is_active => true)
end

Overlapped records

If you need to know what records are in conflict, pass the {load_overlapped: true } as validator option and validator will set instance variable @overlapped_records to the validated object.

class ActiveMeeting < ActiveRecord::Base
  validates :starts_at, :ends_at, :overlap => {:load_overlapped => true}

  def overlapped_records
    @overlapped_records || []
  end
end

Rails 4.1 update

If you just upgraded your application to rails 4.1 you can discover some issue with custom scopes. In older versions we suggest to use definition like

  {:overlap => {:query_options => {:active => true}}}

but this code is not longer working. Currently please change your code to

  {:overlap => {:query_options => {:active => nil}}}

Thanks @supertinou for discovering and fix of this bug.