ri_cal 0.5.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 (130) hide show
  1. data/History.txt +45 -0
  2. data/Manifest.txt +129 -0
  3. data/README.txt +394 -0
  4. data/Rakefile +31 -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 +144 -0
  21. data/lib/ri_cal/component.rb +247 -0
  22. data/lib/ri_cal/component/alarm.rb +21 -0
  23. data/lib/ri_cal/component/calendar.rb +219 -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/t_z_info_timezone.rb +123 -0
  28. data/lib/ri_cal/component/timezone.rb +196 -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 +53 -0
  32. data/lib/ri_cal/component/todo.rb +43 -0
  33. data/lib/ri_cal/core_extensions.rb +6 -0
  34. data/lib/ri_cal/core_extensions/array.rb +7 -0
  35. data/lib/ri_cal/core_extensions/array/conversions.rb +15 -0
  36. data/lib/ri_cal/core_extensions/date.rb +13 -0
  37. data/lib/ri_cal/core_extensions/date/conversions.rb +61 -0
  38. data/lib/ri_cal/core_extensions/date_time.rb +15 -0
  39. data/lib/ri_cal/core_extensions/date_time/conversions.rb +50 -0
  40. data/lib/ri_cal/core_extensions/object.rb +8 -0
  41. data/lib/ri_cal/core_extensions/object/conversions.rb +20 -0
  42. data/lib/ri_cal/core_extensions/string.rb +8 -0
  43. data/lib/ri_cal/core_extensions/string/conversions.rb +63 -0
  44. data/lib/ri_cal/core_extensions/time.rb +13 -0
  45. data/lib/ri_cal/core_extensions/time/calculations.rb +153 -0
  46. data/lib/ri_cal/core_extensions/time/conversions.rb +61 -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 +88 -0
  49. data/lib/ri_cal/floating_timezone.rb +32 -0
  50. data/lib/ri_cal/invalid_property_value.rb +8 -0
  51. data/lib/ri_cal/invalid_timezone_identifer.rb +20 -0
  52. data/lib/ri_cal/occurrence_enumerator.rb +206 -0
  53. data/lib/ri_cal/occurrence_period.rb +17 -0
  54. data/lib/ri_cal/parser.rb +138 -0
  55. data/lib/ri_cal/properties/alarm.rb +390 -0
  56. data/lib/ri_cal/properties/calendar.rb +164 -0
  57. data/lib/ri_cal/properties/event.rb +1526 -0
  58. data/lib/ri_cal/properties/freebusy.rb +594 -0
  59. data/lib/ri_cal/properties/journal.rb +1240 -0
  60. data/lib/ri_cal/properties/timezone.rb +151 -0
  61. data/lib/ri_cal/properties/timezone_period.rb +416 -0
  62. data/lib/ri_cal/properties/todo.rb +1562 -0
  63. data/lib/ri_cal/property_value.rb +149 -0
  64. data/lib/ri_cal/property_value/array.rb +27 -0
  65. data/lib/ri_cal/property_value/cal_address.rb +11 -0
  66. data/lib/ri_cal/property_value/date.rb +175 -0
  67. data/lib/ri_cal/property_value/date_time.rb +335 -0
  68. data/lib/ri_cal/property_value/date_time/additive_methods.rb +44 -0
  69. data/lib/ri_cal/property_value/date_time/time_machine.rb +181 -0
  70. data/lib/ri_cal/property_value/date_time/timezone_support.rb +96 -0
  71. data/lib/ri_cal/property_value/duration.rb +110 -0
  72. data/lib/ri_cal/property_value/geo.rb +11 -0
  73. data/lib/ri_cal/property_value/integer.rb +12 -0
  74. data/lib/ri_cal/property_value/occurrence_list.rb +144 -0
  75. data/lib/ri_cal/property_value/period.rb +82 -0
  76. data/lib/ri_cal/property_value/recurrence_rule.rb +145 -0
  77. data/lib/ri_cal/property_value/recurrence_rule/enumeration_support_methods.rb +97 -0
  78. data/lib/ri_cal/property_value/recurrence_rule/enumerator.rb +79 -0
  79. data/lib/ri_cal/property_value/recurrence_rule/initialization_methods.rb +148 -0
  80. data/lib/ri_cal/property_value/recurrence_rule/negative_setpos_enumerator.rb +53 -0
  81. data/lib/ri_cal/property_value/recurrence_rule/numbered_span.rb +31 -0
  82. data/lib/ri_cal/property_value/recurrence_rule/occurence_incrementer.rb +793 -0
  83. data/lib/ri_cal/property_value/recurrence_rule/recurring_day.rb +131 -0
  84. data/lib/ri_cal/property_value/recurrence_rule/recurring_month_day.rb +60 -0
  85. data/lib/ri_cal/property_value/recurrence_rule/recurring_numbered_week.rb +33 -0
  86. data/lib/ri_cal/property_value/recurrence_rule/recurring_year_day.rb +49 -0
  87. data/lib/ri_cal/property_value/recurrence_rule/validations.rb +125 -0
  88. data/lib/ri_cal/property_value/text.rb +40 -0
  89. data/lib/ri_cal/property_value/uri.rb +11 -0
  90. data/lib/ri_cal/property_value/utc_offset.rb +33 -0
  91. data/lib/ri_cal/required_timezones.rb +55 -0
  92. data/ri_cal.gemspec +49 -0
  93. data/sample_ical_files/from_ical_dot_app/test1.ics +38 -0
  94. data/script/console +10 -0
  95. data/script/destroy +14 -0
  96. data/script/generate +14 -0
  97. data/script/txt2html +71 -0
  98. data/spec/ri_cal/component/alarm_spec.rb +12 -0
  99. data/spec/ri_cal/component/calendar_spec.rb +54 -0
  100. data/spec/ri_cal/component/event_spec.rb +601 -0
  101. data/spec/ri_cal/component/freebusy_spec.rb +12 -0
  102. data/spec/ri_cal/component/journal_spec.rb +37 -0
  103. data/spec/ri_cal/component/t_z_info_timezone_spec.rb +36 -0
  104. data/spec/ri_cal/component/timezone_spec.rb +218 -0
  105. data/spec/ri_cal/component/todo_spec.rb +112 -0
  106. data/spec/ri_cal/component_spec.rb +224 -0
  107. data/spec/ri_cal/core_extensions/string/conversions_spec.rb +78 -0
  108. data/spec/ri_cal/core_extensions/time/calculations_spec.rb +188 -0
  109. data/spec/ri_cal/core_extensions/time/week_day_predicates_spec.rb +45 -0
  110. data/spec/ri_cal/occurrence_enumerator_spec.rb +573 -0
  111. data/spec/ri_cal/parser_spec.rb +303 -0
  112. data/spec/ri_cal/property_value/date_spec.rb +53 -0
  113. data/spec/ri_cal/property_value/date_time_spec.rb +383 -0
  114. data/spec/ri_cal/property_value/duration_spec.rb +126 -0
  115. data/spec/ri_cal/property_value/occurrence_list_spec.rb +72 -0
  116. data/spec/ri_cal/property_value/period_spec.rb +49 -0
  117. data/spec/ri_cal/property_value/recurrence_rule/recurring_year_day_spec.rb +21 -0
  118. data/spec/ri_cal/property_value/recurrence_rule_spec.rb +1814 -0
  119. data/spec/ri_cal/property_value/text_spec.rb +25 -0
  120. data/spec/ri_cal/property_value/utc_offset_spec.rb +48 -0
  121. data/spec/ri_cal/property_value_spec.rb +125 -0
  122. data/spec/ri_cal/required_timezones_spec.rb +67 -0
  123. data/spec/ri_cal_spec.rb +53 -0
  124. data/spec/spec.opts +4 -0
  125. data/spec/spec_helper.rb +46 -0
  126. data/tasks/gem_loader/load_active_support.rb +3 -0
  127. data/tasks/gem_loader/load_tzinfo_gem.rb +2 -0
  128. data/tasks/ri_cal.rake +410 -0
  129. data/tasks/spec.rake +50 -0
  130. metadata +221 -0
