awallis-ri_cal 0.8.8

Sign up to get free protection for your applications and to get access to all the features.
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