demingfactor-ri_cal 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. checksums.yaml +7 -0
  2. data/History.txt +402 -0
  3. data/Manifest.txt +161 -0
  4. data/README.txt +410 -0
  5. data/Rakefile +69 -0
  6. data/VERSION +1 -0
  7. data/bin/ri_cal +8 -0
  8. data/component_attributes/alarm.yml +10 -0
  9. data/component_attributes/calendar.yml +4 -0
  10. data/component_attributes/component_property_defs.yml +180 -0
  11. data/component_attributes/event.yml +45 -0
  12. data/component_attributes/freebusy.yml +16 -0
  13. data/component_attributes/journal.yml +35 -0
  14. data/component_attributes/timezone.yml +3 -0
  15. data/component_attributes/timezone_period.yml +11 -0
  16. data/component_attributes/todo.yml +46 -0
  17. data/copyrights.txt +1 -0
  18. data/docs/draft-ietf-calsify-2446bis-08.txt +7280 -0
  19. data/docs/draft-ietf-calsify-rfc2445bis-09.txt +10416 -0
  20. data/docs/incrementers.txt +7 -0
  21. data/docs/rfc2445.pdf +0 -0
  22. data/lib/ri_cal/component/alarm.rb +19 -0
  23. data/lib/ri_cal/component/calendar.rb +257 -0
  24. data/lib/ri_cal/component/event.rb +58 -0
  25. data/lib/ri_cal/component/freebusy.rb +16 -0
  26. data/lib/ri_cal/component/journal.rb +27 -0
  27. data/lib/ri_cal/component/non_standard.rb +33 -0
  28. data/lib/ri_cal/component/t_z_info_timezone.rb +153 -0
  29. data/lib/ri_cal/component/timezone/daylight_period.rb +25 -0
  30. data/lib/ri_cal/component/timezone/standard_period.rb +23 -0
  31. data/lib/ri_cal/component/timezone/timezone_period.rb +76 -0
  32. data/lib/ri_cal/component/timezone.rb +197 -0
  33. data/lib/ri_cal/component/todo.rb +42 -0
  34. data/lib/ri_cal/component.rb +256 -0
  35. data/lib/ri_cal/core_extensions/array/conversions.rb +15 -0
  36. data/lib/ri_cal/core_extensions/array.rb +7 -0
  37. data/lib/ri_cal/core_extensions/date/conversions.rb +56 -0
  38. data/lib/ri_cal/core_extensions/date.rb +13 -0
  39. data/lib/ri_cal/core_extensions/date_time/conversions.rb +50 -0
  40. data/lib/ri_cal/core_extensions/date_time.rb +15 -0
  41. data/lib/ri_cal/core_extensions/object/conversions.rb +20 -0
  42. data/lib/ri_cal/core_extensions/object.rb +8 -0
  43. data/lib/ri_cal/core_extensions/string/conversions.rb +57 -0
  44. data/lib/ri_cal/core_extensions/string.rb +8 -0
  45. data/lib/ri_cal/core_extensions/time/calculations.rb +153 -0
  46. data/lib/ri_cal/core_extensions/time/conversions.rb +42 -0
  47. data/lib/ri_cal/core_extensions/time/tzid_access.rb +50 -0
  48. data/lib/ri_cal/core_extensions/time/week_day_predicates.rb +55 -0
  49. data/lib/ri_cal/core_extensions/time.rb +14 -0
  50. data/lib/ri_cal/core_extensions.rb +11 -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 +148 -0
  58. data/lib/ri_cal/properties/alarm.rb +390 -0
  59. data/lib/ri_cal/properties/calendar.rb +164 -0
  60. data/lib/ri_cal/properties/event.rb +1523 -0
  61. data/lib/ri_cal/properties/freebusy.rb +593 -0
  62. data/lib/ri_cal/properties/journal.rb +1237 -0
  63. data/lib/ri_cal/properties/timezone.rb +150 -0
  64. data/lib/ri_cal/properties/timezone_period.rb +416 -0
  65. data/lib/ri_cal/properties/todo.rb +1559 -0
  66. data/lib/ri_cal/properties.rb +12 -0
  67. data/lib/ri_cal/property_value/array.rb +27 -0
  68. data/lib/ri_cal/property_value/cal_address.rb +11 -0
  69. data/lib/ri_cal/property_value/date.rb +184 -0
  70. data/lib/ri_cal/property_value/date_time/additive_methods.rb +44 -0
  71. data/lib/ri_cal/property_value/date_time/time_machine.rb +159 -0
  72. data/lib/ri_cal/property_value/date_time/timezone_support.rb +100 -0
  73. data/lib/ri_cal/property_value/date_time.rb +359 -0
  74. data/lib/ri_cal/property_value/duration.rb +110 -0
  75. data/lib/ri_cal/property_value/geo.rb +11 -0
  76. data/lib/ri_cal/property_value/integer.rb +12 -0
  77. data/lib/ri_cal/property_value/occurrence_list.rb +144 -0
  78. data/lib/ri_cal/property_value/period.rb +86 -0
  79. data/lib/ri_cal/property_value/recurrence_rule/enumeration_support_methods.rb +100 -0
  80. data/lib/ri_cal/property_value/recurrence_rule/enumerator.rb +79 -0
  81. data/lib/ri_cal/property_value/recurrence_rule/initialization_methods.rb +148 -0
  82. data/lib/ri_cal/property_value/recurrence_rule/negative_setpos_enumerator.rb +53 -0
  83. data/lib/ri_cal/property_value/recurrence_rule/numbered_span.rb +31 -0
  84. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_day_incrementer.rb +86 -0
  85. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_hour_incrementer.rb +31 -0
  86. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_minute_incrementer.rb +32 -0
  87. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_month_incrementer.rb +52 -0
  88. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_monthday_incrementer.rb +31 -0
  89. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_numbered_day_incrementer.rb +38 -0
  90. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_second_incrementer.rb +32 -0
  91. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_weekno_incrementer.rb +69 -0
  92. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_yearday_incrementer.rb +31 -0
  93. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/daily_incrementer.rb +28 -0
  94. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/frequency_incrementer.rb +80 -0
  95. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/hourly_incrementer.rb +23 -0
  96. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/list_incrementer.rb +106 -0
  97. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/minutely_incrementer.rb +23 -0
  98. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/monthly_incrementer.rb +33 -0
  99. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/null_sub_cycle_incrementer.rb +43 -0
  100. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/secondly_incrementer.rb +28 -0
  101. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/weekly_incrementer.rb +37 -0
  102. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/yearly_incrementer.rb +57 -0
  103. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer.rb +135 -0
  104. data/lib/ri_cal/property_value/recurrence_rule/recurring_day.rb +131 -0
  105. data/lib/ri_cal/property_value/recurrence_rule/recurring_month_day.rb +64 -0
  106. data/lib/ri_cal/property_value/recurrence_rule/recurring_numbered_week.rb +33 -0
  107. data/lib/ri_cal/property_value/recurrence_rule/recurring_year_day.rb +53 -0
  108. data/lib/ri_cal/property_value/recurrence_rule/time_manipulation.rb +42 -0
  109. data/lib/ri_cal/property_value/recurrence_rule/validations.rb +125 -0
  110. data/lib/ri_cal/property_value/recurrence_rule.rb +154 -0
  111. data/lib/ri_cal/property_value/text.rb +44 -0
  112. data/lib/ri_cal/property_value/uri.rb +11 -0
  113. data/lib/ri_cal/property_value/utc_offset.rb +33 -0
  114. data/lib/ri_cal/property_value/zulu_date_time.rb +34 -0
  115. data/lib/ri_cal/property_value.rb +159 -0
  116. data/lib/ri_cal/required_timezones.rb +55 -0
  117. data/lib/ri_cal.rb +187 -0
  118. data/parked_specs/ri_cal/claudio_a_bug_spec.rb +100 -0
  119. data/performance/empty_propval/subject.rb +43 -0
  120. data/performance/paris_eastern/subject.rb +90 -0
  121. data/performance/penultimate_weekday/subject.rb +15 -0
  122. data/performance/psm_big_enum/ical.ics +3171 -0
  123. data/performance/psm_big_enum/subject.rb +16 -0
  124. data/performance/utah_cycling/subject.rb +55 -0
  125. data/ri_cal.gemspec +244 -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 +276 -0
  133. data/spec/ri_cal/component/alarm_spec.rb +12 -0
  134. data/spec/ri_cal/component/calendar_spec.rb +88 -0
  135. data/spec/ri_cal/component/event_spec.rb +735 -0
  136. data/spec/ri_cal/component/freebusy_spec.rb +12 -0
  137. data/spec/ri_cal/component/journal_spec.rb +37 -0
  138. data/spec/ri_cal/component/t_z_info_timezone_spec.rb +60 -0
  139. data/spec/ri_cal/component/timezone_spec.rb +236 -0
  140. data/spec/ri_cal/component/todo_spec.rb +112 -0
  141. data/spec/ri_cal/component_spec.rb +224 -0
  142. data/spec/ri_cal/core_extensions/string/conversions_spec.rb +78 -0
  143. data/spec/ri_cal/core_extensions/time/calculations_spec.rb +188 -0
  144. data/spec/ri_cal/core_extensions/time/week_day_predicates_spec.rb +45 -0
  145. data/spec/ri_cal/fast_date_time_spec.rb +77 -0
  146. data/spec/ri_cal/inf_loop_spec.rb +78 -0
  147. data/spec/ri_cal/occurrence_enumerator_spec.rb +611 -0
  148. data/spec/ri_cal/parser_spec.rb +337 -0
  149. data/spec/ri_cal/property_value/date_spec.rb +53 -0
  150. data/spec/ri_cal/property_value/date_time_spec.rb +383 -0
  151. data/spec/ri_cal/property_value/duration_spec.rb +126 -0
  152. data/spec/ri_cal/property_value/occurrence_list_spec.rb +72 -0
  153. data/spec/ri_cal/property_value/period_spec.rb +63 -0
  154. data/spec/ri_cal/property_value/recurrence_rule/recurring_year_day_spec.rb +21 -0
  155. data/spec/ri_cal/property_value/recurrence_rule_spec.rb +1814 -0
  156. data/spec/ri_cal/property_value/text_spec.rb +25 -0
  157. data/spec/ri_cal/property_value/utc_offset_spec.rb +48 -0
  158. data/spec/ri_cal/property_value_spec.rb +125 -0
  159. data/spec/ri_cal/required_timezones_spec.rb +67 -0
  160. data/spec/ri_cal_spec.rb +53 -0
  161. data/spec/spec.opts +4 -0
  162. data/spec/spec_helper.rb +50 -0
  163. data/tasks/gem_loader/load_active_support.rb +3 -0
  164. data/tasks/gem_loader/load_tzinfo_gem.rb +2 -0
  165. data/tasks/ri_cal.rake +412 -0
  166. data/tasks/spec.rake +102 -0
  167. metadata +246 -0
