ebeigarts-ri_cal 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. data/History.txt +136 -0
  2. data/Manifest.txt +140 -0
  3. data/README.txt +410 -0
  4. data/Rakefile +53 -0
  5. data/bin/ri_cal +8 -0
  6. data/component_attributes/alarm.yml +10 -0
  7. data/component_attributes/calendar.yml +4 -0
  8. data/component_attributes/component_property_defs.yml +180 -0
  9. data/component_attributes/event.yml +45 -0
  10. data/component_attributes/freebusy.yml +16 -0
  11. data/component_attributes/journal.yml +35 -0
  12. data/component_attributes/timezone.yml +3 -0
  13. data/component_attributes/timezone_period.yml +11 -0
  14. data/component_attributes/todo.yml +46 -0
  15. data/copyrights.txt +1 -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/lib/ri_cal.rb +175 -0
  21. data/lib/ri_cal/component.rb +249 -0
  22. data/lib/ri_cal/component/alarm.rb +21 -0
  23. data/lib/ri_cal/component/calendar.rb +259 -0
  24. data/lib/ri_cal/component/event.rb +60 -0
  25. data/lib/ri_cal/component/freebusy.rb +18 -0
  26. data/lib/ri_cal/component/journal.rb +30 -0
  27. data/lib/ri_cal/component/non_standard.rb +35 -0
  28. data/lib/ri_cal/component/t_z_info_timezone.rb +139 -0
  29. data/lib/ri_cal/component/timezone.rb +197 -0
  30. data/lib/ri_cal/component/timezone/daylight_period.rb +25 -0
  31. data/lib/ri_cal/component/timezone/standard_period.rb +23 -0
  32. data/lib/ri_cal/component/timezone/timezone_period.rb +78 -0
  33. data/lib/ri_cal/component/todo.rb +44 -0
  34. data/lib/ri_cal/core_extensions.rb +6 -0
  35. data/lib/ri_cal/core_extensions/array.rb +7 -0
  36. data/lib/ri_cal/core_extensions/array/conversions.rb +15 -0
  37. data/lib/ri_cal/core_extensions/date.rb +13 -0
  38. data/lib/ri_cal/core_extensions/date/conversions.rb +69 -0
  39. data/lib/ri_cal/core_extensions/date_time.rb +15 -0
  40. data/lib/ri_cal/core_extensions/date_time/conversions.rb +55 -0
  41. data/lib/ri_cal/core_extensions/object.rb +8 -0
  42. data/lib/ri_cal/core_extensions/object/conversions.rb +20 -0
  43. data/lib/ri_cal/core_extensions/string.rb +8 -0
  44. data/lib/ri_cal/core_extensions/string/conversions.rb +53 -0
  45. data/lib/ri_cal/core_extensions/time.rb +13 -0
  46. data/lib/ri_cal/core_extensions/time/calculations.rb +153 -0
  47. data/lib/ri_cal/core_extensions/time/conversions.rb +42 -0
  48. data/lib/ri_cal/core_extensions/time/tzid_access.rb +50 -0
  49. data/lib/ri_cal/core_extensions/time/week_day_predicates.rb +55 -0
  50. data/lib/ri_cal/fast_date_time.rb +226 -0
  51. data/lib/ri_cal/floating_timezone.rb +32 -0
  52. data/lib/ri_cal/invalid_property_value.rb +8 -0
  53. data/lib/ri_cal/invalid_timezone_identifer.rb +20 -0
  54. data/lib/ri_cal/occurrence_enumerator.rb +265 -0
  55. data/lib/ri_cal/occurrence_period.rb +17 -0
  56. data/lib/ri_cal/parser.rb +145 -0
  57. data/lib/ri_cal/properties/alarm.rb +390 -0
  58. data/lib/ri_cal/properties/calendar.rb +164 -0
  59. data/lib/ri_cal/properties/event.rb +1526 -0
  60. data/lib/ri_cal/properties/freebusy.rb +594 -0
  61. data/lib/ri_cal/properties/journal.rb +1240 -0
  62. data/lib/ri_cal/properties/timezone.rb +151 -0
  63. data/lib/ri_cal/properties/timezone_period.rb +416 -0
  64. data/lib/ri_cal/properties/todo.rb +1562 -0
  65. data/lib/ri_cal/property_value.rb +149 -0
  66. data/lib/ri_cal/property_value/array.rb +27 -0
  67. data/lib/ri_cal/property_value/cal_address.rb +11 -0
  68. data/lib/ri_cal/property_value/date.rb +169 -0
  69. data/lib/ri_cal/property_value/date_time.rb +351 -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/duration.rb +110 -0
  74. data/lib/ri_cal/property_value/geo.rb +11 -0
  75. data/lib/ri_cal/property_value/integer.rb +12 -0
  76. data/lib/ri_cal/property_value/occurrence_list.rb +144 -0
  77. data/lib/ri_cal/property_value/period.rb +86 -0
  78. data/lib/ri_cal/property_value/recurrence_rule.rb +145 -0
  79. data/lib/ri_cal/property_value/recurrence_rule/enumeration_support_methods.rb +97 -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/occurence_incrementer.rb +793 -0
  85. data/lib/ri_cal/property_value/recurrence_rule/recurring_day.rb +131 -0
  86. data/lib/ri_cal/property_value/recurrence_rule/recurring_month_day.rb +60 -0
  87. data/lib/ri_cal/property_value/recurrence_rule/recurring_numbered_week.rb +33 -0
  88. data/lib/ri_cal/property_value/recurrence_rule/recurring_year_day.rb +49 -0
  89. data/lib/ri_cal/property_value/recurrence_rule/validations.rb +125 -0
  90. data/lib/ri_cal/property_value/text.rb +40 -0
  91. data/lib/ri_cal/property_value/uri.rb +11 -0
  92. data/lib/ri_cal/property_value/utc_offset.rb +33 -0
  93. data/lib/ri_cal/required_timezones.rb +55 -0
  94. data/performance/paris_eastern/subject.rb +90 -0
  95. data/performance/penultimate_weekday/subject.rb +15 -0
  96. data/performance/psm_big_enum/ical.ics +3171 -0
  97. data/performance/psm_big_enum/subject.rb +16 -0
  98. data/performance/utah_cycling/subject.rb +55 -0
  99. data/ri_cal.gemspec +48 -0
  100. data/sample_ical_files/from_ical_dot_app/test1.ics +38 -0
  101. data/script/benchmark_subject +23 -0
  102. data/script/console +10 -0
  103. data/script/destroy +14 -0
  104. data/script/generate +14 -0
  105. data/script/profile_subject +27 -0
  106. data/script/txt2html +71 -0
  107. data/spec/ri_cal/bugreports_spec.rb +230 -0
  108. data/spec/ri_cal/component/alarm_spec.rb +12 -0
  109. data/spec/ri_cal/component/calendar_spec.rb +88 -0
  110. data/spec/ri_cal/component/event_spec.rb +718 -0
  111. data/spec/ri_cal/component/freebusy_spec.rb +12 -0
  112. data/spec/ri_cal/component/journal_spec.rb +37 -0
  113. data/spec/ri_cal/component/t_z_info_timezone_spec.rb +59 -0
  114. data/spec/ri_cal/component/timezone_spec.rb +236 -0
  115. data/spec/ri_cal/component/todo_spec.rb +112 -0
  116. data/spec/ri_cal/component_spec.rb +224 -0
  117. data/spec/ri_cal/core_extensions/string/conversions_spec.rb +78 -0
  118. data/spec/ri_cal/core_extensions/time/calculations_spec.rb +188 -0
  119. data/spec/ri_cal/core_extensions/time/week_day_predicates_spec.rb +45 -0
  120. data/spec/ri_cal/fast_date_time_spec.rb +67 -0
  121. data/spec/ri_cal/occurrence_enumerator_spec.rb +611 -0
  122. data/spec/ri_cal/parser_spec.rb +337 -0
  123. data/spec/ri_cal/property_value/date_spec.rb +53 -0
  124. data/spec/ri_cal/property_value/date_time_spec.rb +383 -0
  125. data/spec/ri_cal/property_value/duration_spec.rb +126 -0
  126. data/spec/ri_cal/property_value/occurrence_list_spec.rb +72 -0
  127. data/spec/ri_cal/property_value/period_spec.rb +63 -0
  128. data/spec/ri_cal/property_value/recurrence_rule/recurring_year_day_spec.rb +21 -0
  129. data/spec/ri_cal/property_value/recurrence_rule_spec.rb +1814 -0
  130. data/spec/ri_cal/property_value/text_spec.rb +25 -0
  131. data/spec/ri_cal/property_value/utc_offset_spec.rb +48 -0
  132. data/spec/ri_cal/property_value_spec.rb +125 -0
  133. data/spec/ri_cal/required_timezones_spec.rb +67 -0
  134. data/spec/ri_cal_spec.rb +53 -0
  135. data/spec/spec.opts +4 -0
  136. data/spec/spec_helper.rb +46 -0
  137. data/tasks/gem_loader/load_active_support.rb +3 -0
  138. data/tasks/gem_loader/load_tzinfo_gem.rb +2 -0
  139. data/tasks/ri_cal.rake +412 -0
  140. data/tasks/spec.rake +102 -0
  141. metadata +230 -0
