icalendar 1.5.4 → 2.0.0.beta.1

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 (100) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -1
  3. data/.rspec +2 -0
  4. data/.travis.yml +1 -2
  5. data/History.txt +2 -7
  6. data/README.md +82 -107
  7. data/Rakefile +6 -7
  8. data/icalendar.gemspec +10 -9
  9. data/lib/icalendar.rb +17 -33
  10. data/lib/icalendar/alarm.rb +35 -0
  11. data/lib/icalendar/calendar.rb +17 -100
  12. data/lib/icalendar/component.rb +41 -403
  13. data/lib/icalendar/event.rb +51 -0
  14. data/lib/icalendar/freebusy.rb +27 -0
  15. data/lib/icalendar/has_components.rb +83 -0
  16. data/lib/icalendar/has_properties.rb +156 -0
  17. data/lib/icalendar/journal.rb +39 -0
  18. data/lib/icalendar/parser.rb +75 -403
  19. data/lib/icalendar/timezone.rb +53 -0
  20. data/lib/icalendar/todo.rb +52 -0
  21. data/lib/icalendar/tzinfo.rb +30 -30
  22. data/lib/icalendar/value.rb +80 -0
  23. data/lib/icalendar/values/array.rb +43 -0
  24. data/lib/icalendar/values/binary.rb +31 -0
  25. data/lib/icalendar/values/boolean.rb +17 -0
  26. data/lib/icalendar/values/cal_address.rb +8 -0
  27. data/lib/icalendar/values/date.rb +26 -0
  28. data/lib/icalendar/values/date_time.rb +34 -0
  29. data/lib/icalendar/values/duration.rb +48 -0
  30. data/lib/icalendar/values/float.rb +17 -0
  31. data/lib/icalendar/values/integer.rb +17 -0
  32. data/lib/icalendar/values/period.rb +46 -0
  33. data/lib/icalendar/values/recur.rb +63 -0
  34. data/lib/icalendar/values/text.rb +26 -0
  35. data/lib/icalendar/values/time.rb +34 -0
  36. data/lib/icalendar/values/time_with_zone.rb +31 -0
  37. data/lib/icalendar/values/uri.rb +19 -0
  38. data/lib/icalendar/values/utc_offset.rb +39 -0
  39. data/lib/icalendar/version.rb +5 -0
  40. data/spec/alarm_spec.rb +108 -0
  41. data/spec/calendar_spec.rb +167 -0
  42. data/spec/event_spec.rb +108 -0
  43. data/{test/fixtures/folding.ics → spec/fixtures/nondefault_values.ics} +2 -2
  44. data/{test → spec}/fixtures/single_event.ics +11 -14
  45. data/spec/fixtures/timezone.ics +35 -0
  46. data/spec/freebusy_spec.rb +7 -0
  47. data/spec/journal_spec.rb +7 -0
  48. data/spec/parser_spec.rb +26 -0
  49. data/spec/roundtrip_spec.rb +40 -0
  50. data/spec/spec_helper.rb +25 -0
  51. data/spec/timezone_spec.rb +31 -0
  52. data/spec/todo_spec.rb +24 -0
  53. data/spec/tzinfo_spec.rb +85 -0
  54. data/spec/values/date_time_spec.rb +80 -0
  55. data/spec/values/duration_spec.rb +67 -0
  56. data/spec/values/period_spec.rb +47 -0
  57. data/spec/values/recur_spec.rb +47 -0
  58. data/spec/values/text_spec.rb +72 -0
  59. data/spec/values/utc_offset_spec.rb +41 -0
  60. metadata +129 -88
  61. data/GPL +0 -340
  62. data/examples/create_cal.rb +0 -45
  63. data/examples/parse_cal.rb +0 -20
  64. data/examples/single_event.ics +0 -18
  65. data/lib/hash_attrs.rb +0 -34
  66. data/lib/icalendar/base.rb +0 -47
  67. data/lib/icalendar/component/alarm.rb +0 -47
  68. data/lib/icalendar/component/event.rb +0 -131
  69. data/lib/icalendar/component/freebusy.rb +0 -38
  70. data/lib/icalendar/component/journal.rb +0 -60
  71. data/lib/icalendar/component/timezone.rb +0 -91
  72. data/lib/icalendar/component/todo.rb +0 -64
  73. data/lib/icalendar/conversions.rb +0 -107
  74. data/lib/icalendar/helpers.rb +0 -109
  75. data/lib/icalendar/parameter.rb +0 -33
  76. data/lib/icalendar/rrule.rb +0 -133
  77. data/lib/meta.rb +0 -32
  78. data/script/console +0 -10
  79. data/script/recur1.ics +0 -38
  80. data/script/tryit.rb +0 -13
  81. data/test/component/test_event.rb +0 -253
  82. data/test/component/test_timezone.rb +0 -74
  83. data/test/component/test_todo.rb +0 -31
  84. data/test/fixtures/life.ics +0 -46
  85. data/test/fixtures/nonstandard.ics +0 -25
  86. data/test/fixtures/simplecal.ics +0 -119
  87. data/test/interactive.rb +0 -17
  88. data/test/read_write.rb +0 -23
  89. data/test/test_calendar.rb +0 -167
  90. data/test/test_component.rb +0 -102
  91. data/test/test_conversions.rb +0 -104
  92. data/test/test_helper.rb +0 -7
  93. data/test/test_parameter.rb +0 -91
  94. data/test/test_parser.rb +0 -100
  95. data/test/test_tzinfo.rb +0 -83
  96. data/website/index.html +0 -70
  97. data/website/index.txt +0 -38
  98. data/website/javascripts/rounded_corners_lite.inc.js +0 -285
  99. data/website/stylesheets/screen.css +0 -159
  100. data/website/template.html.erb +0 -50
