micahwedemeyer-ri_cal 0.8.9
Sign up to get free protection for your applications and to get access to all the features.
- data/.rvmrc +2 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +21 -0
- data/History.txt +402 -0
- data/Manifest.txt +161 -0
- data/README.txt +410 -0
- data/Rakefile +66 -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 +145 -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/micahwedemeyer-ri_cal.gemspec +227 -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/sample_ical_files/from_ical_dot_app/test1.ics +38 -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 +287 -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
- data/website/images/rubytrends.png +0 -0
- data/website/javascripts/rounded_corners_lite.inc.js +285 -0
- data/website/stylesheets/screen.css +159 -0
- metadata +307 -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
|