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,154 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
4
+ #
5
+ # RiCal::PropertyValue::RecurrenceRule represents an icalendar Recurrence Rule property value
6
+ # which is defined in
7
+ # rfc 2445 section 4.3.10 pp 40-45
8
+ class RecurrenceRule < PropertyValue
9
+
10
+ autoload :EnumerationSupportMethods, "ri_cal/property_value/recurrence_rule/enumeration_support_methods.rb"
11
+ autoload :OccurrenceIncrementer, "ri_cal/property_value/recurrence_rule/occurrence_incrementer.rb"
12
+ autoload :Enumerator, "ri_cal/property_value/recurrence_rule/enumerator.rb"
13
+ autoload :InitializationMethods, "ri_cal/property_value/recurrence_rule/initialization_methods.rb"
14
+ autoload :NegativeSetposEnumerator, "ri_cal/property_value/recurrence_rule/negative_setpos_enumerator.rb"
15
+ autoload :NumberedSpan, "ri_cal/property_value/recurrence_rule/numbered_span.rb"
16
+ autoload :RecurringDay, "ri_cal/property_value/recurrence_rule/recurring_day.rb"
17
+ autoload :RecurringMonthDay, "ri_cal/property_value/recurrence_rule/recurring_month_day.rb"
18
+ autoload :RecurringNumberedWeek, "ri_cal/property_value/recurrence_rule/recurring_numbered_week.rb"
19
+ autoload :RecurringYearDay, "ri_cal/property_value/recurrence_rule/recurring_year_day.rb"
20
+ autoload :TimeManipulation, "ri_cal/property_value/recurrence_rule/time_manipulation.rb"
21
+ autoload :Validations, "ri_cal/property_value/recurrence_rule/validations.rb"
22
+
23
+ def initialize(parent, value_hash) # :nodoc:
24
+ @by_list_hash = {}
25
+ super
26
+ init_by_lists
27
+ @by_list_hash = nil
28
+ end
29
+
30
+ def self.convert(parent, value) #:nodoc:
31
+ if String === value
32
+ result = new(parent, :value => value)
33
+ else
34
+ result = new(parent, value)
35
+ end
36
+ result
37
+ end
38
+
39
+ include Validations
40
+ include InitializationMethods
41
+ include EnumerationSupportMethods
42
+
43
+ # The integer count value of the receiver, or nil
44
+ attr_reader :count
45
+ # The DATE-TIME value of until limit of the receiver, or nil
46
+ attr_reader :until
47
+
48
+ def value=(string) # :nodoc:
49
+ if string
50
+ @value = string
51
+ dup_hash = {}
52
+ string.split(";").each do |value_part|
53
+ initialize_from_value_part(value_part, dup_hash)
54
+ end
55
+ end
56
+ end
57
+
58
+ # Set the frequency of the recurrence rule
59
+ # freq_value:: a String which should be in %w[SECONDLY MINUTELY HOURLY DAILY WEEKLY MONTHLY YEARLY]
60
+ #
61
+ # This method resets the receivers list of errors
62
+ def freq=(freq_value)
63
+ reset_errors
64
+ @freq = freq_value
65
+ end
66
+
67
+ # return the frequency of the rule which will be a string
68
+ def freq
69
+ @freq.upcase
70
+ end
71
+
72
+ # return the starting week day for the recurrence rule, which for a valid instance will be one of
73
+ # "SU", "MO", "TU", "WE", "TH", "FR", or "SA"
74
+ def wkst
75
+ @wkst || 'MO'
76
+ end
77
+
78
+ def wkst_day # :nodoc:
79
+ @wkst_day ||= (%w{SU MO TU WE FR SA}.index(wkst) || 1)
80
+ end
81
+
82
+ # Set the starting week day for the recurrence rule, which should be one of
83
+ # "SU", "MO", "TU", "WE", "TH", "FR", or "SA" for the instance to be valid.
84
+ # The parameter is however case-insensitive.
85
+ #
86
+ # This method resets the receivers list of errors
87
+ def wkst=(value)
88
+ reset_errors
89
+ @wkst = value
90
+ @wkst_day = nil
91
+ end
92
+
93
+ # Set the count parameter of the recurrence rule, the count value will be converted to an integer using to_i
94
+ #
95
+ # This method resets the receivers list of errors
96
+
97
+ def count=(count_value)
98
+ reset_errors
99
+ @count = count_value
100
+ @until = nil unless @count.nil? || @by_list_hash
101
+ end
102
+
103
+ # Set the until parameter of the recurrence rule
104
+ #
105
+ # until_value:: the value to be set, this may be either a string in RFC 2446 Date or DateTime value format
106
+ # Or a Date, Time, DateTime, RiCal::PropertyValue::Date, or RiCal::PropertyValue::DateTime
107
+ #
108
+ def until=(until_value)
109
+ reset_errors
110
+ @until = until_value && until_value.to_ri_cal_date_or_date_time_value(timezone_finder)
111
+ @count = nil unless @count.nil? || @by_list_hash
112
+ end
113
+
114
+ # return the INTERVAL parameter of the recurrence rule
115
+ # This returns an Integer
116
+ def interval
117
+ @interval ||= 1
118
+ end
119
+
120
+ # Set the INTERVAL parameter of the recurrence rule
121
+ #
122
+ # interval_value:: an Integer
123
+ #
124
+ # This method resets the receivers list of errors
125
+ def interval=(interval_value)
126
+ reset_errors
127
+ @interval = interval_value
128
+ end
129
+
130
+ def value #:nodoc:
131
+ @value || to_ical
132
+ end
133
+
134
+ # Return a string containing the RFC 2445 representation of the recurrence rule
135
+ def to_ical
136
+ result = ["FREQ=#{freq}"]
137
+ result << "INTERVAL=#{interval}" unless interval == 1
138
+ result << "COUNT=#{count}" if count
139
+ result << "UNTIL=#{self.until.value}" if self.until
140
+ %w{bysecond byminute byhour byday bymonthday byyearday byweekno bymonth bysetpos}.each do |by_part|
141
+ val = by_list[by_part.to_sym]
142
+ result << "#{by_part.upcase}=#{[val].flatten.join(',')}" if val
143
+ end
144
+ result << "WKST=#{wkst}" unless wkst == "MO"
145
+ result.join(";")
146
+ end
147
+
148
+ # Predicate to determine if the receiver generates a bounded or infinite set of occurrences
149
+ def bounded?
150
+ @count || @until
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,44 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
4
+ #
5
+ # RiCal::PropertyValue::Text represents an icalendar Text property value
6
+ # which is defined in
7
+ # rfc 2445 section 4.3.11 pp 45-46
8
+ class Text < PropertyValue
9
+
10
+ # Return the string value of the receiver
11
+ def ruby_value
12
+ if value
13
+ value.gsub(/\\[;,nN\\]/) {|match|
14
+ case match[1,1]
15
+ when /[,;\\]/
16
+ match[1,1]
17
+ when 'n', 'N'
18
+ "\n"
19
+ else
20
+ match
21
+ end
22
+ }
23
+ else
24
+ nil
25
+ end
26
+ end
27
+
28
+ def to_ri_cal_text_property
29
+ self
30
+ end
31
+
32
+ def self.convert(parent, string) #:nodoc:
33
+ ical_str = string.gsub(/\n\r?|\r\n?|,|;|\\/) {|match|
34
+ if ["\n", "\r", "\n\r", "\r\n"].include?(match)
35
+ '\\n'
36
+ else
37
+ "\\#{match}"
38
+ end
39
+ }
40
+ self.new(parent, :value => ical_str)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,11 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
4
+ #
5
+ # RiCal::PropertyValue::Uri represents an icalendar Uri property value
6
+ # which is defined in
7
+ # rfc 2445 section 4.8.4.6 p 110
8
+ class Uri < PropertyValue
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,33 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
4
+ #
5
+ class UtcOffset < PropertyValue # :nodoc:
6
+ attr_accessor :sign, :hours, :minutes, :seconds
7
+
8
+ def value=(string)
9
+ @value = string
10
+ parse_match = /([+-])(\d\d)(\d\d)(\d\d)?/.match(string)
11
+ if parse_match
12
+ @sign = parse_match[1] == "+" ? 1 : -1
13
+ @hours = parse_match[2].to_i
14
+ @minutes = parse_match[3].to_i
15
+ @seconds = parse_match[4].to_i || 0
16
+ end
17
+ end
18
+
19
+ def to_seconds
20
+ @sign * ((((hours*60) + minutes) * 60) + seconds)
21
+ end
22
+
23
+ def add_to_date_time_value(date_time_value)
24
+ date_time_value.advance(:hours => sign * hours, :minutes => sign * minutes, :seconds => sign * minutes)
25
+ end
26
+
27
+ def subtract_from_date_time_value(date_time_value)
28
+ signum = -1 * sign
29
+ date_time_value.advance(:hours => signum * hours, :minutes => signum * minutes, :seconds => signum * minutes)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,34 @@
1
+ require 'date'
2
+ module RiCal
3
+ class PropertyValue
4
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ # RiCal::PropertyValue::CalAddress represents an icalendar CalAddress property value
7
+ # which is defined in RFC 2445 section 4.3.5 pp 35-37
8
+ class ZuluDateTime < PropertyValue::DateTime
9
+
10
+ def tzid
11
+ "UTC"
12
+ end
13
+
14
+ def value=(val) # :nodoc:
15
+ if DateTime === val
16
+ @date_time_value = val
17
+ else
18
+ super(val)
19
+ end
20
+ @date_time_value = @date_time_value.utc if @date_time_value
21
+ end
22
+
23
+ def to_ri_cal_zulu_date_time
24
+ self
25
+ end
26
+
27
+ def self.convert(timezone_finder, ruby_object) # :nodoc:
28
+ result = super
29
+ result.to_ri_cal_zulu_date_time
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,159 @@
1
+ module RiCal
2
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
3
+ #
4
+ # PropertyValue provides common implementation of various RFC 2445 property value types
5
+ class PropertyValue
6
+
7
+ autoload :Array, "ri_cal/property_value/array.rb"
8
+ autoload :CalAddress, "ri_cal/property_value/cal_address.rb"
9
+ autoload :Date, "ri_cal/property_value/date.rb"
10
+ autoload :DateTime, "ri_cal/property_value/date_time.rb"
11
+ autoload :Duration, "ri_cal/property_value/duration.rb"
12
+ autoload :Geo, "ri_cal/property_value/geo.rb"
13
+ autoload :Integer, "ri_cal/property_value/integer.rb"
14
+ autoload :OccurrenceList, "ri_cal/property_value/occurrence_list.rb"
15
+ autoload :Period, "ri_cal/property_value/period.rb"
16
+ autoload :RecurrenceRule, "ri_cal/property_value/recurrence_rule.rb"
17
+ autoload :Text, "ri_cal/property_value/text.rb"
18
+ autoload :Uri, "ri_cal/property_value/uri.rb"
19
+ autoload :UtcOffset, "ri_cal/property_value/utc_offset.rb"
20
+ autoload :ZuluDateTime, "ri_cal/property_value/zulu_date_time.rb"
21
+
22
+ attr_writer :params, :value #:nodoc:
23
+ attr_reader :timezone_finder #:nodoc:
24
+ def initialize(timezone_finder, options={}) # :nodoc:
25
+ @timezone_finder = timezone_finder
26
+ validate_value(options)
27
+ ({:params => {}}).merge(options).each do |attribute, val|
28
+ unless attribute == :name
29
+ setter = :"#{attribute.to_s.downcase}="
30
+ send(setter, val)
31
+ end
32
+ end
33
+ end
34
+
35
+ def self.if_valid_string(timezone_finder, string) #:nodoc:
36
+ if valid_string?(string)
37
+ new(timezone_finder, :value => string)
38
+ else
39
+ nil
40
+ end
41
+ end
42
+
43
+ def validate_value(options) #:nodoc:
44
+ val = options[:value]
45
+ raise "Invalid property value #{val.inspect}" if val.kind_of?(String) && /^;/.match(val)
46
+ end
47
+
48
+ # return a hash containing the parameters and values, if any
49
+ def params
50
+ @params ||= {}
51
+ end
52
+
53
+ def to_options_hash #:nodoc:
54
+ options_hash = {:value => value}
55
+ options_hash[:params] = params unless params.empty?
56
+ end
57
+
58
+ def self.date_or_date_time(timezone_finder, separated_line) # :nodoc:
59
+ match = separated_line[:value].match(/(\d\d\d\d)(\d\d)(\d\d)((T?)((\d\d)(\d\d)(\d\d))(Z?))?/)
60
+ raise Exception.new("Invalid date") unless match
61
+ if match[5] == "T" # date-time
62
+ time = Time.utc(match[1].to_i, match[2].to_i, match[3].to_i, match[7].to_i, match[8].to_i, match[9].to_i)
63
+ parms = (separated_line[:params] ||{}).dup
64
+ if match[10] == "Z"
65
+ raise Exception.new("Invalid time, cannot combine Zulu with timezone reference") if parms[:tzid]
66
+ parms['TZID'] = "UTC"
67
+ end
68
+ PropertyValue::DateTime.new(timezone_finder, separated_line.merge(:params => parms))
69
+ else
70
+ PropertyValue::Date.new(timezone_finder, separated_line)
71
+ end
72
+ end
73
+
74
+ def self.date_or_date_time_or_period(timezone_finder, separated_line) #:nodoc:
75
+ if separated_line[:value].include?("/")
76
+ PropertyValue::Period.new(timezone_finder, separated_line)
77
+ else
78
+ date_or_date_time(timezone_finder, separated_line)
79
+ end
80
+ end
81
+
82
+ # def self.from_string(string) # :nodoc:
83
+ # new(nil, :value => string)
84
+ # end
85
+
86
+ def self.convert(timezone_finder, value) #:nodoc:
87
+ new(timezone_finder, :value => value)
88
+ end
89
+
90
+ # Determine if another object is equivalent to the receiver.
91
+ def ==(o)
92
+ if o.class == self.class
93
+ equality_value == o.equality_value
94
+ else
95
+ super
96
+ end
97
+ end
98
+
99
+ # Return the string value
100
+ def value
101
+ @value
102
+ end
103
+
104
+ def equality_value #:nodoc:
105
+ value
106
+ end
107
+
108
+ def visible_params # :nodoc:
109
+ params
110
+ end
111
+
112
+ def parms_string #:nodoc:
113
+ if (vp = visible_params) && !vp.empty?
114
+ # We only sort for testability reasons
115
+ vp.keys.sort.map {|key| ";#{key}=#{vp[key]}"}.join
116
+ else
117
+ ""
118
+ end
119
+ end
120
+
121
+ # Return a string representing the receiver in RFC 2445 format
122
+ def to_s #:nodoc:
123
+ "#{parms_string}:#{value}"
124
+ end
125
+
126
+ # return the ruby value
127
+ def ruby_value
128
+ self.value
129
+ end
130
+
131
+ def to_ri_cal_property_value #:nodoc:
132
+ self
133
+ end
134
+
135
+ def find_timezone(timezone_identifier) #:nodoc:
136
+ if timezone_finder
137
+ timezone_finder.find_timezone(timezone_identifier)
138
+ else
139
+ raise "Unable to find timezone with tzid #{timezone_identifier}"
140
+ end
141
+ end
142
+
143
+ def default_tzid #:nodoc:
144
+ if timezone_finder
145
+ timezone_finder.default_tzid
146
+ else
147
+ PropertyValue::DateTime.default_tzid
148
+ end
149
+ end
150
+
151
+ def tz_info_source? #:nodoc:
152
+ if timezone_finder
153
+ timezone_finder.tz_info_source?
154
+ else
155
+ true
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,55 @@
1
+ module RiCal
2
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
3
+ #
4
+ # RequireTimezones collects the timezones used by a given calendar component or set of calendar components
5
+ # For each timezone we collect it's id, and the earliest and latest times which reference the zone
6
+ class RequiredTimezones #:nodoc:
7
+
8
+
9
+ # A required timezone represents a single timezone and the earliest and latest times which reference it.
10
+ class RequiredTimezone #:nodoc:
11
+
12
+ attr_reader :first_time, :last_time, :timezone
13
+
14
+ def initialize(tzid)
15
+ @timezone = RiCal::Component::TZInfoTimezone.new(TZInfo::Timezone.get(tzid))
16
+ end
17
+
18
+ def tzid
19
+ @timezone.identifier
20
+ end
21
+
22
+ def add_datetime(date_time)
23
+ if @first_time
24
+ @first_time = date_time if date_time < @first_time
25
+ else
26
+ @first_time = date_time
27
+ end
28
+ if @last_time
29
+ @last_time = date_time if date_time > @last_time
30
+ else
31
+ @last_time = date_time
32
+ end
33
+ end
34
+ end
35
+
36
+ def required_timezones
37
+ @required_zones ||= {}
38
+ end
39
+
40
+ def required_zones
41
+ required_timezones.values
42
+ end
43
+
44
+ def export_to(export_stream)
45
+ required_zones.each do |z|
46
+ tzinfo_timezone =z.timezone
47
+ tzinfo_timezone.export_local_to(export_stream, z.first_time, z.last_time)
48
+ end
49
+ end
50
+
51
+ def add_datetime(date_time, tzid)
52
+ (required_timezones[tzid] ||= RequiredTimezone.new(tzid)).add_datetime(date_time)
53
+ end
54
+ end
55
+ end
data/lib/ri_cal.rb ADDED
@@ -0,0 +1,187 @@
1
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
2
+ #
3
+ # The RiCal module provides the outermost namespace, along with several convenience methods for parsing
4
+ # and building calendars and calendar components.
5
+ module RiCal
6
+
7
+ require 'stringio'
8
+ require 'rational'
9
+
10
+ my_dir = File.dirname(__FILE__)
11
+
12
+ $LOAD_PATH << my_dir unless $LOAD_PATH.include?(my_dir)
13
+
14
+ if Object.const_defined?(:ActiveSupport)
15
+ as = Object.const_get(:ActiveSupport)
16
+ if as.const_defined?(:TimeWithZone)
17
+ time_with_zone = as.const_get(:TimeWithZone)
18
+ end
19
+ end
20
+
21
+ # TimeWithZone will be set to ActiveSupport::TimeWithZone if the activesupport gem is loaded
22
+ # otherwise it will be nil
23
+ TimeWithZone = time_with_zone
24
+
25
+ autoload :Component, "ri_cal/component.rb"
26
+ autoload :CoreExtensions, "ri_cal/core_extensions.rb"
27
+ autoload :FastDateTime, "ri_cal/fast_date_time.rb"
28
+ autoload :FloatingTimezone, "ri_cal/floating_timezone.rb"
29
+ autoload :InvalidPropertyValue, "ri_cal/invalid_property_value.rb"
30
+ autoload :InvalidTimezoneIdentifier, "ri_cal/invalid_timezone_identifier.rb"
31
+ autoload :OccurrenceEnumerator, "ri_cal/occurrence_enumerator.rb"
32
+ autoload :OccurrencePeriod, "ri_cal/occurrence_period.rb"
33
+ autoload :TimezonePeriod, "ri_cal/properties/timezone_period.rb"
34
+ autoload :Parser, "ri_cal/parser.rb"
35
+ autoload :Properties, "ri_cal/properties.rb"
36
+ autoload :PropertyValue, "ri_cal/property_value.rb"
37
+ autoload :RequiredTimezones, "ri_cal/required_timezones.rb"
38
+ require "ri_cal/core_extensions.rb"
39
+ # :stopdoc:
40
+ VERSION = '0.8.5'
41
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
42
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
43
+
44
+ # Returns the version string for the library.
45
+ #
46
+ def self.version
47
+ VERSION
48
+ end
49
+
50
+ # Returns the library path for the module. If any arguments are given,
51
+ # they will be joined to the end of the libray path using
52
+ # <tt>File.join</tt>.
53
+ #
54
+ def self.libpath( *args )
55
+ args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
56
+ end
57
+
58
+ # Returns the lpath for the module. If any arguments are given,
59
+ # they will be joined to the end of the path using
60
+ # <tt>File.join</tt>.
61
+ #
62
+ def self.path( *args )
63
+ args.empty? ? PATH : ::File.join(PATH, args.flatten)
64
+ end
65
+
66
+ # Utility method used to rquire all files ending in .rb that lie in the
67
+ # directory below this file that has the same name as the filename passed
68
+ # in. Optionally, a specific _directory_ name can be passed in such that
69
+ # the _filename_ does not have to be equivalent to the directory.
70
+ #
71
+ def self.require_all_libs_relative_to( fname, dir = nil )
72
+ dir ||= ::File.basename(fname, '.*')
73
+ search_me = ::File.expand_path(::File.join(::File.dirname(fname), dir, '**', '*.rb'))
74
+ Dir.glob(search_me).sort.each {|rb|
75
+ require rb}
76
+ end
77
+
78
+ # :startdoc:
79
+
80
+ # Parse an io stream and return an array of iCalendar entities.
81
+ # Normally this will be an array of RiCal::Component::Calendar instances
82
+ def self.parse(io)
83
+ Parser.new(io).parse
84
+ end
85
+
86
+ # Parse a string and return an array of iCalendar entities.
87
+ # see RiCal.parse
88
+ def self.parse_string(string)
89
+ parse(StringIO.new(string))
90
+ end
91
+
92
+ def self.debug # :nodoc:
93
+ @debug
94
+ end
95
+
96
+ def self.debug=(val) # :nodoc:
97
+ @debug = val
98
+ end
99
+
100
+ # return a new Alarm event or todo component. If a block is provided it will will be executed in
101
+ # the context of a builder object which can be used to initialize the properties of the
102
+ # new Alarm.
103
+ def self.Alarm(&init_block)
104
+ Component::Alarm.new(&init_block)
105
+ end
106
+
107
+ # return a new Calendar. If a block is provided it will will be executed in
108
+ # the context of a builder object which can be used to initialize the properties and components of the
109
+ # new calendar.
110
+ def self.Calendar(&init_block)
111
+ Component::Calendar.new(&init_block)
112
+ end
113
+
114
+ # return a new Event calendar component. If a block is provided it will will be executed in
115
+ # the context of a builder object which can be used to initialize the properties and alarms of the
116
+ # new Event.
117
+ def self.Event(&init_block)
118
+ Component::Event.new(&init_block)
119
+ end
120
+
121
+ # return a new Freebusy calendar component. If a block is provided it will will be executed in
122
+ # the context of a builder object which can be used to initialize the properties and components of the
123
+ # new Freebusy.
124
+ def self.Freebusy(&init_block)
125
+ Component::Freebusy.new(&init_block)
126
+ end
127
+
128
+ # return a new Journal calendar component. If a block is provided it will will be executed in
129
+ # the context of a builder object which can be used to initialize the properties and components of the
130
+ # new Event.
131
+ def self.Journal(&init_block)
132
+ Component::Journal.new(&init_block)
133
+ end
134
+
135
+ # return a new Timezone calendar component. If a block is provided it will will be executed in
136
+ # the context of a builder object which can be used to initialize the properties and timezone periods of the
137
+ # new Timezone.
138
+ def self.Timezone(&init_block)
139
+ Component::Timezone.new(&init_block)
140
+ end
141
+
142
+ # return a new TimezonePeriod timezone component. If a block is provided it will will be executed in
143
+ # the context of a builder object which can be used to initialize the properties of the
144
+ # new TimezonePeriod.
145
+ def self.TimezonePeriod(&init_block)
146
+ Component::TimezonePeriod.new(&init_block)
147
+ end
148
+
149
+ # return a new Todo calendar component. If a block is provided it will will be executed in
150
+ # the context of a builder object which can be used to initialize the properties and alarms of the
151
+ # new Todo.
152
+ def self.Todo(&init_block)
153
+ Component::Todo.new(&init_block)
154
+ end
155
+
156
+ def self.ro_calls=(value)
157
+ @ro_calls = value
158
+ end
159
+
160
+ def self.ro_calls
161
+ @ro_calls ||= 0
162
+ end
163
+
164
+ def self.ro_misses=(value)
165
+ @ro_misses = value
166
+ end
167
+
168
+ def self.ro_misses
169
+ @ro_misses ||= 0
170
+ end
171
+
172
+ def self.RationalOffset
173
+ self.ro_calls += 1
174
+ @rational_offset ||= Hash.new {|h, seconds|
175
+ self.ro_misses += 1
176
+ h[seconds] = Rational(seconds, 86400)}
177
+ end
178
+
179
+ end # module RiCal
180
+
181
+ (-12..12).each do |hour_offset|
182
+ RiCal.RationalOffset[hour_offset * 86400]
183
+ end
184
+
185
+
186
+ #RiCal.require_all_libs_relative_to(__FILE__)
187
+ # EOF