@@ -0,0 +1,35 @@
1
+ module Icalendar
2
+
3
+ class Alarm < Component
4
+
5
+ required_property :action
6
+ required_property :trigger, Icalendar::Values::Duration
7
+ required_property :description, Icalendar::Values::Text,
8
+ ->(alarm, description) { alarm.action.downcase == 'audio' || !description.nil? }
9
+ required_property :summary, Icalendar::Values::Text,
10
+ ->(alarm, summary) { alarm.action.downcase != 'email' || !summary.nil? }
11
+ required_multi_property :attendee, Icalendar::Values::CalAddress,
12
+ ->(alarm, attendees) { alarm.action.downcase != 'email' || !attendees.compact.empty? }
13
+
14
+ optional_single_property :duration, Icalendar::Values::Duration
15
+ optional_single_property :repeat, Icalendar::Values::Integer
16
+
17
+ optional_property :attach, Icalendar::Values::Uri
18
+
19
+ def initialize
20
+ super 'alarm'
21
+ end
22
+
23
+ def valid?(strict = false)
24
+ if strict
25
+ # must be part of event or todo
26
+ !(parent.nil? || parent.name == 'event' || parent.name == 'todo') and return false
27
+ end
28
+ # either both duration and repeat or neither should be set
29
+ [duration, repeat].compact.size == 1 and return false
30
+ # attach must be single for audio actions
31
+ action.downcase == 'audio' && attach.compact.size > 1 and return false
32
+ super
33
+ end
34
+ end
35
+ end
@@ -1,110 +1,27 @@
1
- =begin
2
- Copyright (C) 2005 Jeff Rose
3
-
4
- This library is free software; you can redistribute it and/or modify it
5
- under the same terms as the ruby language itself, see the file COPYING for
6
- details.
7
- =end
8
-
9
1
  module Icalendar
10
2
 
11
3
  class Calendar < Component
