friflaj_ri_cal 0.9.0

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