icalendar 1.5.4 → 2.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
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