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.
Files changed (167) hide show
  1. checksums.yaml +7 -0
  2. data/History.txt +402 -0
  3. data/Manifest.txt +161 -0
  4. data/README.txt +410 -0
  5. data/Rakefile +69 -0
  6. data/VERSION +1 -0
  7. data/bin/ri_cal +8 -0
  8. data/component_attributes/alarm.yml +10 -0
  9. data/component_attributes/calendar.yml +4 -0
  10. data/component_attributes/component_property_defs.yml +180 -0
  11. data/component_attributes/event.yml +45 -0
  12. data/component_attributes/freebusy.yml +16 -0
  13. data/component_attributes/journal.yml +35 -0
  14. data/component_attributes/timezone.yml +3 -0
  15. data/component_attributes/timezone_period.yml +11 -0
  16. data/component_attributes/todo.yml +46 -0
  17. data/copyrights.txt +1 -0
  18. data/docs/draft-ietf-calsify-2446bis-08.txt +7280 -0
  19. data/docs/draft-ietf-calsify-rfc2445bis-09.txt +10416 -0
  20. data/docs/incrementers.txt +7 -0
  21. data/docs/rfc2445.pdf +0 -0
  22. data/lib/ri_cal/component/alarm.rb +19 -0
  23. data/lib/ri_cal/component/calendar.rb +257 -0
  24. data/lib/ri_cal/component/event.rb +58 -0
  25. data/lib/ri_cal/component/freebusy.rb +16 -0
  26. data/lib/ri_cal/component/journal.rb +27 -0
  27. data/lib/ri_cal/component/non_standard.rb +33 -0
  28. data/lib/ri_cal/component/t_z_info_timezone.rb +153 -0
  29. data/lib/ri_cal/component/timezone/daylight_period.rb +25 -0
  30. data/lib/ri_cal/component/timezone/standard_period.rb +23 -0
  31. data/lib/ri_cal/component/timezone/timezone_period.rb +76 -0
  32. data/lib/ri_cal/component/timezone.rb +197 -0
  33. data/lib/ri_cal/component/todo.rb +42 -0
  34. data/lib/ri_cal/component.rb +256 -0
  35. data/lib/ri_cal/core_extensions/array/conversions.rb +15 -0
  36. data/lib/ri_cal/core_extensions/array.rb +7 -0
  37. data/lib/ri_cal/core_extensions/date/conversions.rb +56 -0
  38. data/lib/ri_cal/core_extensions/date.rb +13 -0
  39. data/lib/ri_cal/core_extensions/date_time/conversions.rb +50 -0
  40. data/lib/ri_cal/core_extensions/date_time.rb +15 -0
  41. data/lib/ri_cal/core_extensions/object/conversions.rb +20 -0
  42. data/lib/ri_cal/core_extensions/object.rb +8 -0
  43. data/lib/ri_cal/core_extensions/string/conversions.rb +57 -0
  44. data/lib/ri_cal/core_extensions/string.rb +8 -0
  45. data/lib/ri_cal/core_extensions/time/calculations.rb +153 -0
  46. data/lib/ri_cal/core_extensions/time/conversions.rb +42 -0
  47. data/lib/ri_cal/core_extensions/time/tzid_access.rb +50 -0
  48. data/lib/ri_cal/core_extensions/time/week_day_predicates.rb +55 -0
  49. data/lib/ri_cal/core_extensions/time.rb +14 -0
  50. data/lib/ri_cal/core_extensions.rb +11 -0
  51. data/lib/ri_cal/fast_date_time.rb +234 -0
  52. data/lib/ri_cal/floating_timezone.rb +32 -0
  53. data/lib/ri_cal/invalid_property_value.rb +8 -0
  54. data/lib/ri_cal/invalid_timezone_identifier.rb +20 -0
  55. data/lib/ri_cal/occurrence_enumerator.rb +265 -0
  56. data/lib/ri_cal/occurrence_period.rb +17 -0
  57. data/lib/ri_cal/parser.rb +148 -0
  58. data/lib/ri_cal/properties/alarm.rb +390 -0
  59. data/lib/ri_cal/properties/calendar.rb +164 -0
  60. data/lib/ri_cal/properties/event.rb +1523 -0
  61. data/lib/ri_cal/properties/freebusy.rb +593 -0
  62. data/lib/ri_cal/properties/journal.rb +1237 -0
  63. data/lib/ri_cal/properties/timezone.rb +150 -0
  64. data/lib/ri_cal/properties/timezone_period.rb +416 -0
  65. data/lib/ri_cal/properties/todo.rb +1559 -0
  66. data/lib/ri_cal/properties.rb +12 -0
  67. data/lib/ri_cal/property_value/array.rb +27 -0
  68. data/lib/ri_cal/property_value/cal_address.rb +11 -0
  69. data/lib/ri_cal/property_value/date.rb +184 -0
  70. data/lib/ri_cal/property_value/date_time/additive_methods.rb +44 -0
  71. data/lib/ri_cal/property_value/date_time/time_machine.rb +159 -0
  72. data/lib/ri_cal/property_value/date_time/timezone_support.rb +100 -0
  73. data/lib/ri_cal/property_value/date_time.rb +359 -0
  74. data/lib/ri_cal/property_value/duration.rb +110 -0
  75. data/lib/ri_cal/property_value/geo.rb +11 -0
  76. data/lib/ri_cal/property_value/integer.rb +12 -0
  77. data/lib/ri_cal/property_value/occurrence_list.rb +144 -0
  78. data/lib/ri_cal/property_value/period.rb +86 -0
  79. data/lib/ri_cal/property_value/recurrence_rule/enumeration_support_methods.rb +100 -0
  80. data/lib/ri_cal/property_value/recurrence_rule/enumerator.rb +79 -0
  81. data/lib/ri_cal/property_value/recurrence_rule/initialization_methods.rb +148 -0
  82. data/lib/ri_cal/property_value/recurrence_rule/negative_setpos_enumerator.rb +53 -0
  83. data/lib/ri_cal/property_value/recurrence_rule/numbered_span.rb +31 -0
  84. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_day_incrementer.rb +86 -0
  85. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_hour_incrementer.rb +31 -0
  86. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_minute_incrementer.rb +32 -0
  87. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_month_incrementer.rb +52 -0
  88. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_monthday_incrementer.rb +31 -0
  89. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_numbered_day_incrementer.rb +38 -0
  90. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_second_incrementer.rb +32 -0
  91. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_weekno_incrementer.rb +69 -0
  92. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_yearday_incrementer.rb +31 -0
  93. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/daily_incrementer.rb +28 -0
  94. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/frequency_incrementer.rb +80 -0
  95. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/hourly_incrementer.rb +23 -0
  96. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/list_incrementer.rb +106 -0
  97. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/minutely_incrementer.rb +23 -0
  98. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/monthly_incrementer.rb +33 -0
  99. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/null_sub_cycle_incrementer.rb +43 -0
  100. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/secondly_incrementer.rb +28 -0
  101. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/weekly_incrementer.rb +37 -0
  102. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/yearly_incrementer.rb +57 -0
  103. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer.rb +135 -0
  104. data/lib/ri_cal/property_value/recurrence_rule/recurring_day.rb +131 -0
  105. data/lib/ri_cal/property_value/recurrence_rule/recurring_month_day.rb +64 -0
  106. data/lib/ri_cal/property_value/recurrence_rule/recurring_numbered_week.rb +33 -0
  107. data/lib/ri_cal/property_value/recurrence_rule/recurring_year_day.rb +53 -0
  108. data/lib/ri_cal/property_value/recurrence_rule/time_manipulation.rb +42 -0
  109. data/lib/ri_cal/property_value/recurrence_rule/validations.rb +125 -0
  110. data/lib/ri_cal/property_value/recurrence_rule.rb +154 -0
  111. data/lib/ri_cal/property_value/text.rb +44 -0
  112. data/lib/ri_cal/property_value/uri.rb +11 -0
  113. data/lib/ri_cal/property_value/utc_offset.rb +33 -0
  114. data/lib/ri_cal/property_value/zulu_date_time.rb +34 -0
  115. data/lib/ri_cal/property_value.rb +159 -0
  116. data/lib/ri_cal/required_timezones.rb +55 -0
  117. data/lib/ri_cal.rb +187 -0
  118. data/parked_specs/ri_cal/claudio_a_bug_spec.rb +100 -0
  119. data/performance/empty_propval/subject.rb +43 -0
  120. data/performance/paris_eastern/subject.rb +90 -0
  121. data/performance/penultimate_weekday/subject.rb +15 -0
  122. data/performance/psm_big_enum/ical.ics +3171 -0
  123. data/performance/psm_big_enum/subject.rb +16 -0
  124. data/performance/utah_cycling/subject.rb +55 -0
  125. data/ri_cal.gemspec +244 -0
  126. data/script/benchmark_subject +23 -0
  127. data/script/console +10 -0
  128. data/script/destroy +14 -0
  129. data/script/generate +14 -0
  130. data/script/profile_subject +29 -0
  131. data/script/txt2html +71 -0
  132. data/spec/ri_cal/bugreports_spec.rb +276 -0
  133. data/spec/ri_cal/component/alarm_spec.rb +12 -0
  134. data/spec/ri_cal/component/calendar_spec.rb +88 -0
  135. data/spec/ri_cal/component/event_spec.rb +735 -0
  136. data/spec/ri_cal/component/freebusy_spec.rb +12 -0
  137. data/spec/ri_cal/component/journal_spec.rb +37 -0
  138. data/spec/ri_cal/component/t_z_info_timezone_spec.rb +60 -0
  139. data/spec/ri_cal/component/timezone_spec.rb +236 -0
  140. data/spec/ri_cal/component/todo_spec.rb +112 -0
  141. data/spec/ri_cal/component_spec.rb +224 -0
  142. data/spec/ri_cal/core_extensions/string/conversions_spec.rb +78 -0
  143. data/spec/ri_cal/core_extensions/time/calculations_spec.rb +188 -0
  144. data/spec/ri_cal/core_extensions/time/week_day_predicates_spec.rb +45 -0
  145. data/spec/ri_cal/fast_date_time_spec.rb +77 -0
  146. data/spec/ri_cal/inf_loop_spec.rb +78 -0
  147. data/spec/ri_cal/occurrence_enumerator_spec.rb +611 -0
  148. data/spec/ri_cal/parser_spec.rb +337 -0
  149. data/spec/ri_cal/property_value/date_spec.rb +53 -0
  150. data/spec/ri_cal/property_value/date_time_spec.rb +383 -0
  151. data/spec/ri_cal/property_value/duration_spec.rb +126 -0
  152. data/spec/ri_cal/property_value/occurrence_list_spec.rb +72 -0
  153. data/spec/ri_cal/property_value/period_spec.rb +63 -0
  154. data/spec/ri_cal/property_value/recurrence_rule/recurring_year_day_spec.rb +21 -0
  155. data/spec/ri_cal/property_value/recurrence_rule_spec.rb +1814 -0
  156. data/spec/ri_cal/property_value/text_spec.rb +25 -0
  157. data/spec/ri_cal/property_value/utc_offset_spec.rb +48 -0
  158. data/spec/ri_cal/property_value_spec.rb +125 -0
  159. data/spec/ri_cal/required_timezones_spec.rb +67 -0
  160. data/spec/ri_cal_spec.rb +53 -0
  161. data/spec/spec.opts +4 -0
  162. data/spec/spec_helper.rb +50 -0
  163. data/tasks/gem_loader/load_active_support.rb +3 -0
  164. data/tasks/gem_loader/load_tzinfo_gem.rb +2 -0
  165. data/tasks/ri_cal.rake +412 -0
  166. data/tasks/spec.rake +102 -0
  167. 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