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.
- checksums.yaml +7 -0
- data/History.txt +402 -0
- data/Manifest.txt +161 -0
- data/README.txt +410 -0
- data/Rakefile +69 -0
- data/VERSION +1 -0
- data/bin/ri_cal +8 -0
- data/component_attributes/alarm.yml +10 -0
- data/component_attributes/calendar.yml +4 -0
- data/component_attributes/component_property_defs.yml +180 -0
- data/component_attributes/event.yml +45 -0
- data/component_attributes/freebusy.yml +16 -0
- data/component_attributes/journal.yml +35 -0
- data/component_attributes/timezone.yml +3 -0
- data/component_attributes/timezone_period.yml +11 -0
- data/component_attributes/todo.yml +46 -0
- data/copyrights.txt +1 -0
- data/docs/draft-ietf-calsify-2446bis-08.txt +7280 -0
- data/docs/draft-ietf-calsify-rfc2445bis-09.txt +10416 -0
- data/docs/incrementers.txt +7 -0
- data/docs/rfc2445.pdf +0 -0
- data/lib/ri_cal/component/alarm.rb +19 -0
- data/lib/ri_cal/component/calendar.rb +257 -0
- data/lib/ri_cal/component/event.rb +58 -0
- data/lib/ri_cal/component/freebusy.rb +16 -0
- data/lib/ri_cal/component/journal.rb +27 -0
- data/lib/ri_cal/component/non_standard.rb +33 -0
- data/lib/ri_cal/component/t_z_info_timezone.rb +153 -0
- data/lib/ri_cal/component/timezone/daylight_period.rb +25 -0
- data/lib/ri_cal/component/timezone/standard_period.rb +23 -0
- data/lib/ri_cal/component/timezone/timezone_period.rb +76 -0
- data/lib/ri_cal/component/timezone.rb +197 -0
- data/lib/ri_cal/component/todo.rb +42 -0
- data/lib/ri_cal/component.rb +256 -0
- data/lib/ri_cal/core_extensions/array/conversions.rb +15 -0
- data/lib/ri_cal/core_extensions/array.rb +7 -0
- data/lib/ri_cal/core_extensions/date/conversions.rb +56 -0
- data/lib/ri_cal/core_extensions/date.rb +13 -0
- data/lib/ri_cal/core_extensions/date_time/conversions.rb +50 -0
- data/lib/ri_cal/core_extensions/date_time.rb +15 -0
- data/lib/ri_cal/core_extensions/object/conversions.rb +20 -0
- data/lib/ri_cal/core_extensions/object.rb +8 -0
- data/lib/ri_cal/core_extensions/string/conversions.rb +57 -0
- data/lib/ri_cal/core_extensions/string.rb +8 -0
- data/lib/ri_cal/core_extensions/time/calculations.rb +153 -0
- data/lib/ri_cal/core_extensions/time/conversions.rb +42 -0
- data/lib/ri_cal/core_extensions/time/tzid_access.rb +50 -0
- data/lib/ri_cal/core_extensions/time/week_day_predicates.rb +55 -0
- data/lib/ri_cal/core_extensions/time.rb +14 -0
- data/lib/ri_cal/core_extensions.rb +11 -0
- data/lib/ri_cal/fast_date_time.rb +234 -0
- data/lib/ri_cal/floating_timezone.rb +32 -0
- data/lib/ri_cal/invalid_property_value.rb +8 -0
- data/lib/ri_cal/invalid_timezone_identifier.rb +20 -0
- data/lib/ri_cal/occurrence_enumerator.rb +265 -0
- data/lib/ri_cal/occurrence_period.rb +17 -0
- data/lib/ri_cal/parser.rb +148 -0
- data/lib/ri_cal/properties/alarm.rb +390 -0
- data/lib/ri_cal/properties/calendar.rb +164 -0
- data/lib/ri_cal/properties/event.rb +1523 -0
- data/lib/ri_cal/properties/freebusy.rb +593 -0
- data/lib/ri_cal/properties/journal.rb +1237 -0
- data/lib/ri_cal/properties/timezone.rb +150 -0
- data/lib/ri_cal/properties/timezone_period.rb +416 -0
- data/lib/ri_cal/properties/todo.rb +1559 -0
- data/lib/ri_cal/properties.rb +12 -0
- data/lib/ri_cal/property_value/array.rb +27 -0
- data/lib/ri_cal/property_value/cal_address.rb +11 -0
- data/lib/ri_cal/property_value/date.rb +184 -0
- data/lib/ri_cal/property_value/date_time/additive_methods.rb +44 -0
- data/lib/ri_cal/property_value/date_time/time_machine.rb +159 -0
- data/lib/ri_cal/property_value/date_time/timezone_support.rb +100 -0
- data/lib/ri_cal/property_value/date_time.rb +359 -0
- data/lib/ri_cal/property_value/duration.rb +110 -0
- data/lib/ri_cal/property_value/geo.rb +11 -0
- data/lib/ri_cal/property_value/integer.rb +12 -0
- data/lib/ri_cal/property_value/occurrence_list.rb +144 -0
- data/lib/ri_cal/property_value/period.rb +86 -0
- data/lib/ri_cal/property_value/recurrence_rule/enumeration_support_methods.rb +100 -0
- data/lib/ri_cal/property_value/recurrence_rule/enumerator.rb +79 -0
- data/lib/ri_cal/property_value/recurrence_rule/initialization_methods.rb +148 -0
- data/lib/ri_cal/property_value/recurrence_rule/negative_setpos_enumerator.rb +53 -0
- data/lib/ri_cal/property_value/recurrence_rule/numbered_span.rb +31 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_day_incrementer.rb +86 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_hour_incrementer.rb +31 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_minute_incrementer.rb +32 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_month_incrementer.rb +52 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_monthday_incrementer.rb +31 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_numbered_day_incrementer.rb +38 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_second_incrementer.rb +32 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_weekno_incrementer.rb +69 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_yearday_incrementer.rb +31 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/daily_incrementer.rb +28 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/frequency_incrementer.rb +80 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/hourly_incrementer.rb +23 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/list_incrementer.rb +106 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/minutely_incrementer.rb +23 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/monthly_incrementer.rb +33 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/null_sub_cycle_incrementer.rb +43 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/secondly_incrementer.rb +28 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/weekly_incrementer.rb +37 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/yearly_incrementer.rb +57 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer.rb +135 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_day.rb +131 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_month_day.rb +64 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_numbered_week.rb +33 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_year_day.rb +53 -0
- data/lib/ri_cal/property_value/recurrence_rule/time_manipulation.rb +42 -0
- data/lib/ri_cal/property_value/recurrence_rule/validations.rb +125 -0
- data/lib/ri_cal/property_value/recurrence_rule.rb +154 -0
- data/lib/ri_cal/property_value/text.rb +44 -0
- data/lib/ri_cal/property_value/uri.rb +11 -0
- data/lib/ri_cal/property_value/utc_offset.rb +33 -0
- data/lib/ri_cal/property_value/zulu_date_time.rb +34 -0
- data/lib/ri_cal/property_value.rb +159 -0
- data/lib/ri_cal/required_timezones.rb +55 -0
- data/lib/ri_cal.rb +187 -0
- data/parked_specs/ri_cal/claudio_a_bug_spec.rb +100 -0
- data/performance/empty_propval/subject.rb +43 -0
- data/performance/paris_eastern/subject.rb +90 -0
- data/performance/penultimate_weekday/subject.rb +15 -0
- data/performance/psm_big_enum/ical.ics +3171 -0
- data/performance/psm_big_enum/subject.rb +16 -0
- data/performance/utah_cycling/subject.rb +55 -0
- data/ri_cal.gemspec +244 -0
- data/script/benchmark_subject +23 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/profile_subject +29 -0
- data/script/txt2html +71 -0
- data/spec/ri_cal/bugreports_spec.rb +276 -0
- data/spec/ri_cal/component/alarm_spec.rb +12 -0
- data/spec/ri_cal/component/calendar_spec.rb +88 -0
- data/spec/ri_cal/component/event_spec.rb +735 -0
- data/spec/ri_cal/component/freebusy_spec.rb +12 -0
- data/spec/ri_cal/component/journal_spec.rb +37 -0
- data/spec/ri_cal/component/t_z_info_timezone_spec.rb +60 -0
- data/spec/ri_cal/component/timezone_spec.rb +236 -0
- data/spec/ri_cal/component/todo_spec.rb +112 -0
- data/spec/ri_cal/component_spec.rb +224 -0
- data/spec/ri_cal/core_extensions/string/conversions_spec.rb +78 -0
- data/spec/ri_cal/core_extensions/time/calculations_spec.rb +188 -0
- data/spec/ri_cal/core_extensions/time/week_day_predicates_spec.rb +45 -0
- data/spec/ri_cal/fast_date_time_spec.rb +77 -0
- data/spec/ri_cal/inf_loop_spec.rb +78 -0
- data/spec/ri_cal/occurrence_enumerator_spec.rb +611 -0
- data/spec/ri_cal/parser_spec.rb +337 -0
- data/spec/ri_cal/property_value/date_spec.rb +53 -0
- data/spec/ri_cal/property_value/date_time_spec.rb +383 -0
- data/spec/ri_cal/property_value/duration_spec.rb +126 -0
- data/spec/ri_cal/property_value/occurrence_list_spec.rb +72 -0
- data/spec/ri_cal/property_value/period_spec.rb +63 -0
- data/spec/ri_cal/property_value/recurrence_rule/recurring_year_day_spec.rb +21 -0
- data/spec/ri_cal/property_value/recurrence_rule_spec.rb +1814 -0
- data/spec/ri_cal/property_value/text_spec.rb +25 -0
- data/spec/ri_cal/property_value/utc_offset_spec.rb +48 -0
- data/spec/ri_cal/property_value_spec.rb +125 -0
- data/spec/ri_cal/required_timezones_spec.rb +67 -0
- data/spec/ri_cal_spec.rb +53 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +50 -0
- data/tasks/gem_loader/load_active_support.rb +3 -0
- data/tasks/gem_loader/load_tzinfo_gem.rb +2 -0
- data/tasks/ri_cal.rake +412 -0
- data/tasks/spec.rake +102 -0
- 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
|
data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_minute_incrementer.rb
ADDED
@@ -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
|