awallis-ri_cal 0.8.8

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 (166) hide show
  1. data/History.txt +402 -0
  2. data/Manifest.txt +161 -0
  3. data/README.txt +410 -0
  4. data/Rakefile +69 -0
  5. data/VERSION +1 -0
  6. data/bin/ri_cal +8 -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/copyrights.txt +1 -0
  17. data/docs/draft-ietf-calsify-2446bis-08.txt +7280 -0
  18. data/docs/draft-ietf-calsify-rfc2445bis-09.txt +10416 -0
  19. data/docs/incrementers.txt +7 -0
  20. data/docs/rfc2445.pdf +0 -0
  21. data/lib/ri_cal.rb +187 -0
  22. data/lib/ri_cal/component.rb +256 -0
  23. data/lib/ri_cal/component/alarm.rb +19 -0
  24. data/lib/ri_cal/component/calendar.rb +257 -0
  25. data/lib/ri_cal/component/event.rb +58 -0
  26. data/lib/ri_cal/component/freebusy.rb +16 -0
  27. data/lib/ri_cal/component/journal.rb +27 -0
  28. data/lib/ri_cal/component/non_standard.rb +33 -0
  29. data/lib/ri_cal/component/t_z_info_timezone.rb +153 -0
  30. data/lib/ri_cal/component/timezone.rb +197 -0
  31. data/lib/ri_cal/component/timezone/daylight_period.rb +25 -0
  32. data/lib/ri_cal/component/timezone/standard_period.rb +23 -0
  33. data/lib/ri_cal/component/timezone/timezone_period.rb +76 -0
  34. data/lib/ri_cal/component/todo.rb +42 -0
  35. data/lib/ri_cal/core_extensions.rb +11 -0
  36. data/lib/ri_cal/core_extensions/array.rb +7 -0
  37. data/lib/ri_cal/core_extensions/array/conversions.rb +15 -0
  38. data/lib/ri_cal/core_extensions/date.rb +13 -0
  39. data/lib/ri_cal/core_extensions/date/conversions.rb +56 -0
  40. data/lib/ri_cal/core_extensions/date_time.rb +15 -0
  41. data/lib/ri_cal/core_extensions/date_time/conversions.rb +50 -0
  42. data/lib/ri_cal/core_extensions/object.rb +8 -0
  43. data/lib/ri_cal/core_extensions/object/conversions.rb +20 -0
  44. data/lib/ri_cal/core_extensions/string.rb +8 -0
  45. data/lib/ri_cal/core_extensions/string/conversions.rb +57 -0
  46. data/lib/ri_cal/core_extensions/time.rb +14 -0
  47. data/lib/ri_cal/core_extensions/time/calculations.rb +153 -0
  48. data/lib/ri_cal/core_extensions/time/conversions.rb +42 -0
  49. data/lib/ri_cal/core_extensions/time/tzid_access.rb +50 -0
  50. data/lib/ri_cal/core_extensions/time/week_day_predicates.rb +55 -0
  51. data/lib/ri_cal/fast_date_time.rb +234 -0
  52. data/lib/ri_cal/floating_timezone.rb +32 -0
  53. data/lib/ri_cal/invalid_property_value.rb +8 -0
  54. data/lib/ri_cal/invalid_timezone_identifier.rb +20 -0
  55. data/lib/ri_cal/occurrence_enumerator.rb +265 -0
  56. data/lib/ri_cal/occurrence_period.rb +17 -0
  57. data/lib/ri_cal/parser.rb +145 -0
  58. data/lib/ri_cal/properties.rb +12 -0
  59. data/lib/ri_cal/properties/alarm.rb +390 -0
  60. data/lib/ri_cal/properties/calendar.rb +164 -0
  61. data/lib/ri_cal/properties/event.rb +1523 -0
  62. data/lib/ri_cal/properties/freebusy.rb +593 -0
  63. data/lib/ri_cal/properties/journal.rb +1237 -0
  64. data/lib/ri_cal/properties/timezone.rb +150 -0
  65. data/lib/ri_cal/properties/timezone_period.rb +416 -0
  66. data/lib/ri_cal/properties/todo.rb +1559 -0
  67. data/lib/ri_cal/property_value.rb +159 -0
  68. data/lib/ri_cal/property_value/array.rb +27 -0
  69. data/lib/ri_cal/property_value/cal_address.rb +11 -0
  70. data/lib/ri_cal/property_value/date.rb +184 -0
  71. data/lib/ri_cal/property_value/date_time.rb +359 -0
  72. data/lib/ri_cal/property_value/date_time/additive_methods.rb +44 -0
  73. data/lib/ri_cal/property_value/date_time/time_machine.rb +159 -0
  74. data/lib/ri_cal/property_value/date_time/timezone_support.rb +100 -0
  75. data/lib/ri_cal/property_value/duration.rb +110 -0
  76. data/lib/ri_cal/property_value/geo.rb +11 -0
  77. data/lib/ri_cal/property_value/integer.rb +12 -0
  78. data/lib/ri_cal/property_value/occurrence_list.rb +144 -0
  79. data/lib/ri_cal/property_value/period.rb +86 -0
  80. data/lib/ri_cal/property_value/recurrence_rule.rb +154 -0
  81. data/lib/ri_cal/property_value/recurrence_rule/enumeration_support_methods.rb +100 -0
  82. data/lib/ri_cal/property_value/recurrence_rule/enumerator.rb +79 -0
  83. data/lib/ri_cal/property_value/recurrence_rule/initialization_methods.rb +148 -0
  84. data/lib/ri_cal/property_value/recurrence_rule/negative_setpos_enumerator.rb +53 -0
  85. data/lib/ri_cal/property_value/recurrence_rule/numbered_span.rb +31 -0
  86. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer.rb +135 -0
  87. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_day_incrementer.rb +86 -0
  88. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_hour_incrementer.rb +31 -0
  89. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_minute_incrementer.rb +32 -0
  90. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_month_incrementer.rb +52 -0
  91. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_monthday_incrementer.rb +31 -0
  92. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_numbered_day_incrementer.rb +38 -0
  93. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_second_incrementer.rb +32 -0
  94. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_weekno_incrementer.rb +69 -0
  95. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_yearday_incrementer.rb +31 -0
  96. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/daily_incrementer.rb +28 -0
  97. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/frequency_incrementer.rb +80 -0
  98. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/hourly_incrementer.rb +23 -0
  99. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/list_incrementer.rb +106 -0
  100. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/minutely_incrementer.rb +23 -0
  101. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/monthly_incrementer.rb +33 -0
  102. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/null_sub_cycle_incrementer.rb +43 -0
  103. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/secondly_incrementer.rb +28 -0
  104. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/weekly_incrementer.rb +37 -0
  105. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/yearly_incrementer.rb +57 -0
  106. data/lib/ri_cal/property_value/recurrence_rule/recurring_day.rb +131 -0
  107. data/lib/ri_cal/property_value/recurrence_rule/recurring_month_day.rb +64 -0
  108. data/lib/ri_cal/property_value/recurrence_rule/recurring_numbered_week.rb +33 -0
  109. data/lib/ri_cal/property_value/recurrence_rule/recurring_year_day.rb +53 -0
  110. data/lib/ri_cal/property_value/recurrence_rule/time_manipulation.rb +42 -0
  111. data/lib/ri_cal/property_value/recurrence_rule/validations.rb +125 -0
  112. data/lib/ri_cal/property_value/text.rb +44 -0
  113. data/lib/ri_cal/property_value/uri.rb +11 -0
  114. data/lib/ri_cal/property_value/utc_offset.rb +33 -0
  115. data/lib/ri_cal/property_value/zulu_date_time.rb +34 -0
  116. data/lib/ri_cal/required_timezones.rb +55 -0
  117. data/parked_specs/ri_cal/claudio_a_bug_spec.rb +100 -0
  118. data/performance/empty_propval/subject.rb +43 -0
  119. data/performance/paris_eastern/subject.rb +90 -0
  120. data/performance/penultimate_weekday/subject.rb +15 -0
  121. data/performance/psm_big_enum/ical.ics +3171 -0
  122. data/performance/psm_big_enum/subject.rb +16 -0
  123. data/performance/utah_cycling/subject.rb +55 -0
  124. data/ri_cal.gemspec +244 -0
  125. data/script/benchmark_subject +23 -0
  126. data/script/console +10 -0
  127. data/script/destroy +14 -0
  128. data/script/generate +14 -0
  129. data/script/profile_subject +29 -0
  130. data/script/txt2html +71 -0
  131. data/spec/ri_cal/bugreports_spec.rb +276 -0
  132. data/spec/ri_cal/component/alarm_spec.rb +12 -0
  133. data/spec/ri_cal/component/calendar_spec.rb +88 -0
  134. data/spec/ri_cal/component/event_spec.rb +735 -0
  135. data/spec/ri_cal/component/freebusy_spec.rb +12 -0
  136. data/spec/ri_cal/component/journal_spec.rb +37 -0
  137. data/spec/ri_cal/component/t_z_info_timezone_spec.rb +60 -0
  138. data/spec/ri_cal/component/timezone_spec.rb +236 -0
  139. data/spec/ri_cal/component/todo_spec.rb +112 -0
  140. data/spec/ri_cal/component_spec.rb +224 -0
  141. data/spec/ri_cal/core_extensions/string/conversions_spec.rb +78 -0
  142. data/spec/ri_cal/core_extensions/time/calculations_spec.rb +188 -0
  143. data/spec/ri_cal/core_extensions/time/week_day_predicates_spec.rb +45 -0
  144. data/spec/ri_cal/fast_date_time_spec.rb +77 -0
  145. data/spec/ri_cal/inf_loop_spec.rb +78 -0
  146. data/spec/ri_cal/occurrence_enumerator_spec.rb +611 -0
  147. data/spec/ri_cal/parser_spec.rb +337 -0
  148. data/spec/ri_cal/property_value/date_spec.rb +53 -0
  149. data/spec/ri_cal/property_value/date_time_spec.rb +383 -0
  150. data/spec/ri_cal/property_value/duration_spec.rb +126 -0
  151. data/spec/ri_cal/property_value/occurrence_list_spec.rb +72 -0
  152. data/spec/ri_cal/property_value/period_spec.rb +63 -0
  153. data/spec/ri_cal/property_value/recurrence_rule/recurring_year_day_spec.rb +21 -0
  154. data/spec/ri_cal/property_value/recurrence_rule_spec.rb +1814 -0
  155. data/spec/ri_cal/property_value/text_spec.rb +25 -0
  156. data/spec/ri_cal/property_value/utc_offset_spec.rb +48 -0
  157. data/spec/ri_cal/property_value_spec.rb +125 -0
  158. data/spec/ri_cal/required_timezones_spec.rb +67 -0
  159. data/spec/ri_cal_spec.rb +53 -0
  160. data/spec/spec.opts +4 -0
  161. data/spec/spec_helper.rb +50 -0
  162. data/tasks/gem_loader/load_active_support.rb +3 -0
  163. data/tasks/gem_loader/load_tzinfo_gem.rb +2 -0
  164. data/tasks/ri_cal.rake +412 -0
  165. data/tasks/spec.rake +102 -0
  166. metadata +255 -0
