Skip to content

Instantly share code, notes, and snippets.

@guilleiguaran
Created June 30, 2011 06:10
Show Gist options
  • Save guilleiguaran/1055717 to your computer and use it in GitHub Desktop.
Save guilleiguaran/1055717 to your computer and use it in GitHub Desktop.
Simple No Overlapping Validation
class NoOverlappingValidator < ActiveModel::Validator
def validate(record)
message = options[:message] || "Record is invalid"
start = record.read_attribute(options[:start])
finish = record.read_attribute(options[:finish])
klass = record.class
t = klass.arel_table
c1 = t[:start].lt(finish).and(t[:start].gteq(start))
c2 = t[:finish].gt(start).and(t[:finish].lteq(finish))
c3 = t[:start].lteq(start).and(t[:finish].gteq(finish))
c4 = t[:start].gt(start).and(t[:finish].lt(finish))
c = (c1.or(c2.or(c3.or(c4)))).and(t[:id].not_eq(record.id))
overlapped_items = klass.where(c)
if options[:scope]
scope_value = record.read_attribute(options[:scope])
overlapped_items = overlapped_items.where(options[:scope] => scope_value)
end
record.errors[:base] = message unless overlapped_items.empty?
end
module ClassMethods
def validates_no_overlapped(*args)
validates_with NoOverlappingValidator, {:start => args[0], :finish => args[1]}.merge(args[2])
end
end
end
class Timeslot < ActiveRecord::Base
extend NoOverlappingValidator::ClassMethods
belongs_to :venue
# - Validations
validates :start, :presence => true
validates :finish, :presence => true
validates_associated :venue
validates_no_overlapped :start, :finish, :scope => :venue_id,
:message => "Timeslot is already filled!"
# This also works:
# validates_with NoOverlappingValidator, :start => :start, :finish => :finish,
# :scope => :venue_id, :message => "Timeslot is already filled!"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment