range_validator

Range validator for ActiveModel

validates :field, :range => true
validates :field, :range => { :overlapping => :other_records }
validates :field, :range => { :not_overlapping => Proc.new{ |record| record.other_records } }

Examples

Suppose we have an Event model and want to ensure its duration is a range.

class Event
  include ActiveModel::Validations
  attr_accessor :duration

  validates :duration, :range => true
end

event = Event.new
event.valid? # => false
event.errors # => #<OrderedHash {:duration=>["is not a range"]}>

event.duration = 1..10
event.valid? # => true

We can also validate that the range does or does not overlap with other records. This is easiest to demonstrate using ActiveRecord. First we create a migration for a Meeting model.

class CreateMeetings < ActiveRecord::Migration
  def self.up
    create_table :meetings do |t|
      t.datetime :meeting_start
      t.datetime :meeting_end
      t.integer :room
    end
  end

  def self.down
    drop_table :meetings
  end
end

We can use the range validator to ensure that two meetings cannot be booked in the same room at the same time.

class Meeting < ActiveRecord::Base
  validates :duration, :range => { :not_overlapping => Proc.new{ |m| Meeting.where(:room => m.room) } }

  def duration
    meeting_start..meeting_end
  end
end

Now meetings will validate like so:

meeting = Meeting.new
meeting.meeting_start = DateTime.now
meeting.meeting_end = DateTime.now + 1.hour
meeting.room = 12

meeting.valid? # => true
meeting.save

other_meeting = Meeting.new
other_meeting.meeting_start = DateTime.now
other_meeting.meeting_end = DateTime.now + 1.hour
other_meeting.room = 12

other_meeting.valid? # => false
other_meeting.errors # => #<OrderedHash {:duration=>["overlaps"]}>

other_meeting.room = 4
other_meeting.valid? # => true

Constructing complex procs in the validates method can become messy fast. We can move that logic into an instance method on the Meeting class and pass a symbol to the range validator instead.

class Meeting < ActiveRecord::Base
  validates :duration, :range => { :not_overlapping => :meetings_in_same_room }

  def duration
    meeting_start..meeting_end
  end

  def meetings_in_same_room
    Meeting.where :room => room
  end
end

Contributing to range_validator

Copyright © 2011 Chris Baker. See LICENSE for details.