@@ -0,0 +1,25 @@
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::Text do
6
+
7
+ context ".ruby_value" do
8
+
9
+ it "should handle escapes according to RFC2445 Sec 4.3.11 p 45" do
10
+ expected = "this\\ has\, \nescaped\;\n\\x characters"
11
+ it = RiCal::PropertyValue::Text.new(nil, :value => 'this\\ has\, \nescaped\;\N\x characters')
12
+ it.ruby_value.should == expected
13
+ end
14
+ end
15
+
16
+ context ".convert" do
17
+
18
+ it "should handle escapes according to RFC2445 Sec 4.3.11 p 45" do
19
+ expected = ':this has\, \nescaped\;\n characters\ncr\nnlcr\ncrnl'
20
+ it = RiCal::PropertyValue::Text.convert(nil, "this\ has, \nescaped;\n characters\rcr\n\rnlcr\r\ncrnl")
21
+ it.to_s.should == expected
22
+ end
23
+ end
24
+
25
+ end
@@ -0,0 +1,48 @@
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::UtcOffset do
6
+
7
+ describe "with a positive sign and seconds" do
8
+ before(:each) do
9
+ @it = RiCal::PropertyValue::UtcOffset.new(nil, :value => "+013015")
10
+ end
11
+
12
+ it "should have +1 as its sign" do
13
+ @it.sign.should == 1
14
+ end
15
+
16
+ it "should have 1 as its hours" do
17
+ @it.hours.should == 1
18
+ end
19
+
20
+ it "should have 30 as its minutes" do
21
+ @it.minutes.should == 30
22
+ end
23
+
24
+ it "should have 15 as its seconds" do
25
+ @it.seconds.should == 15
26
+ end
27
+ end
28
+
29
+ describe "with seconds omitted" do
30
+ before(:each) do
31
+ @it = RiCal::PropertyValue::UtcOffset.new(nil, :value => "+0130")
32
+ end
33
+
34
+ it "should have 0 as its seconds" do
35
+ @it.seconds.should == 0
36
+ end
37
+ end
38
+ describe "with a negative sign" do
39
+ before(:each) do
40
+ @it = RiCal::PropertyValue::UtcOffset.new(nil, :value => "-013015")
41
+ end
42
+
43
+ it "should have +1 as its sign" do
44
+ @it.sign.should == -1
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,125 @@
1
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
2
+ require File.join(File.dirname(__FILE__), %w[.. spec_helper])
3
+ require 'tzinfo'
4
+
5
+ describe RiCal::PropertyValue do
6
+
7
+ describe ".initialize" do
8
+
9
+ it "should reject a value starting with ';'" do
10
+ lambda {RiCal::PropertyValue.new(nil, :value => ";bogus")}.should raise_error {|err| err.message.should == "Invalid property value \";bogus\""}
11
+ end
12
+ end
13
+
14
+ describe "#date_or_date_time" do
15
+
16
+ it "should raise an exception on an invalid date" do
17
+ lambda {RiCal::PropertyValue.date_or_date_time(nil, :value => "foo")}.should raise_error
18
+ end
19
+
20
+
21
+ describe "rfc 2445 section 4.3.4 p 34" do
22
+ before(:each) do
23
+ @prop = RiCal::PropertyValue.date_or_date_time(nil, :value => "19970714")
24
+ end
25
+
26
+ it "should return a PropertyValue::Date" do
27
+ @prop.should be_kind_of(RiCal::PropertyValue::Date)
28
+ end
29
+
30
+ it "should set the correct date" do
31
+ @prop.to_ri_cal_ruby_value.should == Date.parse("Jul 14, 1997")
32
+ end
33
+ end
34
+
35
+ describe "rfc 2445 section 4.3.5 p 35" do
36
+ describe "FORM #1 date with local time p 36" do
37
+ before(:each) do
38
+ @prop = RiCal::PropertyValue.date_or_date_time(nil, :value => "19970714T123456")
39
+ end
40
+
41
+ it "should return a PropertyValue::DateTime" do
42
+ @prop.should be_kind_of(RiCal::PropertyValue::DateTime)
43
+ end
44
+
45
+ it "should have the right ruby value" do
46
+ @prop.to_ri_cal_ruby_value.should == DateTime.parse("19970714T123456")
47
+ end
48
+
49
+ it "should have the right value" do
50
+ @prop.value.should == "19970714T123456"
51
+ end
52
+
53
+ it "should have a nil tzid" do
54
+ @prop.tzid.should == nil
55
+ end
56
+ end
57
+
58
+ describe "FORM #2 date with UTC time p 36" do
59
+ before(:each) do
60
+ @prop = RiCal::PropertyValue.date_or_date_time(nil, :value => "19970714T123456Z")
61
+ end
62
+
63
+ it "should return a PropertyValue::DateTime" do
64
+ @prop.should be_kind_of(RiCal::PropertyValue::DateTime)
65
+ end
66
+
67
+ it "should have the right value" do
68
+ @prop.value.should == "19970714T123456Z"
69
+ end
70
+
71
+ it "should have the right ruby value" do
72
+ @prop.to_ri_cal_ruby_value.should == DateTime.parse("19970714T123456Z")
73
+ end
74
+
75
+ it "should have a tzid of UTC" do
76
+ @prop.tzid.should == "UTC"
77
+ end
78
+
79
+ end
80
+
81
+ describe "FORM #3 date with local time and time zone reference p 36" do
82
+ before(:each) do
83
+ timezone = mock("Timezone",
84
+ :rational_utc_offset => Rational(-4, 24),
85
+ :local_to_utc => RiCal::PropertyValue.date_or_date_time(nil, :value => "19970714T163456Z"),
86
+ :name => 'US-Eastern'
87
+ )
88
+ timezone_finder = mock("tz_finder", :find_timezone => timezone)
89
+ @prop = RiCal::PropertyValue.date_or_date_time(timezone_finder, :value => "19970714T123456", :params => {'TZID' => 'US-Eastern'})
90
+ end
91
+
92
+ it "should return a PropertyValue::DateTime" do
93
+ @prop.should be_kind_of(RiCal::PropertyValue::DateTime)
94
+ end
95
+
96
+ it "should have the right value" do
97
+ @prop.value.should == "19970714T123456"
98
+ end
99
+
100
+ context "it's Ruby value" do
101
+ before(:each) do
102
+ @it = @prop.ruby_value
103
+ end
104
+
105
+ it "should be the right DateTime" do
106
+ @it.should == DateTime.civil(1997,7, 14, 12, 34, 56, Rational(-4, 24))
107
+ end
108
+
109
+ it "should have the right tzid" do
110
+ @it.tzid.should == "US-Eastern"
111
+ end
112
+ end
113
+
114
+ it "should have the right tzid" do
115
+ @prop.tzid.should == "US-Eastern"
116
+ end
117
+
118
+ it "should raise an error if combined with a zulu time" do
119
+ lambda {RiCal::PropertyValue.date_or_date_time(nil, :value => "19970714T123456Z", :params => {:tzid => 'US-Eastern'})}.should raise_error
120
+ end
121
+ end
122
+ end
123
+
124
+ end
125
+ end
@@ -0,0 +1,67 @@
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
+ require 'tzinfo'
6
+
7
+ describe RiCal::RequiredTimezones::RequiredTimezone do
8
+
9
+ before(:each) do
10
+ @tzid = "America/New_York"
11
+ @it = RiCal::RequiredTimezones::RequiredTimezone.new(@tzid)
12
+ end
13
+
14
+ it "should have the right timezone" do
15
+ @it.timezone.identifier.should == @tzid
16
+ end
17
+
18
+ it "should set the first_time to the first date_time added" do
19
+ dt1 = dt_prop(DateTime.parse("Mar 22, 2009 10:48"))
20
+ @it.add_datetime(dt1)
21
+ @it.first_time.should == dt1
22
+ end
23
+
24
+ it "should set the first_time to the earliest time added" do
25
+ dt1 = dt_prop(DateTime.parse("Mar 22, 2009 10:48"))
26
+ dt2 = dt_prop(DateTime.parse("Mar 22, 2009 9:00"))
27
+ dt3 = dt_prop(DateTime.parse("Mar 22, 2009 10:00"))
28
+ @it.add_datetime(dt1)
29
+ @it.add_datetime(dt2)
30
+ @it.add_datetime(dt3)
31
+ @it.first_time.should == dt2
32
+ end
33
+
34
+ it "should set the last_time to the first date_time added" do
35
+ dt1 = dt_prop(DateTime.parse("Mar 22, 2009 10:48"))
36
+ @it.add_datetime(dt1)
37
+ @it.last_time.should == dt1
38
+ end
39
+
40
+ it "should set the first_time to the earliest time added" do
41
+ dt1 = dt_prop(DateTime.parse("Mar 22, 2009 10:48"))
42
+ dt2 = dt_prop(DateTime.parse("Mar 22, 2009 9:00"))
43
+ dt3 = dt_prop(DateTime.parse("Mar 22, 2009 10:00"))
44
+ @it.add_datetime(dt1)
45
+ @it.add_datetime(dt2)
46
+ @it.add_datetime(dt3)
47
+ @it.last_time.should == dt1
48
+ end
49
+ end
50
+
51
+ describe RiCal::RequiredTimezones do
52
+ before(:each) do
53
+ @it = RiCal::RequiredTimezones.new
54
+ end
55
+
56
+ def localtime_and_zone(date_time, tzid = "US/Eastern")
57
+ [dt_prop(DateTime.parse(date_time), tzid), tzid]
58
+ end
59
+
60
+
61
+ it "should create a RequiredTimezone for each new timezone presented" do
62
+ @it.add_datetime(*localtime_and_zone("Mar 22, 2009 1:00"))
63
+ @it.add_datetime(*localtime_and_zone("Apr 16, 2008 12:00", "US/Central"))
64
+ @it.add_datetime(*localtime_and_zone("Apr 16, 2008 12:00"))
65
+ @it.required_zones.map {|zone| zone.tzid}.sort.should == ["US/Central", "US/Eastern"]
66
+ end
67
+ 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 do
6
+
7
+
8
+ describe "#parse" do
9
+
10
+ before(:each) do
11
+ @mock_parser = mock("parser", :parse => [])
12
+ RiCal::Parser.stub!(:new).and_return(@mock_parser)
13
+ end
14
+
15
+ it "should create a parser using the io parameter" do
16
+ io = StringIO.new("")
17
+ RiCal::Parser.should_receive(:new).with(io).and_return(@mock_parser)
18
+ RiCal.parse(io)
19
+ end
20
+
21
+ it "should delegate to the parser" do
22
+ io = StringIO.new("")
23
+ @mock_parser.should_receive(:parse)
24
+ RiCal.parse(io)
25
+ end
26
+
27
+ it "should return the results of the parse" do
28
+ io = StringIO.new("")
29
+ @mock_parser.stub!(:parse).and_return(:parse_result)
30
+ RiCal.parse(io).should == :parse_result
31
+ end
32
+ end
33
+
34
+ describe "#parse_string" do
35
+ before(:each) do
36
+ @mock_io = :mock_io
37
+ StringIO.stub!(:new).and_return(@mock_io)
38
+ RiCal.stub!(:parse)
39
+ end
40
+
41
+ it "should create a StringIO from the string" do
42
+ string = "test string"
43
+ StringIO.should_receive(:new).with(string)
44
+ RiCal.parse_string(string)
45
+ end
46
+
47
+ it "should parse" do
48
+ RiCal.should_receive(:parse).with(@mock_io)
49
+ RiCal.parse_string("")
50
+ end
51
+ end
52
+
53
+ end
@@ -0,0 +1,4 @@
1
+ --colour
2
+ --format profile
3
+ --reverse
4
+ --backtrace
@@ -0,0 +1,50 @@
1
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), %w[.. lib ri_cal]))
4
+ require 'cgi'
5
+ require 'tzinfo'
6
+
7
+
8
+ module Kernel
9
+ if ENV.keys.find {|env_var| env_var.match(/^TM_/)}
10
+ def rputs(*args)
11
+ puts( *["<pre>", args.collect {|a| CGI.escapeHTML(a.to_s)}, "</pre>"])
12
+ end
13
+ else
14
+ alias_method :rputs, :puts
15
+ end
16
+ end
17
+
18
+ def date_time_with_zone(date_time, tzid = "US/Eastern")
19
+ date_time.dup.set_tzid(tzid)
20
+ end
21
+
22
+ def dt_prop(date_time, tzid = "US/Eastern")
23
+ RiCal::PropertyValue::DateTime.convert(nil, date_time_with_zone(date_time, tzid))
24
+ end
25
+
26
+ def offset_for_tzid(year, month, day, hour, min, sec, tzid, alternate)
27
+ tz = TZInfo::Timezone.get(tzid) rescue nil
28
+ if tz
29
+ Rational(tz.period_for_local(DateTime.civil(year, month, day, hour, min, sec)).utc_total_offset, 86400)
30
+ else
31
+ provided_offset
32
+ end
33
+ end
34
+
35
+ def rectify_ical(string)
36
+ string.gsub(/^\s+/, "")
37
+ end
38
+
39
+ if RiCal::TimeWithZone
40
+ def result_time_in_zone(year, month, day, hour, min, sec, tzid, alternate_offset = nil)
41
+ DateTime.civil(year, month, day, hour, min, sec,
42
+ offset_for_tzid(year, month, day, hour, min, sec, tzid, alternate_offset)).in_time_zone(tzid)
43
+ end
44
+ else
45
+ def result_time_in_zone(year, month, day, hour, min, sec, tzid, alternate_offset = nil)
46
+ DateTime.civil(year, month, day, hour, min, sec,
47
+ offset_for_tzid(year, month, day, hour, min, sec, tzid, alternate_offset)).set_tzid(tzid)
48
+ end
49
+ end
50
+
@@ -0,0 +1,3 @@
1
+ require 'rubygems'
2
+ gem 'activesupport', '>=2.2'
3
+ require 'active_support'
@@ -0,0 +1,2 @@
1
+ require 'rubygems'
2
+ require 'tzinfo'
@@ -0,0 +1,412 @@
1
+ #require 'active_support'
2
+ require 'yaml'
3
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
4
+ #
5
+ # code stolen from ActiveSupport Gem
6
+ unless String.instance_methods.include?("camelize")
7
+ class String
8
+ def camelize
9
+ self.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
10
+ end
11
+ end
12
+ end
13
+
14
+ class VEntityUpdater
15
+ Pluralization = {
16
+ "attach" => "attachments",
17
+ "categories" => "multiple_categories",
18
+ "free_busy" => "free_busies",
19
+ "security_class" => "security_classes",
20
+ "request_status" => "request_statuses",
21
+ "related_to" => "multiple_related_to",
22
+ "resources" => "multiple_resources"
23
+ }
24
+ def initialize(target, defs_file)
25
+ @target = target
26
+ @name=File.basename(target).sub(".rb","")
27
+ @indent = ""
28
+ @property_map = {}
29
+ @property_defs = YAML.load_file(defs_file)
30
+ @all_props = {}
31
+ @date_time_props = []
32
+ end
33
+
34
+ def property(prop_name_or_hash)
35
+ if Hash === prop_name_or_hash
36
+ name = prop_name_or_hash.keys[0]
37
+ override_options = prop_name_or_hash[name] || {}
38
+ else
39
+ name = prop_name_or_hash
40
+ override_options = {}
41
+ end
42
+ standard_options = @property_defs[name]
43
+ unless standard_options
44
+ puts "**** WARNING, no definition found for property #{name}"
45
+ standard_options = {}
46
+ end
47
+ options = {'type' => 'Text', 'ruby_name' => name}.merge(standard_options).merge(override_options)
48
+ named_property(name, options)
49
+ end
50
+
51
+ def indent(string)
52
+ @outfile.puts("#{@indent}#{string}")
53
+ end
54
+
55
+ def comment(*strings)
56
+ strings.each do |string|
57
+ indent("\# #{string}")
58
+ end
59
+ end
60
+
61
+ def no_doc(string)
62
+ indent("#{string} \# :nodoc:")
63
+ end
64
+
65
+ def blank_line
66
+ @outfile.puts
67
+ end
68
+
69
+ def describe_type(type)
70
+ case type
71
+ when 'date_time_or_date'
72
+ "either DateTime or Date"
73
+ when 'Text'
74
+ 'String'
75
+ else
76
+ type
77
+ end
78
+ end
79
+
80
+ def describe_property(type)
81
+ case type
82
+ when 'date_time_or_date'
83
+ "either RiCal::PropertyValue::DateTime or RiCal::PropertyValue::Date"
84
+ else
85
+ "RiCal::PropertyValue#{type}"
86
+ end
87
+ end
88
+
89
+ def type_class(type)
90
+ if type == 'date_time_or_date'
91
+ "RiCal::PropertyValue::DateTime"
92
+ else
93
+ "RiCal::PropertyValue::#{type}"
94
+ end
95
+ end
96
+
97
+ def cast_value(ruby_val, type)
98
+ "#{type_class(type)}.convert(self, #{ruby_val.inspect})"
99
+ end
100
+
101
+ def lazy_init_var(var, options)
102
+ const_val = options["constant_value"]
103
+ default_val = options["default_value"]
104
+ if options["multi"]
105
+ puts("*** Warning default_value of #{default_val} for multi-value property #{name} ignored") if default_val
106
+ puts("*** Warning const_value of #{const_val} for multi-value property #{name} ignored") if const_val
107
+ if var
108
+ "@#{var} ||= []"
109
+ else
110
+ "[]"
111
+ end
112
+ else
113
+ puts("*** Warning default_value of #{default_val} for property #{name} with constant_value of #{const_val}") if const_val && default_val
114
+ init_val = const_val || default_val
115
+ if init_val
116
+ if var
117
+ "@#{var} ||= #{cast_value(init_val, options["type"])}"
118
+ else
119
+ init_val.inspect
120
+ end
121
+ else
122
+ "@#{var}"
123
+ end
124
+ end
125
+ end
126
+
127
+ def pluralize(name)
128
+ Pluralization[name] || "#{name}s"
129
+ end
130
+
131
+ def named_property(name, options)
132
+ ruby_name = options['ruby_name']
133
+ multi = options['multi']
134
+ type = options['type']
135
+ rfc_ref = options['rfc_ref']
136
+ conflicts = options['conflicts_with']
137
+ type_constraint = options['type_constraint']
138
+ options.keys.each do |opt_key|
139
+ unless %w{
140
+ ruby_name
141
+ type
142
+ multi
143
+ rfc_ref
144
+ conflicts_with
145
+ purpose
146
+ constant_value
147
+ default_value
148
+ }.include?(opt_key)
149
+ puts "**** WARNING: unprocessed option key #{opt_key} for property #{name}"
150
+ end
151
+ end
152
+ if conflicts
153
+ mutually_exclusive(name, *conflicts)
154
+ end
155
+ needs_tz_access = %w{OccurrenceList date_time_or_date DateTime Date}.include?(type)
156
+ ruby_name = ruby_name.tr("-", "_")
157
+ ruby_method = ruby_name.downcase
158
+ property = "#{name.tr("-", "_").downcase}_property"
159
+ if type_constraint != 'must_be_utc' && %w[date_time_or_date DateTime Period OccurrenceList].include?(type)
160
+ @date_time_props << property
161
+ end
162
+ @all_props[property] = name.upcase
163
+ @property_map[name.upcase] = :"#{property}_from_string"
164
+ parent_set = needs_tz_access ? " ? property_value.for_parent(self) : nil" : ""
165
+ if type == 'date_time_or_date'
166
+ line_evaluator = "RiCal::PropertyValue::DateTime.or_date(self, line)"
167
+ else
168
+ line_evaluator = "#{type_class(type)}.new(self, line)"
169
+ end
170
+
171
+ if %w{Array, OccurrenceList}.include?(type)
172
+ ruby_val_parm = "*ruby_value"
173
+ val_parm = "*val"
174
+ else
175
+ ruby_val_parm = "ruby_value"
176
+ val_parm = "val"
177
+ end
178
+ blank_line
179
+ if multi
180
+ comment(
181
+ "return the the #{name.upcase} property",
182
+ "which will be an array of instances of #{describe_property(type)}"
183
+ )
184
+ comment("", "[purpose (from RFC 2445)]", options["purpose"]) if options["purpose"]
185
+ comment("", "see RFC 2445 #{rfc_ref}") if rfc_ref
186
+ indent("def #{property}")
187
+ indent(" #{lazy_init_var(property,options)}")
188
+ indent("end")
189
+ unless (options["constant_value"])
190
+ plural_ruby_method = pluralize(ruby_method)
191
+ blank_line
192
+ comment("set the the #{name.upcase} property")
193
+ comment("one or more instances of #{describe_property(type)} may be passed to this method")
194
+ indent("def #{property}=(*property_values)")
195
+ if needs_tz_access
196
+ indent(" @#{property}= property_values.map{|prop| prop.for_parent(self)}")
197
+ else
198
+ indent(" @#{property}= property_values")
199
+ end
200
+ indent("end")
201
+ blank_line
202
+ comment("set the value of the #{name.upcase} property to multiple values")
203
+ comment("one or more instances of #{describe_type(type)} may be passed to this method")
204
+ indent("def #{plural_ruby_method}=(ruby_values)")
205
+ indent(" @#{property} = ruby_values.map {|val| #{type_class(type)}.convert(self, #{val_parm})}")
206
+ indent("end")
207
+ blank_line
208
+ comment("set the value of the #{name.upcase} property to a single value")
209
+ comment("one instance of #{describe_type(type)} may be passed to this method")
210
+ indent("def #{ruby_method}=(#{ruby_val_parm})")
211
+ indent(" @#{property} = [#{type_class(type)}.convert(self, #{ruby_val_parm})]")
212
+ indent("end")
213
+ blank_line
214
+ comment("add one or more values to the #{name.upcase} property")
215
+ comment("one or more instances of #{describe_type(type)} may be passed to this method")
216
+ indent("def add_#{plural_ruby_method}(*ruby_values)")
217
+ indent(" ruby_values.each {|val| self.#{property} << #{type_class(type)}.convert(self, #{val_parm})}")
218
+ indent("end")
219
+ blank_line
220
+ comment("add one value to the #{name.upcase} property")
221
+ comment("one instances of #{describe_type(type)} may be passed to this method")
222
+ indent("def add_#{ruby_method}(#{ruby_val_parm})")
223
+ indent(" self.#{property} << #{type_class(type)}.convert(self, #{ruby_val_parm})")
224
+ indent("end")
225
+ blank_line
226
+ comment("remove one or more values from the #{name.upcase} property")
227
+ comment("one or more instances of #{describe_type(type)} may be passed to this method")
228
+ indent("def remove_#{plural_ruby_method}(*ruby_values)")
229
+ indent(" ruby_values.each {|val| self.#{property}.delete(#{type_class(type)}.convert(self, #{val_parm}))}")
230
+ indent("end")
231
+ blank_line
232
+ comment("remove one value from the #{name.upcase} property")
233
+ comment("one instances of #{describe_type(type)} may be passed to this method")
234
+ indent("def remove_#{ruby_method}(#{ruby_val_parm})")
235
+ indent(" self.#{property}.delete(#{type_class(type)}.convert(self, #{ruby_val_parm}))")
236
+ indent("end")
237
+ end
238
+ blank_line
239
+ comment("return the value of the #{name.upcase} property")
240
+ comment("which will be an array of instances of #{describe_type(type)}")
241
+ indent("def #{ruby_method}")
242
+ indent(" #{property}.map {|prop| prop ? prop.ruby_value : prop}")
243
+ indent("end")
244
+ blank_line
245
+ no_doc("def #{property}_from_string(line)")
246
+ indent(" #{property} << #{line_evaluator}")
247
+ indent("end")
248
+ else
249
+ comment(
250
+ "return the the #{name.upcase} property",
251
+ "which will be an instances of #{describe_property(type)}"
252
+ )
253
+ comment("", "[purpose (from RFC 2445)]", options["purpose"]) if options["purpose"]
254
+ comment("", "see RFC 2445 #{rfc_ref}") if rfc_ref
255
+ indent("def #{property}")
256
+ indent(" #{lazy_init_var(property,options)}")
257
+ indent("end")
258
+ unless (options["constant_value"])
259
+ blank_line
260
+ comment("set the #{name.upcase} property")
261
+ comment("property value should be an instance of #{describe_property(type)}")
262
+ indent("def #{property}=(property_value)")
263
+ indent(" @#{property} = property_value#{parent_set}")
264
+ if conflicts
265
+ conflicts.each do |conflict|
266
+ indent(" @#{conflict}_property = nil")
267
+ end
268
+ end
269
+ indent("end")
270
+ blank_line
271
+ comment("set the value of the #{name.upcase} property")
272
+ indent("def #{ruby_method}=(ruby_value)")
273
+ indent(" self.#{property}= #{type_class(type)}.convert(self, ruby_value)")
274
+ indent("end")
275
+ end
276
+ blank_line
277
+ comment("return the value of the #{name.upcase} property")
278
+ comment("which will be an instance of #{describe_type(type)}")
279
+ indent("def #{ruby_method}")
280
+ indent(" #{property} ? #{property}.ruby_value : nil")
281
+ indent("end")
282
+ blank_line
283
+ no_doc("def #{property}_from_string(line)")
284
+ indent(" @#{property} = #{line_evaluator}")
285
+ indent("end")
286
+ @outfile.puts
287
+ end
288
+ end
289
+
290
+ def mutually_exclusive *prop_names
291
+ exclusives = prop_names.map {|str| :"#{str}_property"}.sort {|a, b| a.to_s <=> b.to_s}
292
+ unless mutually_exclusive_properties.include?(exclusives)
293
+ mutually_exclusive_properties << prop_names.map {|str| :"#{str}_property"}
294
+ end
295
+ end
296
+
297
+ def mutually_exclusive_properties
298
+ @mutually_exclusive_properties ||= []
299
+ end
300
+
301
+ def generate_support_methods
302
+ blank_line
303
+ indent("def export_properties_to(export_stream) #:nodoc:")
304
+ @all_props.each do |prop_attr, prop_name|
305
+ indent(" export_prop_to(export_stream, #{prop_name.inspect}, @#{prop_attr})")
306
+ end
307
+ indent("end")
308
+ blank_line
309
+ indent("def ==(o) #:nodoc:")
310
+ indent(" if o.class == self.class")
311
+ @all_props.keys.each_with_index do |prop_name, i|
312
+ and_str = i < @all_props.length - 1 ? " &&" : ""
313
+ indent(" (#{prop_name} == o.#{prop_name})#{and_str}")
314
+ end
315
+ indent(" else")
316
+ indent(" super")
317
+ indent(" end")
318
+ indent("end")
319
+ blank_line
320
+ indent("def initialize_copy(o) #:nodoc:")
321
+ indent(" super")
322
+ @all_props.each_key do |prop_name|
323
+ indent(" #{prop_name} = #{prop_name} && #{prop_name}.dup")
324
+ end
325
+ indent("end")
326
+ blank_line
327
+ indent("def add_date_times_to(required_timezones) #:nodoc:")
328
+ @date_time_props.each do | prop_name|
329
+ indent(" add_property_date_times_to(required_timezones, #{prop_name})")
330
+ end
331
+ indent("end")
332
+ blank_line
333
+ indent("module ClassMethods #:nodoc:")
334
+ indent(" def property_parser #:nodoc:")
335
+ indent(" #{@property_map.inspect}")
336
+ indent(" end")
337
+ indent("end")
338
+ blank_line
339
+ indent("def self.included(mod) #:nodoc:")
340
+ indent(" mod.extend ClassMethods")
341
+ indent("end")
342
+ blank_line
343
+ indent("def mutual_exclusion_violation #:nodoc:")
344
+ if mutually_exclusive_properties.empty?
345
+ indent(" false")
346
+ else
347
+ mutually_exclusive_properties.each do |mutex_set|
348
+ indent(" return true if #{mutex_set.inspect}.inject(0) {|sum, prop| send(prop) ? sum + 1 : sum} > 1")
349
+ end
350
+ indent(" false")
351
+ end
352
+ indent "end"
353
+ end
354
+
355
+ def update
356
+ File.open(File.join(File.dirname(__FILE__), '..', 'lib', 'ri_cal', 'properties' , "#{@name}.rb"), 'w') do |ruby_out_file|
357
+ @outfile = ruby_out_file
358
+ module_name = @name.camelize
359
+ class_name = module_name.sub(/Properties$/, "")
360
+ ruby_out_file.puts("module RiCal")
361
+ ruby_out_file.puts(" module Properties #:nodoc:")
362
+ @indent = " "
363
+ ruby_out_file.puts(" #- ©2009 Rick DeNatale")
364
+ ruby_out_file.puts(" #- All rights reserved. Refer to the file README.txt for the license")
365
+ ruby_out_file.puts(" #")
366
+ ruby_out_file.puts(" # Properties::#{module_name} provides property accessing methods for the #{class_name} class")
367
+ ruby_out_file.puts(" # This source file is generated by the rical:gen_propmodules rake tasks, DO NOT EDIT")
368
+ ruby_out_file.puts(" module #{module_name}")
369
+ @indent = " "
370
+ YAML.load_file(File.join(File.dirname(__FILE__), '..', 'component_attributes', "#{@name}.yml")).each do |att_def|
371
+ property(att_def)
372
+ end
373
+ generate_support_methods
374
+ ruby_out_file.puts(" end")
375
+ ruby_out_file.puts(" end")
376
+ ruby_out_file.puts("end")
377
+ end
378
+ @outfile = nil
379
+ end
380
+ end
381
+
382
+ def updateTask srcGlob, taskSymbol
383
+ targetDir = File.join(File.dirname(__FILE__), '..', 'lib', 'ri_cal', 'properties')
384
+ defsFile = File.join(File.dirname(__FILE__), '..', 'component_attributes', 'component_property_defs.yml')
385
+ FileList[srcGlob].each do |f|
386
+ unless f == defsFile
387
+ target = File.join targetDir, File.basename(f).gsub(".yml", ".rb")
388
+ file target => [f, defsFile, __FILE__] do |t|
389
+ VEntityUpdater.new(target, defsFile).update
390
+ end
391
+ task taskSymbol => target
392
+ end
393
+ end
394
+ end
395
+
396
+
397
+ namespace :rical do
398
+
399
+ desc '(RE)Generate VEntity attributes'
400
+ task :gen_propmodules do |t|
401
+ end
402
+
403
+ updateTask File.join(File.dirname(__FILE__), '..', '/component_attributes', '*.yml'), :gen_propmodules
404
+
405
+ end # namespace :rical
406
+
407
+ desc 'add or update copyright in code and specs'
408
+ task :copyrights do
409
+ require 'mmcopyrights'
410
+ MM::Copyrights.process('lib', "rb", "#-", IO.read('copyrights.txt'))
411
+ MM::Copyrights.process('spec', "rb", "#-", IO.read('copyrights.txt'))
412
+ end