Skip to content

Instantly share code, notes, and snippets.

@ahoward
Created July 14, 2010 16:02
Show Gist options
  • Save ahoward/475616 to your computer and use it in GitHub Desktop.
Save ahoward/475616 to your computer and use it in GitHub Desktop.
module VSRG
class Period
class << Period
def for(*args, &block)
options = args.options
first = args.first
last = args.last
case first
when Period
@begins_at = first.begins_at
@ends_at = first.ends_at
when ActiveRecord::Base
@begins_at = first.begins_at
@ends_at = first.ends_at
when Range
@begins_at = first.begin
@ends_at = first.end
when Array
@begins_at = first.first
@ends_at = first.last
else
if period_like?(first)
@begins_at = first.begins_at
@ends_at = first.ends_at
else
@begins_at = first
@ends_at = last
end
end
@begins_at ||= options.getopt(:begins_at)
@ends_at ||= options.getopt(:ends_at)
if @begins_at.nil? and @ends_at.nil? and not options.empty?
@begins_at, @ends_at, *ignored = options.to_a.flatten
end
new(:begins_at => @begins_at, :ends_at => @ends_at)
end
def [](*args, &block)
Period.for(*args, &block)
end
def period_like?(other)
other.respond_to?(:begins_at) && other.respond_to?(:ends_at)
end
end
attr_accessor :begins_at
attr_accessor :ends_at
def initialize(*args, &block)
options = args.options
@begins_at = time_for(args.shift || options[:begins_at])
@ends_at = time_for(args.shift || options[:ends_at])
if finite?
raise ArgumentError, 'begins_at > ends_at' if @begins_at > @ends_at
end
end
def time_for(time)
VSRG.cast(time, :time) if time
end
def period
self
end
def finite
begins_at and ends_at
end
alias_method 'finite?', 'finite'
def infinite
begins_at.nil? or ends_at.nil?
end
alias_method 'infinite?', 'infinite'
def begins_at_infinity
!begins_at
end
alias_method 'begins_at_infinity?', 'begins_at_infinity'
def ends_at_infinity
!ends_at
end
alias_method 'ends_at_infinity?', 'ends_at_infinity'
def forever
begins_at.nil? and ends_at.nil?
end
alias_method 'forever?', 'forever'
def duration
return Float::Infinity unless finite?
ends_at - begins_at
end
def <=>(other)
other = Period.for(other)
return -1 if before?(other)
return 1 if after?(other)
return -1 if starts_before?(other) # overlapping
return -1 if ends_before?(other) # overlapping
return 0
end
# period operators
#
def before(other)
other = Period.for(other)
return false if period.ends_at_infinity?
return false if other.begins_at_infinity?
less_than?(period.ends_at, other.begins_at) or equal?(period.ends_at, other.begins_at)
end
alias_method 'before?', 'before'
# TODO
#
def after(other)
other = Period.for(other)
return false if period.begins_at_infinity?
return false if other.ends_at_infinity?
more_than?(other.begins_at, period.ends_at) or equal?(other.begins_at, period.ends_at)
end
alias_method 'after?', 'after'
def start_before(other)
other = Period.for(other)
less_than?(period.begins_at, other.begins_at)
end
alias_method 'start_before?', 'start_before'
alias_method 'starts_before?', 'start_before'
def end_before(other)
other = Period.for(other)
less_than?(period.ends_at, other.ends_at)
end
alias_method 'end_before?', 'end_before'
alias_method 'ends_before?', 'end_before'
def start_after(other)
other = Period.for(other)
more_than?(other.begins_at, period.begins_at)
end
alias_method 'start_after?', 'start_after'
alias_method 'starts_after?', 'start_after'
def start_at(other)
other = Period.for(other)
equal?(other.begins_at, period.begins_at)
end
alias_method 'start_at?', 'start_at'
alias_method 'starts_at?', 'start_at'
def start_during(other)
other = Period.for(other)
less_than?(period.begins_at, other.ends_at) and
(more_than?(period.begins_at, other.begins_at) or equal?(period.begins_at, other.begins_at))
end
alias_method 'start_during?', 'start_during'
alias_method 'starts_during?', 'start_during'
def end_after(other)
other = Period.for(other)
more_than?(other.ends_at, period.ends_at)
end
alias_method 'end_after?', 'end_after'
alias_method 'ends_after?', 'end_after'
def end_at(other)
other = Period.for(other)
equal?(other.ends_at, period.ends_at)
end
alias_method 'end_at?', 'end_at'
alias_method 'ends_at?', 'end_at'
def end_during(other)
other = Period.for(other)
more_than?(period.ends_at, other.begins_at) and
(less_than?(period.ends_at, other.ends_at) or equal?(period.ends_at, other.ends_at))
end
alias_method 'end_during?', 'end_during'
alias_method 'ends_during?', 'end_during'
def overlap(other)
other = Period.for(other)
end
alias_method 'overlap?', 'overlap'
alias_method 'overlaps', 'overlap'
alias_method 'overlaps?', 'overlap'
def contains(other)
other = Period.for(other)
(period.begins_at >= other.begins_at and period.begins_at < other.ends_at) and
(period.ends_at <= other.ends_at and period.ends_at > other.ends_at)
end
alias_method 'contains?', 'contains'
# operators
# a==b -> -infinity
# a==b -> infinity
#
private
def less_than(a, b)
return a < b if a and b
return true
end
alias_method 'less_than?', 'less_than'
alias_method 'lt', 'less_than'
alias_method 'lt?', 'less_than'
def less_than_or_equal(a, b)
return a <= b if a and b
return true
end
alias_method 'less_than_or_equal?', 'less_than_or_equal'
alias_method 'lte', 'less_than_or_equal'
alias_method 'lte?', 'less_than_or_equal'
def more_than(a, b)
return a > b if a and b
return true
end
alias_method 'more_than?', 'more_than'
alias_method 'mt', 'more_than'
alias_method 'mt?', 'more_than'
alias_method 'greater_than?', 'more_than'
alias_method 'gt', 'more_than'
alias_method 'gt?', 'more_than'
def more_than_or_equal(a, b)
return a > b if a and b
return true
end
alias_method 'more_than_or_equal?', 'more_than_or_equal'
alias_method 'gte', 'less_than_or_equal'
alias_method 'gte?', 'less_than_or_equal'
def equal(a, b)
return a == b if a and b
return false
end
alias_method 'equal?', 'equal'
alias_method 'eq', 'equal'
alias_method 'eq?', 'equal'
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment