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,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
|