demingfactor-ri_cal 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/History.txt +402 -0
- data/Manifest.txt +161 -0
- data/README.txt +410 -0
- data/Rakefile +69 -0
- data/VERSION +1 -0
- data/bin/ri_cal +8 -0
- data/component_attributes/alarm.yml +10 -0
- data/component_attributes/calendar.yml +4 -0
- data/component_attributes/component_property_defs.yml +180 -0
- data/component_attributes/event.yml +45 -0
- data/component_attributes/freebusy.yml +16 -0
- data/component_attributes/journal.yml +35 -0
- data/component_attributes/timezone.yml +3 -0
- data/component_attributes/timezone_period.yml +11 -0
- data/component_attributes/todo.yml +46 -0
- data/copyrights.txt +1 -0
- data/docs/draft-ietf-calsify-2446bis-08.txt +7280 -0
- data/docs/draft-ietf-calsify-rfc2445bis-09.txt +10416 -0
- data/docs/incrementers.txt +7 -0
- data/docs/rfc2445.pdf +0 -0
- data/lib/ri_cal/component/alarm.rb +19 -0
- data/lib/ri_cal/component/calendar.rb +257 -0
- data/lib/ri_cal/component/event.rb +58 -0
- data/lib/ri_cal/component/freebusy.rb +16 -0
- data/lib/ri_cal/component/journal.rb +27 -0
- data/lib/ri_cal/component/non_standard.rb +33 -0
- data/lib/ri_cal/component/t_z_info_timezone.rb +153 -0
- data/lib/ri_cal/component/timezone/daylight_period.rb +25 -0
- data/lib/ri_cal/component/timezone/standard_period.rb +23 -0
- data/lib/ri_cal/component/timezone/timezone_period.rb +76 -0
- data/lib/ri_cal/component/timezone.rb +197 -0
- data/lib/ri_cal/component/todo.rb +42 -0
- data/lib/ri_cal/component.rb +256 -0
- data/lib/ri_cal/core_extensions/array/conversions.rb +15 -0
- data/lib/ri_cal/core_extensions/array.rb +7 -0
- data/lib/ri_cal/core_extensions/date/conversions.rb +56 -0
- data/lib/ri_cal/core_extensions/date.rb +13 -0
- data/lib/ri_cal/core_extensions/date_time/conversions.rb +50 -0
- data/lib/ri_cal/core_extensions/date_time.rb +15 -0
- data/lib/ri_cal/core_extensions/object/conversions.rb +20 -0
- data/lib/ri_cal/core_extensions/object.rb +8 -0
- data/lib/ri_cal/core_extensions/string/conversions.rb +57 -0
- data/lib/ri_cal/core_extensions/string.rb +8 -0
- data/lib/ri_cal/core_extensions/time/calculations.rb +153 -0
- data/lib/ri_cal/core_extensions/time/conversions.rb +42 -0
- data/lib/ri_cal/core_extensions/time/tzid_access.rb +50 -0
- data/lib/ri_cal/core_extensions/time/week_day_predicates.rb +55 -0
- data/lib/ri_cal/core_extensions/time.rb +14 -0
- data/lib/ri_cal/core_extensions.rb +11 -0
- data/lib/ri_cal/fast_date_time.rb +234 -0
- data/lib/ri_cal/floating_timezone.rb +32 -0
- data/lib/ri_cal/invalid_property_value.rb +8 -0
- data/lib/ri_cal/invalid_timezone_identifier.rb +20 -0
- data/lib/ri_cal/occurrence_enumerator.rb +265 -0
- data/lib/ri_cal/occurrence_period.rb +17 -0
- data/lib/ri_cal/parser.rb +148 -0
- data/lib/ri_cal/properties/alarm.rb +390 -0
- data/lib/ri_cal/properties/calendar.rb +164 -0
- data/lib/ri_cal/properties/event.rb +1523 -0
- data/lib/ri_cal/properties/freebusy.rb +593 -0
- data/lib/ri_cal/properties/journal.rb +1237 -0
- data/lib/ri_cal/properties/timezone.rb +150 -0
- data/lib/ri_cal/properties/timezone_period.rb +416 -0
- data/lib/ri_cal/properties/todo.rb +1559 -0
- data/lib/ri_cal/properties.rb +12 -0
- data/lib/ri_cal/property_value/array.rb +27 -0
- data/lib/ri_cal/property_value/cal_address.rb +11 -0
- data/lib/ri_cal/property_value/date.rb +184 -0
- data/lib/ri_cal/property_value/date_time/additive_methods.rb +44 -0
- data/lib/ri_cal/property_value/date_time/time_machine.rb +159 -0
- data/lib/ri_cal/property_value/date_time/timezone_support.rb +100 -0
- data/lib/ri_cal/property_value/date_time.rb +359 -0
- data/lib/ri_cal/property_value/duration.rb +110 -0
- data/lib/ri_cal/property_value/geo.rb +11 -0
- data/lib/ri_cal/property_value/integer.rb +12 -0
- data/lib/ri_cal/property_value/occurrence_list.rb +144 -0
- data/lib/ri_cal/property_value/period.rb +86 -0
- data/lib/ri_cal/property_value/recurrence_rule/enumeration_support_methods.rb +100 -0
- data/lib/ri_cal/property_value/recurrence_rule/enumerator.rb +79 -0
- data/lib/ri_cal/property_value/recurrence_rule/initialization_methods.rb +148 -0
- data/lib/ri_cal/property_value/recurrence_rule/negative_setpos_enumerator.rb +53 -0
- data/lib/ri_cal/property_value/recurrence_rule/numbered_span.rb +31 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_day_incrementer.rb +86 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_hour_incrementer.rb +31 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_minute_incrementer.rb +32 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_month_incrementer.rb +52 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_monthday_incrementer.rb +31 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_numbered_day_incrementer.rb +38 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_second_incrementer.rb +32 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_weekno_incrementer.rb +69 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_yearday_incrementer.rb +31 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/daily_incrementer.rb +28 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/frequency_incrementer.rb +80 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/hourly_incrementer.rb +23 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/list_incrementer.rb +106 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/minutely_incrementer.rb +23 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/monthly_incrementer.rb +33 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/null_sub_cycle_incrementer.rb +43 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/secondly_incrementer.rb +28 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/weekly_incrementer.rb +37 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/yearly_incrementer.rb +57 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer.rb +135 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_day.rb +131 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_month_day.rb +64 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_numbered_week.rb +33 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_year_day.rb +53 -0
- data/lib/ri_cal/property_value/recurrence_rule/time_manipulation.rb +42 -0
- data/lib/ri_cal/property_value/recurrence_rule/validations.rb +125 -0
- data/lib/ri_cal/property_value/recurrence_rule.rb +154 -0
- data/lib/ri_cal/property_value/text.rb +44 -0
- data/lib/ri_cal/property_value/uri.rb +11 -0
- data/lib/ri_cal/property_value/utc_offset.rb +33 -0
- data/lib/ri_cal/property_value/zulu_date_time.rb +34 -0
- data/lib/ri_cal/property_value.rb +159 -0
- data/lib/ri_cal/required_timezones.rb +55 -0
- data/lib/ri_cal.rb +187 -0
- data/parked_specs/ri_cal/claudio_a_bug_spec.rb +100 -0
- data/performance/empty_propval/subject.rb +43 -0
- data/performance/paris_eastern/subject.rb +90 -0
- data/performance/penultimate_weekday/subject.rb +15 -0
- data/performance/psm_big_enum/ical.ics +3171 -0
- data/performance/psm_big_enum/subject.rb +16 -0
- data/performance/utah_cycling/subject.rb +55 -0
- data/ri_cal.gemspec +244 -0
- data/script/benchmark_subject +23 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/profile_subject +29 -0
- data/script/txt2html +71 -0
- data/spec/ri_cal/bugreports_spec.rb +276 -0
- data/spec/ri_cal/component/alarm_spec.rb +12 -0
- data/spec/ri_cal/component/calendar_spec.rb +88 -0
- data/spec/ri_cal/component/event_spec.rb +735 -0
- data/spec/ri_cal/component/freebusy_spec.rb +12 -0
- data/spec/ri_cal/component/journal_spec.rb +37 -0
- data/spec/ri_cal/component/t_z_info_timezone_spec.rb +60 -0
- data/spec/ri_cal/component/timezone_spec.rb +236 -0
- data/spec/ri_cal/component/todo_spec.rb +112 -0
- data/spec/ri_cal/component_spec.rb +224 -0
- data/spec/ri_cal/core_extensions/string/conversions_spec.rb +78 -0
- data/spec/ri_cal/core_extensions/time/calculations_spec.rb +188 -0
- data/spec/ri_cal/core_extensions/time/week_day_predicates_spec.rb +45 -0
- data/spec/ri_cal/fast_date_time_spec.rb +77 -0
- data/spec/ri_cal/inf_loop_spec.rb +78 -0
- data/spec/ri_cal/occurrence_enumerator_spec.rb +611 -0
- data/spec/ri_cal/parser_spec.rb +337 -0
- data/spec/ri_cal/property_value/date_spec.rb +53 -0
- data/spec/ri_cal/property_value/date_time_spec.rb +383 -0
- data/spec/ri_cal/property_value/duration_spec.rb +126 -0
- data/spec/ri_cal/property_value/occurrence_list_spec.rb +72 -0
- data/spec/ri_cal/property_value/period_spec.rb +63 -0
- data/spec/ri_cal/property_value/recurrence_rule/recurring_year_day_spec.rb +21 -0
- data/spec/ri_cal/property_value/recurrence_rule_spec.rb +1814 -0
- data/spec/ri_cal/property_value/text_spec.rb +25 -0
- data/spec/ri_cal/property_value/utc_offset_spec.rb +48 -0
- data/spec/ri_cal/property_value_spec.rb +125 -0
- data/spec/ri_cal/required_timezones_spec.rb +67 -0
- data/spec/ri_cal_spec.rb +53 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +50 -0
- data/tasks/gem_loader/load_active_support.rb +3 -0
- data/tasks/gem_loader/load_tzinfo_gem.rb +2 -0
- data/tasks/ri_cal.rake +412 -0
- data/tasks/spec.rake +102 -0
- metadata +246 -0
@@ -0,0 +1,611 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
|
3
|
+
|
4
|
+
require File.join(File.dirname(__FILE__), %w[.. spec_helper.rb])
|
5
|
+
|
6
|
+
def mock_enumerator(name, next_occurrence)
|
7
|
+
mock(name, :next_occurrence => next_occurrence, :bounded? => true, :empty? => false)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Note that this is more of a functional spec
|
11
|
+
describe RiCal::OccurrenceEnumerator do
|
12
|
+
|
13
|
+
Fr13Unbounded_Zulu = <<-TEXT
|
14
|
+
BEGIN:VEVENT
|
15
|
+
DTSTART:19970902T090000Z
|
16
|
+
DTEND: 19970902T100000Z
|
17
|
+
EXDATE:19970902T090000Z
|
18
|
+
RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13
|
19
|
+
END:VEVENT
|
20
|
+
TEXT
|
21
|
+
|
22
|
+
Fr13Unbounded_Eastern = <<-TEXT
|
23
|
+
BEGIN:VEVENT
|
24
|
+
DTSTART;TZID=US-Eastern:19970902T090000
|
25
|
+
DTEND;TZID=US-Eastern:19970902T100000
|
26
|
+
EXDATE;TZID=US-Eastern:19970902T090000
|
27
|
+
RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13
|
28
|
+
END:VEVENT
|
29
|
+
TEXT
|
30
|
+
|
31
|
+
Fr13UnboundedZuluExpectedFive = [
|
32
|
+
"19980213T090000Z",
|
33
|
+
"19980313T090000Z",
|
34
|
+
"19981113T090000Z",
|
35
|
+
"19990813T090000Z",
|
36
|
+
"20001013T090000Z"
|
37
|
+
].map {|start| src = <<-TEXT
|
38
|
+
BEGIN:VEVENT
|
39
|
+
DTSTART:#{start}
|
40
|
+
DTEND:#{start.gsub("T09","T10")}
|
41
|
+
RECURRENCE-ID:#{start}
|
42
|
+
END:VEVENT
|
43
|
+
TEXT
|
44
|
+
RiCal.parse_string(src).first
|
45
|
+
}
|
46
|
+
|
47
|
+
describe ".occurrences" do
|
48
|
+
describe "with an unbounded component" do
|
49
|
+
before(:each) do
|
50
|
+
@it = RiCal.parse_string(Fr13Unbounded_Zulu).first
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should raise an ArgumentError with no options to limit result" do
|
54
|
+
lambda {@it.occurrences}.should raise_error(ArgumentError)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should have the right five occurrences when :count => 5 option is used" do
|
58
|
+
result = @it.occurrences(:count => 5)
|
59
|
+
result.should == Fr13UnboundedZuluExpectedFive
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "with :starting specified" do
|
63
|
+
it "should exclude dates before :starting" do
|
64
|
+
result = @it.occurrences(:starting => Fr13UnboundedZuluExpectedFive[1].dtstart,
|
65
|
+
:before => Fr13UnboundedZuluExpectedFive[-1].dtstart)
|
66
|
+
result.map{|o|o.dtstart}.should == Fr13UnboundedZuluExpectedFive[1..-2].map{|e| e.dtstart}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "with :before specified" do
|
71
|
+
it "should exclude dates after :before" do
|
72
|
+
result = @it.occurrences(:before => Fr13UnboundedZuluExpectedFive[3].dtstart,
|
73
|
+
:count => 5)
|
74
|
+
result.map{|o|o.dtstart}.should == Fr13UnboundedZuluExpectedFive[0..2].map{|e| e.dtstart}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "with :overlapping specified" do
|
79
|
+
it "should include occurrences which overlap" do
|
80
|
+
result = @it.occurrences(:overlapping =>
|
81
|
+
[DateTime.parse("19981113T093000Z"), # occurrence[2].dtstart + 1/2 hour
|
82
|
+
DateTime.parse("20001013T083000Z")]) # occurrence[4].dtstart - 1/2 hour
|
83
|
+
result.map{|o|o.dtstart}.should == Fr13UnboundedZuluExpectedFive[2..3].map{|e| e.dtstart}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "for a non-recurring event" do
|
89
|
+
before(:each) do
|
90
|
+
@event_start = Time.now.utc
|
91
|
+
@event = RiCal.Event do |event|
|
92
|
+
event.dtstart = @event_start
|
93
|
+
event.dtend = @event_start + 3600
|
94
|
+
# event.rrule (no recurrence, single event)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should enumerate no occurrences if dtstart is before :starting" do
|
99
|
+
@event.occurrences(:starting => @event_start + 1).should be_empty
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should enumerate no occurrences if dtstart is after :before" do
|
103
|
+
@event.occurrences(:before => @event_start - 1).should be_empty
|
104
|
+
end
|
105
|
+
|
106
|
+
#Bug reported by K.J. Wierenga
|
107
|
+
it "should not raise a NoMethodError when specifying just the :count option" do
|
108
|
+
lambda {
|
109
|
+
@event.occurrences(:count => 1)
|
110
|
+
}.should_not raise_error
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe ".each" do
|
116
|
+
describe " for Every Friday the 13th, forever" do
|
117
|
+
before(:each) do
|
118
|
+
event = RiCal.parse_string(Fr13Unbounded_Zulu).first
|
119
|
+
@result = []
|
120
|
+
event.each do |occurrence|
|
121
|
+
break if @result.length >= 5
|
122
|
+
@result << occurrence
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should have the right first six occurrences" do
|
127
|
+
# TODO - Need to properly deal with timezones
|
128
|
+
@result.should == Fr13UnboundedZuluExpectedFive
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe "#zulu_occurrence_range" do
|
135
|
+
context "For an unbounded recurring event" do
|
136
|
+
before(:each) do
|
137
|
+
@event = RiCal.Event do |e|
|
138
|
+
e.dtstart = "TZID=America/New_York:20090525T143500"
|
139
|
+
e.dtend = "TZID=America/New_York:20090525T153500"
|
140
|
+
e.add_rrule("FREQ=DAILY")
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should not be bounded" do
|
145
|
+
@event.should_not be_bounded
|
146
|
+
end
|
147
|
+
|
148
|
+
it "should return an array with the first dtstart and nil" do
|
149
|
+
@event.zulu_occurrence_range.should == [DateTime.civil(2009,5,25,18,35,00, 0), nil]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context "For a bounded recurring event" do
|
154
|
+
before(:each) do
|
155
|
+
@event = RiCal.Event do |e|
|
156
|
+
e.dtstart = "TZID=America/New_York:20090525T143500"
|
157
|
+
e.dtend = "TZID=America/New_York:20090525T153500"
|
158
|
+
e.add_rrule("FREQ=DAILY;COUNT=3")
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should return an array with the first dtstart last dtend converted to utc" do
|
163
|
+
@event.zulu_occurrence_range.should == [DateTime.civil(2009,5,25,18,35,00, 0), DateTime.civil(2009,5,27,19,35,00, 0)]
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
context "For an event with no recurrence rules" do
|
168
|
+
context "with a non-floating dtstart and dtend" do
|
169
|
+
before(:each) do
|
170
|
+
@event = RiCal.Event do |e|
|
171
|
+
e.dtstart = "TZID=America/New_York:20090525T143500"
|
172
|
+
e.dtend = "TZID=America/New_York:20090525T153500"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should return an array with dtstart and dtend converted to zulu time" do
|
177
|
+
@event.zulu_occurrence_range.should == [DateTime.civil(2009,5,25,18,35,00, 0), DateTime.civil(2009,5,25,19,35,00, 0)]
|
178
|
+
end
|
179
|
+
end
|
180
|
+
context "with a floating dtstart and dtend" do
|
181
|
+
before(:each) do
|
182
|
+
@event = RiCal.Event do |e|
|
183
|
+
e.dtstart = "20090525T143500"
|
184
|
+
e.dtend = "20090525T153500"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should return an array with dtstart in the first timezone and dtend in the last time zone converted to zulu time" do
|
189
|
+
@event.zulu_occurrence_range.should == [DateTime.civil(2009,5,25,2,35,00, 0), DateTime.civil(2009,5,26,3,35,00, 0)]
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
context "Ticket #4 from paulsm" do
|
196
|
+
it "should produce 4 occurrences" do
|
197
|
+
cal = RiCal.parse_string rectify_ical(<<-ENDCAL)
|
198
|
+
BEGIN:VCALENDAR
|
199
|
+
METHOD:PUBLISH
|
200
|
+
PRODID:-//Apple Inc.//iCal 3.0//EN
|
201
|
+
CALSCALE:GREGORIAN
|
202
|
+
X-WR-CALNAME:Australian32Holidays
|
203
|
+
X-WR-CALDESC:Australian Public Holidays. Compiled from http://www.indust
|
204
|
+
rialrelations.nsw.gov.au/holidays/default.html and the links for the oth
|
205
|
+
er states at the bottom of that page
|
206
|
+
X-WR-RELCALID:AC1E4CE8-6690-49F6-A144-2F8891DFFD8D
|
207
|
+
VERSION:2.0
|
208
|
+
X-WR-TIMEZONE:Australia/Sydney
|
209
|
+
BEGIN:VEVENT
|
210
|
+
SEQUENCE:8
|
211
|
+
TRANSP:OPAQUE
|
212
|
+
UID:5B5579F3-2137-11D7-B491-00039301B0C2
|
213
|
+
DTSTART;VALUE=DATE:20020520
|
214
|
+
DTSTAMP:20060602T045619Z
|
215
|
+
SUMMARY:Adelaide Cup Carnival and Volunteers Day (SA)
|
216
|
+
EXDATE;VALUE=DATE:20060515
|
217
|
+
CREATED:20080916T000924Z
|
218
|
+
DTEND;VALUE=DATE:20020521
|
219
|
+
RRULE:FREQ=YEARLY;INTERVAL=1;UNTIL=20070520;BYMONTH=5;BYDAY=3MO
|
220
|
+
END:VEVENT
|
221
|
+
END:VCALENDAR
|
222
|
+
ENDCAL
|
223
|
+
cal.first.events.first.occurrences.length.should == 4
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
context "Ticket #2 from paulsm" do
|
228
|
+
before(:each) do
|
229
|
+
cals = RiCal.parse_string rectify_ical(<<-ENDCAL)
|
230
|
+
BEGIN:VCALENDAR
|
231
|
+
X-WR-TIMEZONE:America/New_York
|
232
|
+
PRODID:-//Apple Inc.//iCal 3.0//EN
|
233
|
+
CALSCALE:GREGORIAN
|
234
|
+
X-WR-CALNAME:test
|
235
|
+
VERSION:2.0
|
236
|
+
X-WR-RELCALID:1884C7F8-BC8E-457F-94AC-297871967D5E
|
237
|
+
X-APPLE-CALENDAR-COLOR:#2CA10B
|
238
|
+
BEGIN:VTIMEZONE
|
239
|
+
TZID:US/Eastern
|
240
|
+
BEGIN:DAYLIGHT
|
241
|
+
TZOFFSETFROM:-0500
|
242
|
+
TZOFFSETTO:-0400
|
243
|
+
DTSTART:20070311T020000
|
244
|
+
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
|
245
|
+
TZNAME:EDT
|
246
|
+
END:DAYLIGHT
|
247
|
+
BEGIN:STANDARD
|
248
|
+
TZOFFSETFROM:-0400
|
249
|
+
TZOFFSETTO:-0500
|
250
|
+
DTSTART:20071104T020000
|
251
|
+
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
|
252
|
+
TZNAME:EST
|
253
|
+
END:STANDARD
|
254
|
+
END:VTIMEZONE
|
255
|
+
BEGIN:VEVENT
|
256
|
+
SEQUENCE:5
|
257
|
+
TRANSP:OPAQUE
|
258
|
+
UID:00481E53-9258-4EA7-9F8D-947D3041A3F2
|
259
|
+
DTSTART;TZID=US/Eastern:20090224T090000
|
260
|
+
DTSTAMP:20090225T000908Z
|
261
|
+
SUMMARY:Test Event
|
262
|
+
CREATED:20090225T000839Z
|
263
|
+
DTEND;TZID=US/Eastern:20090224T100000
|
264
|
+
RRULE:FREQ=DAILY;INTERVAL=1;UNTIL=20090228T045959Z
|
265
|
+
END:VEVENT
|
266
|
+
END:VCALENDAR
|
267
|
+
ENDCAL
|
268
|
+
@event = cals.first.events.first
|
269
|
+
end
|
270
|
+
|
271
|
+
|
272
|
+
it "the event should be enumerable" do
|
273
|
+
lambda {@event.occurrences}.should_not raise_error
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
context "Lighthouse bug #3" do
|
278
|
+
before(:each) do
|
279
|
+
cals = RiCal.parse_string rectify_ical(<<-ENDCAL)
|
280
|
+
BEGIN:VCALENDAR
|
281
|
+
VERSION:2.0
|
282
|
+
PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
|
283
|
+
BEGIN:VTIMEZONE
|
284
|
+
TZID:/mozilla.org/20070129_1/Europe/Paris
|
285
|
+
X-LIC-LOCATION:Europe/Paris
|
286
|
+
BEGIN:DAYLIGHT
|
287
|
+
TZOFFSETFROM:+0100
|
288
|
+
TZOFFSETTO:+0200
|
289
|
+
TZNAME:CEST
|
290
|
+
DTSTART:19700329T020000
|
291
|
+
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3
|
292
|
+
END:DAYLIGHT
|
293
|
+
BEGIN:STANDARD
|
294
|
+
TZOFFSETFROM:+0200
|
295
|
+
TZOFFSETTO:+0100
|
296
|
+
TZNAME:CET
|
297
|
+
DTSTART:19701025T030000
|
298
|
+
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10
|
299
|
+
END:STANDARD
|
300
|
+
END:VTIMEZONE
|
301
|
+
BEGIN:VEVENT
|
302
|
+
CREATED:20070606T141629Z
|
303
|
+
LAST-MODIFIED:20070606T154611Z
|
304
|
+
DTSTAMP:20070607T120859Z
|
305
|
+
UID:5d1ae55f-3910-4de9-8b65-d652768fb2f2
|
306
|
+
SUMMARY:Lundi de Pâques
|
307
|
+
DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20070409
|
308
|
+
DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20070410
|
309
|
+
CATEGORIES:Jours fériés
|
310
|
+
END:VEVENT
|
311
|
+
END:VCALENDAR
|
312
|
+
ENDCAL
|
313
|
+
@event = cals.first.events.first
|
314
|
+
end
|
315
|
+
|
316
|
+
it "should be able to enumerate occurrences" do
|
317
|
+
@event.occurrences.should == [@event]
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
context "An event with a DATE dtstart, Ticket #6" do
|
322
|
+
before(:each) do
|
323
|
+
cal = RiCal.parse_string rectify_ical(<<-ENDCAL)
|
324
|
+
BEGIN:VCALENDAR
|
325
|
+
PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
|
326
|
+
VERSION:2.0
|
327
|
+
BEGIN:VEVENT
|
328
|
+
CREATED:20090520T092032Z
|
329
|
+
LAST-MODIFIED:20090520T092052Z
|
330
|
+
DTSTAMP:20090520T092032Z
|
331
|
+
UID:d41c124a-65c3-400e-bd04-1d2ee7b98352
|
332
|
+
SUMMARY:event2
|
333
|
+
RRULE:FREQ=MONTHLY;INTERVAL=1
|
334
|
+
DTSTART;VALUE=DATE:20090603
|
335
|
+
DTEND;VALUE=DATE:20090604
|
336
|
+
TRANSP:TRANSPARENT
|
337
|
+
END:VEVENT
|
338
|
+
END:VCALENDAR
|
339
|
+
ENDCAL
|
340
|
+
@occurrences = cal.first.events.first.occurrences(
|
341
|
+
:after => Date.parse('01/01/1990'),
|
342
|
+
:before => Date.parse("01/01/2010")
|
343
|
+
)
|
344
|
+
end
|
345
|
+
|
346
|
+
it "should produce the right dtstart values" do
|
347
|
+
@occurrences.map {|o| o.dtstart}.should == [
|
348
|
+
Date.parse("2009-06-03"),
|
349
|
+
Date.parse("2009-07-03"),
|
350
|
+
Date.parse("2009-08-03"),
|
351
|
+
Date.parse("2009-09-03"),
|
352
|
+
Date.parse("2009-10-03"),
|
353
|
+
Date.parse("2009-11-03"),
|
354
|
+
Date.parse("2009-12-03")
|
355
|
+
]
|
356
|
+
end
|
357
|
+
|
358
|
+
it "should produce events whose dtstarts are all dates" do
|
359
|
+
@occurrences.all? {|o| o.dtstart.class == ::Date}.should be_true
|
360
|
+
end
|
361
|
+
|
362
|
+
it "should produce the right dtend values" do
|
363
|
+
@occurrences.map {|o| o.dtend}.should == [
|
364
|
+
Date.parse("2009-06-04"),
|
365
|
+
Date.parse("2009-07-04"),
|
366
|
+
Date.parse("2009-08-04"),
|
367
|
+
Date.parse("2009-09-04"),
|
368
|
+
Date.parse("2009-10-04"),
|
369
|
+
Date.parse("2009-11-04"),
|
370
|
+
Date.parse("2009-12-04")
|
371
|
+
]
|
372
|
+
end
|
373
|
+
|
374
|
+
it "should produce events whose dtstends are all dates" do
|
375
|
+
@occurrences.all? {|o| o.dtend.class == ::Date}.should be_true
|
376
|
+
end
|
377
|
+
end
|
378
|
+
context "bounded? bug" do
|
379
|
+
before(:each) do
|
380
|
+
events = RiCal.parse_string rectify_ical(<<-ENDCAL)
|
381
|
+
BEGIN:VEVENT
|
382
|
+
EXDATE:20090114T163000
|
383
|
+
EXDATE:20090128T163000
|
384
|
+
EXDATE:20090121T163000
|
385
|
+
EXDATE:20090211T163000
|
386
|
+
EXDATE:20090204T163000
|
387
|
+
EXDATE:20090218T163000
|
388
|
+
TRANSP:OPAQUE
|
389
|
+
DTSTAMP;VALUE=DATE-TIME:20090107T024340Z
|
390
|
+
CREATED;VALUE=DATE-TIME:20090107T024012Z
|
391
|
+
DTEND;TZID=US/Mountain;VALUE=DATE-TIME:20090114T180000
|
392
|
+
DTSTART;TZID=US/Mountain;VALUE=DATE-TIME:20090114T163000
|
393
|
+
UID:15208112-E0FA-4A7C-954C-CFDF19D1B0E7
|
394
|
+
RRULE:FREQ=WEEKLY;INTERVAL=1;UNTIL=20090219T065959Z
|
395
|
+
SUMMARY:Wild Rose XC/Skate Training Series
|
396
|
+
SEQUENCE:11
|
397
|
+
LOCATION:Mountain Dell Golf Course
|
398
|
+
END:VEVENT
|
399
|
+
ENDCAL
|
400
|
+
@event = events.first
|
401
|
+
end
|
402
|
+
|
403
|
+
it "should be able to enumerate occurrences" do
|
404
|
+
@event.should be_bounded
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
context "EXDATES with timezones bug" do
|
409
|
+
before(:each) do
|
410
|
+
cals = RiCal.parse_string rectify_ical(<<-ENDCAL)
|
411
|
+
BEGIN:VCALENDAR
|
412
|
+
METHOD:PUBLISH
|
413
|
+
PRODID:-//Apple Inc.//iCal 3.0//EN
|
414
|
+
CALSCALE:GREGORIAN
|
415
|
+
X-WR-CALNAME:Utah Cycling
|
416
|
+
X-WR-RELCALID:BF579011-36BF-49C6-8C7D-E96F03DE8055
|
417
|
+
VERSION:2.0
|
418
|
+
X-WR-TIMEZONE:US/Mountain
|
419
|
+
BEGIN:VTIMEZONE
|
420
|
+
TZID:US/Mountain
|
421
|
+
BEGIN:DAYLIGHT
|
422
|
+
TZOFFSETFROM:-0700
|
423
|
+
TZOFFSETTO:-0600
|
424
|
+
DTSTART:20070311T020000
|
425
|
+
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
|
426
|
+
TZNAME:MDT
|
427
|
+
END:DAYLIGHT
|
428
|
+
BEGIN:STANDARD
|
429
|
+
TZOFFSETFROM:-0600
|
430
|
+
TZOFFSETTO:-0700
|
431
|
+
DTSTART:20071104T020000
|
432
|
+
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
|
433
|
+
TZNAME:MST
|
434
|
+
END:STANDARD
|
435
|
+
END:VTIMEZONE
|
436
|
+
BEGIN:VEVENT
|
437
|
+
SEQUENCE:11
|
438
|
+
TRANSP:OPAQUE
|
439
|
+
UID:15208112-E0FA-4A7C-954C-CFDF19D1B0E7
|
440
|
+
DTSTART;TZID=US/Mountain:20090114T163000
|
441
|
+
DTSTAMP:20090107T024340Z
|
442
|
+
SUMMARY:Wild Rose XC/Skate Training Series
|
443
|
+
EXDATE;TZID=US/Mountain:20090114T163000
|
444
|
+
EXDATE;TZID=US/Mountain:20090128T163000
|
445
|
+
EXDATE;TZID=US/Mountain:20090121T163000
|
446
|
+
EXDATE;TZID=US/Mountain:20090211T163000
|
447
|
+
EXDATE;TZID=US/Mountain:20090204T163000
|
448
|
+
EXDATE;TZID=US/Mountain:20090218T163000
|
449
|
+
CREATED:20090107T024012Z
|
450
|
+
DTEND;TZID=US/Mountain:20090114T180000
|
451
|
+
LOCATION:Mountain Dell Golf Course
|
452
|
+
RRULE:FREQ=WEEKLY;INTERVAL=1;UNTIL=20090219T065959Z
|
453
|
+
END:VEVENT
|
454
|
+
END:VCALENDAR
|
455
|
+
ENDCAL
|
456
|
+
@event = cals.first.events.first
|
457
|
+
end
|
458
|
+
|
459
|
+
it "should have no occurrences" do
|
460
|
+
@event.occurrences.length.should == 0
|
461
|
+
end
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
describe RiCal::OccurrenceEnumerator::OccurrenceMerger do
|
466
|
+
before(:each) do
|
467
|
+
@merger = RiCal::OccurrenceEnumerator::OccurrenceMerger
|
468
|
+
end
|
469
|
+
|
470
|
+
describe ".for" do
|
471
|
+
it "should return an EmptyEnumerator if the rules parameter is nil" do
|
472
|
+
@merger.for(nil, nil).should == RiCal::OccurrenceEnumerator::EmptyRulesEnumerator
|
473
|
+
end
|
474
|
+
|
475
|
+
it "should return an EmptyEnumerator if the rules parameter is empty" do
|
476
|
+
@merger.for(nil, []).should == RiCal::OccurrenceEnumerator::EmptyRulesEnumerator
|
477
|
+
end
|
478
|
+
|
479
|
+
describe "with a single rrule" do
|
480
|
+
before(:each) do
|
481
|
+
@component = mock("component", :dtstart => :dtstart_value)
|
482
|
+
@rrule = mock("rrule", :enumerator => :rrule_enumerator)
|
483
|
+
end
|
484
|
+
|
485
|
+
it "should return the enumerator the rrule" do
|
486
|
+
@merger.for(@component, [@rrule]).should == :rrule_enumerator
|
487
|
+
end
|
488
|
+
|
489
|
+
it "should pass the component to the enumerator instantiation" do
|
490
|
+
@rrule.should_receive(:enumerator).with(@component)
|
491
|
+
@merger.for(@component, [@rrule])
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
describe "with multiple rrules" do
|
496
|
+
before(:each) do
|
497
|
+
@component = mock("component", :dtstart => :dtstart_value)
|
498
|
+
@enum1 = mock_enumerator("rrule_enumerator1", :occ1)
|
499
|
+
@enum2 = mock_enumerator("rrule_enumerator2", :occ2)
|
500
|
+
@rrule1 = mock("rrule", :enumerator => @enum1)
|
501
|
+
@rrule2 = mock("rrule", :enumerator => @enum2)
|
502
|
+
end
|
503
|
+
|
504
|
+
it "should return an instance of RiCal::OccurrenceEnumerator::OccurrenceMerger" do
|
505
|
+
@merger.for(@component, [@rrule1, @rrule2]).should be_kind_of(RiCal::OccurrenceEnumerator::OccurrenceMerger)
|
506
|
+
end
|
507
|
+
|
508
|
+
it "should pass the component to the enumerator instantiation" do
|
509
|
+
@rrule1.should_receive(:enumerator).with(@component).and_return(@enum1)
|
510
|
+
@rrule2.should_receive(:enumerator).with(@component).and_return(@enum2)
|
511
|
+
@merger.for(@component, [@rrule1, @rrule2])
|
512
|
+
end
|
513
|
+
|
514
|
+
it "should preload the next occurrences" do
|
515
|
+
@enum1.should_receive(:next_occurrence).and_return(:occ1)
|
516
|
+
@enum2.should_receive(:next_occurrence).and_return(:occ2)
|
517
|
+
@merger.for(@component, [@rrule1, @rrule2])
|
518
|
+
end
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
describe "#zulu_occurrence_range" do
|
523
|
+
end
|
524
|
+
|
525
|
+
describe "#next_occurence" do
|
526
|
+
|
527
|
+
describe "with unique nexts" do
|
528
|
+
before(:each) do
|
529
|
+
@enum1 = mock_enumerator("rrule_enumerator1",3)
|
530
|
+
@enum2 = mock_enumerator("rrule_enumerator2", 2)
|
531
|
+
@rrule1 = mock("rrule", :enumerator => @enum1)
|
532
|
+
@rrule2 = mock("rrule", :enumerator => @enum2)
|
533
|
+
@it = @merger.new(0, [@rrule1, @rrule2])
|
534
|
+
end
|
535
|
+
|
536
|
+
it "should return the earliest occurrence" do
|
537
|
+
@it.next_occurrence.should == 2
|
538
|
+
end
|
539
|
+
|
540
|
+
it "should advance the enumerator which returned the result" do
|
541
|
+
@enum2.should_receive(:next_occurrence).and_return(4)
|
542
|
+
@it.next_occurrence
|
543
|
+
end
|
544
|
+
|
545
|
+
it "should not advance the other enumerator" do
|
546
|
+
@enum1.should_not_receive(:next_occurrence)
|
547
|
+
@it.next_occurrence
|
548
|
+
end
|
549
|
+
|
550
|
+
it "should properly update the next array" do
|
551
|
+
@enum2.stub!(:next_occurrence).and_return(4)
|
552
|
+
@it.next_occurrence
|
553
|
+
@it.nexts.should == [3, 4]
|
554
|
+
end
|
555
|
+
end
|
556
|
+
|
557
|
+
describe "with duplicated nexts" do
|
558
|
+
before(:each) do
|
559
|
+
@enum1 = mock_enumerator("rrule_enumerator1", 2)
|
560
|
+
@enum2 = mock_enumerator("rrule_enumerator2", 2)
|
561
|
+
@rrule1 = mock("rrule", :enumerator => @enum1)
|
562
|
+
@rrule2 = mock("rrule", :enumerator => @enum2)
|
563
|
+
@it = @merger.new(0, [@rrule1, @rrule2])
|
564
|
+
end
|
565
|
+
|
566
|
+
it "should return the earliest occurrence" do
|
567
|
+
@it.next_occurrence.should == 2
|
568
|
+
end
|
569
|
+
|
570
|
+
it "should advance both enumerators" do
|
571
|
+
@enum1.should_receive(:next_occurrence).and_return(5)
|
572
|
+
@enum2.should_receive(:next_occurrence).and_return(4)
|
573
|
+
@it.next_occurrence
|
574
|
+
end
|
575
|
+
|
576
|
+
it "should properly update the next array" do
|
577
|
+
@enum1.stub!(:next_occurrence).and_return(5)
|
578
|
+
@enum2.stub!(:next_occurrence).and_return(4)
|
579
|
+
@it.next_occurrence
|
580
|
+
@it.nexts.should == [5, 4]
|
581
|
+
end
|
582
|
+
|
583
|
+
end
|
584
|
+
|
585
|
+
describe "with all enumerators at end" do
|
586
|
+
before(:each) do
|
587
|
+
@enum1 = mock_enumerator("rrule_enumerator1", nil)
|
588
|
+
@enum2 = mock_enumerator("rrule_enumerator2", nil)
|
589
|
+
@rrule1 = mock("rrule", :enumerator => @enum1)
|
590
|
+
@rrule2 = mock("rrule", :enumerator => @enum2)
|
591
|
+
@it = @merger.new(0, [@rrule1, @rrule2])
|
592
|
+
end
|
593
|
+
|
594
|
+
it "should return nil" do
|
595
|
+
@it.next_occurrence.should == nil
|
596
|
+
end
|
597
|
+
|
598
|
+
it "should not advance the enumerators which returned the result" do
|
599
|
+
@enum1.should_not_receive(:next_occurrence)
|
600
|
+
@enum2.should_not_receive(:next_occurrence)
|
601
|
+
@it.next_occurrence
|
602
|
+
end
|
603
|
+
end
|
604
|
+
end
|
605
|
+
|
606
|
+
|
607
|
+
|
608
|
+
|
609
|
+
|
610
|
+
|
611
|
+
end
|