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

  • Check out the latest master to make sure the feature hasn’t been implemented or the bug hasn’t been fixed yet

  • Check out the issue tracker to make sure someone already hasn’t requested it and/or contributed it

  • Fork the project

  • Start a feature/bugfix branch

  • Commit and push until you are happy with your contribution

  • Make sure to add tests for it. This is important so I don’t break it in a future version unintentionally.

  • Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.

Copyright © 2011 Chris Baker. See LICENSE for details.