@@ -0,0 +1,76 @@
1
+ module RiCal
2
+ class Component
3
+ class Timezone
4
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ # A TimezonePeriod is a component of a timezone representing a period during which a particular offset from UTC is
7
+ # in effect.
8
+ #
9
+ # to see the property accessing methods for this class see the RiCal::Properties::TimezonePeriod module
10
+ class TimezonePeriod < Component
11
+ include Properties::TimezonePeriod
12
+
13
+ include OccurrenceEnumerator
14
+
15
+ def occurrence_cache #:nodoc:
16
+ @occurrence_cache ||= []
17
+ end
18
+
19
+ def zone_identifier #:nodoc:
20
+ tzname.first
21
+ end
22
+
23
+ def dtend #:nodoc:
24
+ nil
25
+ end
26
+
27
+ def exdate_property #:nodoc:
28
+ nil
29
+ end
30
+
31
+ def utc_total_offset #:nodoc:
32
+ tzoffsetto_property.to_seconds
33
+ end
34
+
35
+ def exrule_property #:nodoc:
36
+ nil
37
+ end
38
+
39
+ def last_before_utc(utc_time) #:nodoc:
40
+ last_before_local(utc_time + tzoffsetfrom_property)
41
+ end
42
+
43
+ def fill_cache(local_time)
44
+ if occurrence_cache.empty? || occurrence_cache.last.dtstart_property <= local_time
45
+ while true
46
+ occurrence = enumeration_instance.next_occurrence
47
+ break unless occurrence
48
+ occurrence = recurrence(occurrence)
49
+ occurrence_cache << occurrence
50
+ break if occurrence.dtstart_property > local_time
51
+ end
52
+ end
53
+ end
54
+
55
+ def last_before_local(local_time) #:nodoc:
56
+ if recurs?
57
+ fill_cache(local_time)
58
+ cand_occurrence = nil
59
+ occurrence_cache.each do |occurrence|
60
+ return cand_occurrence if occurrence.dtstart_property > local_time
61
+ cand_occurrence = occurrence
62
+ end
63
+ return cand_occurrence
64
+ else
65
+ return self
66
+ end
67
+ end
68
+
69
+ def enumeration_instance
70
+ @enumeration_instance ||= super
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+
@@ -0,0 +1,197 @@
1
+ module RiCal
2
+ class Component
3
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
4
+ #
5
+ # An Timezone (VTIMEZONE) calendar component describes a timezone used within the calendar.
6
+ # A Timezone has two or more TimezonePeriod subcomponents which describe the transitions between
7
+ # standard and daylight saving time.
8
+ #
9
+ # to see the property accessing methods for this class see the RiCal::Properties::Timezone module
10
+ class Timezone < Component
11
+
12
+ autoload :TimezonePeriod, "ri_cal/component/timezone/timezone_period.rb"
13
+ autoload :StandardPeriod, "ri_cal/component/timezone/standard_period.rb"
14
+ autoload :DaylightPeriod, "ri_cal/component/timezone/daylight_period.rb"
15
+
16
+ include RiCal::Properties::Timezone
17
+
18
+ # The identifier of the timezone, e.g. "Europe/Paris".
19
+ def identifier
20
+ tzid
21
+ end
22
+
23
+ # An alias for identifier.
24
+ def name
25
+ # Don't use alias, as identifier gets overridden.
26
+ identifier
27
+ end
28
+
29
+ def rational_utc_offset(local) #:nodoc:
30
+ # 86400 is the number of seconds in a day
31
+ RiCal.RationalOffset[period_for_local(local, true).utc_total_offset]
32
+ end
33
+
34
+ # Returns the TimezonePeriod for the given UTC time. utc can either be a DateTime,
35
+ # Time or integer timestamp (Time.to_i). Any timezone information in utc is ignored (it is treated as a UTC time).
36
+ def period_for_utc(time)
37
+ last_period(last_before_utc(standard, time), last_before_utc(daylight, time))
38
+ end
39
+
40
+ # Returns the set of TimezonePeriod instances that are valid for the given
41
+ # local time as an array. If you just want a single period, use
42
+ # period_for_local instead and specify how ambiguities should be resolved.
43
+ # Returns an empty array if no periods are found for the given time.
44
+ def periods_for_local(local)
45
+ local = local.to_ri_cal_date_time_value
46
+ candidate_standard = last_before_local(standard, local)
47
+ candidate_daylight = last_before_local(daylight, local)
48
+ if candidate_daylight && candidate_daylight.swallows_local?(local, candidate_standard)
49
+ [] # Invalid local time
50
+ elsif candidate_standard
51
+ if candidate_daylight
52
+ if candidate_daylight.dtstart > candidate_standard.dtstart
53
+ [candidate_daylight]
54
+ elsif candidate_standard.ambiguous_local?(local)
55
+ [candidate_daylight, candidate_standard]
56
+ else
57
+ [candidate_standard].compact
58
+ end
59
+ else
60
+ [candidate_standard].compact
61
+ end
62
+ end
63
+ end
64
+
65
+
66
+ # Returns the TimezonePeriod for the given local time. local can either be
67
+ # a DateTime, Time or integer timestamp (Time.to_i). Any timezone
68
+ # information in local is ignored (it is treated as a time in the current
69
+ # timezone).
70
+ #
71
+ # Warning: There are local times that have no equivalent UTC times (e.g.
72
+ # in the transition from standard time to daylight savings time). There are
73
+ # also local times that have more than one UTC equivalent (e.g. in the
74
+ # transition from daylight savings time to standard time).
75
+ #
76
+ # In the first case (no equivalent UTC time), a PeriodNotFound exception
77
+ # will be raised.
78
+ #
79
+ # In the second case (more than one equivalent UTC time), an AmbiguousTime
80
+ # exception will be raised unless the optional dst parameter or block
81
+ # handles the ambiguity.
82
+ #
83
+ # If the ambiguity is due to a transition from daylight savings time to
84
+ # standard time, the dst parameter can be used to select whether the
85
+ # daylight savings time or local time is used. For example,
86
+ #
87
+ # Timezone.get('America/New_York').period_for_local(DateTime.new(2004,10,31,1,30,0))
88
+ #
89
+ # would raise an AmbiguousTime exception.
90
+ #
91
+ # Specifying dst=true would the daylight savings period from April to
92
+ # October 2004. Specifying dst=false would return the standard period
93
+ # from October 2004 to April 2005.
94
+ #
95
+ # If the dst parameter does not resolve the ambiguity, and a block is
96
+ # specified, it is called. The block must take a single parameter - an
97
+ # array of the periods that need to be resolved. The block can select and
98
+ # return a single period or return nil or an empty array
99
+ # to cause an AmbiguousTime exception to be raised.
100
+ #
101
+ # TODO: need to check for ambiguity
102
+ def period_for_local(local, dst=nil)
103
+ results = periods_for_local(local)
104
+
105
+ if results.empty?
106
+ raise TZInfo::PeriodNotFound
107
+ elsif results.size < 2
108
+ results.first
109
+ else
110
+ # ambiguous result try to resolve
111
+
112
+ unless dst.nil?
113
+ matches = results.find_all {|period| period.dst? == dst}
114
+ results = matches unless matches.empty?
115
+ end
116
+
117
+ if results.size < 2
118
+ results.first
119
+ else
120
+ # still ambiguous, try the block
121
+
122
+ if block_given?
123
+ results = yield results
124
+ end
125
+
126
+ if results.is_a?(TimezonePeriod)
127
+ results
128
+ elsif results && results.size == 1
129
+ results.first
130
+ else
131
+ raise TZInfo::AmbiguousTime, "#{local} is an ambiguous local time."
132
+ end
133
+ end
134
+ end
135
+ end
136
+
137
+ # convert time from utc time to this time zone
138
+ def utc_to_local(time)
139
+ time = time.to_ri_cal_date_time_value
140
+ converted = time + period_for_utc(time).tzoffsetto_property
141
+ converted.tzid = identifier
142
+ converted
143
+ end
144
+
145
+ # convert time from this time zone to utc time
146
+ def local_to_utc(time)
147
+ time = time.to_ri_cal_date_time_value
148
+ period = period_for_local(time)
149
+ converted = time - period.tzoffsetto_property
150
+ converted.tzid = "UTC"
151
+ converted
152
+ end
153
+ end
154
+
155
+ def self.entity_name #:nodoc:
156
+ "VTIMEZONE"
157
+ end
158
+
159
+ def standard #:nodoc:
160
+ @subcomponents["STANDARD"]
161
+ end
162
+
163
+ def daylight #:nodoc:
164
+ @subcomponents["DAYLIGHT"]
165
+ end
166
+
167
+ def last_period(standard, daylight) #:nodoc:
168
+ if standard
169
+ if daylight
170
+ standard.dtstart > daylight.dtstart ? standard : daylight
171
+ else
172
+ standard
173
+ end
174
+ else
175
+ daylight
176
+ end
177
+ end
178
+
179
+ def last_before_utc(period_array, time) #:nodoc:
180
+ candidates = period_array.map {|period|
181
+ period.last_before_utc(time)
182
+ }
183
+ result = candidates.max {|a, b| a.dtstart_property <=> b.dtstart_property}
184
+ result
185
+ end
186
+
187
+ def last_before_local(period_array, time) #:nodoc:
188
+ candidates = period_array.map {|period|
189
+ period.last_before_local(time)
190
+ }
191
+ result = candidates.max {|a, b| a.dtstart_property <=> b.dtstart_property}
192
+ result
193
+ end
194
+ end
195
+ end
196
+
197
+
@@ -0,0 +1,42 @@
1
+ module RiCal
2
+ class Component
3
+ #- ©2009 Rick DeNatale
4
+ #- All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ # A Todo (VTODO) calendar component groups properties describing a to-do
7
+ # Todos may have multiple occurrences
8
+ #
9
+ # Todos may also contain one or more ALARM subcomponents
10
+ # to see the property accessing methods for this class see the RiCal::Properties::Todo module
11
+ # to see the methods for enumerating occurrences of recurring to-dos see the RiCal::OccurrenceEnumerator module
12
+ class Todo < Component
13
+ include Properties::Todo
14
+ include OccurrenceEnumerator
15
+
16
+ def self.entity_name #:nodoc:
17
+ "VTODO"
18
+ end
19
+
20
+ def subcomponent_class #:nodoc:
21
+ {:alarm => Alarm }
22
+ end
23
+
24
+ # Return a date_time representing the time at which the todo should start
25
+ def start_time
26
+ dtstart_property ? dtstart.to_datetime : nil
27
+ end
28
+
29
+ # Return a date_time representing the time at which the todo is due
30
+ def finish_time
31
+ if due
32
+ due_property.to_finish_time
33
+ elsif duration_property && dtstart_property
34
+ (dtstart_property + duration_property).to_finish_time
35
+ else
36
+ nil
37
+ end
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,256 @@
1
+ module RiCal
2
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
3
+ #
4
+ class Component #:nodoc:
5
+
6
+ autoload :Alarm, "ri_cal/component/alarm.rb"
7
+ autoload :Calendar, "ri_cal/component/calendar.rb"
8
+ autoload :Event, "ri_cal/component/event.rb"
9
+ autoload :Freebusy, "ri_cal/component/freebusy.rb"
10
+ autoload :Journal, "ri_cal/component/journal.rb"
11
+ autoload :NonStandard, "ri_cal/component/non_standard.rb"
12
+ autoload :TZInfoTimezone, "ri_cal/component/t_z_info_timezone.rb"
13
+ autoload :Timezone, "ri_cal/component/timezone.rb"
14
+ autoload :Todo, "ri_cal/component/todo.rb"
15
+
16
+ class ComponentBuilder #:nodoc:
17
+ def initialize(component)
18
+ @component = component
19
+ end
20
+
21
+ def method_missing(selector, *args, &init_block) #:nodoc:
22
+ if(sub_comp_class = @component.subcomponent_class[selector])
23
+ if init_block
24
+ sub_comp = sub_comp_class.new(@component)
25
+ if init_block.arity == 1
26
+ yield ComponentBuilder.new(sub_comp)
27
+ else
28
+ ComponentBuilder.new(sub_comp).instance_eval(&init_block)
29
+ end
30
+ self.add_subcomponent(sub_comp)
31
+ end
32
+ else
33
+ sel = selector.to_s
34
+ sel = "#{sel}=" unless /(^(add_)|(remove_))|(=$)/ =~ sel
35
+ if @component.respond_to?(sel)
36
+ @component.send(sel, *args)
37
+ else
38
+ super
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ attr_accessor :imported #:nodoc:
45
+
46
+ def initialize(parent=nil, entity_name = nil, &init_block) #:nodoc:
47
+ @parent = parent
48
+ if block_given?
49
+ if init_block.arity == 1
50
+ init_block.call(ComponentBuilder.new(self))
51
+ else
52
+ ComponentBuilder.new(self).instance_eval(&init_block)
53
+ end
54
+ end
55
+ end
56
+
57
+ def default_tzid #:nodoc:
58
+ if @parent
59
+ @parent.default_tzid
60
+ else
61
+ PropertyValue::DateTime.default_tzid
62
+ end
63
+ end
64
+
65
+ def find_timezone(identifier) #:nodoc:
66
+ if @parent
67
+ @parent.find_timezone(identifier)
68
+ else
69
+ begin
70
+ Calendar::TZInfoWrapper.new(TZInfo::Timezone.get(identifier), self)
71
+ rescue ::TZInfo::InvalidTimezoneIdentifier => ex
72
+ raise RiCal::InvalidTimezoneIdentifier.invalid_tzinfo_identifier(identifier)
73
+ end
74
+ end
75
+ end
76
+
77
+ def tz_info_source?
78
+ if @parent
79
+ @parent.tz_info_source?
80
+ else
81
+ true
82
+ end
83
+ end
84
+
85
+ def time_zone_for(ruby_object) #:nodoc:
86
+ @parent.time_zone_for(ruby_object) #:nodoc:
87
+ end
88
+
89
+ def subcomponent_class #:nodoc:
90
+ {}
91
+ end
92
+
93
+ def self.from_parser(parser, parent, entity_name) #:nodoc:
94
+ entity = self.new(parent, entity_name)
95
+ entity.imported = true
96
+ line = parser.next_separated_line
97
+ while parser.still_in(entity_name, line)
98
+ entity.process_line(parser, line)
99
+ line = parser.next_separated_line
100
+ end
101
+ entity
102
+ end
103
+
104
+ def self.parse(io) #:nodoc:
105
+ Parser.new(io).parse
106
+ end
107
+
108
+ def imported? #:nodoc:
109
+ imported
110
+ end
111
+
112
+ def self.parse_string(string) #:nodoc:
113
+ parse(StringIO.new(string))
114
+ end
115
+
116
+ def subcomponents #:nodoc:
117
+ @subcomponents ||= Hash.new {|h, k| h[k] = []}
118
+ end
119
+
120
+ def entity_name #:nodoc:
121
+ self.class.entity_name
122
+ end
123
+
124
+ # return an array of Alarm components within this component :nodoc:
125
+ # Alarms may be contained within Events, and Todos
126
+ def alarms
127
+ subcomponents["VALARM"]
128
+ end
129
+
130
+ def add_subcomponent(component) #:nodoc:
131
+ subcomponents[component.entity_name] << component
132
+ end
133
+
134
+ def parse_subcomponent(parser, line) #:nodoc:
135
+ subcomponents[line[:value]] << parser.parse_one(line, self)
136
+ end
137
+
138
+ def process_line(parser, line) #:nodoc:
139
+ if line[:name] == "BEGIN"
140
+ parse_subcomponent(parser, line)
141
+ else
142
+ setter = self.class.property_parser[line[:name]]
143
+ if setter
144
+ send(setter, line)
145
+ else
146
+ self.add_x_property(line[:name], PropertyValue::Text.new(self, line))
147
+ end
148
+ end
149
+ end
150
+
151
+ # return a hash of any extended properties, (i.e. those with a property name starting with "X-"
152
+ # representing an extension to the RFC 2445 specification)
153
+ def x_properties
154
+ @x_properties ||= Hash.new {|h,k| h[k] = []}
155
+ end
156
+
157
+ # Add a n extended property
158
+ def add_x_property(name, prop, debug=false)
159
+ x_properties[name.gsub("_","-").upcase] << prop.to_ri_cal_text_property
160
+ end
161
+
162
+ def method_missing(selector, *args, &b) #:nodoc:
163
+ xprop_candidate = selector.to_s
164
+ if (match = /^(x_.+)(=?)$/.match(xprop_candidate))
165
+ x_property_key = match[1].gsub('_','-').upcase
166
+ if match[2] == "="
167
+ args.each do |val|
168
+ add_x_property(x_property_key, val)
169
+ end
170
+ else
171
+ x_properties[x_property_key].map {|property| property.value}
172
+ end
173
+ else
174
+ super
175
+ end
176
+ end
177
+
178
+ # Predicate to determine if the component is valid according to RFC 2445
179
+ def valid?
180
+ !mutual_exclusion_violation
181
+ end
182
+
183
+ def initialize_copy(original) #:nodoc:
184
+ end
185
+
186
+ def prop_string(prop_name, *properties) #:nodoc:
187
+ properties = properties.flatten.compact
188
+ if properties && !properties.empty?
189
+ properties.map {|prop| "#{prop_name}#{prop.to_s}"}.join("\n")
190
+ else
191
+ nil
192
+ end
193
+ end
194
+
195
+ def add_property_date_times_to(required_timezones, property) #:nodoc:
196
+ if property
197
+ if Array === property
198
+ property.each do |prop|
199
+ prop.add_date_times_to(required_timezones)
200
+ end
201
+ else
202
+ property.add_date_times_to(required_timezones)
203
+ end
204
+ end
205
+ end
206
+
207
+ def export_prop_to(export_stream, name, prop) #:nodoc:
208
+ if prop
209
+ string = prop_string(name, prop)
210
+ export_stream.puts(string) if string
211
+ end
212
+ end
213
+
214
+ def export_x_properties_to(export_stream) #:nodoc:
215
+ x_properties.each do |name, props|
216
+ props.each do | prop |
217
+ export_stream.puts("#{name}:#{prop}")
218
+ end
219
+ end
220
+ end
221
+
222
+ def export_subcomponent_to(export_stream, subcomponent) #:nodoc:
223
+ subcomponent.each do |component|
224
+ component.export_to(export_stream)
225
+ end
226
+ end
227
+
228
+ # return a string containing the rfc2445 format of the component
229
+ def to_s
230
+ io = StringIO.new
231
+ export_to(io)
232
+ io.string
233
+ end
234
+
235
+ # Export this component to an export stream
236
+ def export_to(export_stream)
237
+ export_stream.puts("BEGIN:#{entity_name}")
238
+ export_properties_to(export_stream)
239
+ export_x_properties_to(export_stream)
240
+ subcomponents.values.each do |sub|
241
+ export_subcomponent_to(export_stream, sub)
242
+ end
243
+ export_stream.puts("END:#{entity_name}")
244
+ end
245
+
246
+ # Export this single component as an iCalendar component containing only this component and
247
+ # any required additional components (i.e. VTIMEZONES referenced from this component)
248
+ # if stream is nil (the default) then this method will return a string,
249
+ # otherwise stream should be an IO to which the iCalendar file contents will be written
250
+ def export(stream=nil)
251
+ wrapper_calendar = Calendar.new
252
+ wrapper_calendar.add_subcomponent(self)
253
+ wrapper_calendar.export(stream)
254
+ end
255
+ end
256
+ end
@@ -0,0 +1,15 @@
1
+ module RiCal
2
+ module CoreExtensions #:nodoc:
3
+ module Array #:nodoc:
4
+ #- ©2009 Rick DeNatale
5
+ #- All rights reserved. Refer to the file README.txt for the license
6
+ #
7
+ module Conversions
8
+ # return the concatenation of the elements representation in rfc 2445 format
9
+ def to_rfc2445_string # :doc:
10
+ join(",")
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ require "ri_cal/core_extensions/array/conversions.rb"
2
+ class Array #:nodoc:
3
+ #- ©2009 Rick DeNatale
4
+ #- All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ include RiCal::CoreExtensions::Array::Conversions
7
+ end
@@ -0,0 +1,56 @@
1
+ module RiCal
2
+ module CoreExtensions #:nodoc:
3
+ module Date #:nodoc:
4
+ #- ©2009 Rick DeNatale
5
+ #- All rights reserved. Refer to the file README.txt for the license
6
+ #
7
+ module Conversions #:nodoc:
8
+ # Return an RiCal::PropertyValue::DateTime representing the receiver
9
+ def to_ri_cal_date_time_value(timezone_finder = nil)
10
+ RiCal::PropertyValue::DateTime.new(timezone_finder, :value => self)
11
+ end
12
+
13
+ # Return an RiCal::PropertyValue::Date representing the receiver
14
+ def to_ri_cal_date_value(timezone_finder = nil)
15
+ RiCal::PropertyValue::Date.new(timezone_finder, :value => self)
16
+ end
17
+
18
+ alias_method :to_ri_cal_date_or_date_time_value, :to_ri_cal_date_value
19
+ alias_method :to_ri_cal_occurrence_list_value, :to_ri_cal_date_value
20
+
21
+ # Return the natural ri_cal_property for this object
22
+ def to_ri_cal_property_value(timezone_finder = nil)
23
+ to_ri_cal_date_value(timezone_finder)
24
+ end
25
+
26
+ def to_overlap_range_start
27
+ to_datetime
28
+ end
29
+
30
+ def to_overlap_range_end
31
+ to_ri_cal_date_time_value.end_of_day.to_datetime
32
+ end
33
+
34
+ unless Date.instance_methods.map {|selector| selector.to_sym}.include?(:to_date)
35
+ # A method to keep Time, Date and DateTime instances interchangeable on conversions.
36
+ # In this case, it simply returns +self+.
37
+ def to_date
38
+ self
39
+ end
40
+ end
41
+ unless Date.instance_methods.map {|selector| selector.to_sym}.include?(:to_datetime)
42
+ # Converts a Date instance to a DateTime, where the time is set to the beginning of the day
43
+ # and UTC offset is set to 0.
44
+ #
45
+ # ==== Examples
46
+ # date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
47
+ #
48
+ # date.to_datetime # => Sat, 10 Nov 2007 00:00:00 0000
49
+ def to_datetime
50
+ ::DateTime.civil(year, month, day, 0, 0, 0, 0)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,13 @@
1
+ require "ri_cal/core_extensions/date/conversions.rb"
2
+ require "ri_cal/core_extensions/time/week_day_predicates.rb"
3
+ require "ri_cal/core_extensions/time/calculations.rb"
4
+ require 'date'
5
+
6
+ class Date #:nodoc:
7
+ #- ©2009 Rick DeNatale
8
+ #- All rights reserved. Refer to the file README.txt for the license
9
+ #
10
+ include RiCal::CoreExtensions::Time::WeekDayPredicates
11
+ include RiCal::CoreExtensions::Time::Calculations
12
+ include RiCal::CoreExtensions::Date::Conversions
13
+ end