@@ -0,0 +1,303 @@
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::Parser do
6
+
7
+ def self.describe_property(entity_name, prop_name, params, value, type = RiCal::PropertyValue::Text)
8
+ describe_named_property(entity_name, prop_name, prop_name, params, value, false, type)
9
+ end
10
+
11
+ def self.describe_multi_property(entity_name, prop_name, params, value, type = RiCal::PropertyValue::Text)
12
+ describe_named_property(entity_name, prop_name, prop_name, params, value, true, type)
13
+ end
14
+
15
+ def self.describe_named_property(entity_name, prop_text, prop_name, params, value, multi, type = RiCal::PropertyValue::Text)
16
+ ruby_value_name = prop_name.tr("-", "_").downcase
17
+ ruby_prop_name = "#{prop_text.tr('-', '_').downcase}_property"
18
+ expected_ruby_value = type.convert(nil, value).ruby_value
19
+ describe "#{prop_name} with value of #{value.inspect}" do
20
+ parse_input = params.inject("BEGIN:#{entity_name.upcase}\n#{prop_text.upcase}") { |pi, assoc| "#{pi};#{assoc[0]}=#{assoc[1]}"}
21
+ parse_input = "#{parse_input}:#{value.to_rfc2445_string}\nEND:#{entity_name.upcase}"
22
+
23
+ it "should parse an event with an #{prop_text.upcase} property" do
24
+ lambda {RiCal::Parser.parse(StringIO.new(parse_input))}.should_not raise_error
25
+ end
26
+
27
+ describe "property characteristics" do
28
+ before(:each) do
29
+ @entity = RiCal::Parser.parse(StringIO.new(parse_input)).first
30
+ @prop = @entity.send(ruby_prop_name.to_sym)
31
+ if multi && Array === @prop
32
+ @prop = @prop.first
33
+ end
34
+ end
35
+
36
+ it "should be a #{type.name}" do
37
+ @prop.class.should == type
38
+ end
39
+
40
+ it "should have the right value" do
41
+ @prop.value.should == value
42
+ end
43
+
44
+ it "should make the value accessible directly" do
45
+ val = @entity.send(ruby_value_name)
46
+ val = val.first if multi && Array === val
47
+ val.should == expected_ruby_value
48
+ end
49
+
50
+ it "should have the right parameters" do
51
+ params.each do | key, value |
52
+ @prop.params[key].should == value
53
+ end
54
+ end
55
+ end
56
+
57
+ end
58
+ end
59
+
60
+ describe ".next_line" do
61
+ it "should return line by line" do
62
+ RiCal::Parser.new(StringIO.new("abc\ndef")).next_line.should == "abc"
63
+ end
64
+
65
+ it "should combine lines" do
66
+ RiCal::Parser.new(StringIO.new("abc\n def\n ghi")).next_line.should == "abcdef ghi"
67
+ end
68
+ end
69
+
70
+ describe ".separate_line" do
71
+
72
+ before(:each) do
73
+ @parser = RiCal::Parser.new
74
+ end
75
+
76
+ it "should return a hash" do
77
+ @parser.separate_line("abc;x=y;z=1,2:value").should be_kind_of(Hash)
78
+ end
79
+
80
+ it "should find the name" do
81
+ @parser.separate_line("abc;x=y;z=1,2:value")[:name].should == "abc"
82
+ end
83
+
84
+ it "should find the parameters" do
85
+ @parser.separate_line("abc;x=y;z=1,2:value")[:params].should == {"x" => "y","z" => "1,2"}
86
+ end
87
+
88
+ it "should find the value" do
89
+ @parser.separate_line("abc;x=y;z=1,2:value")[:value].should == "value"
90
+ end
91
+ end
92
+
93
+ describe ".parse" do
94
+
95
+ it "should reject a file which doesn't start with BEGIN" do
96
+ parser = RiCal::Parser.new(StringIO.new("END:VCALENDAR"))
97
+ lambda {parser.parse}.should raise_error
98
+ end
99
+
100
+ describe "parsing an event" do
101
+ it "should parse an event" do
102
+ parser = RiCal::Parser.new(StringIO.new("BEGIN:VEVENT"))
103
+ RiCal::Component::Event.should_receive(:from_parser).with(parser, nil)
104
+ parser.parse
105
+ end
106
+
107
+ it "should parse an event and return a Event" do
108
+ cal = RiCal::Parser.parse(StringIO.new("BEGIN:VEVENT\nEND:VEVENT")).first
109
+ cal.should be_kind_of(RiCal::Component::Event)
110
+ end
111
+
112
+ #RFC 2445 section 4.8.1.1 pp 77
113
+ describe_multi_property("VEVENT", "ATTACH", {"FMTTYPE" => "application/postscript"}, "FMTTYPE=application/postscript:ftp//xyzCorp.com/put/reports/r-960812.ps", RiCal::PropertyValue::Uri)
114
+
115
+ #RFC 2445 section 4.8.1.2 pp 78
116
+ describe_multi_property("VEVENT", "CATEGORIES", {"LANGUAGE" => "us-EN"}, "APPOINTMENT,EDUCATION", RiCal::PropertyValue::Array)
117
+
118
+ #RFC 2445 section 4.8.1.3 pp 79
119
+ describe_named_property("VEVENT", "CLASS", "security_class", {"X-FOO" => "BAR"}, "PUBLIC", false)
120
+
121
+ #RFC 2445 section 4.8.1.4 pp 80
122
+ describe_multi_property("VEVENT", "COMMENT", {"X-FOO" => "BAR"}, "Event comment")
123
+
124
+ #RFC 2445 section 4.8.1.5 pp 81
125
+ describe_property("VEVENT", "DESCRIPTION", {"X-FOO" => "BAR"}, "Event description")
126
+
127
+ #RFC 2445 section 4.8.1.6 pp 82
128
+ describe_property("VEVENT", "GEO", {"X-FOO" => "BAR"}, "37.386013;-122.082932", RiCal::PropertyValue::Geo)
129
+
130
+ #RFC 2445 section 4.8.1.7 pp 84
131
+ describe_property("VEVENT", "LOCATION", {"ALTREP" => "\"http://xyzcorp.com/conf-rooms/f123.vcf\""}, "Conference Room - F123, Bldg. 002")
132
+
133
+ #RFC 2445 section 4.8.1.8 PERCENT-COMPLETE does not apply to Events
134
+
135
+ #RFC 2445 section 4.8.1.9 pp 84
136
+ describe_property("VEVENT", "PRIORITY", {"X-FOO" => "BAR"}, 1, RiCal::PropertyValue::Integer)
137
+
138
+ #RFC 2445 section 4.8.1.10 pp 87
139
+ describe_multi_property("VEVENT", "RESOURCES", {"X-FOO" => "BAR"}, "Easel,Projector,VCR", RiCal::PropertyValue::Array)
140
+
141
+ #RFC 2445 section 4.8.1.11 pp 88
142
+ describe_property("VEVENT", "STATUS", {"X-FOO" => "BAR"}, "CONFIRMED")
143
+
144
+ #RFC 2445 section 4.8.1.12 pp 89
145
+ describe_property("VEVENT", "SUMMARY", {"X-FOO" => "BAR"}, "Department Party")
146
+
147
+ #RFC 2445 section 4.8.2.1 COMPLETED does not apply to Events
148
+
149
+ #RFC 2445 section 4.8.2.2 DTEND p91
150
+ describe_property("VEVENT", "DTEND", {"X-FOO" => "BAR"}, "19970714", RiCal::PropertyValue::Date)
151
+ describe_property("VEVENT", "DTEND", {"X-FOO" => "BAR"}, "19970714T235959Z", RiCal::PropertyValue::DateTime)
152
+
153
+ #RFC 2445 section 4.8.2.3 DUE does not apply to Events
154
+
155
+ #RFC 2445 section 4.8.2.4 DTSTART p93
156
+ describe_property("VEVENT", "DTSTART", {"X-FOO" => "BAR"}, "19970714", RiCal::PropertyValue::Date)
157
+ describe_property("VEVENT", "DTSTART", {"X-FOO" => "BAR"}, "19970714T235959Z", RiCal::PropertyValue::DateTime)
158
+
159
+ #RFC 2445 section 4.8.2.5 DURATION p94
160
+ describe_property("VEVENT", "DURATION", {"X-FOO" => "BAR"}, "P1H", RiCal::PropertyValue::Duration)
161
+
162
+ #RFC 2445 section 4.8.2.6 FREEBUSY does not apply to Events
163
+
164
+ #RFC 2445 section 4.8.2.4 TRANSP p93
165
+ describe_property("VEVENT", "TRANSP", {"X-FOO" => "BAR"}, "OPAQUE")
166
+ #TO-DO need to spec that values are constrained to OPAQUE and TRANSPARENT
167
+ # and that this property can be specified at most once
168
+
169
+ #RFC 2445 section 4.8.4.1 ATTENDEE p102
170
+ describe_multi_property("VEVENT", "ATTENDEE", {"X-FOO" => "BAR"}, "MAILTO:jane_doe@host.com", RiCal::PropertyValue::CalAddress)
171
+ #TO-DO need to handle param values
172
+
173
+ #RFC 2445 section 4.8.4.2 CONTACT p104
174
+ describe_multi_property("VEVENT", "CONTACT", {"X-FOO" => "BAR"}, "Contact info")
175
+
176
+ #RFC 2445 section 4.8.4.3 ORGANIZER p106
177
+ describe_property("VEVENT", "ORGANIZER", {"X-FOO" => "BAR", "CN" => "John Smith"}, "MAILTO:jsmith@host1.com", RiCal::PropertyValue::CalAddress)
178
+ #TO-DO need to handle param values
179
+
180
+ #RFC 2445 section 4.8.4.4 RECURRENCE-ID p107
181
+ describe_property("VEVENT", "RECURRENCE-ID", {"X-FOO" => "BAR", "VALUE" => "DATE"}, "19970714", RiCal::PropertyValue::Date)
182
+ describe_property("VEVENT", "RECURRENCE-ID", {"X-FOO" => "BAR", "VALUE" => "DATE-TIME"}, "19970714T235959Z", RiCal::PropertyValue::DateTime)
183
+ #TO-DO need to handle parameters
184
+
185
+ #RFC 2445 section 4.8.4.5 RELATED-TO p109
186
+ describe_multi_property("VEVENT", "RELATED-TO", {"X-FOO" => "BAR"}, "<jsmith.part7.19960817T083000.xyzMail@host3.com")
187
+
188
+ #RFC 2445 section 4.8.4.6 URL p110
189
+ describe_property("VEVENT", "URL", {"X-FOO" => "BAR"}, "http://abc.com/pub/calendars/jsmith/mytime.ics", RiCal::PropertyValue::Uri)
190
+
191
+ #RFC 2445 section 4.8.4.7 UID p111
192
+ describe_property("VEVENT", "UID", {"X-FOO" => "BAR"}, "19960401T080045Z-4000F192713-0052@host1.com")
193
+
194
+ #RFC 2445 section 4.8.5.1 EXDATE p112
195
+ describe_multi_property("VEVENT", "EXDATE", {"X-FOO" => "BAR"}, "19960402T010000,19960403T010000,19960404T010000", RiCal::PropertyValue::OccurrenceList)
196
+
197
+ #RFC 2445 section 4.8.5.2 EXRULE p114
198
+ describe_multi_property("VEVENT", "EXRULE", {"X-FOO" => "BAR"}, "FREQ=DAILY;COUNT=10", RiCal::PropertyValue::RecurrenceRule)
199
+
200
+ #RFC 2445 section 4.8.5.3 RDATE p115
201
+ describe_multi_property("VEVENT", "RDATE", {"X-FOO" => "BAR"}, "19960402T010000,19960403T010000,19960404T010000", RiCal::PropertyValue::OccurrenceList)
202
+
203
+ #RFC 2445 section 4.8.5.2 RRULE p117
204
+ describe_multi_property("VEVENT", "RRULE", {"X-FOO" => "BAR"}, "FREQ=DAILY;COUNT=10", RiCal::PropertyValue::RecurrenceRule)
205
+
206
+ #RFC 2445 section 4.8.7.1 CREATED p129
207
+ describe_property("VEVENT", "CREATED", {"X-FOO" => "BAR"}, "19960329T133000Z", RiCal::PropertyValue::DateTime)
208
+
209
+ #RFC 2445 section 4.8.7.2 DTSTAMP p129
210
+ describe_property("VEVENT", "DTSTAMP", {"X-FOO" => "BAR"}, "19971210T080000Z", RiCal::PropertyValue::DateTime)
211
+
212
+ #RFC 2445 section 4.8.7.3 LAST-MODIFIED p131
213
+ describe_property("VEVENT", "LAST-MODIFIED", {"X-FOO" => "BAR"}, "19960817T133000Z", RiCal::PropertyValue::DateTime)
214
+
215
+ #RFC 2445 section 4.8.7.3 SEQUENCE p131
216
+ describe_property("VEVENT", "SEQUENCE", {"X-FOO" => "BAR"}, 2, RiCal::PropertyValue::Integer)
217
+
218
+ #RFC 2445 section 4.8.8.2 REQUEST-STATUS p131
219
+ describe_multi_property("VEVENT", "REQUEST-STATUS", {"X-FOO" => "BAR"}, "2.0;Success")
220
+ end
221
+
222
+ describe "parsing a calendar" do
223
+
224
+ it "should parse a calendar" do
225
+ parser = RiCal::Parser.new(StringIO.new("BEGIN:VCALENDAR"))
226
+ RiCal::Component::Calendar.should_receive(:from_parser).with(parser, nil)
227
+ parser.parse
228
+ end
229
+
230
+ it "should parse a calendar and return an array of 1 Calendar" do
231
+ cal = RiCal::Parser.parse(StringIO.new("BEGIN:VCALENDAR\nEND:VCALENDAR")).first
232
+ cal.should be_kind_of(RiCal::Component::Calendar)
233
+ end
234
+
235
+ # RFC 2445, section 4.6 section 4.7.1, pp 73-74
236
+ describe_property("VCALENDAR", "CALSCALE", {"X-FOO" => "Y"}, "GREGORIAN")
237
+
238
+ # RFC 2445, section 4.6 section 4.7.2, pp 74-75
239
+ describe_named_property("VCALENDAR", "METHOD", 'icalendar_method', {"X-FOO" => "Y"}, "REQUEST", false)
240
+
241
+ # RFC 2445, section 4.6, pp 51-52, section 4.7.3, p 75-76
242
+ describe_property("VCALENDAR", "PRODID", {"X-FOO" => "Y"}, "-//ABC CORPORATION//NONSGML/ My Product//EN")
243
+
244
+ # RFC 2445, section 4.6, pp 51-52, section 4.7.3, p 75-76
245
+ describe_property("VCALENDAR", "VERSION", {"X-FOO" => "Y"}, "2.0")
246
+
247
+
248
+ # RFC2445 p 51
249
+ it "should parse a calendar with an X property" do
250
+ lambda {RiCal::Parser.parse(StringIO.new("BEGIN:VCALENDAR\nX-PROP;X-FOO=Y:BAR\nEND:VCALENDAR"))}.should_not raise_error
251
+ end
252
+
253
+ describe 'the X property' do
254
+ before(:each) do
255
+ @x_props = RiCal::Parser.parse(StringIO.new("BEGIN:VCALENDAR\nX-PROP;X-FOO=Y:BAR\nEND:VCALENDAR")).first.x_properties
256
+ @x_prop = @x_props["X-PROP"]
257
+ end
258
+
259
+ it "should be a PropertyValue::Text" do
260
+ @x_prop.should be_kind_of(RiCal::PropertyValue::Text)
261
+ end
262
+
263
+ it "should have the right value" do
264
+ @x_prop.value.should == "BAR"
265
+ end
266
+
267
+ it "should have the right parameters" do
268
+ @x_prop.params.should == {"X-FOO" => "Y"}
269
+ end
270
+ end
271
+ end
272
+
273
+ it "should parse a to-do" do
274
+ parser = RiCal::Parser.new(StringIO.new("BEGIN:VTODO"))
275
+ RiCal::Component::Todo.should_receive(:from_parser).with(parser, nil)
276
+ parser.parse
277
+ end
278
+
279
+ it "should parse a journal entry" do
280
+ parser = RiCal::Parser.new(StringIO.new("BEGIN:VJOURNAL"))
281
+ RiCal::Component::Journal.should_receive(:from_parser).with(parser, nil)
282
+ parser.parse
283
+ end
284
+
285
+ it "should parse a free/busy component" do
286
+ parser = RiCal::Parser.new(StringIO.new("BEGIN:VFREEBUSY"))
287
+ RiCal::Component::Freebusy.should_receive(:from_parser).with(parser, nil)
288
+ parser.parse
289
+ end
290
+
291
+ it "should parse a timezone component" do
292
+ parser = RiCal::Parser.new(StringIO.new("BEGIN:VTIMEZONE"))
293
+ RiCal::Component::Timezone.should_receive(:from_parser).with(parser, nil)
294
+ parser.parse
295
+ end
296
+
297
+ it "should parse an alarm component" do
298
+ parser = RiCal::Parser.new(StringIO.new("BEGIN:VALARM"))
299
+ RiCal::Component::Alarm.should_receive(:from_parser).with(parser, nil)
300
+ parser.parse
301
+ end
302
+ end
303
+ end
@@ -0,0 +1,53 @@
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::PropertyValue::Date do
6
+ context ".advance" do
7
+ it "should advance by one week if passed :days => 7" do
8
+ dt1 = RiCal::PropertyValue::Date.new(nil, :value => "20050131")
9
+ dt2 = RiCal::PropertyValue::Date.new(nil, :value => "20050207")
10
+ dt1.advance(:days => 7).should == dt2
11
+ end
12
+
13
+ context ".==" do
14
+ it "should return true for two instances representing the same date" do
15
+ dt1 = RiCal::PropertyValue::Date.new(nil, :value => DateTime.parse("20050131T010000"))
16
+ dt2 = RiCal::PropertyValue::Date.new(nil, :value => DateTime.parse("20050131T010001"))
17
+ dt1.should == dt2
18
+ end
19
+ end
20
+ end
21
+
22
+ context ".-" do
23
+
24
+ it "should return a Duration property when the argument is also a Date property" do
25
+ dt1 = RiCal::PropertyValue::Date.new(nil, :value => DateTime.parse("20090519"))
26
+ dt2 = RiCal::PropertyValue::Date.new(nil, :value => DateTime.parse("20090518"))
27
+ (dt1 - dt2).should == RiCal::PropertyValue::Duration.new(nil, :value => "+P1D")
28
+ end
29
+
30
+ it "should return a Duration property when the argument is a DateTime property" do
31
+ dt1 = RiCal::PropertyValue::Date.new(nil, :value => DateTime.parse("20090519"))
32
+ dt2 = RiCal::PropertyValue::DateTime.new(nil, :value => DateTime.parse("20090518T120000"))
33
+ (dt1 - dt2).should == RiCal::PropertyValue::Duration.new(nil, :value => "+PT12H")
34
+ end
35
+
36
+ it "should return a DateTime property when the argument is a Duration Property" do
37
+ dt1 = RiCal::PropertyValue::Date.new(nil, :value => "19980119")
38
+ duration = RiCal::PropertyValue::Duration.new(nil, :value => "+PT2H")
39
+ @it = dt1 - duration
40
+ @it.should == RiCal::PropertyValue::DateTime.new(nil, :value => "19980118T220000")
41
+ end
42
+ end
43
+
44
+ context ".+" do
45
+
46
+ it "should return a DateTime property when the argument is a Duration Property" do
47
+ dt1 = RiCal::PropertyValue::Date.new(nil, :value => "19980119")
48
+ duration = RiCal::PropertyValue::Duration.new(nil, :value => "+PT2H")
49
+ @it = dt1 + duration
50
+ @it.should == RiCal::PropertyValue::DateTime.new(nil, :value => "19980119T020000")
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,383 @@
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
+ require 'tzinfo'
5
+
6
+ describe RiCal::PropertyValue::DateTime do
7
+ before(:each) do
8
+ @cal = RiCal.Calendar
9
+ end
10
+
11
+ def utc_datetime(string)
12
+ RiCal::PropertyValue::DateTime.new(@cal, :value => "#{string}Z")
13
+ end
14
+
15
+ def local_datetime(string, tzid = "America/New_York")
16
+ RiCal::PropertyValue::DateTime.new(@cal, :value => string, :params => {'TZID' => tzid})
17
+ end
18
+
19
+ context "time_with_zone_methods" do
20
+ context ".utc" do
21
+ context "for a datetime already in zulu time" do
22
+ before(:each) do
23
+ @it = utc_datetime("19970101T012300").utc
24
+ end
25
+
26
+ it "should return the same datetime" do
27
+ @it.should == utc_datetime("19970101T012300")
28
+ end
29
+
30
+ it "should return a result with a tzid of UTC" do
31
+ @it.utc.tzid.should == "UTC"
32
+ end
33
+ end
34
+
35
+ it "should raise an invalid timezone exception if the timezone of the receiver is unknown" do
36
+ lambda {local_datetime("19970101T012300", 'America/Caspian').utc}.should raise_error(RiCal::InvalidTimezoneIdentifier)
37
+ end
38
+
39
+ context "for a datetime with a tzid of America/New_York" do
40
+ before(:each) do
41
+ @it = local_datetime("19970101T012300")
42
+ @it = @it.utc
43
+ end
44
+
45
+ it "should return the equivalent utc time" do
46
+ @it.should == utc_datetime("19970101T062300")
47
+ end
48
+
49
+ it "should return a result with a tzid of UTC" do
50
+ @it.tzid.should == "UTC"
51
+ end
52
+ end
53
+
54
+ context ".in_timezone('America/New_York')" do
55
+
56
+ it "should raise an invalid timezone exception if the timezone of the receiver is unknown" do
57
+ lambda {local_datetime("19970101T012300", 'America/Caspian').in_time_zone('America/New_York')}.should raise_error(RiCal::InvalidTimezoneIdentifier)
58
+ end
59
+
60
+ context "for a datetime 19970101T012300 in zulu time" do
61
+ before(:each) do
62
+ @it = utc_datetime("19970101T012300").in_time_zone('America/New_York')
63
+ end
64
+
65
+ it "should return the 8:23 p. Dec 31, 1996" do
66
+ @it.should == local_datetime("19961231T202300")
67
+ end
68
+
69
+ it "should return a result with a tzid of UTC" do
70
+ @it.tzid.should == "America/New_York"
71
+ end
72
+ end
73
+
74
+ context "for a datetime 19970101T012300 with a tzid of America/New_York" do
75
+ before(:each) do
76
+ @it = local_datetime("19970101T012300").in_time_zone('America/New_York')
77
+ end
78
+
79
+ it "should return the same time" do
80
+ @it.should == local_datetime("19970101T012300")
81
+ end
82
+
83
+ it "should return a result with a tzid of UTC" do
84
+ @it.tzid.should == "America/New_York"
85
+ end
86
+ end
87
+
88
+ context "for a datetime 19970101T012300 with a tzid of America/Chicago" do
89
+ before(:each) do
90
+ @it = local_datetime("19970101T012300", "America/Chicago").in_time_zone('America/New_York')
91
+ end
92
+
93
+ it "should return Jan 1, 1997 02:23 a.m." do
94
+ @it.should == local_datetime("19970101T022300")
95
+ end
96
+
97
+ it "should return a result with a tzid of America/New_York" do
98
+ @it.tzid.should == "America/New_York"
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ context "for a datetime from an imported calendar" do
105
+
106
+ before(:each) do
107
+ cals = RiCal.parse_string <<-END_OF_DATA
108
+ BEGIN:VCALENDAR
109
+ METHOD:PUBLISH
110
+ X-WR-TIMEZONE:America/New_York
111
+ PRODID:-//Apple Inc.//iCal 3.0//EN
112
+ CALSCALE:GREGORIAN
113
+ X-WR-CALNAME:Test
114
+ VERSION:2.0
115
+ X-WR-RELCALID:58DB0663-196B-4B6B-A05A-A53049661280
116
+ X-APPLE-CALENDAR-COLOR:#0252D4
117
+ BEGIN:VTIMEZONE
118
+ TZID:Europe/Paris
119
+ BEGIN:DAYLIGHT
120
+ TZOFFSETFROM:+0100
121
+ TZOFFSETTO:+0200
122
+ DTSTART:19810329T020000
123
+ RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
124
+ TZNAME:CEST
125
+ END:DAYLIGHT
126
+ BEGIN:STANDARD
127
+ TZOFFSETFROM:+0200
128
+ TZOFFSETTO:+0100
129
+ DTSTART:19961027T030000
130
+ RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
131
+ TZNAME:CET
132
+ END:STANDARD
133
+ END:VTIMEZONE
134
+ BEGIN:VTIMEZONE
135
+ TZID:US/Eastern
136
+ BEGIN:DAYLIGHT
137
+ TZOFFSETFROM:-0500
138
+ TZOFFSETTO:-0400
139
+ DTSTART:20070311T020000
140
+ RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
141
+ TZNAME:EDT
142
+ END:DAYLIGHT
143
+ BEGIN:STANDARD
144
+ TZOFFSETFROM:-0400
145
+ TZOFFSETTO:-0500
146
+ DTSTART:20071104T020000
147
+ RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
148
+ TZNAME:EST
149
+ END:STANDARD
150
+ END:VTIMEZONE
151
+ BEGIN:VEVENT
152
+ SEQUENCE:3
153
+ TRANSP:OPAQUE
154
+ UID:AC2EFB19-A8A8-49EF-929F-CA0975374ED6
155
+ DTSTART:20090501T000000Z
156
+ DTSTAMP:20090501T174405Z
157
+ SUMMARY:UTC Event
158
+ CREATED:20090501T174347Z
159
+ DTEND:20090501T010000Z
160
+ END:VEVENT
161
+ BEGIN:VEVENT
162
+ SEQUENCE:2
163
+ TRANSP:OPAQUE
164
+ UID:A5517A09-C53E-4E1F-A642-EA47680BF2B6
165
+ DTSTART;TZID=US/Eastern:20090430T140000
166
+ DTSTAMP:20090501T174428Z
167
+ SUMMARY:Eastern Event
168
+ CREATED:20090501T174415Z
169
+ DTEND;TZID=US/Eastern:20090430T150000
170
+ END:VEVENT
171
+ BEGIN:VEVENT
172
+ SEQUENCE:3
173
+ TRANSP:OPAQUE
174
+ UID:B5024763-9197-4A60-A96E-D8D59D578BB2
175
+ DTSTART;TZID=Europe/Paris:20090430T140000
176
+ DTSTAMP:20090501T174509Z
177
+ SUMMARY:Paris Event
178
+ CREATED:20090501T174439Z
179
+ DTEND;TZID=Europe/Paris:20090430T150000
180
+ END:VEVENT
181
+ END:VCALENDAR
182
+ END_OF_DATA
183
+ @cal = cals.first
184
+ end
185
+
186
+ def find_event(summary)
187
+ @cal.events.find {|event| event.summary == summary}
188
+ end
189
+
190
+ context ".utc" do
191
+
192
+ it "should raise an invalid timezone exception if the timezone of the receiver is not in the calendar" do
193
+ lambda {local_datetime("19970101T012300", 'America/New_York').utc}.should raise_error(RiCal::InvalidTimezoneIdentifier)
194
+ end
195
+
196
+ context "for the DTSTART of the UTC Event" do
197
+ before(:each) do
198
+ @it = find_event("UTC Event").dtstart_property.utc
199
+ end
200
+
201
+ it "should return the same datetime" do
202
+ @it.should == utc_datetime("20090501T000000")
203
+ end
204
+
205
+ it "should return a result with a tzid of UTC" do
206
+ @it.utc.tzid.should == "UTC"
207
+ end
208
+ end
209
+
210
+ context "for the DTSTART of the Eastern Event" do
211
+ before(:each) do
212
+ @it = find_event("Eastern Event").dtstart_property.utc
213
+ end
214
+
215
+ it "should return the equivalent utc time" do
216
+ @it.should == utc_datetime("20090430T180000")
217
+ end
218
+
219
+ it "should return a result with a tzid of UTC" do
220
+ @it.tzid.should == "UTC"
221
+ end
222
+ end
223
+ end
224
+
225
+ context ".in_timezone('US/Eastern')" do
226
+
227
+ it "should raise an invalid timezone exception if the timezone of the receiver is not in the calendar" do
228
+ lambda {local_datetime("19970101T012300", 'America/New_York').in_time_zone("US/Eastern")}.should raise_error(RiCal::InvalidTimezoneIdentifier)
229
+ end
230
+
231
+ context "for the DTSTART of the UTC Event" do
232
+ before(:each) do
233
+ @it = find_event("UTC Event").dtstart_property.in_time_zone("US/Eastern")
234
+ end
235
+
236
+ it "should return 8:00 p.m. Apr 30, 2009" do
237
+ @it.should == local_datetime("20090430T2000000", "US/Eastern")
238
+ end
239
+
240
+ it "should return a result with a tzid of US/Eastern" do
241
+ @it.tzid.should == "US/Eastern"
242
+ end
243
+ end
244
+
245
+ context "for the DTSTART of the Eastern Event" do
246
+ before(:each) do
247
+ @it = find_event("Eastern Event").dtstart_property.in_time_zone("US/Eastern")
248
+ end
249
+
250
+ it "should return the same time" do
251
+ @it.should == local_datetime("20090430T140000", "US/Eastern")
252
+ end
253
+
254
+ it "should return a result with a tzid of UTC" do
255
+ @it.tzid.should == "US/Eastern"
256
+ end
257
+ end
258
+
259
+ context "for the DTSTART of the Paris Event" do
260
+ before(:each) do
261
+ @it = find_event("Paris Event").dtstart_property.in_time_zone("US/Eastern")
262
+ end
263
+
264
+ it "should return 8:00 a.m. on Apr 30, 2009" do
265
+ @it.should == local_datetime("20090430T080000", "US/Eastern")
266
+ end
267
+
268
+ it "should return a result with a tzid of UTC" do
269
+ @it.tzid.should == "US/Eastern"
270
+ end
271
+ end
272
+ end
273
+ end
274
+ end
275
+
276
+ context ".from_separated_line" do
277
+ it "should return a RiCal::PropertyValue::Date if the value doesn't contain a time specification" do
278
+ RiCal::PropertyValue::DateTime.or_date(nil, :value => "19970714").should be_kind_of(RiCal::PropertyValue::Date)
279
+ end
280
+
281
+ it "should return a RiCal::PropertyValue::DateTime if the value does contain a time specification" do
282
+ RiCal::PropertyValue::DateTime.or_date(nil, :value => "19980118T230000").should be_kind_of(RiCal::PropertyValue::DateTime)
283
+ end
284
+ end
285
+
286
+ context ".advance" do
287
+ it "should advance by one week if passed :days => 7" do
288
+ dt1 = RiCal::PropertyValue::DateTime.new(nil, :value => "20050131T230000")
289
+ dt2 = RiCal::PropertyValue::DateTime.new(nil, :value => "20050207T230000")
290
+ dt1.advance(:days => 7).should == dt2
291
+ end
292
+ end
293
+
294
+ context "subtracting one date-time from another" do
295
+
296
+ it "should produce the right RiCal::PropertyValue::Duration" do
297
+ dt1 = RiCal::PropertyValue::DateTime.new(nil, :value => "19980118T230000")
298
+ dt2 = RiCal::PropertyValue::DateTime.new(nil, :value => "19980119T010000")
299
+ @it = dt2 - dt1
300
+ @it.should == RiCal::PropertyValue::Duration.new(nil, :value => "+PT2H")
301
+ end
302
+ end
303
+
304
+ context "adding a RiCal::PropertyValue::Duration to a RiCal::PropertyValue::DateTime" do
305
+
306
+ it "should produce the right RiCal::PropertyValue::DateTime" do
307
+ dt1 = RiCal::PropertyValue::DateTime.new(nil, :value => "19980118T230000")
308
+ duration = RiCal::PropertyValue::Duration.new(nil, :value => "+PT2H")
309
+ @it = dt1 + duration
310
+ @it.should == RiCal::PropertyValue::DateTime.new(nil, :value => "19980119T010000")
311
+ end
312
+ end
313
+
314
+ context "subtracting a RiCal::PropertyValue::Duration from a RiCal::PropertyValue::DateTime" do
315
+
316
+ it "should produce the right RiCal::PropertyValue::DateTime" do
317
+ dt1 = RiCal::PropertyValue::DateTime.new(nil, :value => "19980119T010000")
318
+ duration = RiCal::PropertyValue::Duration.new(nil, :value => "+PT2H")
319
+ @it = dt1 - duration
320
+ @it.should == RiCal::PropertyValue::DateTime.new(nil, :value => "19980118T230000")
321
+ end
322
+ end
323
+ context "when setting the default timezone identifier" do
324
+
325
+ before(:each) do
326
+ RiCal::PropertyValue::DateTime.default_tzid = "America/Chicago"
327
+ @time = Time.mktime(2009,2,5,19,17,11)
328
+ @it = RiCal::PropertyValue::DateTime.convert(nil, @time)
329
+ end
330
+
331
+ after(:each) do
332
+ RiCal::PropertyValue::DateTime.default_tzid = "UTC"
333
+ end
334
+
335
+ it "should update the default timezone to America/Chicago" do
336
+ @it.params.should == {'TZID' => 'America/Chicago'}
337
+ end
338
+
339
+ it "should not have a tzid when default_tzid is :floating" do
340
+ RiCal::PropertyValue::DateTime.default_tzid = :floating
341
+ dt = RiCal::PropertyValue::DateTime.convert(nil, @time)
342
+ dt.params.should == {}
343
+ end
344
+ end
345
+
346
+ context ".convert(rubyobject)" do
347
+ describe "for a Time instance of Feb 05 19:17:11"
348
+ before(:each) do
349
+ @time = Time.mktime(2009,2,5,19,17,11)
350
+ end
351
+
352
+ context "with a normal a normal time instance" do
353
+ describe "when the default timezone identifier is UTC" do
354
+ before(:each) do
355
+ @it = RiCal::PropertyValue::DateTime.convert(nil, @time)
356
+ end
357
+
358
+ it "should have a TZID of UTC" do
359
+ @it.tzid.should == 'UTC'
360
+ end
361
+
362
+ it "should have the right value" do
363
+ @it.value.should == "20090205T191711Z"
364
+ end
365
+ end
366
+
367
+ context "when the default timezone has been set to 'America/Chicago" do
368
+ before(:each) do
369
+ RiCal::PropertyValue::DateTime.stub!(:default_tzid).and_return("America/Chicago")
370
+ @it = RiCal::PropertyValue::DateTime.convert(nil, @time)
371
+ end
372
+
373
+ it "should have a TZID of America/Chicago" do
374
+ @it.tzid.should == 'America/Chicago'
375
+ end
376
+
377
+ it "should have the right value" do
378
+ @it.value.should == "20090205T191711"
379
+ end
380
+ end
381
+ end
382
+ end
383
+ end