12
- ical_component :timezones, :events, :todos, :journals, :freebusys
13
-
14
- ical_property :calscale, :calendar_scale
15
- ical_property :prodid, :product_id
16
- ical_property :version
17
- ical_property :ip_method
18
-
19
- def initialize()
20
- super("VCALENDAR")
4
+ required_property :version
5
+ required_property :prodid
6
+ optional_single_property :calscale
7
+ optional_single_property :ip_method
21
8
 
22
- # Set some defaults
23
- self.calscale = "GREGORIAN" # Who knows, but this is the only one in the spec.
24
- self.prodid = "iCalendar-Ruby" # Current product... Should be overwritten by apps that use the library
25
- self.version = "2.0" # Version of the specification
26
- end
27
-
28
- def print_headers
29
- "VERSION:#{version}\r\n"
30
- end
9
+ component :timezone, :tzid
10
+ component :event
11
+ component :todo
12
+ component :journal
13
+ component :freebusy
31
14
 
32
- def properties_to_print
33
- @properties.select { |k,v| k != 'version' }
15
+ def initialize
16
+ super 'calendar'
17
+ self.prodid = 'icalendar-ruby'
18
+ self.version = '2.0'
19
+ self.calscale = 'GREGORIAN'
34
20
  end
35
21
 
36
- def event(&block)
37
- calendar_tzid = timezone_id
38
- build_component Event.new do
39
- # Note: I'm not sure this is the best way to pass this down, but it works
40
- self.tzid = calendar_tzid
41
-
42
- if block
43
- instance_eval(&block)
44
- if tzid
45
- dtstart.ical_params = { "TZID" => tzid }
46
- dtend.ical_params = { "TZID" => tzid } unless dtend.nil?
47
- end
48
- end
49
- end
50
- end
51
-
52
- def find_event(uid)
53
- events.find {|e| e.uid == uid}
54
- end
55
-
56
- def todo(&block)
57
- build_component Todo.new, &block
58
- end
59
-
60
- def find_todo(uid)
61
- todos.find {|t| t.uid == uid}
62
- end
63
-
64
- def journal(&block)
65
- build_component Journal.new, &block
66
- end
67
-
68
- def find_journal(uid)
69
- journals.find {|j| j.uid == uid}
70
- end
71
-
72
- def freebusy(&block)
73
- build_component Freebusy.new, &block
74
- end
75
-
76
- def find_freebusy(uid)
77
- freebusys.find {|f| f.uid == uid}
78
- end
79
-
80
- def timezone(&block)
81
- build_component Timezone.new, &block
82
- end
83
-
84
- # The "PUBLISH" method in a "VEVENT" calendar component is an
85
- # unsolicited posting of an iCalendar object. Any CU may add published
86
- # components to their calendar. The "Organizer" MUST be present in a
87
- # published iCalendar component. "Attendees" MUST NOT be present. Its
88
- # expected usage is for encapsulating an arbitrary event as an
89
- # iCalendar object. The "Organizer" may subsequently update (with
90
- # another "PUBLISH" method), add instances to (with an "ADD" method),
91
- # or cancel (with a "CANCEL" method) a previously published "VEVENT"
92
- # calendar component.
93
22
  def publish
94
- self.ip_method = "PUBLISH"
23
+ self.ip_method = 'PUBLISH'
95
24
  end
25
+ end
96
26
 
97
- private
98
-
99
- def build_component(component, &block)
100
- add_component component
101
- component.instance_eval(&block) if block
102
- component
103
- end
104
-
105
- def timezone_id
106
- timezones[0].tzid if timezones.length > 0
107
- end
108
- end # class Calendar
109
-
110
- end # module Icalendar
27
+ end
@@ -1,432 +1,70 @@
1
- =begin
2
- Copyright (C) 2005 Jeff Rose
3
-
4
- This library is free software; you can redistribute it and/or modify it
5
- under the same terms as the ruby language itself, see the file COPYING for
6
- details.
7
- =end
1
+ require 'socket'
8
2
 
