diff --git a/lib/ice_cube.rb b/lib/ice_cube.rb index 4e0d3bdf..89b62512 100644 --- a/lib/ice_cube.rb +++ b/lib/ice_cube.rb @@ -17,6 +17,7 @@ module IceCube autoload :Rule, 'ice_cube/rule' autoload :Schedule, 'ice_cube/schedule' + autoload :Occurrence, 'ice_cube/occurrence' autoload :IcalBuilder, 'ice_cube/builders/ical_builder' autoload :HashBuilder, 'ice_cube/builders/hash_builder' diff --git a/lib/ice_cube/occurrence.rb b/lib/ice_cube/occurrence.rb new file mode 100644 index 00000000..736624bd --- /dev/null +++ b/lib/ice_cube/occurrence.rb @@ -0,0 +1,64 @@ +require 'forwardable' +require 'delegate' + +module IceCube + + class Occurrence < SimpleDelegator + + # Optimize for common methods to avoid method_missing + extend Forwardable + def_delegators :start_time, :to_s, :to_i, :<=>, :== + def_delegators :to_range, :cover?, :include?, :each, :first, :last + + attr_reader :start_time, :end_time + + def initialize(start_time, end_time=nil) + @start_time = start_time + @end_time = end_time || start_time + __setobj__ @start_time + end + + def intersects? other + if other.is_a?(Occurrence) || other.is_a?(Range) + lower_bound_1 = first + 1 + upper_bound_1 = last # exclude end + lower_bound_2 = other.first + 1 + upper_bound_2 = other.last + 1 + if (lower_bound_2 <=> upper_bound_2) > 0 + false + elsif (lower_bound_1 <=> upper_bound_1) > 0 + false + else + (upper_bound_1 <=> lower_bound_2) >= 0 and + (upper_bound_2 <=> lower_bound_1) >= 0 + end + else + cover? other + end + end + + def comparable_time + start_time + end + + def duration + end_time - start_time + end + + def to_range + start_time..end_time + end + + def to_time + @start_time + end + + def to_s + if duration > 0 + "#{start_time} - #{end_time}" + else + "#{start_time}" + end + end + end +end diff --git a/spec/examples/active_support_spec.rb b/spec/examples/active_support_spec.rb index bd4dec8b..f7e15895 100644 --- a/spec/examples/active_support_spec.rb +++ b/spec/examples/active_support_spec.rb @@ -86,3 +86,15 @@ end end + +describe IceCube::Occurrence do + + it 'can be subtracted from a time' do + start_time = Time.now + occurrence = Occurrence.new(start_time) + + difference = (start_time + 60) - occurrence + difference.should == 60 + end + +end diff --git a/spec/examples/occurrence_spec.rb b/spec/examples/occurrence_spec.rb new file mode 100644 index 00000000..0b1a93f7 --- /dev/null +++ b/spec/examples/occurrence_spec.rb @@ -0,0 +1,97 @@ +require File.dirname(__FILE__) + '/../spec_helper' + +include IceCube + +describe Occurrence do + + describe :to_s do + it "looks like a Time for a zero duration" do + start_time = Time.now + occurrence = Occurrence.new(start_time) + + occurrence.to_s.should == start_time.to_s + end + + it "looks like a range for a non-zero duration" do + start_time = Time.now + end_time = start_time + ONE_HOUR + occurrence = Occurrence.new(start_time, end_time) + + occurrence.to_s.should == "#{start_time} - #{end_time}" + end + end + + describe :end_time do + + it 'defaults to start_time' do + start_time = Time.now + occurrence = Occurrence.new(start_time) + + occurrence.end_time.should == start_time + end + + it 'returns specified end_time' do + start_time = Time.now + end_time = start_time + 3600 + occurrence = Occurrence.new(start_time, end_time) + + occurrence.end_time.should == end_time + end + + end + + describe :arithmetic do + + let(:start_time) { Time.now } + let(:occurrence) { Occurrence.new(start_time) } + + it 'returns a time when adding' do + new_time = occurrence + 60 + new_time.should == start_time + 60 + end + + it 'can get difference from a time' do + difference = occurrence - (start_time - 60) + difference.should == 60 + end + + end + + describe :intersects? do + + let(:start_time) { Time.now } + let(:end_time) { start_time + 3600 } + + it 'is true for a time during the occurrence' do + occurrence = Occurrence.new(start_time, end_time) + + inclusion = occurrence.intersects? start_time + 1800 + inclusion.should be_true + end + + it 'is false for a time outside the occurrence' do + occurrence = Occurrence.new(start_time, end_time) + + inclusion = occurrence.intersects? start_time + 3601 + inclusion.should be_false + end + + it 'is true for an intersecting occurrence' do + occurrence1 = Occurrence.new(start_time, end_time) + occurrence2 = Occurrence.new(start_time + 1, end_time + 1) + + inclusion = occurrence1.intersects? occurrence2 + inclusion.should be_true + end + + it 'is false for a non-intersecting occurrence' do + occurrence1 = Occurrence.new(start_time, end_time) + occurrence2 = Occurrence.new(end_time) + + inclusion = occurrence1.intersects? occurrence2 + inclusion.should be_false + end + end + +end +