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,86 @@
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::CalAddress represents an icalendar Period property value
6
+ # which is defined in
7
+ # rfc 2445 section 4.3.9 p 39
8
+ #
9
+ # Known bugs. This doesn't properly work when dtstart, dtend or duration are changed independently
10
+ class Period < PropertyValue
11
+
12
+ # The DATE-TIME on which the period starts
13
+ attr_accessor :dtstart
14
+ # The DATE-TIME on which the period ends
15
+ attr_accessor :dtend
16
+ # The DURATION of the period
17
+ attr_accessor :duration
18
+
19
+ def value=(string) # :nodoc:
20
+ starter, terminator = *string.split("/")
21
+ self.dtstart = PropertyValue::DateTime.new(self, :value => starter)
22
+ if /P/ =~ terminator
23
+ self.duration = PropertyValue::Duration.new(self, :value => terminator)
24
+ self.dtend = dtstart + duration
25
+ else
26
+ self.dtend = PropertyValue::DateTime.new(self, :value => terminator)
27
+ self.duration = PropertyValue::Duration.from_datetimes(self, dtstart.to_datetime, dtend.to_datetime)
28
+ end
29
+ @value = string
30
+ end
31
+
32
+ def ruby_value
33
+ self
34
+ end
35
+
36
+ def self.valid_string?(string) # :nodoc:
37
+ return false unless string.include?("/")
38
+ starter, terminator = *string.split("/")
39
+ return false unless PropertyValue::DateTime.valid_string?(starter)
40
+ if /P/ =~ terminator
41
+ return false unless PropertyValue::Duration.valid_string?(terminator)
42
+ else
43
+ return false unless PropertyValue::DateTime.valid_string?(terminator)
44
+ end
45
+ true
46
+ end
47
+
48
+ # Nop to allow occurrence list to try to set it
49
+ def tzid=(val)#:nodoc:
50
+ end
51
+
52
+ def tzid #:nodoc:
53
+ nil
54
+ end
55
+
56
+ def for_parent(parent) #:nodoc:
57
+ if timezone_finder.nil
58
+ @timezone_finder = parent
59
+ self
60
+ elsif timezone_finder == parent
61
+ self
62
+ else
63
+ Period.new(parent, :value => value)
64
+ end
65
+ end
66
+
67
+ def self.convert(parent, ruby_object) # :nodoc:
68
+ ruby_object.to_ri_cal_period_value.for_parent(parent)
69
+ end
70
+
71
+ # return the receiver
72
+ def to_ri_cal_period_value
73
+ self
74
+ end
75
+
76
+ def occurrence_period(default_duration) #:nodoc:
77
+ RiCal::OccurrencePeriod.new(self, (default_duration ? self + default_duration : nil))
78
+ end
79
+
80
+ def add_date_times_to(required_timezones) #:nodoc:
81
+ dtstart.add_date_times_to(required_timezones)
82
+ dtend.add_date_times_to(required_timezones)
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,100 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ class RecurrenceRule < PropertyValue
4
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ module EnumerationSupportMethods # :nodoc:
7
+
8
+ # if the recurrence rule has a bysetpos part we need to search starting with the
9
+ # first time in the frequency period containing the start time specified by DTSTART
10
+ def adjust_start(start_time) # :nodoc:
11
+ if by_list[:bysetpos]
12
+ case freq
13
+ when "SECONDLY"
14
+ start_time
15
+ when "MINUTELY"
16
+ start_time.change(:seconds => 0)
17
+ when "HOURLY"
18
+ start_time.change(
19
+ :minutes => 0,
20
+ :seconds => start_time.sec
21
+ )
22
+ when "DAILY"
23
+ start_time.change(
24
+ :hour => 0,
25
+ :minutes => start_time.min,
26
+ :seconds => start_time.sec
27
+ )
28
+ when "WEEKLY"
29
+ start_of_week(time)
30
+ when "MONTHLY"
31
+ start_time.change(
32
+ :day => 1,
33
+ :hour => start_time.hour,
34
+ :minutes => start_time.min,
35
+ :seconds => start_time.sec
36
+ )
37
+ when "YEARLY"
38
+ start_time.change(
39
+ :month => 1,
40
+ :day => start_time.day,
41
+ :hour => start_time.hour,
42
+ :minutes => start_time.min,
43
+ :seconds => start_time.sec
44
+ )
45
+ end
46
+ else
47
+ start_time
48
+ end
49
+ end
50
+
51
+ def enumerator(component) # :nodoc:
52
+ Enumerator.for(self, component, by_list[:bysetpos])
53
+ end
54
+
55
+ def exhausted?(count, time) # :nodoc:
56
+ (@count && count > @count) || (@until && (time > @until))
57
+ end
58
+
59
+ def in_same_set?(time1, time2) # :nodoc:
60
+ case freq
61
+ when "SECONDLY"
62
+ [time1.year, time1.month, time1.day, time1.hour, time1.min, time1.sec] ==
63
+ [time2.year, time2.month, time2.day, time2.hour, time2.min, time2.sec]
64
+ when "MINUTELY"
65
+ [time1.year, time1.month, time1.day, time1.hour, time1.min] ==
66
+ [time2.year, time2.month, time2.day, time2.hour, time2.min]
67
+ when "HOURLY"
68
+ [time1.year, time1.month, time1.day, time1.hour] ==
69
+ [time2.year, time2.month, time2.day, time2.hour]
70
+ when "DAILY"
71
+ [time1.year, time1.month, time1.day] ==
72
+ [time2.year, time2.month, time2.day]
73
+ when "WEEKLY"
74
+ sow1 = start_of_week(time1)
75
+ sow2 = start_of_week(time2)
76
+ [sow1.year, sow1.month, sow1.day] ==
77
+ [sow2.year, sow2.month, sow2.day]
78
+ when "MONTHLY"
79
+ [time1.year, time1.month] ==
80
+ [time2.year, time2.month]
81
+ when "YEARLY"
82
+ time1.year == time2.year
83
+ end
84
+ end
85
+
86
+ def by_rule_list(which) # :nodoc:
87
+ if @by_list
88
+ @by_list[which]
89
+ else
90
+ nil
91
+ end
92
+ end
93
+
94
+ def incrementer_from_start_time(start_time)
95
+ RecurrenceRule::OccurrenceIncrementer.from_rrule(self, start_time)
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,79 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ class RecurrenceRule < PropertyValue
4
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ class Enumerator # :nodoc:
7
+ # base_time gets changed everytime the time is updated by the recurrence rule's frequency
8
+ attr_accessor :start_time, :duration, :next_time, :recurrence_rule, :base_time
9
+ def initialize(recurrence_rule, component, setpos_list)
10
+ self.recurrence_rule = recurrence_rule
11
+ self.start_time = component.default_start_time
12
+ self.duration = component.default_duration
13
+ self.next_time = recurrence_rule.adjust_start(self.start_time)
14
+ self.base_time = next_time
15
+ @bounded = recurrence_rule.bounded?
16
+ @count = 0
17
+ @setpos_list = setpos_list
18
+ @setpos = 1
19
+ @next_occurrence_count = 0
20
+ @incrementer = recurrence_rule.incrementer_from_start_time(start_time)
21
+ end
22
+
23
+ def self.for(recurrence_rule, component, setpos_list) # :nodoc:
24
+ if !setpos_list || setpos_list.all? {|setpos| setpos > 1}
25
+ self.new(recurrence_rule, component, setpos_list)
26
+ else
27
+ NegativeSetposEnumerator.new(recurrence_rule, component, setpos_list)
28
+ end
29
+ end
30
+
31
+ def empty?
32
+ false
33
+ end
34
+
35
+ def bounded?
36
+ @bounded
37
+ end
38
+
39
+ def result_occurrence_period(date_time_value)
40
+ RiCal::OccurrencePeriod.new(date_time_value, nil)
41
+ end
42
+
43
+ def result_passes_setpos_filter?(result)
44
+ result_setpos = @setpos
45
+ if recurrence_rule.in_same_set?(result, next_time)
46
+ @setpos += 1
47
+ else
48
+ @setpos = 1
49
+ end
50
+ if (result == start_time) || (result > start_time && @setpos_list.include?(result_setpos))
51
+ return true
52
+ else
53
+ return false
54
+ end
55
+ end
56
+
57
+ def result_passes_filters?(result)
58
+ if @setpos_list
59
+ result_passes_setpos_filter?(result)
60
+ else
61
+ result >= start_time
62
+ end
63
+ end
64
+
65
+ def next_occurrence
66
+ while true
67
+ @next_occurrence_count += 1
68
+ result = next_time
69
+ self.next_time = @incrementer.next_time(result)
70
+ if result_passes_filters?(result)
71
+ @count += 1
72
+ return recurrence_rule.exhausted?(@count, result) ? nil : result_occurrence_period(result)
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,148 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ class RecurrenceRule < PropertyValue
4
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ module InitializationMethods # :nodoc:
7
+
8
+ attr_reader :by_day_scope
9
+
10
+ def add_to_options_hash(options_hash, key, value)
11
+ options_hash[key] = value if value
12
+ options_hash
13
+ end
14
+
15
+ def add_byrule_strings_to_options_hash(options_hash, key)
16
+ if (rules = by_list[key])
17
+ if rules.length == 1
18
+ options_hash[key] = rules.first.source
19
+ else
20
+ options_hash[key] = rules.map {|rule| rule.source}
21
+ end
22
+ end
23
+ end
24
+
25
+ def to_options_hash
26
+ options_hash = {:freq => freq, :interval => interval}
27
+ options_hash[:params] = params unless params.empty?
28
+ add_to_options_hash(options_hash, :count, @count)
29
+ add_to_options_hash(options_hash, :until, @until)
30
+ add_to_options_hash(options_hash, :interval, @interval)
31
+ [:bysecond, :byminute, :byhour, :bymonth, :bysetpos].each do |bypart|
32
+ add_to_options_hash(options_hash, bypart, by_list[bypart])
33
+ end
34
+ [:byday, :bymonthday, :byyearday, :byweekno].each do |bypart|
35
+ add_byrule_strings_to_options_hash(options_hash, bypart)
36
+ end
37
+ options_hash
38
+ end
39
+
40
+ def initialize_from_value_part(part, dup_hash) # :nodoc:
41
+ part_name, value = part.split("=")
42
+ attribute = part_name.downcase
43
+ errors << "Repeated rule part #{attribute} last occurrence was used" if dup_hash[attribute]
44
+ case attribute
45
+ when "freq"
46
+ self.freq = value
47
+ when "wkst"
48
+ self.wkst = value
49
+ when "until"
50
+ @until = PropertyValue.date_or_date_time(self, :value => value)
51
+ when "count"
52
+ @count = value.to_i
53
+ when "interval"
54
+ self.interval = value.to_i
55
+ when "bysecond", "byminute", "byhour", "bymonthday", "byyearday", "byweekno", "bymonth", "bysetpos"
56
+ send("#{attribute}=", value.split(",").map {|int| int.to_i})
57
+ when "byday"
58
+ self.byday = value.split(",")
59
+ else
60
+ errors << "Invalid rule part #{part}"
61
+ end
62
+ end
63
+
64
+ def by_list
65
+ @by_list ||= {}
66
+ end
67
+
68
+ def calc_by_day_scope
69
+ case freq
70
+ when "YEARLY"
71
+ scope = :yearly
72
+ when "MONTHLY"
73
+ scope = :monthly
74
+ when "WEEKLY"
75
+ scope = :weekly
76
+ else
77
+ scope = :daily
78
+ end
79
+ scope = :monthly if scope != :weekly && @by_list_hash[:bymonth]
80
+ scope = :weekly if scope != :daily && @by_list_hash[:byweekno]
81
+ @by_day_scope = scope
82
+ end
83
+
84
+ def bysecond=(val)
85
+ @by_list_hash[:bysecond] = val
86
+ end
87
+
88
+ def byminute=(val)
89
+ @by_list_hash[:byminute] = val
90
+ end
91
+
92
+ def byhour=(val)
93
+ @by_list_hash[:byhour] = val
94
+ end
95
+
96
+ def bymonth=(val)
97
+ @by_list_hash[:bymonth] = val
98
+ end
99
+
100
+ def bysetpos=(val)
101
+ @by_list_hash[:bysetpos] = val
102
+ end
103
+
104
+ def byday=(val)
105
+ @by_list_hash[:byday] = val
106
+ end
107
+
108
+ def bymonthday=(val)
109
+ @by_list_hash[:bymonthday] = val
110
+ end
111
+
112
+ def byyearday=(val)
113
+ @by_list_hash[:byyearday] = val
114
+ end
115
+
116
+ def byweekno=(val)
117
+ @by_list_hash[:byweekno] = val
118
+ end
119
+
120
+ def init_by_lists
121
+ [:bysecond,
122
+ :byminute,
123
+ :byhour,
124
+ :bymonth,
125
+ :bysetpos
126
+ ].each do |which|
127
+ if val = @by_list_hash[which]
128
+ by_list[which] = [val].flatten.sort
129
+ end
130
+ end
131
+ if val = @by_list_hash[:byday]
132
+ byday_scope = calc_by_day_scope
133
+ by_list[:byday] = [val].flatten.map {|day| RecurringDay.new(day, self, byday_scope)}
134
+ end
135
+ if val = @by_list_hash[:bymonthday]
136
+ by_list[:bymonthday] = [val].flatten.map {|md| RecurringMonthDay.new(md)}
137
+ end
138
+ if val = @by_list_hash[:byyearday]
139
+ by_list[:byyearday] = [val].flatten.map {|yd| RecurringYearDay.new(yd)}
140
+ end
141
+ if val = @by_list_hash[:byweekno]
142
+ by_list[:byweekno] = [val].flatten.map {|wkno| RecurringNumberedWeek.new(wkno, self)}
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,53 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ class RecurrenceRule < PropertyValue
4
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ class NegativeSetposEnumerator < Enumerator # :nodoc:
7
+
8
+ def initialize(recurrence_rule, component, setpos_list)
9
+ super
10
+ @current_set = []
11
+ @valids = []
12
+ end
13
+
14
+ def next_occurrence
15
+ while true
16
+ result = advance
17
+ if result >= start_time
18
+ @count += 1
19
+ return recurrence_rule.exhausted?(@count, result) ? nil : result_occurrence_period(result)
20
+ end
21
+ end
22
+ end
23
+
24
+
25
+
26
+ def advance
27
+ if @valids.empty?
28
+ fill_set
29
+ @valids = @setpos_list.map {|sp| sp < 0 ? @current_set.length + sp : sp - 1}
30
+ current_time_index = @current_set.index(@start_time)
31
+ if current_time_index
32
+ @valids << current_time_index
33
+ end
34
+ @valids = @valids.uniq.sort
35
+ end
36
+ @current_set[@valids.shift]
37
+ end
38
+
39
+ def fill_set
40
+ @current_set = [next_time]
41
+ while true
42
+ self.next_time = @incrementer.next_time(next_time)
43
+ if recurrence_rule.in_same_set?(@current_set.last, next_time)
44
+ @current_set << next_time
45
+ else
46
+ return
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,31 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ class RecurrenceRule < PropertyValue
4
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ class NumberedSpan # :nodoc:
7
+ attr_reader :source
8
+ def initialize(source, rule = nil)
9
+ @source = source
10
+ @rule = rule
11
+ end
12
+
13
+ def valid?
14
+ (1..last).include?(source) || (-last..-1).include?(source)
15
+ end
16
+
17
+ def ==(another)
18
+ self.class == another.class && source == another.source
19
+ end
20
+
21
+ def to_s
22
+ source.to_s
23
+ end
24
+
25
+ def ordinal
26
+ @source
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,86 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ class RecurrenceRule < PropertyValue
4
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ class OccurrenceIncrementer # :nodoc:
7
+ class ByDayIncrementer < ListIncrementer #:nodoc:
8
+
9
+ def initialize(rrule, list, by_monthday_list, by_yearday_list, parent)
10
+ super(rrule, list, parent)
11
+ @monthday_filters = by_monthday_list
12
+ @yearday_filters = by_yearday_list
13
+ @by_day_scope = rrule.by_day_scope
14
+
15
+ case rrule.by_day_scope
16
+ when :yearly
17
+ @cycle_advance_proc = lambda {|date_time| first_day_of_year(advance_year(date_time))}
18
+ @current_proc = lambda {|date_time| same_year?(current, date_time)}
19
+ @first_day_proc = lambda {|date_time| first_day_of_year(date_time)}
20
+ when :monthly
21
+ @cycle_advance_proc = lambda {|date_time| first_day_of_month(advance_month(date_time))}
22
+ @current_proc = lambda {|date_time| same_month?(current, date_time)}
23
+ @first_day_proc = lambda {|date_time| first_day_of_month(date_time)}
24
+ when :weekly
25
+ @cycle_advance_proc = lambda {|date_time| first_day_of_week(rrule.wkst_day, advance_week(date_time))}
26
+ @current_proc = lambda {|date_time| same_week?(rrule.wkst_day, current, date_time)}
27
+ @first_day_proc = lambda {|date_time| first_day_of_week(rrule.wkst_day, date_time)}
28
+ else
29
+ raise "Invalid recurrence rule, byday needs to be scoped by month, week or year"
30
+ end
31
+ end
32
+
33
+ def self.for_rrule(rrule)
34
+ list = rrule.by_rule_list(:byday)
35
+ if list
36
+ sub_cycle_incrementer = OccurrenceIncrementer::DailyIncrementer.for_rrule(rrule)
37
+ new(rrule, list, rrule.by_rule_list(:bymonthday), rrule.by_rule_list(:byyearday), sub_cycle_incrementer)
38
+ else
39
+ OccurrenceIncrementer::ByYeardayIncrementer.for_rrule(rrule)
40
+ end
41
+ end
42
+
43
+ def unneeded?(candidate)
44
+ false
45
+ end
46
+
47
+ def daily_incrementer?
48
+ true
49
+ end
50
+
51
+ def start_of_cycle(date_time)
52
+ @first_day_proc.call(date_time)
53
+ end
54
+
55
+ def occurrences_for(date_time)
56
+ first_day = start_of_cycle(date_time)
57
+ result = list.map {|recurring_day| recurring_day.matches_for(first_day)}.flatten.uniq.sort
58
+ if @monthday_filters
59
+ result = result.select {|occurrence| @monthday_filters.any? {|recurring_day| recurring_day.include?(occurrence)}}
60
+ end
61
+ if @yearday_filters
62
+ result = result.select {|occurrence| @yearday_filters.any? {|recurring_day| recurring_day.include?(occurrence)}}
63
+ end
64
+ result
65
+ end
66
+
67
+ def candidate_acceptable?(candidate)
68
+ list.any? {|recurring_day| recurring_day.include?(candidate)}
69
+ end
70
+
71
+ def varying_time_attribute
72
+ :day
73
+ end
74
+
75
+ def advance_cycle(date_time)
76
+ @cycle_advance_proc.call(date_time)
77
+ end
78
+
79
+ def end_of_occurrence(date_time)
80
+ date_time.end_of_day
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,31 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ class RecurrenceRule < PropertyValue
4
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ class OccurrenceIncrementer # :nodoc:
7
+ class ByHourIncrementer < ListIncrementer #:nodoc:
8
+ def self.for_rrule(rrule)
9
+ conditional_incrementer(rrule, :byhour, HourlyIncrementer)
10
+ end
11
+
12
+ def start_of_cycle(date_time)
13
+ date_time.change(:hour => 0)
14
+ end
15
+
16
+ def varying_time_attribute
17
+ :hour
18
+ end
19
+
20
+ def advance_cycle(date_time)
21
+ first_hour_of_day(advance_day(date_time))
22
+ end
23
+
24
+ def end_of_occurrence(date_time)
25
+ date_time.end_of_hour
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,32 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ class RecurrenceRule < PropertyValue
4
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ class OccurrenceIncrementer # :nodoc:
7
+ class ByMinuteIncrementer < ListIncrementer #:nodoc:
8
+ def self.for_rrule(rrule)
9
+ conditional_incrementer(rrule, :byminute, MinutelyIncrementer)
10
+ end
11
+
12
+ def advance_cycle(date_time)
13
+ date_time.advance(:hours => 1).start_of_hour
14
+ end
15
+
16
+ def start_of_cycle(date_time)
17
+ date_time.change(:min => 0)
18
+ end
19
+
20
+ def end_of_occurrence(date_time)
21
+ date_time.end_of_minute
22
+ end
23
+
24
+ def varying_time_attribute
25
+ :min
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+ end
32
+ end