9
3
  module Icalendar
10
- require 'socket'
11
-
12
- MAX_LINE_LENGTH = 75
13
-
14
- class Geo < Icalendar::Base
15
- attr_accessor :latitude, :longitude
16
- alias :lat :latitude
17
- alias :lat= :latitude=
18
- alias :long :longitude
19
- alias :long= :longitude=
20
-
21
- def initialize(lat, long)
22
- @latitude = lat
23
- @longitude = long
24
- end
25
-
26
- def to_ical
27
- "#{@latitude.to_ical};#{@longitude.to_ical}"
28
- end
29
- end
30
-
31
- # The body of the iCalendar object consists of a sequence of calendar
32
- # properties and one or more calendar components. The calendar
33
- # properties are attributes that apply to the calendar as a whole. The
34
- # calendar components are collections of properties that express a
35
- # particular calendar semantic. For example, the calendar component can
36
- # specify an Event, a Todo, a Journal entry, Timezone information, or
37
- # Freebusy time information, or an Alarm.
38
- class Component < Icalendar::Base
39
-
40
- CAL_EXTENSION_REGEX = /\Ax_[a-z_]+=?\Z/
41
4
 
42
- meta_include HashAttrs
5
+ class Component
6
+ include HasProperties
7
+ include HasComponents
43
8
 
44
9
  attr_reader :name
45
- attr_accessor :properties
10
+ attr_reader :ical_name
11
+ attr_accessor :parent
46
12
 
47
- @@multi_properties = {}
48
- @@multiline_properties = {}
49
-
50
- def initialize(name)
13
+ def initialize(name, ical_name = nil)
51
14
  @name = name
52
- @components = Hash.new { |h, k| h[k] = [] }
53
- @properties = {}
54
-
55
- @@logger.info("New #{@name[1,@name.size].capitalize}...")
56
- end
57
-
58
- # Add a sub-component to the current component object.
59
- def add_component(component)
60
- @components[component.key_name] << component
61
- end
62
-
63
- # Add a component to the calendar.
64
- alias add add_component
65
-
66
- # Add an event to the calendar.
67
- alias add_event add_component
68
-
69
- # Add a todo item to the calendar.
70
- alias add_todo add_component
71
-
72
- # Add a journal item to the calendar.
73
- alias add_journal add_component
74
-
75
- def remove_component(component)
76
- @components[component.key_name].delete(component)
15
+ @ical_name = ical_name || "V#{name.upcase}"
16
+ super()
77
17
  end
78
18
 
79
- # Remove a component from the calendar.
80
- alias remove remove_component
81
-
82
- # Remove an event from the calendar.
83
- alias remove_event remove_component
84
-
85
- # Remove a todo item from the calendar.
86
- alias remove_todo remove_component
87
-
88
- # Remove a journal item from the calendar.
89
- alias remove_journal remove_component
90
-
91
- # Used to generate unique component ids
92
19
  def new_uid
93
20
  "#{DateTime.now}_#{rand(999999999)}@#{Socket.gethostname}"
94
21
  end
95
22
 
96
- # Output in the icalendar format
97
23
  def to_ical
98
- printer do
99
- [print_headers,
100
- print_properties,
101
- print_subcomponents].join
102
- end
103
- end
104
-
105
- # Print this icalendar component
106
- def print_component
107
- to_ical
24
+ [
25
+ "BEGIN:#{ical_name}",
26
+ ical_properties,
27
+ ical_components,
28
+ "END:#{ical_name}\r\n"
29
+ ].compact.join "\r\n"
108
30
  end
109
31
 