@@ -0,0 +1,12 @@
1
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
2
+
3
+ require File.join(File.dirname(__FILE__), %w[.. .. spec_helper])
4
+
5
+ describe RiCal::Component::Alarm do
6
+
7
+ describe ".entity_name" do
8
+ it "should be VALARM" do
9
+ RiCal::Component::Alarm.entity_name.should == "VALARM"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,88 @@
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])
5
+
6
+ describe RiCal::Component::Calendar do
7
+
8
+ context ".entity_name" do
9
+ it "should be VCALENDAR" do
10
+ RiCal::Component::Calendar.entity_name.should == "VCALENDAR"
11
+ end
12
+ end
13
+
14
+ context "a new instance" do
15
+ before(:each) do
16
+ @it = RiCal.Calendar
17
+ end
18
+
19
+ it "should have a tz_source of 'TZ_INFO" do
20
+ @it.tz_source.should == "TZINFO"
21
+ end
22
+
23
+ it "should export a product id with an X-RICAL-TZSOURCE parameter of TZINFO" do
24
+ @it.export.should match(%r{\nPRODID;X-RICAL-TZSOURCE=TZINFO:-//com.denhaven2/NONSGML ri_cal gem//EN\n})
25
+ end
26
+ end
27
+
28
+ context "an imported instance with a tzinfo source" do
29
+ before(:each) do
30
+ @it = RiCal.parse_string("BEGIN:VCALENDAR\nPRODID;X-RICAL-TZSOURCE=TZINFO:-\/\/com.denhaven2\/NONSGML ri_cal gem\/\/EN\nCALSCALE:GREGORIAN\nVERSION:2.0\nEND:VCALENDAR\n").first
31
+ end
32
+
33
+ it "should have a tz_source of 'TZ_INFO" do
34
+ @it.tz_source.should == "TZINFO"
35
+ end
36
+
37
+ it "should export a product id with an X-RICAL-TZSOURCE parameter of TZINFO" do
38
+ @it.export.should match(%r{\nPRODID;X-RICAL-TZSOURCE=TZINFO:-//com.denhaven2/NONSGML ri_cal gem//EN\n})
39
+ end
40
+ end
41
+
42
+ context "an imported instance without a tzinfo source" do
43
+ before(:each) do
44
+ @it = RiCal.parse_string("BEGIN:VCALENDAR\nPRODID:-//Apple Inc.//iCal 3.0//EN\nEND:VCALENDAR\n").first
45
+ end
46
+
47
+ it "should have a tz_source of nil" do
48
+ @it.tz_source.should be_nil
49
+ end
50
+
51
+ it "should export not export a product id with an X-RICAL-TZSOURCE parameter of TZINFO" do
52
+ @it.export.should_not match(%r{X-RICAL-TZSOURCE=TZINFO:})
53
+ end
54
+ end
55
+
56
+ describe RiCal::Component::Calendar::FoldingStream do
57
+ before(:each) do
58
+ @it = RiCal::Component::Calendar::FoldingStream.new(nil)
59
+ end
60
+
61
+ describe "#utf_safe_split" do
62
+ it "should properly split an ascii string" do
63
+ @it.utf8_safe_split("abcdef", 3).should == ["abc", "def"]
64
+ end
65
+
66
+ it "should return a nil remainder if the string has less than n characters" do
67
+ @it.utf8_safe_split("a",2).should == ["a", nil]
68
+ end
69
+
70
+ it "should return a nil remainder if the string has exactly n characters" do
71
+ @it.utf8_safe_split("ab",2).should == ["ab", nil]
72
+ end
73
+
74
+ it "should not split a 2-byte utf character" do
75
+ @it.utf8_safe_split("Café", 3).should == ["Caf", "é"]
76
+ @it.utf8_safe_split("Café", 4).should == ["Caf", "é"]
77
+ @it.utf8_safe_split("Café", 5).should == ["Café", nil]
78
+ end
79
+
80
+ it "should not split a 3-byte utf character" do
81
+ @it.utf8_safe_split("Prix €200", 5).should == ["Prix ", "€200"]
82
+ @it.utf8_safe_split("Prix €200", 6).should == ["Prix ", "€200"]
83
+ @it.utf8_safe_split("Prix €200", 7).should == ["Prix ", "€200"]
84
+ @it.utf8_safe_split("Prix €200", 8).should == ["Prix €", "200"]
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,718 @@
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])
5
+
6
+ describe RiCal::Component::Event do
7
+
8
+ context ".finish_time" do
9
+ it "should be the end of the start day for an event with a date dtstart and no dtend or duration" do
10
+ @it = RiCal.Event do |evt|
11
+ evt.dtstart = "20090704"
12
+ end
13
+ @it.finish_time.should == DateTime.parse("20090704T235959")
14
+ end
15
+
16
+ it "should be the end of the end day for an event with a date dtstart and a dtend" do
17
+ @it = RiCal.Event do |evt|
18
+ evt.dtstart = "20090704"
19
+ evt.dtend = "20090706"
20
+ end
21
+ @it.finish_time.should == DateTime.parse("20090706T235959")
22
+ end
23
+
24
+ it "should be the start time for an event with a datetime dtstart and no dtend or duration" do
25
+ @it = RiCal.Event do |evt|
26
+ evt.dtstart = "20090704T013000Z"
27
+ end
28
+ @it.finish_time.should == DateTime.parse("20090704T013000Z")
29
+ end
30
+
31
+ it "should be the end time for an event with a datetime dtend" do
32
+ @it = RiCal.Event do |evt|
33
+ evt.dtstart = "20090704"
34
+ evt.dtend = "20090706T120000"
35
+ end
36
+ @it.finish_time.should == DateTime.parse("20090706T120000")
37
+ end
38
+
39
+ it "should be the end time for an event with a datetime dtstart and a duration" do
40
+ @it = RiCal.Event do |evt|
41
+ evt.dtstart = "20090704T120000Z"
42
+ evt.duration = "PT1H30M"
43
+ end
44
+ @it.finish_time.should == DateTime.parse("20090704T133000Z")
45
+ end
46
+
47
+ it "should uset the timezone of dtstart when event has a duration" do
48
+ @it = RiCal.Event do |evt|
49
+ evt.dtstart = "TZID=Australia/Sydney:20090712T200000"
50
+ evt.duration = "PT1H"
51
+ end
52
+ @it.finish_time.should == DateTime.parse("2009-07-12T21:00:00+10:00")
53
+ end
54
+ end
55
+
56
+ context ".before_range?" do
57
+ context "with a Date dtstart and no dtend" do
58
+ before(:each) do
59
+ @it = RiCal.Event do |evt|
60
+ evt.dtstart = "20090704"
61
+ end
62
+ end
63
+
64
+ it "should be false if the range start is a date before the start date" do
65
+ @it.before_range?([Date.parse("20090703"), :anything]).should_not be
66
+ end
67
+
68
+ it "should be false if the range start is the start date" do
69
+ @it.before_range?([Date.parse("20090704"), :anything]).should_not be
70
+ end
71
+
72
+ it "should be true if the range start is a date after the start date" do
73
+ @it.before_range?([Date.parse("20090705"), :anything]).should be
74
+ end
75
+ end
76
+
77
+ context "with a Date dtstart and date dtend" do
78
+ before(:each) do
79
+ @it = RiCal.Event do |evt|
80
+ evt.dtstart = "20090704"
81
+ evt.dtend = "20090706"
82
+ end
83
+ end
84
+
85
+ it "should be false if the range start is a date before the end date" do
86
+ @it.before_range?([Date.parse("20090705"), :anything]).should_not be
87
+ end
88
+
89
+ it "should be false if the range start is the end date" do
90
+ @it.before_range?([Date.parse("20090706"), :anything]).should_not be
91
+ end
92
+
93
+ it "should be true if the range start is a date after the end date" do
94
+ @it.before_range?([Date.parse("20090707"), :anything]).should be
95
+ end
96
+ end
97
+ end
98
+
99
+ context "bug report from Noboyuki Tomizawa" do
100
+ before(:each) do
101
+
102
+ @it = RiCal.Calendar do |cal|
103
+ cal.event do |event|
104
+ event.description = "test"
105
+ event.dtstart = "TZID=Asia/Tokyo:20090530T123000"
106
+ event.dtend = "TZID=Asia/Tokyo:20090530T123001"
107
+ end
108
+ end
109
+ end
110
+
111
+ it "should not fail" do
112
+ lambda {@it.export}.should_not raise_error
113
+ end
114
+ end
115
+
116
+ context "rdate property methods" do
117
+ before(:each) do
118
+ @event = RiCal.Event do
119
+ rdate "20090101"
120
+ end
121
+ end
122
+
123
+ context "#rdate=" do
124
+
125
+ it "should accept a single Date and replace the existing rdate" do
126
+ @event.rdate = Date.parse("20090102")
127
+ @event.rdate.should == [[Date.parse("20090102")]]
128
+ end
129
+
130
+ it "should accept a single rfc2445 date format string and replace the existing rdate" do
131
+ @event.rdate = "20090102"
132
+ @event.rdate.should == [[Date.parse("20090102")]]
133
+ end
134
+
135
+ it "should accept a single DateTime and replace the existing rdate" do
136
+ @event.rdate = DateTime.parse("20090102T012345")
137
+ @event.rdate.should == [[DateTime.parse("20090102T012345")]]
138
+ end
139
+
140
+ it "should accept a single Time and replace the existing rdate" do
141
+ ::RiCal::PropertyValue::DateTime.default_tzid = 'UTC'
142
+ @event.rdate = Time.local(2009, 1, 2, 1, 23, 45)
143
+ @event.rdate.should == [[result_time_in_zone(2009, 1, 2, 1, 23, 45, "UTC")]]
144
+ end
145
+
146
+ it "should accept a single rfc2445 date-time format string and replace the existing rdate" do
147
+ @event.rdate = "20090102T012345"
148
+ @event.rdate.should == [[DateTime.parse("20090102T012345")]]
149
+ end
150
+
151
+ it "should accept a tzid prefixed rfc2445 date-time format string and replace the existing rdate" do
152
+ @event.rdate = "TZID=America/New_York:20090102T012345"
153
+ @event.rdate.should == [[result_time_in_zone(2009, 1, 2, 1, 23, 45, "America/New_York")]]
154
+ end
155
+
156
+ end
157
+
158
+ end
159
+
160
+ context "comment property methods" do
161
+ before(:each) do
162
+ @event = RiCal.Event
163
+ @event.comment = "Comment"
164
+ end
165
+
166
+ context "#comment=" do
167
+ it "should result in a single comment for the event" do
168
+ @event.comment.should == ["Comment"]
169
+ end
170
+
171
+ it "should replace existing comments" do
172
+ @event.comment = "Replacement"
173
+ @event.comment.should == ["Replacement"]
174
+ end
175
+ end
176
+
177
+ context "#comments=" do
178
+ it "should result in a multiple comments for the event replacing existing comments" do
179
+ @event.comments = "New1", "New2"
180
+ @event.comment.should == ["New1", "New2"]
181
+ end
182
+ end
183
+
184
+ context "#add_comment" do
185
+ it "should add a single comment" do
186
+ @event.add_comment "New1"
187
+ @event.comment.should == ["Comment", "New1"]
188
+ end
189
+ end
190
+
191
+ context "#add_comments" do
192
+ it "should add multiple comments" do
193
+ @event.add_comments "New1", "New2"
194
+ @event.comment.should == ["Comment", "New1", "New2"]
195
+ end
196
+ end
197
+
198
+ context "#remove_comment" do
199
+ it "should remove a single comment" do
200
+ @event.add_comment "New1"
201
+ @event.remove_comment "Comment"
202
+ @event.comment.should == ["New1"]
203
+ end
204
+ end
205
+
206
+ context "#remove_comments" do
207
+ it "should remove multiple comments" do
208
+ @event.add_comments "New1", "New2", "New3"
209
+ @event.remove_comments "New2", "Comment"
210
+ @event.comment.should == ["New1", "New3"]
211
+ end
212
+ end
213
+ end
214
+
215
+ context ".dtstart=" do
216
+ before(:each) do
217
+ @event = RiCal.Event
218
+ end
219
+
220
+ context "with a datetime only string" do
221
+ before(:each) do
222
+ @event.dtstart = "20090514T202400"
223
+ @it = @event.dtstart
224
+ end
225
+
226
+ it "should interpret it as the correct date-time" do
227
+ @it.should == DateTime.civil(2009, 5, 14, 20, 24, 00, Rational(0,24))
228
+ end
229
+
230
+ it "should interpret it as a floating date" do
231
+ @it.tzid.should == :floating
232
+ end
233
+ end
234
+
235
+ context "with a TZID and datetime string" do
236
+ before(:each) do
237
+ @event.dtstart = "TZID=America/New_York:20090514T202400"
238
+ @it = @event.dtstart
239
+ end
240
+
241
+ it "should interpret it as the correct date-time" do
242
+ @it.should == result_time_in_zone(2009, 5, 14, 20, 24, 00, "America/New_York")
243
+ end
244
+
245
+ it "should set the tzid to America/New_York" do
246
+ @it.tzid.should == "America/New_York"
247
+ end
248
+ end
249
+
250
+ context "with a zulu datetime only string" do
251
+ before(:each) do
252
+ @event.dtstart = "20090514T202400Z"
253
+ @it = @event.dtstart
254
+ end
255
+
256
+ it "should interpret it as the correct date-time" do
257
+ @it.should == DateTime.civil(2009, 5, 14, 20, 24, 00, Rational(0,24))
258
+ end
259
+
260
+ it "should set the tzid to UTC" do
261
+ @it.tzid.should == "UTC"
262
+ end
263
+ end
264
+
265
+ context "with a date string" do
266
+ before(:each) do
267
+ @event.dtstart = "20090514"
268
+ @it = @event.dtstart
269
+ end
270
+
271
+ it "should interpret it as the correct date-time" do
272
+ @it.should == Date.parse("14 May 2009")
273
+ end
274
+ end
275
+ end
276
+
277
+ context ".entity_name" do
278
+ it "should be VEVENT" do
279
+ RiCal::Component::Event.entity_name.should == "VEVENT"
280
+ end
281
+ end
282
+
283
+ context "with an rrule" do
284
+ before(:each) do
285
+ @it = RiCal::Component::Event.parse_string("BEGIN:VEVENT\nRRULE:FREQ=DAILY\nEND:VEVENT").first
286
+ end
287
+
288
+ it "should have an array of rrules" do
289
+ @it.rrule.should be_kind_of(Array)
290
+ end
291
+ end
292
+
293
+ context ".start_time" do
294
+
295
+ it "should be nil if there is no dtstart property" do
296
+ RiCal.Event.start_time.should be_nil
297
+ end
298
+
299
+ it "should be the same as dtstart for a date time" do
300
+ event = RiCal.Event {|e| e.dtstart = "20090525T151900"}
301
+ event.start_time.should == DateTime.civil(2009,05,25,15,19,0,0)
302
+ end
303
+
304
+ it "should be the start of the day of dtstart for a date" do
305
+ event = RiCal.Event {|e| e.dtstart = "20090525"}
306
+ event.start_time.should == DateTime.civil(2009,05,25,0,0,0,0)
307
+ end
308
+ end
309
+
310
+ context ".finish_time" do
311
+ before(:each) do
312
+ @event = RiCal.Event {|e| e.dtstart = "20090525T151900"}
313
+ end
314
+
315
+ context "with a given dtend" do
316
+ it "should be the same as dtend for a date time" do
317
+ @event.dtend = "20090525T161900"
318
+ @event.finish_time.should == DateTime.civil(2009,05,25,16,19,0,0)
319
+ end
320
+
321
+
322
+ end
323
+
324
+ context "with no dtend" do
325
+ context "and a duration" do
326
+ it "should be the dtstart plus the duration" do
327
+ @event.duration = "+PT1H"
328
+ @event.finish_time.should == DateTime.civil(2009,5,25,16,19,0,0)
329
+ end
330
+ end
331
+
332
+ context "and no duration" do
333
+ context "when the dtstart is not set" do
334
+ before(:each) do
335
+ @event.dtstart_property = nil
336
+ end
337
+
338
+ it "should be nil" do
339
+ @event.finish_time.should be_nil
340
+ end
341
+ end
342
+ context "when the dstart is a datetime" do
343
+ # For cases where a "VEVENT" calendar component
344
+ # specifies a "DTSTART" property with a DATE-TIME data type but no
345
+ # "DTEND" property, the event ends on the same calendar date and time
346
+ # of day specified by the "DTSTART" property. RFC 2445 p 53
347
+ it "should be the same as start_time" do
348
+ @event.finish_time.should == @event.start_time
349
+ end
350
+ end
351
+ context "when the dtstart is a date" do
352
+ # For cases where a "VEVENT" calendar component specifies a "DTSTART" property with a DATE data type
353
+ # but no "DTEND" property, the events non-inclusive end is the end of the calendar date specified by
354
+ # the "DTSTART" property. RFC 2445 p 53
355
+
356
+ it "should be the end of the same day as start_time" do
357
+ @event.dtstart = "20090525"
358
+ @event.finish_time.should == DateTime.civil(2009,5,25,23,59,59,0)
359
+ end
360
+ end
361
+ end
362
+ end
363
+
364
+ end
365
+
366
+ context ".zulu_occurrence_range_start_time" do
367
+
368
+ it "should be nil if there is no dtstart property" do
369
+ RiCal.Event.zulu_occurrence_range_start_time.should be_nil
370
+ end
371
+
372
+ it "should be the utc equivalent of dtstart for a date time" do
373
+ event = RiCal.Event {|e| e.dtstart = "TZID=America/New_York:20090525T151900"}
374
+ event.zulu_occurrence_range_start_time.should == DateTime.civil(2009,05,25,19,19,0,0)
375
+ end
376
+
377
+ it "should be the utc time of the start of the day of dtstart in the earliest timezone for a date" do
378
+ event = RiCal.Event {|e| e.dtstart = "20090525"}
379
+ result = event.zulu_occurrence_range_start_time
380
+ result.should == DateTime.civil(2009,05,24,12,0,0,0)
381
+ end
382
+
383
+ it "should be the utc time of the dtstart in the earliest timezone if dtstart is a floating datetime" do
384
+ event = RiCal.Event {|e| e.dtstart = "20090525T151900"}
385
+ event.zulu_occurrence_range_start_time.should == DateTime.civil(2009,05,25,3,19,0,0)
386
+ end
387
+ end
388
+
389
+ context ".zulu_occurrence_range_finish_time" do
390
+ before(:each) do
391
+ @event = RiCal.Event {|e| e.dtstart = "TZID=America/New_York:20090525T151900"}
392
+ end
393
+
394
+ context "with a given dtend" do
395
+ it "should be the utc equivalent of dtend if dtend is a date time" do
396
+ @event.dtend = "TZID=America/New_York:20090525T161900"
397
+ @event.zulu_occurrence_range_finish_time.should == DateTime.civil(2009,05,25, 20,19,0,0)
398
+ end
399
+ end
400
+
401
+ context "with no dtend" do
402
+ context "and a duration" do
403
+ it "should be the dtstart plus the duration" do
404
+ @event.duration = "+PT1H"
405
+ @event.zulu_occurrence_range_finish_time.should == DateTime.civil(2009,5,25,20 ,19,0,0)
406
+ end
407
+ end
408
+
409
+ context "and no duration" do
410
+ context "when the dtstart is not set" do
411
+ before(:each) do
412
+ @event.dtstart_property = nil
413
+ end
414
+
415
+ it "should be nil" do
416
+ @event.zulu_occurrence_range_finish_time.should be_nil
417
+ end
418
+ end
419
+
420
+ context "when the dstart is a datetime" do
421
+
422
+ it "should be the same as start_time" do
423
+ @event.zulu_occurrence_range_finish_time.should == @event.zulu_occurrence_range_start_time
424
+ end
425
+ end
426
+
427
+ context "when the dtstart is a date" do
428
+ it "should be the utc of end of the same day as start_time in the westermost time zone" do
429
+ @event.dtstart = "20090525"
430
+ @event.zulu_occurrence_range_finish_time.should == DateTime.civil(2009,5,26,11,59,59,0)
431
+ end
432
+ end
433
+ end
434
+ end
435
+ end
436
+
437
+ context "description property" do
438
+ before(:each) do
439
+ @ical_desc = "posted by Joyce per Zan\\nASheville\\, Rayan's Restauratn\\, Biltm\n ore Square"
440
+ @ruby_desc = "posted by Joyce per Zan\nASheville, Rayan's Restauratn, Biltmore Square"
441
+ @it = RiCal::Component::Event.parse_string("BEGIN:VEVENT\nDESCRIPTION:#{@ical_desc}\nEND:VEVENT").first
442
+ end
443
+
444
+ it "should product the converted ruby value" do
445
+ @it.description.should == @ruby_desc
446
+ end
447
+
448
+ it "should produce escaped text for ical" do
449
+ @it.description = "This is a\nnew description, yes; it is"
450
+ @it.description_property.value.should == 'This is a\nnew description\, yes\; it is'
451
+ end
452
+
453
+ end
454
+
455
+ context "with both dtend and duration specified" do
456
+ before(:each) do
457
+ @it = RiCal::Component::Event.parse_string("BEGIN:VEVENT\nDTEND:19970903T190000Z\nDURATION:H1\nEND:VEVENT").first
458
+ end
459
+
460
+ it "should be invalid" do
461
+ @it.should_not be_valid
462
+ end
463
+ end
464
+
465
+ context "with a duration property" do
466
+ before(:each) do
467
+ @it = RiCal::Component::Event.parse_string("BEGIN:VEVENT\nDURATION:H1\nEND:VEVENT").first
468
+ end
469
+
470
+ it "should have a duration property" do
471
+ @it.duration_property.should be
472
+ end
473
+
474
+ it "should have a duration of 1 Hour" do
475
+ @it.duration_property.value.should == "H1"
476
+ end
477
+
478
+ it "should reset the duration property if the dtend property is set" do
479
+ @it.dtend_property = "19970101T123456".to_ri_cal_date_time_value
480
+ @it.duration_property.should be_nil
481
+ end
482
+
483
+ it "should reset the duration property if the dtend ruby value is set" do
484
+ @it.dtend = "19970101"
485
+ @it.duration_property.should == nil
486
+ end
487
+ end
488
+
489
+ context "with a dtend property" do
490
+ before(:each) do
491
+ @it = RiCal::Component::Event.parse_string("BEGIN:VEVENT\nDTEND:19970903T190000Z\nEND:VEVENT").first
492
+ end
493
+
494
+ it "should have a duration property" do
495
+ @it.dtend_property.should be
496
+ end
497
+
498
+ it "should reset the dtend property if the duration property is set" do
499
+ @it.duration_property = "PT1H".to_ri_cal_duration_value
500
+ @it.dtend_property.should be_nil
501
+ end
502
+
503
+ it "should reset the dtend property if the duration ruby value is set" do
504
+ @it.duration = "PT1H".to_ri_cal_duration_value
505
+ @it.dtend_property.should be_nil
506
+ end
507
+ end
508
+
509
+ context "with a nested alarm component" do
510
+ before(:each) do
511
+ @it = RiCal::Component::Event.parse_string("BEGIN:VEVENT\nDTEND:19970903T190000Z\n\nBEGIN:VALARM\nEND:VALARM\nEND:VEVENT").first
512
+ end
513
+
514
+ it "should have one alarm" do
515
+ @it.alarms.length.should == 1
516
+ end
517
+
518
+ it "which should be an Alarm component" do
519
+ @it.alarms.first.should be_kind_of(RiCal::Component::Alarm)
520
+ end
521
+ end
522
+
523
+ context ".export" do
524
+ require 'rubygems'
525
+ require 'tzinfo'
526
+
527
+ def date_time_with_tzinfo_zone(date_time, timezone="America/New_York")
528
+ date_time.dup.set_tzid(timezone)
529
+ end
530
+
531
+ # Undo the effects of RFC2445 line folding
532
+ def unfold(string)
533
+ string.gsub("\n ", "")
534
+ end
535
+
536
+ before(:each) do
537
+ cal = RiCal.Calendar
538
+ @it = RiCal::Component::Event.new(cal)
539
+ end
540
+
541
+ it "should cause a VTIMEZONE to be included for a dtstart with a local timezone" do
542
+ @it.dtstart = date_time_with_tzinfo_zone(DateTime.parse("April 22, 2009 17:55"), "America/New_York")
543
+ unfold(@it.export).should match(/BEGIN:VTIMEZONE\nTZID;X-RICAL-TZSOURCE=TZINFO:America\/New_York\n/)
544
+ end
545
+
546
+ it "should properly format dtstart with a UTC date-time" do
547
+ @it.dtstart = DateTime.parse("April 22, 2009 1:23:45").set_tzid("UTC")
548
+ unfold(@it.export).should match(/^DTSTART;VALUE=DATE-TIME:20090422T012345Z$/)
549
+ end
550
+
551
+ it "should properly format dtstart with a floating date-time" do
552
+ @it.dtstart = DateTime.parse("April 22, 2009 1:23:45").with_floating_timezone
553
+ unfold(@it.export).should match(/^DTSTART;VALUE=DATE-TIME:20090422T012345$/)
554
+ end
555
+
556
+ it "should properly format dtstart with a local time zone" do
557
+ @it.dtstart = date_time_with_tzinfo_zone(DateTime.parse("April 22, 2009 17:55"), "America/New_York")
558
+ unfold(@it.export).should match(/^DTSTART;TZID=America\/New_York;VALUE=DATE-TIME:20090422T175500$/)
559
+ end
560
+
561
+ it "should properly format dtstart with a date" do
562
+ @it.dtstart = Date.parse("April 22, 2009")
563
+ unfold(@it.export).should match(/^DTSTART;VALUE=DATE:20090422$/)
564
+ end
565
+
566
+ it "should properly fold on export when the description contains a carriage return" do
567
+ @it.description = "Weather report looks nice, 80 degrees and partly cloudy, so following Michael's suggestion, let's meet at the food court at Crossroads:\n\rhttp://www.shopcrossroadsplaza.c...\n"
568
+ export_string = @it.export
569
+ export_string.should match(%r(^DESCRIPTION:Weather report looks nice\\, 80 degrees and partly cloudy\\, so$))
570
+ export_string.should match(%r(^ following Michael's suggestion\\, let's meet at the food court at Crossr$))
571
+ export_string.should match(%r(^ oads:\\nhttp://www\.shopcrossroadsplaza.c\.\.\.\\n$))
572
+ end
573
+
574
+ it "should properly fold on export when the description contains multi-byte UTF-8 Characters" do
575
+ @it.description = "Juin 2009 <<Alliance Francaise Reunion>> lieu Café périferôl"
576
+ export_string = @it.export
577
+ export_string.should match(%r(^DESCRIPTION:Juin 2009 <<Alliance Francaise Reunion>> lieu Café périfer$))
578
+ export_string.should match(%r(^ ôl$))
579
+ end
580
+ end
581
+
582
+ if RiCal::TimeWithZone
583
+ context "with ActiveSupport loaded" do
584
+
585
+ context "an event with an timezoned exdate" do
586
+ before(:each) do
587
+ @old_timezone = Time.zone
588
+ Time.zone = "America/New_York"
589
+ @exception_date_time = Time.zone.local(2009, 5, 19, 11, 13)
590
+ cal = RiCal.Calendar do |cal|
591
+ cal.event do |event|
592
+ event.add_exdate @exception_date_time
593
+ end
594
+ end
595
+ @event = cal.events.first
596
+ end
597
+
598
+ after(:each) do
599
+ Time.zone = @old_timezone
600
+ end
601
+
602
+ it "should pickup the timezone in the exdate property" do
603
+ @event.exdate.first.first.tzid.should == "America/New_York"
604
+ end
605
+
606
+ it "should have the timezone in the ical representation of the exdate property" do
607
+ @event.exdate_property.first.to_s.should match(%r{;TZID=America/New_York[:;]})
608
+ end
609
+ end
610
+
611
+ context "An event in a non-tzinfo source calendar" do
612
+ before(:each) do
613
+ cals = RiCal.parse_string <<ENDCAL
614
+ BEGIN:VCALENDAR
615
+ X-WR-TIMEZONE:America/New_York
616
+ PRODID:-//Apple Inc.//iCal 3.0//EN
617
+ CALSCALE:GREGORIAN
618
+ BEGIN:VTIMEZONE
619
+ TZID:US/Eastern
620
+ BEGIN:DAYLIGHT
621
+ TZOFFSETFROM:-0500
622
+ TZOFFSETTO:-0400
623
+ DTSTART:20070311T020000
624
+ RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
625
+ TZNAME:EDT
626
+ END:DAYLIGHT
627
+ BEGIN:STANDARD
628
+ TZOFFSETFROM:-0400
629
+ TZOFFSETTO:-0500
630
+ DTSTART:20071104T020000
631
+ RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
632
+ TZNAME:EST
633
+ END:STANDARD
634
+ END:VTIMEZONE
635
+ BEGIN:VEVENT
636
+ SEQUENCE:5
637
+ TRANSP:OPAQUE
638
+ UID:00481E53-9258-4EA7-9F8D-947D3041A3F2
639
+ DTSTART;TZID=US/Eastern:20090224T090000
640
+ DTSTAMP:20090225T000908Z
641
+ SUMMARY:Test Event
642
+ CREATED:20090225T000839Z
643
+ DTEND;TZID=US/Eastern:20090224T100000
644
+ RRULE:FREQ=DAILY;INTERVAL=1;UNTIL=20090228T045959Z
645
+ END:VEVENT
646
+ END:VCALENDAR
647
+ ENDCAL
648
+ @event = cals.first.events.first
649
+ end
650
+
651
+ it "should produce a DateTime for dtstart" do
652
+ @event.dtstart.should be_instance_of(DateTime)
653
+ end
654
+ end
655
+
656
+ context "An event starting in Paris and ending in New York" do
657
+
658
+ before(:each) do
659
+ @start = Time.now.utc.in_time_zone("Europe/Paris")
660
+ @finish = Time.now.utc.in_time_zone("America/New_York")
661
+ cal = RiCal.Calendar do |ical|
662
+ ical.event do |ievent|
663
+ ievent.dtstart @start
664
+ ievent.dtend @finish
665
+ end
666
+ end
667
+ @event = cal.events.first
668
+ end
669
+
670
+ it "should have the right time zone for dtstart" do
671
+ @event.dtstart.tzid.should == "Europe/Paris"
672
+ end
673
+
674
+ it "should produce a TimeWithZone for dtstart" do
675
+ @event.dtstart.should be_instance_of(RiCal::TimeWithZone)
676
+ end
677
+
678
+ # ActiveRecord::TimeWithZone doesn't implement == as expected
679
+ it "should produce a dtstart which looks like the provided value" do
680
+ @event.dtstart.to_s.should == @start.to_s
681
+ end
682
+
683
+ it "should have the right time zone for dtend" do
684
+ @event.dtend.tzid.should == "America/New_York"
685
+ end
686
+
687
+ it "should produce a TimeWithZone for dtend" do
688
+ @event.dtend.should be_instance_of(RiCal::TimeWithZone)
689
+ end
690
+
691
+ # ActiveRecord::TimeWithZone doesn't implement == as expected
692
+ it "should produce a dtend which looks like the provided value" do
693
+ @event.dtend.to_s.should == @finish.to_s
694
+ end
695
+ end
696
+ end
697
+ end
698
+
699
+ context "An event with a floating start" do
700
+
701
+ before(:each) do
702
+ cal = RiCal.Calendar do |ical|
703
+ ical.event do |ievent|
704
+ ievent.dtstart "20090530T120000"
705
+ end
706
+ end
707
+ @event = cal.events.first
708
+ end
709
+
710
+ it "should produce a DateTime for dtstart" do
711
+ @event.dtstart.should be_instance_of(DateTime)
712
+ end
713
+
714
+ it "should have a floating dtstart" do
715
+ @event.dtstart.should have_floating_timezone
716
+ end
717
+ end
718
+ end