friflaj_ri_cal 0.9.0

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