110
- def print_subcomponents
111
- @components.values.map do |component_parts|
112
- Array(component_parts).map &:to_ical
113
- end.join
114
- end
115
-
116
- def printer
117
- ["BEGIN:#{@name.upcase}\r\n",
118
- yield,
119
- "END:#{@name.upcase}\r\n"].join
120
- end
121
-
122
- def print_properties(properties = properties_to_print)
123
- excludes = %w(geo rrule categories exdate)
124
- properties.sort.map do |key, val|
125
- property = fix_conflict_with_built_in(key)
126
- prelude = property.gsub(/_/, '-').upcase
127
-
128
- if multiline_property? property
129
- val.map do |part|
130
- params = print_parameters part
131
- value = escape_chars ":#{part.to_ical}"
132
- chunk_lines "#{prelude}#{params}#{value}"
133
- end.join
134
- else
135
- params = print_parameters val
136
- value = ":#{val.to_ical}"
137
- value = escape_chars(value) unless excludes.include? property
138
- chunk_lines "#{prelude}#{params}#{value}"
139
- end
140
- end.join
141
- end
142
-
143
- # Take out underscore for property names that conflicted
144
- # with built-in words.
145
- def fix_conflict_with_built_in(key)
146
- key.sub(/\Aip_/, '')
147
- end
148
-
149
- def escape_chars(value)
150
- value.gsub("\\", "\\\\").gsub("\r\n", "\n").gsub("\r", "\n").gsub("\n", "\\n").gsub(",", "\\,").gsub(";", "\\;")
151
- end
152
-
153
- def chunk_lines(str, length = MAX_LINE_LENGTH, separator = "\r\n ")
154
- chunks = str.scan(/.{1,#{length}}/)
155
- lines = chunks.join(separator) << separator
156
- lines.gsub(/ *$/, '')
157
- end
158
-
159
- # Print the parameters for a specific property.
160
- def print_parameters(value)
161
- return "" unless value.respond_to?(:ical_params)
162
-
163
- Array(value.ical_params).map do |key, val|
164
- val = Array(val)
165
- next if val.empty?
166
-
167
- escaped = val.map { |v| Parser.escape(v.to_ical) }.join(',')
168
- ";#{key}=" << escaped
169
- end.join
170
- end
171
-
172
- def properties_to_print
173
- @properties # subclasses can exclude properties
174
- end
175
-
176
- def print_headers
177
- "" # subclasses can specify headers
178
- end
179
-
180
- # TODO: Look into the x-property, x-param stuff...
181
- # This would really only be needed for subclassing to add additional
182
- # properties to an application using the API.
183
- def custom_property(name, value)
184
- @properties[name] = value
185
- end
186
-
187
- def multi_property?(name)
188
- @@multi_properties.has_key?(name.downcase)
189
- end
190
-
191
- def multiline_property?(name)
192
- @@multiline_properties.has_key?(name.downcase)
193
- end
194
-
195
- # Make it protected so we can monitor usage...
196
- protected
197
-
198
- def key_name
199
- (self.class.to_s.downcase + 's').gsub('icalendar::', '').to_sym
200
- end
201
-
202
- def self.ical_component(*syms)
203
- hash_accessor :@components, *syms
204
- end
205
-
206
- # Define a set of methods supporting a new property
207
- def self.ical_property(property, alias_name = nil, prop_name = nil)
208
- property = "#{property}".strip.downcase
209
- alias_name = "#{alias_name}".strip.downcase unless alias_name.nil?
210
- # If a prop_name was given then we use that for the actual storage
211
- property = "#{prop_name}".strip.downcase unless prop_name.nil?
212
-
213
- generate_getter(property, alias_name)
214
- generate_setter(property, alias_name)
215
- generate_query(property, alias_name)
216
- end
217
-
218
- # Define a set of methods defining a new property, which
219
- # supports multiple values for the same property name.
220
- def self.ical_multi_property(property, singular, plural)
221
- property = "#{property}".strip.downcase.gsub(/-/, '_')
222
- plural = "#{plural}".strip.downcase
223
-
224
- # Set this key so the parser knows to use an array for
225
- # storing this property type.
226
- @@multi_properties["#{property}"] = true
227
-
228
- generate_multi_getter(property, plural)
229
- generate_multi_setter(property, plural)
230
- generate_multi_query(property, plural)
231
- generate_multi_adder(property, singular)
232
- generate_multi_remover(property, singular)
233
- end
234
-
235
- # Define a set of methods defining a new property, which
236
- # supports multiple values in multiple lines with same property name
237
- def self.ical_multiline_property(property, singular, plural)
238
- @@multiline_properties["#{property}"] = true
239
- ical_multi_property(property, singular, plural)
240
- end
241
-
242
-
243
32
  private
244
33
 
245
- def self.generate_getter(property, alias_name)
246
- unless instance_methods.include? property
247
- code = <<-code
248
- def #{property}(val = nil, params = nil)
249
- return @properties["#{property}"] if val.nil?
250
-
251
- unless val.respond_to?(:to_ical)
252
- raise(NotImplementedError, "Value of type (" + val.class.to_s + ") does not support to_ical method!")
253
- end
254
-
255
- unless params.nil?
256
- val = FrozenProxy.new val if val.frozen?
257
- # Extend with the parameter methods only if we have to...
258
- unless val.respond_to?(:ical_params)
259
- val.class.class_eval { attr_accessor :ical_params }
260
- end
261
- val.ical_params = params
262
- end
263
-
264
- @properties["#{property}"] = val
265
- end
266
- code
267
-
268
- class_eval code, "component.rb", 219
269
- alias_method("#{alias_name}", "#{property}") unless alias_name.nil?
270
- end
271
- end
272
-
273
- def self.generate_setter(property, alias_name)
274
- setter = property + '='
275
- unless instance_methods.include? setter
276
- code = <<-code
277
- def #{setter}(val)
278
- #{property}(val)
279
- end
280
- code
281
-
282
- class_eval code, "component.rb", 233
283
- alias_method("#{alias_name}=", "#{property+'='}") unless alias_name.nil?
284
- end
285
- end
286
-
287
- def self.generate_query(property, alias_name)
288
- query = "#{property}?"
289
- unless instance_methods.include? query
290
- code = <<-code
291
- def #{query}
292
- @properties.has_key?("#{property.downcase}")
293
- end
294
- code
295
-
296
- class_eval code, "component.rb", 226
297
-
298
- alias_method("#{alias_name}\?", "#{query}") unless alias_name.nil?
299
- end
300
- end
301
-
302
- def self.generate_multi_getter(property, plural)
303
- # Getter for whole array
304
- unless instance_methods.include? plural
305
- code = <<-code
306
- def #{plural}(a = nil, params = nil)
307
- if a.nil?
308
- @properties["#{property}"] || []
309
- else
310
- self.#{plural}=(a).tap do |val|
311
- unless params.nil?
312
- unless val.respond_to?(:ical_params)
313
- val.class.class_eval { attr_accessor :ical_params }
314
- end
315
- val.ical_params = params
316
- end
317
- end
318
- end
319
- end
320
- code
321
-
322
- class_eval code, "component.rb", 186
323
- alias_method property, plural
324
- end
325
- end
326
-
327
- def self.generate_multi_setter(property, plural)
328
- # Setter for whole array
329
- unless instance_methods.include? plural+'+'
330
- code = <<-code
331
- def #{plural}=(a)
332
- if a.respond_to?(:to_ary)
333
- @properties["#{property}"] = a.to_ary
334
- elsif a =~ /^[^"].*(?<!\\\\),.*[^"]$/
335
- @properties["#{property}"] = a.split(/(?<!\\\\),/).to_ary
336
- else
337
- (@properties["#{property}"] ||= []) << a
338
- end
339
- end
340
- code
341
-
342
- class_eval code, "component.rb", 198
343
- end
344
- end
345
-
346
- def self.generate_multi_query(property, plural)
347
- # Query for any of these properties
348
- unless instance_methods.include? plural+'?'
349
- code = <<-code
350
- def #{plural}?
351
- @properties.has_key?("#{property}")
352
- end
353
- code
354
-
355
- class_eval code, "component.rb", 210
356
- end
34
+ def ical_properties
35
+ (self.class.properties + custom_properties.keys).map do |prop|
36
+ value = send prop
37
+ unless value.nil?
38
+ if value.is_a? ::Array
39
+ value.map do |part|
40
+ ical_fold "#{ical_prop_name prop}#{part.to_ical self.class.default_property_types[prop]}"
41
+ end.join "\r\n" unless value.empty?
42
+ else
43
+ ical_fold "#{ical_prop_name prop}#{value.to_ical self.class.default_property_types[prop]}"
44
+ end
45
+ end
46
+ end.compact.join "\r\n"
357
47
  end
358
48
 
359
- def self.generate_multi_adder(property, singular)
360
- adder = "add_"+singular.to_s
361
- # Add another item to this properties array
362
- unless instance_methods.include? adder
363
- code = <<-code
364
- def #{adder}(val, params = {})
365
- unless val.respond_to?(:to_ical)
366
- raise(NotImplementedError, "Property value object does not support to_ical method!")
367
- end
368
-
369
- unless params.nil?
370
- # Extend with the parameter methods only if we have to...
371
- unless val.respond_to?(:ical_params)
372
- val.class.class_eval { attr_accessor :ical_params }
373
- end
374
- val.ical_params = params
375
- end
376
-
377
- if @properties.has_key?("#{property}")
378
- @properties["#{property}"] << val
379
- else
380
- @properties["#{property}"] = [val]
381
- end
382
- end
383
- code
384
-
385
- class_eval code, "component.rb", 289
386
- alias_method("add_#{property.downcase}", "#{adder}")
387
- end
49
+ def ical_prop_name(prop_name)
50
+ prop_name.gsub(/\Aip_/, '').gsub('_', '-').upcase
388
51
  end
389
52
 
390
- def self.generate_multi_remover(property, singular)
391
- # Remove an item from this properties array
392
- unless instance_methods.include? "remove_#{singular}"
393
- code = <<-code
394
- def remove_#{singular}(a)
395
- if @properties.has_key?("#{property}")
396
- @properties["#{property}"].delete(a)
397
- end
398
- end
399
- code
400
-
401
- class_eval code, "component.rb", 303
402
- alias_method("remove_#{property.downcase}", "remove_#{singular}")
403
- end
53
+ def ical_fold(content_line)
54
+ split = content_line.split ''
55
+ [].tap { |a| a << split.shift(Icalendar::MAX_LINE_LENGTH).join until split.empty? }.join "\r\n "
404
56
  end
405
57
 
406
- public
407
-
408
- def method_missing(method_name, *args, &block)
409
- # Allow proprietary calendar extensions to be set
410
- #
411
- # Example:
412
- # cal.x_wr_calname = "iCalendar Calendar"
413
- if method_name =~ CAL_EXTENSION_REGEX
414
-
415
- # Make sure to remove '=' from the end of the method_name so we can
416
- # define it
417
- name = method_name.to_s.chomp '='
418
-
419
- self.class.class_eval do
420
- ical_multiline_property name, name, name
58
+ def ical_components
59
+ collection = []
60
+ (self.class.components + custom_components.keys).each do |component_name|
61
+ components = send component_name
62
+ components.each do |component|
63
+ collection << component.to_ical
421
64
  end
422
- send method_name, *args
423
- else
424
- super
425
65
  end
66
+ collection.empty? ? nil : collection.join.chomp("\r\n")
426
67
  end
68
+ end
427
69
 
428
- def respond_to_missing?(method_name, include_private = false)
429
- method_name.to_s =~ CAL_EXTENSION_REGEX || super
430
- end
431
- end # class Component
432
70
  end