friflaj_ri_cal 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +8 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +26 -0
- data/History.txt +408 -0
- data/README.rdoc +407 -0
- data/Rakefile +9 -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/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/docs/test.ics +38 -0
- data/lib/ri_cal.rb +184 -0
- data/lib/ri_cal/component.rb +254 -0
- data/lib/ri_cal/component/alarm.rb +17 -0
- data/lib/ri_cal/component/calendar.rb +255 -0
- data/lib/ri_cal/component/event.rb +56 -0
- data/lib/ri_cal/component/freebusy.rb +14 -0
- data/lib/ri_cal/component/journal.rb +25 -0
- data/lib/ri_cal/component/non_standard.rb +31 -0
- data/lib/ri_cal/component/t_z_info_timezone.rb +155 -0
- data/lib/ri_cal/component/timezone.rb +195 -0
- data/lib/ri_cal/component/timezone/daylight_period.rb +23 -0
- data/lib/ri_cal/component/timezone/standard_period.rb +21 -0
- data/lib/ri_cal/component/timezone/timezone_period.rb +74 -0
- data/lib/ri_cal/component/todo.rb +39 -0
- data/lib/ri_cal/core_extensions.rb +7 -0
- data/lib/ri_cal/core_extensions/array.rb +16 -0
- data/lib/ri_cal/core_extensions/date.rb +63 -0
- data/lib/ri_cal/core_extensions/date_time.rb +58 -0
- data/lib/ri_cal/core_extensions/object.rb +21 -0
- data/lib/ri_cal/core_extensions/string.rb +58 -0
- data/lib/ri_cal/core_extensions/time.rb +50 -0
- data/lib/ri_cal/core_extensions/time/calculations.rb +150 -0
- data/lib/ri_cal/core_extensions/time/tzid_access.rb +47 -0
- data/lib/ri_cal/core_extensions/time/week_day_predicates.rb +52 -0
- data/lib/ri_cal/fast_date_time.rb +247 -0
- data/lib/ri_cal/floating_timezone.rb +29 -0
- data/lib/ri_cal/invalid_property_value.rb +5 -0
- data/lib/ri_cal/invalid_timezone_identifier.rb +17 -0
- data/lib/ri_cal/occurrence_enumerator.rb +262 -0
- data/lib/ri_cal/occurrence_period.rb +17 -0
- data/lib/ri_cal/parser.rb +142 -0
- data/lib/ri_cal/properties.rb +12 -0
- data/lib/ri_cal/properties/alarm.rb +387 -0
- data/lib/ri_cal/properties/calendar.rb +161 -0
- data/lib/ri_cal/properties/event.rb +1520 -0
- data/lib/ri_cal/properties/freebusy.rb +590 -0
- data/lib/ri_cal/properties/journal.rb +1234 -0
- data/lib/ri_cal/properties/timezone.rb +147 -0
- data/lib/ri_cal/properties/timezone_period.rb +413 -0
- data/lib/ri_cal/properties/todo.rb +1556 -0
- data/lib/ri_cal/property_value.rb +157 -0
- data/lib/ri_cal/property_value/array.rb +25 -0
- data/lib/ri_cal/property_value/cal_address.rb +9 -0
- data/lib/ri_cal/property_value/date.rb +182 -0
- data/lib/ri_cal/property_value/date_time.rb +357 -0
- data/lib/ri_cal/property_value/date_time/additive_methods.rb +42 -0
- data/lib/ri_cal/property_value/date_time/time_machine.rb +157 -0
- data/lib/ri_cal/property_value/date_time/timezone_support.rb +98 -0
- data/lib/ri_cal/property_value/duration.rb +108 -0
- data/lib/ri_cal/property_value/geo.rb +9 -0
- data/lib/ri_cal/property_value/integer.rb +10 -0
- data/lib/ri_cal/property_value/occurrence_list.rb +142 -0
- data/lib/ri_cal/property_value/period.rb +84 -0
- data/lib/ri_cal/property_value/recurrence_rule.rb +152 -0
- data/lib/ri_cal/property_value/recurrence_rule/enumeration_support_methods.rb +98 -0
- data/lib/ri_cal/property_value/recurrence_rule/enumerator.rb +77 -0
- data/lib/ri_cal/property_value/recurrence_rule/initialization_methods.rb +146 -0
- data/lib/ri_cal/property_value/recurrence_rule/negative_setpos_enumerator.rb +51 -0
- data/lib/ri_cal/property_value/recurrence_rule/numbered_span.rb +29 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer.rb +133 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_day_incrementer.rb +84 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_hour_incrementer.rb +29 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_minute_incrementer.rb +30 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_month_incrementer.rb +50 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_monthday_incrementer.rb +29 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_numbered_day_incrementer.rb +36 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_second_incrementer.rb +30 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_weekno_incrementer.rb +67 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_yearday_incrementer.rb +29 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/daily_incrementer.rb +26 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/frequency_incrementer.rb +78 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/hourly_incrementer.rb +21 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/list_incrementer.rb +104 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/minutely_incrementer.rb +21 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/monthly_incrementer.rb +31 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/null_sub_cycle_incrementer.rb +41 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/secondly_incrementer.rb +26 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/weekly_incrementer.rb +35 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/yearly_incrementer.rb +55 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_day.rb +129 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_month_day.rb +62 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_numbered_week.rb +31 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_year_day.rb +51 -0
- data/lib/ri_cal/property_value/recurrence_rule/time_manipulation.rb +40 -0
- data/lib/ri_cal/property_value/recurrence_rule/validations.rb +123 -0
- data/lib/ri_cal/property_value/text.rb +42 -0
- data/lib/ri_cal/property_value/uri.rb +9 -0
- data/lib/ri_cal/property_value/utc_offset.rb +31 -0
- data/lib/ri_cal/property_value/zulu_date_time.rb +32 -0
- data/lib/ri_cal/required_timezones.rb +53 -0
- data/lib/ri_cal/version.rb +3 -0
- data/parked_specs/ri_cal/claudio_a_bug_spec.rb +98 -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/performance_data/benchmarks.out +48 -0
- data/performance_data/empty_propval.calltree +45650 -0
- data/performance_data/paris_eastern.calltree +103058 -0
- data/performance_data/penultimate_weekday.calltree +48686 -0
- data/performance_data/psm_big_enum.calltree +225814 -0
- data/performance_data/utah_cycling.calltree +117950 -0
- data/ri_cal.gemspec +35 -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 +281 -0
- data/spec/ri_cal/component/alarm_spec.rb +10 -0
- data/spec/ri_cal/component/calendar_spec.rb +87 -0
- data/spec/ri_cal/component/event_spec.rb +734 -0
- data/spec/ri_cal/component/freebusy_spec.rb +10 -0
- data/spec/ri_cal/component/journal_spec.rb +35 -0
- data/spec/ri_cal/component/t_z_info_timezone_spec.rb +63 -0
- data/spec/ri_cal/component/timezone_spec.rb +233 -0
- data/spec/ri_cal/component/todo_spec.rb +110 -0
- data/spec/ri_cal/component_spec.rb +225 -0
- data/spec/ri_cal/core_extensions/string/conversions_spec.rb +76 -0
- data/spec/ri_cal/core_extensions/time/calculations_spec.rb +186 -0
- data/spec/ri_cal/core_extensions/time/week_day_predicates_spec.rb +43 -0
- data/spec/ri_cal/fast_date_time_spec.rb +75 -0
- data/spec/ri_cal/inf_loop_spec.rb +75 -0
- data/spec/ri_cal/occurrence_enumerator_spec.rb +610 -0
- data/spec/ri_cal/parser_spec.rb +335 -0
- data/spec/ri_cal/property_value/date_spec.rb +51 -0
- data/spec/ri_cal/property_value/date_time_spec.rb +380 -0
- data/spec/ri_cal/property_value/duration_spec.rb +123 -0
- data/spec/ri_cal/property_value/occurrence_list_spec.rb +70 -0
- data/spec/ri_cal/property_value/period_spec.rb +61 -0
- data/spec/ri_cal/property_value/recurrence_rule/recurring_year_day_spec.rb +19 -0
- data/spec/ri_cal/property_value/recurrence_rule_spec.rb +1810 -0
- data/spec/ri_cal/property_value/text_spec.rb +23 -0
- data/spec/ri_cal/property_value/utc_offset_spec.rb +46 -0
- data/spec/ri_cal/property_value_spec.rb +123 -0
- data/spec/ri_cal/required_timezones_spec.rb +63 -0
- data/spec/ri_cal_spec.rb +50 -0
- data/spec/spec_helper.rb +35 -0
- data/tasks/ri_cal.rake +400 -0
- data/tasks/spec.rake +26 -0
- metadata +260 -0
data/docs/rfc2445.pdf
ADDED
Binary file
|
data/docs/test.ics
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
BEGIN:VCALENDAR
|
2
|
+
METHOD:PUBLISH
|
3
|
+
X-WR-TIMEZONE:America/New_York
|
4
|
+
PRODID:-//Apple Inc.//iCal 3.0//EN
|
5
|
+
CALSCALE:GREGORIAN
|
6
|
+
X-WR-CALNAME:test
|
7
|
+
VERSION:2.0
|
8
|
+
X-WR-RELCALID:1884C7F8-BC8E-457F-94AC-297871967D5E
|
9
|
+
X-APPLE-CALENDAR-COLOR:#2CA10B
|
10
|
+
BEGIN:VTIMEZONE
|
11
|
+
TZID:US/Eastern
|
12
|
+
BEGIN:DAYLIGHT
|
13
|
+
TZOFFSETFROM:-0500
|
14
|
+
TZOFFSETTO:-0400
|
15
|
+
DTSTART:20070311T020000
|
16
|
+
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
|
17
|
+
TZNAME:EDT
|
18
|
+
END:DAYLIGHT
|
19
|
+
BEGIN:STANDARD
|
20
|
+
TZOFFSETFROM:-0400
|
21
|
+
TZOFFSETTO:-0500
|
22
|
+
DTSTART:20071104T020000
|
23
|
+
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
|
24
|
+
TZNAME:EST
|
25
|
+
END:STANDARD
|
26
|
+
END:VTIMEZONE
|
27
|
+
BEGIN:VEVENT
|
28
|
+
SEQUENCE:5
|
29
|
+
TRANSP:OPAQUE
|
30
|
+
UID:00481E53-9258-4EA7-9F8D-947D3041A3F2
|
31
|
+
DTSTART;TZID=US/Eastern:20090224T090000
|
32
|
+
DTSTAMP:20090225T000908Z
|
33
|
+
SUMMARY:Test Event
|
34
|
+
CREATED:20090225T000839Z
|
35
|
+
DTEND;TZID=US/Eastern:20090224T100000
|
36
|
+
RRULE:FREQ=DAILY;INTERVAL=1;UNTIL=20090228T045959Z
|
37
|
+
END:VEVENT
|
38
|
+
END:VCALENDAR
|
data/lib/ri_cal.rb
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
# The RiCal module provides the outermost namespace, along with several convenience methods for parsing
|
2
|
+
# and building calendars and calendar components.
|
3
|
+
module RiCal
|
4
|
+
|
5
|
+
require 'stringio'
|
6
|
+
require 'rational'
|
7
|
+
|
8
|
+
my_dir = File.dirname(__FILE__)
|
9
|
+
|
10
|
+
$LOAD_PATH << my_dir unless $LOAD_PATH.include?(my_dir)
|
11
|
+
|
12
|
+
if Object.const_defined?(:ActiveSupport)
|
13
|
+
as = Object.const_get(:ActiveSupport)
|
14
|
+
if as.const_defined?(:TimeWithZone)
|
15
|
+
time_with_zone = as.const_get(:TimeWithZone)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# TimeWithZone will be set to ActiveSupport::TimeWithZone if the activesupport gem is loaded
|
20
|
+
# otherwise it will be nil
|
21
|
+
TimeWithZone = time_with_zone
|
22
|
+
|
23
|
+
autoload :Component, "ri_cal/component.rb"
|
24
|
+
autoload :CoreExtensions, "ri_cal/core_extensions.rb"
|
25
|
+
autoload :FastDateTime, "ri_cal/fast_date_time.rb"
|
26
|
+
autoload :FloatingTimezone, "ri_cal/floating_timezone.rb"
|
27
|
+
autoload :InvalidPropertyValue, "ri_cal/invalid_property_value.rb"
|
28
|
+
autoload :InvalidTimezoneIdentifier, "ri_cal/invalid_timezone_identifier.rb"
|
29
|
+
autoload :OccurrenceEnumerator, "ri_cal/occurrence_enumerator.rb"
|
30
|
+
autoload :OccurrencePeriod, "ri_cal/occurrence_period.rb"
|
31
|
+
autoload :TimezonePeriod, "ri_cal/properties/timezone_period.rb"
|
32
|
+
autoload :Parser, "ri_cal/parser.rb"
|
33
|
+
autoload :Properties, "ri_cal/properties.rb"
|
34
|
+
autoload :PropertyValue, "ri_cal/property_value.rb"
|
35
|
+
autoload :RequiredTimezones, "ri_cal/required_timezones.rb"
|
36
|
+
require "ri_cal/core_extensions.rb"
|
37
|
+
# :stopdoc:
|
38
|
+
LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
|
39
|
+
PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
|
40
|
+
|
41
|
+
# Returns the version string for the library.
|
42
|
+
#
|
43
|
+
def self.version
|
44
|
+
VERSION
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the library path for the module. If any arguments are given,
|
48
|
+
# they will be joined to the end of the libray path using
|
49
|
+
# <tt>File.join</tt>.
|
50
|
+
#
|
51
|
+
def self.libpath( *args )
|
52
|
+
args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the lpath for the module. If any arguments are given,
|
56
|
+
# they will be joined to the end of the path using
|
57
|
+
# <tt>File.join</tt>.
|
58
|
+
#
|
59
|
+
def self.path( *args )
|
60
|
+
args.empty? ? PATH : ::File.join(PATH, args.flatten)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Utility method used to rquire all files ending in .rb that lie in the
|
64
|
+
# directory below this file that has the same name as the filename passed
|
65
|
+
# in. Optionally, a specific _directory_ name can be passed in such that
|
66
|
+
# the _filename_ does not have to be equivalent to the directory.
|
67
|
+
#
|
68
|
+
def self.require_all_libs_relative_to( fname, dir = nil )
|
69
|
+
dir ||= ::File.basename(fname, '.*')
|
70
|
+
search_me = ::File.expand_path(::File.join(::File.dirname(fname), dir, '**', '*.rb'))
|
71
|
+
Dir.glob(search_me).sort.each {|rb|
|
72
|
+
require rb}
|
73
|
+
end
|
74
|
+
|
75
|
+
# :startdoc:
|
76
|
+
|
77
|
+
# Parse an io stream and return an array of iCalendar entities.
|
78
|
+
# Normally this will be an array of RiCal::Component::Calendar instances
|
79
|
+
def self.parse(io)
|
80
|
+
Parser.new(io).parse
|
81
|
+
end
|
82
|
+
|
83
|
+
# Parse a string and return an array of iCalendar entities.
|
84
|
+
# see RiCal.parse
|
85
|
+
def self.parse_string(string)
|
86
|
+
parse(StringIO.new(string))
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.debug # :nodoc:
|
90
|
+
@debug
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.debug=(val) # :nodoc:
|
94
|
+
@debug = val
|
95
|
+
end
|
96
|
+
|
97
|
+
# return a new Alarm event or todo component. If a block is provided it will will be executed in
|
98
|
+
# the context of a builder object which can be used to initialize the properties of the
|
99
|
+
# new Alarm.
|
100
|
+
def self.Alarm(&init_block)
|
101
|
+
Component::Alarm.new(&init_block)
|
102
|
+
end
|
103
|
+
|
104
|
+
# return a new Calendar. If a block is provided it will will be executed in
|
105
|
+
# the context of a builder object which can be used to initialize the properties and components of the
|
106
|
+
# new calendar.
|
107
|
+
def self.Calendar(&init_block)
|
108
|
+
Component::Calendar.new(&init_block)
|
109
|
+
end
|
110
|
+
|
111
|
+
# return a new Event calendar component. If a block is provided it will will be executed in
|
112
|
+
# the context of a builder object which can be used to initialize the properties and alarms of the
|
113
|
+
# new Event.
|
114
|
+
def self.Event(&init_block)
|
115
|
+
Component::Event.new(&init_block)
|
116
|
+
end
|
117
|
+
|
118
|
+
# return a new Freebusy calendar component. If a block is provided it will will be executed in
|
119
|
+
# the context of a builder object which can be used to initialize the properties and components of the
|
120
|
+
# new Freebusy.
|
121
|
+
def self.Freebusy(&init_block)
|
122
|
+
Component::Freebusy.new(&init_block)
|
123
|
+
end
|
124
|
+
|
125
|
+
# return a new Journal calendar component. If a block is provided it will will be executed in
|
126
|
+
# the context of a builder object which can be used to initialize the properties and components of the
|
127
|
+
# new Event.
|
128
|
+
def self.Journal(&init_block)
|
129
|
+
Component::Journal.new(&init_block)
|
130
|
+
end
|
131
|
+
|
132
|
+
# return a new Timezone calendar component. If a block is provided it will will be executed in
|
133
|
+
# the context of a builder object which can be used to initialize the properties and timezone periods of the
|
134
|
+
# new Timezone.
|
135
|
+
def self.Timezone(&init_block)
|
136
|
+
Component::Timezone.new(&init_block)
|
137
|
+
end
|
138
|
+
|
139
|
+
# return a new TimezonePeriod timezone component. If a block is provided it will will be executed in
|
140
|
+
# the context of a builder object which can be used to initialize the properties of the
|
141
|
+
# new TimezonePeriod.
|
142
|
+
def self.TimezonePeriod(&init_block)
|
143
|
+
Component::TimezonePeriod.new(&init_block)
|
144
|
+
end
|
145
|
+
|
146
|
+
# return a new Todo calendar component. If a block is provided it will will be executed in
|
147
|
+
# the context of a builder object which can be used to initialize the properties and alarms of the
|
148
|
+
# new Todo.
|
149
|
+
def self.Todo(&init_block)
|
150
|
+
Component::Todo.new(&init_block)
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.ro_calls=(value)
|
154
|
+
@ro_calls = value
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.ro_calls
|
158
|
+
@ro_calls ||= 0
|
159
|
+
end
|
160
|
+
|
161
|
+
def self.ro_misses=(value)
|
162
|
+
@ro_misses = value
|
163
|
+
end
|
164
|
+
|
165
|
+
def self.ro_misses
|
166
|
+
@ro_misses ||= 0
|
167
|
+
end
|
168
|
+
|
169
|
+
def self.RationalOffset
|
170
|
+
self.ro_calls += 1
|
171
|
+
@rational_offset ||= Hash.new {|h, seconds|
|
172
|
+
self.ro_misses += 1
|
173
|
+
h[seconds] = Rational(seconds, 86400)}
|
174
|
+
end
|
175
|
+
|
176
|
+
end # module RiCal
|
177
|
+
|
178
|
+
(-12..12).each do |hour_offset|
|
179
|
+
RiCal.RationalOffset[hour_offset * 86400]
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
#RiCal.require_all_libs_relative_to(__FILE__)
|
184
|
+
# EOF
|
@@ -0,0 +1,254 @@
|
|
1
|
+
module RiCal
|
2
|
+
class Component #:nodoc:
|
3
|
+
|
4
|
+
autoload :Alarm, "ri_cal/component/alarm.rb"
|
5
|
+
autoload :Calendar, "ri_cal/component/calendar.rb"
|
6
|
+
autoload :Event, "ri_cal/component/event.rb"
|
7
|
+
autoload :Freebusy, "ri_cal/component/freebusy.rb"
|
8
|
+
autoload :Journal, "ri_cal/component/journal.rb"
|
9
|
+
autoload :NonStandard, "ri_cal/component/non_standard.rb"
|
10
|
+
autoload :TZInfoTimezone, "ri_cal/component/t_z_info_timezone.rb"
|
11
|
+
autoload :Timezone, "ri_cal/component/timezone.rb"
|
12
|
+
autoload :Todo, "ri_cal/component/todo.rb"
|
13
|
+
|
14
|
+
class ComponentBuilder #:nodoc:
|
15
|
+
def initialize(component)
|
16
|
+
@component = component
|
17
|
+
end
|
18
|
+
|
19
|
+
def method_missing(selector, *args, &init_block) #:nodoc:
|
20
|
+
if(sub_comp_class = @component.subcomponent_class[selector])
|
21
|
+
if init_block
|
22
|
+
sub_comp = sub_comp_class.new(@component)
|
23
|
+
if init_block.arity == 1
|
24
|
+
yield ComponentBuilder.new(sub_comp)
|
25
|
+
else
|
26
|
+
ComponentBuilder.new(sub_comp).instance_eval(&init_block)
|
27
|
+
end
|
28
|
+
self.add_subcomponent(sub_comp)
|
29
|
+
end
|
30
|
+
else
|
31
|
+
sel = selector.to_s
|
32
|
+
sel = "#{sel}=" unless /(^(add_)|(remove_))|(=$)/ =~ sel
|
33
|
+
if @component.respond_to?(sel)
|
34
|
+
@component.send(sel, *args)
|
35
|
+
else
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
attr_accessor :imported #:nodoc:
|
43
|
+
|
44
|
+
def initialize(parent=nil, entity_name = nil, &init_block) #:nodoc:
|
45
|
+
@parent = parent
|
46
|
+
if block_given?
|
47
|
+
if init_block.arity == 1
|
48
|
+
init_block.call(ComponentBuilder.new(self))
|
49
|
+
else
|
50
|
+
ComponentBuilder.new(self).instance_eval(&init_block)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def default_tzid #:nodoc:
|
56
|
+
if @parent
|
57
|
+
@parent.default_tzid
|
58
|
+
else
|
59
|
+
PropertyValue::DateTime.default_tzid
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def find_timezone(identifier) #:nodoc:
|
64
|
+
if @parent
|
65
|
+
@parent.find_timezone(identifier)
|
66
|
+
else
|
67
|
+
begin
|
68
|
+
Calendar::TZInfoWrapper.new(TZInfo::Timezone.get(identifier), self)
|
69
|
+
rescue ::TZInfo::InvalidTimezoneIdentifier => ex
|
70
|
+
raise RiCal::InvalidTimezoneIdentifier.invalid_tzinfo_identifier(identifier)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def tz_info_source?
|
76
|
+
if @parent
|
77
|
+
@parent.tz_info_source?
|
78
|
+
else
|
79
|
+
true
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def time_zone_for(ruby_object) #:nodoc:
|
84
|
+
@parent.time_zone_for(ruby_object) #:nodoc:
|
85
|
+
end
|
86
|
+
|
87
|
+
def subcomponent_class #:nodoc:
|
88
|
+
{}
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.from_parser(parser, parent, entity_name) #:nodoc:
|
92
|
+
entity = self.new(parent, entity_name)
|
93
|
+
entity.imported = true
|
94
|
+
line = parser.next_separated_line
|
95
|
+
while parser.still_in(entity_name, line)
|
96
|
+
entity.process_line(parser, line)
|
97
|
+
line = parser.next_separated_line
|
98
|
+
end
|
99
|
+
entity
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.parse(io) #:nodoc:
|
103
|
+
Parser.new(io).parse
|
104
|
+
end
|
105
|
+
|
106
|
+
def imported? #:nodoc:
|
107
|
+
imported
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.parse_string(string) #:nodoc:
|
111
|
+
parse(StringIO.new(string))
|
112
|
+
end
|
113
|
+
|
114
|
+
def subcomponents #:nodoc:
|
115
|
+
@subcomponents ||= Hash.new {|h, k| h[k] = []}
|
116
|
+
end
|
117
|
+
|
118
|
+
def entity_name #:nodoc:
|
119
|
+
self.class.entity_name
|
120
|
+
end
|
121
|
+
|
122
|
+
# return an array of Alarm components within this component :nodoc:
|
123
|
+
# Alarms may be contained within Events, and Todos
|
124
|
+
def alarms
|
125
|
+
subcomponents["VALARM"]
|
126
|
+
end
|
127
|
+
|
128
|
+
def add_subcomponent(component) #:nodoc:
|
129
|
+
subcomponents[component.entity_name] << component
|
130
|
+
end
|
131
|
+
|
132
|
+
def parse_subcomponent(parser, line) #:nodoc:
|
133
|
+
subcomponents[line[:value]] << parser.parse_one(line, self)
|
134
|
+
end
|
135
|
+
|
136
|
+
def process_line(parser, line) #:nodoc:
|
137
|
+
if line[:name] == "BEGIN"
|
138
|
+
parse_subcomponent(parser, line)
|
139
|
+
else
|
140
|
+
setter = self.class.property_parser[line[:name]]
|
141
|
+
if setter
|
142
|
+
send(setter, line)
|
143
|
+
else
|
144
|
+
self.add_x_property(line[:name], PropertyValue::Text.new(self, line))
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# return a hash of any extended properties, (i.e. those with a property name starting with "X-"
|
150
|
+
# representing an extension to the RFC 2445 specification)
|
151
|
+
def x_properties
|
152
|
+
@x_properties ||= Hash.new {|h,k| h[k] = []}
|
153
|
+
end
|
154
|
+
|
155
|
+
# Add a n extended property
|
156
|
+
def add_x_property(name, prop, debug=false)
|
157
|
+
x_properties[name.gsub("_","-").upcase] << prop.to_ri_cal_text_property
|
158
|
+
end
|
159
|
+
|
160
|
+
def method_missing(selector, *args, &b) #:nodoc:
|
161
|
+
xprop_candidate = selector.to_s
|
162
|
+
if (match = /^(x_.+)(=?)$/.match(xprop_candidate))
|
163
|
+
x_property_key = match[1].gsub('_','-').upcase
|
164
|
+
if match[2] == "="
|
165
|
+
args.each do |val|
|
166
|
+
add_x_property(x_property_key, val)
|
167
|
+
end
|
168
|
+
else
|
169
|
+
x_properties[x_property_key].map {|property| property.value}
|
170
|
+
end
|
171
|
+
else
|
172
|
+
super
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Predicate to determine if the component is valid according to RFC 2445
|
177
|
+
def valid?
|
178
|
+
!mutual_exclusion_violation
|
179
|
+
end
|
180
|
+
|
181
|
+
def initialize_copy(original) #:nodoc:
|
182
|
+
end
|
183
|
+
|
184
|
+
def prop_string(prop_name, *properties) #:nodoc:
|
185
|
+
properties = properties.flatten.compact
|
186
|
+
if properties && !properties.empty?
|
187
|
+
properties.map {|prop| "#{prop_name}#{prop.to_s}"}.join("\n")
|
188
|
+
else
|
189
|
+
nil
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def add_property_date_times_to(required_timezones, property) #:nodoc:
|
194
|
+
if property
|
195
|
+
if Array === property
|
196
|
+
property.each do |prop|
|
197
|
+
prop.add_date_times_to(required_timezones)
|
198
|
+
end
|
199
|
+
else
|
200
|
+
property.add_date_times_to(required_timezones)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def export_prop_to(export_stream, name, prop) #:nodoc:
|
206
|
+
if prop
|
207
|
+
string = prop_string(name, prop)
|
208
|
+
export_stream.puts(string) if string
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def export_x_properties_to(export_stream) #:nodoc:
|
213
|
+
x_properties.each do |name, props|
|
214
|
+
props.each do | prop |
|
215
|
+
export_stream.puts("#{name}#{prop}")
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def export_subcomponent_to(export_stream, subcomponent) #:nodoc:
|
221
|
+
subcomponent.each do |component|
|
222
|
+
component.export_to(export_stream)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
# return a string containing the rfc2445 format of the component
|
227
|
+
def to_s
|
228
|
+
io = StringIO.new
|
229
|
+
export_to(io)
|
230
|
+
io.string
|
231
|
+
end
|
232
|
+
|
233
|
+
# Export this component to an export stream
|
234
|
+
def export_to(export_stream)
|
235
|
+
export_stream.puts("BEGIN:#{entity_name}")
|
236
|
+
export_properties_to(export_stream)
|
237
|
+
export_x_properties_to(export_stream)
|
238
|
+
subcomponents.values.each do |sub|
|
239
|
+
export_subcomponent_to(export_stream, sub)
|
240
|
+
end
|
241
|
+
export_stream.puts("END:#{entity_name}")
|
242
|
+
end
|
243
|
+
|
244
|
+
# Export this single component as an iCalendar component containing only this component and
|
245
|
+
# any required additional components (i.e. VTIMEZONES referenced from this component)
|
246
|
+
# if stream is nil (the default) then this method will return a string,
|
247
|
+
# otherwise stream should be an IO to which the iCalendar file contents will be written
|
248
|
+
def export(stream=nil)
|
249
|
+
wrapper_calendar = Calendar.new
|
250
|
+
wrapper_calendar.add_subcomponent(self)
|
251
|
+
wrapper_calendar.export(stream)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|