awallis-ri_cal 0.8.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. data/History.txt +402 -0
  2. data/Manifest.txt +161 -0
  3. data/README.txt +410 -0
  4. data/Rakefile +69 -0
  5. data/VERSION +1 -0
  6. data/bin/ri_cal +8 -0
  7. data/component_attributes/alarm.yml +10 -0
  8. data/component_attributes/calendar.yml +4 -0
  9. data/component_attributes/component_property_defs.yml +180 -0
  10. data/component_attributes/event.yml +45 -0
  11. data/component_attributes/freebusy.yml +16 -0
  12. data/component_attributes/journal.yml +35 -0
  13. data/component_attributes/timezone.yml +3 -0
  14. data/component_attributes/timezone_period.yml +11 -0
  15. data/component_attributes/todo.yml +46 -0
  16. data/copyrights.txt +1 -0
  17. data/docs/draft-ietf-calsify-2446bis-08.txt +7280 -0
  18. data/docs/draft-ietf-calsify-rfc2445bis-09.txt +10416 -0
  19. data/docs/incrementers.txt +7 -0
  20. data/docs/rfc2445.pdf +0 -0
  21. data/lib/ri_cal.rb +187 -0
  22. data/lib/ri_cal/component.rb +256 -0
  23. data/lib/ri_cal/component/alarm.rb +19 -0
  24. data/lib/ri_cal/component/calendar.rb +257 -0
  25. data/lib/ri_cal/component/event.rb +58 -0
  26. data/lib/ri_cal/component/freebusy.rb +16 -0
  27. data/lib/ri_cal/component/journal.rb +27 -0
  28. data/lib/ri_cal/component/non_standard.rb +33 -0
  29. data/lib/ri_cal/component/t_z_info_timezone.rb +153 -0
  30. data/lib/ri_cal/component/timezone.rb +197 -0
  31. data/lib/ri_cal/component/timezone/daylight_period.rb +25 -0
  32. data/lib/ri_cal/component/timezone/standard_period.rb +23 -0
  33. data/lib/ri_cal/component/timezone/timezone_period.rb +76 -0
  34. data/lib/ri_cal/component/todo.rb +42 -0
  35. data/lib/ri_cal/core_extensions.rb +11 -0
  36. data/lib/ri_cal/core_extensions/array.rb +7 -0
  37. data/lib/ri_cal/core_extensions/array/conversions.rb +15 -0
  38. data/lib/ri_cal/core_extensions/date.rb +13 -0
  39. data/lib/ri_cal/core_extensions/date/conversions.rb +56 -0
  40. data/lib/ri_cal/core_extensions/date_time.rb +15 -0
  41. data/lib/ri_cal/core_extensions/date_time/conversions.rb +50 -0
  42. data/lib/ri_cal/core_extensions/object.rb +8 -0
  43. data/lib/ri_cal/core_extensions/object/conversions.rb +20 -0
  44. data/lib/ri_cal/core_extensions/string.rb +8 -0
  45. data/lib/ri_cal/core_extensions/string/conversions.rb +57 -0
  46. data/lib/ri_cal/core_extensions/time.rb +14 -0
  47. data/lib/ri_cal/core_extensions/time/calculations.rb +153 -0
  48. data/lib/ri_cal/core_extensions/time/conversions.rb +42 -0
  49. data/lib/ri_cal/core_extensions/time/tzid_access.rb +50 -0
  50. data/lib/ri_cal/core_extensions/time/week_day_predicates.rb +55 -0
  51. data/lib/ri_cal/fast_date_time.rb +234 -0
  52. data/lib/ri_cal/floating_timezone.rb +32 -0
  53. data/lib/ri_cal/invalid_property_value.rb +8 -0
  54. data/lib/ri_cal/invalid_timezone_identifier.rb +20 -0
  55. data/lib/ri_cal/occurrence_enumerator.rb +265 -0
  56. data/lib/ri_cal/occurrence_period.rb +17 -0
  57. data/lib/ri_cal/parser.rb +145 -0
  58. data/lib/ri_cal/properties.rb +12 -0
  59. data/lib/ri_cal/properties/alarm.rb +390 -0
  60. data/lib/ri_cal/properties/calendar.rb +164 -0
  61. data/lib/ri_cal/properties/event.rb +1523 -0
  62. data/lib/ri_cal/properties/freebusy.rb +593 -0
  63. data/lib/ri_cal/properties/journal.rb +1237 -0
  64. data/lib/ri_cal/properties/timezone.rb +150 -0
  65. data/lib/ri_cal/properties/timezone_period.rb +416 -0
  66. data/lib/ri_cal/properties/todo.rb +1559 -0
  67. data/lib/ri_cal/property_value.rb +159 -0
  68. data/lib/ri_cal/property_value/array.rb +27 -0
  69. data/lib/ri_cal/property_value/cal_address.rb +11 -0
  70. data/lib/ri_cal/property_value/date.rb +184 -0
  71. data/lib/ri_cal/property_value/date_time.rb +359 -0
  72. data/lib/ri_cal/property_value/date_time/additive_methods.rb +44 -0
  73. data/lib/ri_cal/property_value/date_time/time_machine.rb +159 -0
  74. data/lib/ri_cal/property_value/date_time/timezone_support.rb +100 -0
  75. data/lib/ri_cal/property_value/duration.rb +110 -0
  76. data/lib/ri_cal/property_value/geo.rb +11 -0
  77. data/lib/ri_cal/property_value/integer.rb +12 -0
  78. data/lib/ri_cal/property_value/occurrence_list.rb +144 -0
  79. data/lib/ri_cal/property_value/period.rb +86 -0
  80. data/lib/ri_cal/property_value/recurrence_rule.rb +154 -0
  81. data/lib/ri_cal/property_value/recurrence_rule/enumeration_support_methods.rb +100 -0
  82. data/lib/ri_cal/property_value/recurrence_rule/enumerator.rb +79 -0
  83. data/lib/ri_cal/property_value/recurrence_rule/initialization_methods.rb +148 -0
  84. data/lib/ri_cal/property_value/recurrence_rule/negative_setpos_enumerator.rb +53 -0
  85. data/lib/ri_cal/property_value/recurrence_rule/numbered_span.rb +31 -0
  86. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer.rb +135 -0
  87. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_day_incrementer.rb +86 -0
  88. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_hour_incrementer.rb +31 -0
  89. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_minute_incrementer.rb +32 -0
  90. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_month_incrementer.rb +52 -0
  91. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_monthday_incrementer.rb +31 -0
  92. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_numbered_day_incrementer.rb +38 -0
  93. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_second_incrementer.rb +32 -0
  94. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_weekno_incrementer.rb +69 -0
  95. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_yearday_incrementer.rb +31 -0
  96. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/daily_incrementer.rb +28 -0
  97. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/frequency_incrementer.rb +80 -0
  98. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/hourly_incrementer.rb +23 -0
  99. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/list_incrementer.rb +106 -0
  100. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/minutely_incrementer.rb +23 -0
  101. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/monthly_incrementer.rb +33 -0
  102. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/null_sub_cycle_incrementer.rb +43 -0
  103. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/secondly_incrementer.rb +28 -0
  104. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/weekly_incrementer.rb +37 -0
  105. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/yearly_incrementer.rb +57 -0
  106. data/lib/ri_cal/property_value/recurrence_rule/recurring_day.rb +131 -0
  107. data/lib/ri_cal/property_value/recurrence_rule/recurring_month_day.rb +64 -0
  108. data/lib/ri_cal/property_value/recurrence_rule/recurring_numbered_week.rb +33 -0
  109. data/lib/ri_cal/property_value/recurrence_rule/recurring_year_day.rb +53 -0
  110. data/lib/ri_cal/property_value/recurrence_rule/time_manipulation.rb +42 -0
  111. data/lib/ri_cal/property_value/recurrence_rule/validations.rb +125 -0
  112. data/lib/ri_cal/property_value/text.rb +44 -0
  113. data/lib/ri_cal/property_value/uri.rb +11 -0
  114. data/lib/ri_cal/property_value/utc_offset.rb +33 -0
  115. data/lib/ri_cal/property_value/zulu_date_time.rb +34 -0
  116. data/lib/ri_cal/required_timezones.rb +55 -0
  117. data/parked_specs/ri_cal/claudio_a_bug_spec.rb +100 -0
  118. data/performance/empty_propval/subject.rb +43 -0
  119. data/performance/paris_eastern/subject.rb +90 -0
  120. data/performance/penultimate_weekday/subject.rb +15 -0
  121. data/performance/psm_big_enum/ical.ics +3171 -0
  122. data/performance/psm_big_enum/subject.rb +16 -0
  123. data/performance/utah_cycling/subject.rb +55 -0
  124. data/ri_cal.gemspec +244 -0
  125. data/script/benchmark_subject +23 -0
  126. data/script/console +10 -0
  127. data/script/destroy +14 -0
  128. data/script/generate +14 -0
  129. data/script/profile_subject +29 -0
  130. data/script/txt2html +71 -0
  131. data/spec/ri_cal/bugreports_spec.rb +276 -0
  132. data/spec/ri_cal/component/alarm_spec.rb +12 -0
  133. data/spec/ri_cal/component/calendar_spec.rb +88 -0
  134. data/spec/ri_cal/component/event_spec.rb +735 -0
  135. data/spec/ri_cal/component/freebusy_spec.rb +12 -0
  136. data/spec/ri_cal/component/journal_spec.rb +37 -0
  137. data/spec/ri_cal/component/t_z_info_timezone_spec.rb +60 -0
  138. data/spec/ri_cal/component/timezone_spec.rb +236 -0
  139. data/spec/ri_cal/component/todo_spec.rb +112 -0
  140. data/spec/ri_cal/component_spec.rb +224 -0
  141. data/spec/ri_cal/core_extensions/string/conversions_spec.rb +78 -0
  142. data/spec/ri_cal/core_extensions/time/calculations_spec.rb +188 -0
  143. data/spec/ri_cal/core_extensions/time/week_day_predicates_spec.rb +45 -0
  144. data/spec/ri_cal/fast_date_time_spec.rb +77 -0
  145. data/spec/ri_cal/inf_loop_spec.rb +78 -0
  146. data/spec/ri_cal/occurrence_enumerator_spec.rb +611 -0
  147. data/spec/ri_cal/parser_spec.rb +337 -0
  148. data/spec/ri_cal/property_value/date_spec.rb +53 -0
  149. data/spec/ri_cal/property_value/date_time_spec.rb +383 -0
  150. data/spec/ri_cal/property_value/duration_spec.rb +126 -0
  151. data/spec/ri_cal/property_value/occurrence_list_spec.rb +72 -0
  152. data/spec/ri_cal/property_value/period_spec.rb +63 -0
  153. data/spec/ri_cal/property_value/recurrence_rule/recurring_year_day_spec.rb +21 -0
  154. data/spec/ri_cal/property_value/recurrence_rule_spec.rb +1814 -0
  155. data/spec/ri_cal/property_value/text_spec.rb +25 -0
  156. data/spec/ri_cal/property_value/utc_offset_spec.rb +48 -0
  157. data/spec/ri_cal/property_value_spec.rb +125 -0
  158. data/spec/ri_cal/required_timezones_spec.rb +67 -0
  159. data/spec/ri_cal_spec.rb +53 -0
  160. data/spec/spec.opts +4 -0
  161. data/spec/spec_helper.rb +50 -0
  162. data/tasks/gem_loader/load_active_support.rb +3 -0
  163. data/tasks/gem_loader/load_tzinfo_gem.rb +2 -0
  164. data/tasks/ri_cal.rake +412 -0
  165. data/tasks/spec.rake +102 -0
  166. metadata +255 -0
@@ -0,0 +1,7 @@
1
+ Seconds - Freq="SECONDLY", BYSECOND
2
+ Minutes - Freq="MINUTELY", BYMINUTE
3
+ Hours - Freq="HOURLY", BYHOUR
4
+ Days - Freq="DAILY", BYDAY, BYMONTHDAY, BYYEARDAY
5
+ Weeks - Freq="WEEKLY", BYWEEKNO
6
+ Months - Freq="MONTHLY", BYMONTH
7
+ Years - Freq="YEARLY"
Binary file
@@ -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
@@ -0,0 +1,256 @@
1
+ module RiCal
2
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
3
+ #
4
+ class Component #:nodoc:
5
+
6
+ autoload :Alarm, "ri_cal/component/alarm.rb"
7
+ autoload :Calendar, "ri_cal/component/calendar.rb"
8
+ autoload :Event, "ri_cal/component/event.rb"
9
+ autoload :Freebusy, "ri_cal/component/freebusy.rb"
10
+ autoload :Journal, "ri_cal/component/journal.rb"
11
+ autoload :NonStandard, "ri_cal/component/non_standard.rb"
12
+ autoload :TZInfoTimezone, "ri_cal/component/t_z_info_timezone.rb"
13
+ autoload :Timezone, "ri_cal/component/timezone.rb"
14
+ autoload :Todo, "ri_cal/component/todo.rb"
15
+
16
+ class ComponentBuilder #:nodoc:
17
+ def initialize(component)
18
+ @component = component
19
+ end
20
+
21
+ def method_missing(selector, *args, &init_block) #:nodoc:
22
+ if(sub_comp_class = @component.subcomponent_class[selector])
23
+ if init_block
24
+ sub_comp = sub_comp_class.new(@component)
25
+ if init_block.arity == 1
26
+ yield ComponentBuilder.new(sub_comp)
27
+ else
28
+ ComponentBuilder.new(sub_comp).instance_eval(&init_block)
29
+ end
30
+ self.add_subcomponent(sub_comp)
31
+ end
32
+ else
33
+ sel = selector.to_s
34
+ sel = "#{sel}=" unless /(^(add_)|(remove_))|(=$)/ =~ sel
35
+ if @component.respond_to?(sel)
36
+ @component.send(sel, *args)
37
+ else
38
+ super
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ attr_accessor :imported #:nodoc:
45
+
46
+ def initialize(parent=nil, entity_name = nil, &init_block) #:nodoc:
47
+ @parent = parent
48
+ if block_given?
49
+ if init_block.arity == 1
50
+ init_block.call(ComponentBuilder.new(self))
51
+ else
52
+ ComponentBuilder.new(self).instance_eval(&init_block)
53
+ end
54
+ end
55
+ end
56
+
57
+ def default_tzid #:nodoc:
58
+ if @parent
59
+ @parent.default_tzid
60
+ else
61
+ PropertyValue::DateTime.default_tzid
62
+ end
63
+ end
64
+
65
+ def find_timezone(identifier) #:nodoc:
66
+ if @parent
67
+ @parent.find_timezone(identifier)
68
+ else
69
+ begin
70
+ Calendar::TZInfoWrapper.new(TZInfo::Timezone.get(identifier), self)
71
+ rescue ::TZInfo::InvalidTimezoneIdentifier => ex
72
+ raise RiCal::InvalidTimezoneIdentifier.invalid_tzinfo_identifier(identifier)
73
+ end
74
+ end
75
+ end
76
+
77
+ def tz_info_source?
78
+ if @parent
79
+ @parent.tz_info_source?
80
+ else
81
+ true
82
+ end
83
+ end
84
+
85
+ def time_zone_for(ruby_object) #:nodoc:
86
+ @parent.time_zone_for(ruby_object) #:nodoc:
87
+ end
88
+
89
+ def subcomponent_class #:nodoc:
90
+ {}
91
+ end
92
+
93
+ def self.from_parser(parser, parent, entity_name) #:nodoc:
94
+ entity = self.new(parent, entity_name)
95
+ entity.imported = true
96
+ line = parser.next_separated_line
97
+ while parser.still_in(entity_name, line)
98
+ entity.process_line(parser, line)
99
+ line = parser.next_separated_line
100
+ end
101
+ entity
102
+ end
103
+
104
+ def self.parse(io) #:nodoc:
105
+ Parser.new(io).parse
106
+ end
107
+
108
+ def imported? #:nodoc:
109
+ imported
110
+ end
111
+
112
+ def self.parse_string(string) #:nodoc:
113
+ parse(StringIO.new(string))
114
+ end
115
+
116
+ def subcomponents #:nodoc:
117
+ @subcomponents ||= Hash.new {|h, k| h[k] = []}
118
+ end
119
+
120
+ def entity_name #:nodoc:
121
+ self.class.entity_name
122
+ end
123
+
124
+ # return an array of Alarm components within this component :nodoc:
125
+ # Alarms may be contained within Events, and Todos
126
+ def alarms
127
+ subcomponents["VALARM"]
128
+ end
129
+
130
+ def add_subcomponent(component) #:nodoc:
131
+ subcomponents[component.entity_name] << component
132
+ end
133
+
134
+ def parse_subcomponent(parser, line) #:nodoc:
135
+ subcomponents[line[:value]] << parser.parse_one(line, self)
136
+ end
137
+
138
+ def process_line(parser, line) #:nodoc:
139
+ if line[:name] == "BEGIN"
140
+ parse_subcomponent(parser, line)
141
+ else
142
+ setter = self.class.property_parser[line[:name]]
143
+ if setter
144
+ send(setter, line)
145
+ else
146
+ self.add_x_property(line[:name], PropertyValue::Text.new(self, line))
147
+ end
148
+ end
149
+ end
150
+
151
+ # return a hash of any extended properties, (i.e. those with a property name starting with "X-"
152
+ # representing an extension to the RFC 2445 specification)
153
+ def x_properties
154
+ @x_properties ||= Hash.new {|h,k| h[k] = []}
155
+ end
156
+
157
+ # Add a n extended property
158
+ def add_x_property(name, prop, debug=false)
159
+ x_properties[name.gsub("_","-").upcase] << prop.to_ri_cal_text_property
160
+ end
161
+
162
+ def method_missing(selector, *args, &b) #:nodoc:
163
+ xprop_candidate = selector.to_s
164
+ if (match = /^(x_.+)(=?)$/.match(xprop_candidate))
165
+ x_property_key = match[1].gsub('_','-').upcase
166
+ if match[2] == "="
167
+ args.each do |val|
168
+ add_x_property(x_property_key, val)
169
+ end
170
+ else
171
+ x_properties[x_property_key].map {|property| property.value}
172
+ end
173
+ else
174
+ super
175
+ end
176
+ end
177
+
178
+ # Predicate to determine if the component is valid according to RFC 2445
179
+ def valid?
180
+ !mutual_exclusion_violation
181
+ end
182
+
183
+ def initialize_copy(original) #:nodoc:
184
+ end
185
+
186
+ def prop_string(prop_name, *properties) #:nodoc:
187
+ properties = properties.flatten.compact
188
+ if properties && !properties.empty?
189
+ properties.map {|prop| "#{prop_name}#{prop.to_s}"}.join("\n")
190
+ else
191
+ nil
192
+ end
193
+ end
194
+
195
+ def add_property_date_times_to(required_timezones, property) #:nodoc:
196
+ if property
197
+ if Array === property
198
+ property.each do |prop|
199
+ prop.add_date_times_to(required_timezones)
200
+ end
201
+ else
202
+ property.add_date_times_to(required_timezones)
203
+ end
204
+ end
205
+ end
206
+
207
+ def export_prop_to(export_stream, name, prop) #:nodoc:
208
+ if prop
209
+ string = prop_string(name, prop)
210
+ export_stream.puts(string) if string
211
+ end
212
+ end
213
+
214
+ def export_x_properties_to(export_stream) #:nodoc:
215
+ x_properties.each do |name, props|
216
+ props.each do | prop |
217
+ export_stream.puts("#{name}#{prop}")
218
+ end
219
+ end
220
+ end
221
+
222
+ def export_subcomponent_to(export_stream, subcomponent) #:nodoc:
223
+ subcomponent.each do |component|
224
+ component.export_to(export_stream)
225
+ end
226
+ end
227
+
228
+ # return a string containing the rfc2445 format of the component
229
+ def to_s
230
+ io = StringIO.new
231
+ export_to(io)
232
+ io.string
233
+ end
234
+
235
+ # Export this component to an export stream
236
+ def export_to(export_stream)
237
+ export_stream.puts("BEGIN:#{entity_name}")
238
+ export_properties_to(export_stream)
239
+ export_x_properties_to(export_stream)
240
+ subcomponents.values.each do |sub|
241
+ export_subcomponent_to(export_stream, sub)
242
+ end
243
+ export_stream.puts("END:#{entity_name}")
244
+ end
245
+
246
+ # Export this single component as an iCalendar component containing only this component and
247
+ # any required additional components (i.e. VTIMEZONES referenced from this component)
248
+ # if stream is nil (the default) then this method will return a string,
249
+ # otherwise stream should be an IO to which the iCalendar file contents will be written
250
+ def export(stream=nil)
251
+ wrapper_calendar = Calendar.new
252
+ wrapper_calendar.add_subcomponent(self)
253
+ wrapper_calendar.export(stream)
254
+ end
255
+ end
256
+ end