icalendar 0.96 → 0.96.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 (77) hide show
  1. data/README +3 -3
  2. data/Rakefile +3 -4
  3. data/docs/api/classes/Array.html +148 -0
  4. data/docs/api/classes/Date.html +159 -0
  5. data/docs/api/classes/DateTime.html +180 -0
  6. data/docs/api/classes/Fixnum.html +148 -0
  7. data/docs/api/classes/Float.html +148 -0
  8. data/docs/api/classes/HashAttrs.html +199 -0
  9. data/docs/api/classes/Icalendar/Alarm.html +228 -0
  10. data/docs/api/classes/Icalendar/Base.html +120 -0
  11. data/docs/api/classes/Icalendar/Calendar.html +365 -0
  12. data/docs/api/classes/Icalendar/Component.html +689 -0
  13. data/docs/api/classes/Icalendar/DateProp.html +187 -0
  14. data/docs/api/classes/Icalendar/DateProp/ClassMethods.html +195 -0
  15. data/docs/api/classes/Icalendar/Daylight.html +156 -0
  16. data/docs/api/classes/Icalendar/Event.html +650 -0
  17. data/docs/api/classes/Icalendar/Freebusy.html +297 -0
  18. data/docs/api/classes/Icalendar/InvalidPropertyValue.html +117 -0
  19. data/docs/api/classes/Icalendar/Journal.html +459 -0
  20. data/docs/api/classes/Icalendar/Parameter.html +166 -0
  21. data/docs/api/classes/Icalendar/Parser.html +447 -0
  22. data/docs/api/classes/Icalendar/Standard.html +156 -0
  23. data/docs/api/classes/Icalendar/Timezone.html +351 -0
  24. data/docs/api/classes/Icalendar/Todo.html +496 -0
  25. data/docs/api/classes/Icalendar/UnknownComponentClass.html +117 -0
  26. data/docs/api/classes/Icalendar/UnknownPropertyMethod.html +117 -0
  27. data/docs/api/classes/Object.html +270 -0
  28. data/docs/api/classes/String.html +148 -0
  29. data/docs/api/classes/Time.html +163 -0
  30. data/docs/api/classes/URI.html +111 -0
  31. data/docs/api/classes/URI/Generic.html +148 -0
  32. data/docs/api/created.rid +1 -0
  33. data/docs/api/files/COPYING.html +163 -0
  34. data/docs/api/files/GPL.html +531 -0
  35. data/docs/api/files/README.html +254 -0
  36. data/docs/api/files/lib/hash_attrs_rb.html +107 -0
  37. data/docs/api/files/lib/icalendar/base_rb.html +108 -0
  38. data/docs/api/files/lib/icalendar/calendar_rb.html +101 -0
  39. data/docs/api/files/lib/icalendar/component/alarm_rb.html +101 -0
  40. data/docs/api/files/lib/icalendar/component/event_rb.html +101 -0
  41. data/docs/api/files/lib/icalendar/component/freebusy_rb.html +101 -0
  42. data/docs/api/files/lib/icalendar/component/journal_rb.html +101 -0
  43. data/docs/api/files/lib/icalendar/component/timezone_rb.html +101 -0
  44. data/docs/api/files/lib/icalendar/component/todo_rb.html +101 -0
  45. data/docs/api/files/lib/icalendar/component_rb.html +101 -0
  46. data/docs/api/files/lib/icalendar/conversions_rb.html +109 -0
  47. data/docs/api/files/lib/icalendar/helpers_rb.html +101 -0
  48. data/docs/api/files/lib/icalendar/parameter_rb.html +101 -0
  49. data/docs/api/files/lib/icalendar/parser_rb.html +109 -0
  50. data/docs/api/files/lib/icalendar_rb.html +120 -0
  51. data/docs/api/files/lib/meta_rb.html +107 -0
  52. data/docs/api/fr_class_index.html +55 -0
  53. data/docs/api/fr_file_index.html +45 -0
  54. data/docs/api/fr_method_index.html +79 -0
  55. data/docs/api/index.html +24 -0
  56. data/docs/api/rdoc-style.css +208 -0
  57. data/{docs/examples → examples}/create_cal.rb +6 -2
  58. data/{docs/examples → examples}/parse_cal.rb +3 -2
  59. data/{docs/examples → examples}/single_event.ics +0 -0
  60. data/lib/hash_attrs.rb +29 -0
  61. data/lib/icalendar.rb +11 -2
  62. data/lib/icalendar/base.rb +6 -2
  63. data/lib/icalendar/calendar.rb +1 -1
  64. data/lib/icalendar/component.rb +98 -71
  65. data/lib/icalendar/component/alarm.rb +5 -3
  66. data/lib/icalendar/component/event.rb +1 -1
  67. data/lib/icalendar/component/freebusy.rb +23 -23
  68. data/lib/icalendar/component/journal.rb +1 -5
  69. data/lib/icalendar/component/timezone.rb +7 -0
  70. data/lib/icalendar/component/todo.rb +1 -6
  71. data/lib/icalendar/conversions.rb +10 -0
  72. data/lib/icalendar/parser.rb +82 -85
  73. data/lib/meta.rb +32 -0
  74. data/test/component_test.rb +69 -64
  75. metadata +74 -12
  76. data/lib/icalendar/foo.rb +0 -394
  77. data/test/property_helpers.rb +0 -35
@@ -16,6 +16,8 @@ require 'date'
16
16
  # end
17
17
  # end
18
18
 
19
+ require 'uri/generic'
20
+
19
21
  class String
20
22
  def to_ical
21
23
  "#{self.dump[1...-1]}"
@@ -40,6 +42,14 @@ class Array
40
42
  end
41
43
  end
42
44
 
45
+ module URI
46
+ class Generic
47
+ def to_ical
48
+ "#{self}"
49
+ end
50
+ end
51
+ end
52
+
43
53
  class DateTime < Date
44
54
  def to_ical(utc = false)
45
55
  s = ""
@@ -1,7 +1,7 @@
1
1
  =begin
2
2
  Copyright (C) 2005 Jeff Rose
3
3
  Copyright (C) 2005 Sam Roberts
4
-
4
+
5
5
  This library is free software; you can redistribute it and/or modify it
6
6
  under the same terms as the ruby language itself, see the file COPYING for
7
7
  details.
@@ -14,7 +14,7 @@ module Icalendar
14
14
  class Parser < Icalendar::Base
15
15
  # 1*(ALPHA / DIGIT / "=")
16
16
  NAME = '[-a-z0-9]+'
17
-
17
+
18
18
  # <"> <Any character except CTLs, DQUOTE> <">
19
19
  QSTR = '"[^"]*"'
20
20
 
@@ -47,14 +47,14 @@ module Icalendar
47
47
 
48
48
  def initialize(src)
49
49
  @@logger.debug("New Calendar Parser")
50
-
50
+
51
51
  # Setup the parser method hash table
52
52
  setup_parsers()
53
53
 
54
54
  # Define the next line method different depending on whether
55
55
  # this is a string or an IO object so we can be efficient about
56
56
  # parsing large files...
57
-
57
+
58
58
  # Just do the unfolding work in one shot if its a whole string
59
59
  if src.respond_to?(:split)
60
60
  unfolded = []
@@ -95,11 +95,11 @@ module Icalendar
95
95
  if !@prev_line.nil?
96
96
  @prev_line.chomp!
97
97
  end
98
-
98
+
99
99
  # Dynamically define next line for an IO object
100
100
  def next_line
101
101
  line = @prev_line
102
-
102
+
103
103
  if line.nil?
104
104
  return nil
105
105
  end
@@ -110,7 +110,7 @@ module Icalendar
110
110
  if !nextLine.nil?
111
111
  nextLine.chomp!
112
112
  end
113
-
113
+
114
114
  # If it's a continuation line, add it to the last.
115
115
  # If it's an empty line, drop it from the input.
116
116
  if( nextLine =~ /^[ \t]/ )
@@ -127,27 +127,27 @@ module Icalendar
127
127
  raise ArgumentError, "CalendarParser.new cannot be called with a #{src.class} type!"
128
128
  end
129
129
  end
130
-
130
+
131
131
  # Parse the calendar into an object representation
132
132
  def parse
133
133
  calendars = []
134
-
134
+
135
135
  # Outer loop for Calendar objects
136
136
  while (line = next_line)
137
137
  fields = parse_line(line)
138
-
138
+
139
139
  # Just iterate through until we find the beginning of a calendar object
140
140
  if fields[:name] == "BEGIN" and fields[:value] == "VCALENDAR"
141
141
  cal = parse_component
142
142
  calendars << cal
143
143
  end
144
144
  end
145
-
145
+
146
146
  calendars
147
147
  end
148
148
 
149
149
  private
150
-
150
+
151
151
  # Parse a single VCALENDAR object
152
152
  # -- This should consist of the PRODID, VERSION, option METHOD & CALSCALE,
153
153
  # and then one or more calendar components: VEVENT, VTODO, VJOURNAL,
@@ -156,8 +156,8 @@ module Icalendar
156
156
  while (line = next_line)
157
157
  fields = parse_line(line)
158
158
 
159
- name = fields[:name]
160
-
159
+ name = fields[:name].upcase
160
+
161
161
  # Although properties are supposed to come before components, we should
162
162
  # be able to handle them in any order...
163
163
  if name == "END"
@@ -185,48 +185,45 @@ module Icalendar
185
185
  next
186
186
  end
187
187
  else # If its not a component then it should be a property
188
-
189
- # Just set the properties so that the parser can still
190
- # parse invalid files...
191
- @@logger.debug("Setting #{name} => #{fields[:value]}")
192
-
188
+
189
+ params = fields[:params]
190
+ value = fields[:value]
191
+
193
192
  # Lookup the property name to see if we have a string to
194
193
  # object parser for this property type.
195
- if @parsers.has_key?(name.upcase)
196
- val = @parsers[name.upcase].call(name, fields[:params], fields[:value])
197
- else
198
- val = fields[:value]
194
+ if @parsers.has_key?(name)
195
+ value = @parsers[name].call(name, params, value)
199
196
  end
200
197
 
201
- if component.multi_property?(name.upcase)
202
- val = [val]
198
+ name = name.downcase
203
199
 
204
- if fields[:params].empty?
205
- params = []
206
- else
207
- params = fields[:params]
208
- end
209
-
210
- if component.properties.has_key?(name)
211
- component.properties[name] += val
212
- component.property_params[name] += params
200
+ # TODO: check to see if there are any more conflicts.
201
+ if name == 'class' or name == 'method'
202
+ name = "ip_" + name
203
+ end
204
+
205
+ # Replace dashes with underscores
206
+ name = name.gsub('-', '_')
207
+
208
+ if component.multi_property?(name)
209
+ adder = "add_" + name
210
+ if component.respond_to?(adder)
211
+ component.send(adder.to_sym, value, params)
213
212
  else
214
- component.properties[name] = val
215
- component.property_params[name] = params
213
+ raise(UnknownPropertyMethod, "Unknown property type: #{adder}")
216
214
  end
217
-
218
215
  else
219
- component.properties[name] = val
220
-
221
- unless fields[:params].empty?
222
- component.property_params[name] = fields[:params]
216
+ puts "Looking for: #{name}"
217
+ if component.respond_to?(name)
218
+ component.send(name, value, params)
219
+ else
220
+ raise(UnknownPropertyMethod, "Unknown property type: #{name}")
223
221
  end
224
222
  end
225
223
  end
226
224
  end
227
225
 
228
226
  component
229
-
230
227
  end
231
228
 
232
229
  def parse_line(line)
@@ -237,50 +234,50 @@ module Icalendar
237
234
  name = $1.upcase # The case insensitive part is upcased for easier comparison...
238
235
  paramslist = $2
239
236
  value = $3
240
-
237
+
241
238
  # Parse the parameters
242
239
  params = {}
243
240
  if paramslist.size > 1
244
241
  paramslist.scan( %r{#{PARAM}}i ) do
245
-
246
- # parameter names are case-insensitive, and multi-valued
247
- pname = $1
248
- pvals = $3
249
-
250
- # If their isn't an '=' sign then we need to do some custom
251
- # business. Defaults to 'type'
252
- if $2 == ""
253
- pvals = $1
254
- case $1
255
- when /quoted-printable/i
256
- pname = 'encoding'
257
-
258
- when /base64/i
259
- pname = 'encoding'
260
-
261
- else
262
- pname = 'type'
263
- end
264
- end
265
-
266
- # Make entries into the params dictionary where the name
267
- # is the key and the value is an array of values.
268
- unless params.key? pname
269
- params[pname] = []
242
+
243
+ # parameter names are case-insensitive, and multi-valued
244
+ pname = $1
245
+ pvals = $3
246
+
247
+ # If their isn't an '=' sign then we need to do some custom
248
+ # business. Defaults to 'type'
249
+ if $2 == ""
250
+ pvals = $1
251
+ case $1
252
+ when /quoted-printable/i
253
+ pname = 'encoding'
254
+
255
+ when /base64/i
256
+ pname = 'encoding'
257
+
258
+ else
259
+ pname = 'type'
270
260
  end
261
+ end
271
262
 
272
- # Save all the values into the array.
273
- pvals.scan( %r{(#{PVALUE})} ) do
274
- if $1.size > 0
275
- params[pname] << $1
276
- end
263
+ # Make entries into the params dictionary where the name
264
+ # is the key and the value is an array of values.
265
+ unless params.key? pname
266
+ params[pname] = []
267
+ end
268
+
269
+ # Save all the values into the array.
270
+ pvals.scan( %r{(#{PVALUE})} ) do
271
+ if $1.size > 0
272
+ params[pname] << $1
277
273
  end
278
274
  end
275
+ end
279
276
  end
280
-
277
+
281
278
  {:name => name, :params => params, :value => value}
282
279
  end
283
-
280
+
284
281
  ## Following is a collection of parsing functions for various
285
282
  ## icalendar property value data types... First we setup
286
283
  ## a hash with property names pointing to methods...
@@ -313,7 +310,7 @@ module Icalendar
313
310
  @parsers["ATTENDEE"] = m
314
311
  @parsers["ORGANIZER"] = m
315
312
  @parsers["URL"] = m
316
-
313
+
317
314
  # This is a URI by default, and if its not a valid URI
318
315
  # it will be returned as a string which works for binary data
319
316
  # the other possible type.
@@ -324,7 +321,7 @@ module Icalendar
324
321
  @parsers["GEO"] = m
325
322
 
326
323
  end
327
-
324
+
328
325
  # Booleans
329
326
  # NOTE: It appears that although this is a valid data type
330
327
  # there aren't any properties that use it... Maybe get
@@ -336,7 +333,7 @@ module Icalendar
336
333
  true
337
334
  end
338
335
  end
339
-
336
+
340
337
  # Dates, Date-Times & Times
341
338
  # NOTE: invalid dates & times will be returned as strings...
342
339
  def parse_datetime(name, params, value)
@@ -345,34 +342,34 @@ module Icalendar
345
342
  rescue Exception
346
343
  value
347
344
  end
348
-
345
+
349
346
  end
350
-
347
+
351
348
  # Durations
352
349
  # TODO: Need to figure out the best way to represent durations
353
350
  # so just returning string for now.
354
351
  def parse_duration(name, params, value)
355
352
  value
356
353
  end
357
-
354
+
358
355
  # Floats
359
356
  # NOTE: returns 0.0 if it can't parse the value
360
357
  def parse_float(name, params, value)
361
358
  value.to_f
362
359
  end
363
-
360
+
364
361
  # Integers
365
362
  # NOTE: returns 0 if it can't parse the value
366
363
  def parse_integer(name, params, value)
367
364
  value.to_i
368
365
  end
369
-
366
+
370
367
  # Periods
371
368
  # TODO: Got to figure out how to represent periods also...
372
369
  def parse_period(name, params, value)
373
370
  value
374
371
  end
375
-
372
+
376
373
  # Calendar Address's & URI's
377
374
  # NOTE: invalid URI's will be returned as strings...
378
375
  def parse_uri(name, params, value)
@@ -397,6 +394,6 @@ module Icalendar
397
394
  val[1] = strloc[1].to_f
398
395
  val
399
396
  end
400
-
397
+
401
398
  end
402
399
  end
@@ -0,0 +1,32 @@
1
+ # A set of methods to help create meta-programming gizmos.
2
+ class Object
3
+ # The metaclass is the singleton behind every object.
4
+ def metaclass
5
+ class << self
6
+ self
7
+ end
8
+ end
9
+
10
+ # Evaluates the block in the context of the metaclass
11
+ def meta_eval &blk
12
+ metaclass.instance_eval &blk
13
+ end
14
+
15
+ # Acts like an include except it adds the module's methods
16
+ # to the metaclass so they act like class methods.
17
+ def meta_include mod
18
+ meta_eval do
19
+ include mod
20
+ end
21
+ end
22
+
23
+ # Adds methods to a metaclass
24
+ def meta_def name, &blk
25
+ meta_eval { define_method name, &blk }
26
+ end
27
+
28
+ # Defines an instance method within a class
29
+ def class_def name, &blk
30
+ class_eval { define_method name, &blk }
31
+ end
32
+ end
@@ -6,69 +6,74 @@ require 'icalendar'
6
6
 
7
7
  class TestComponent < Test::Unit::TestCase
8
8
 
9
- # Create a calendar with an event for each test.
10
- def setup
11
- @cal = Icalendar::Calendar.new
12
- @event = Icalendar::Event.new
13
- end
14
-
15
- def test_ical_property
16
- # No alias but it does have a prop_name
17
- assert_equal(@event.klass?, false)
18
- @event.klass = "PRIVATE"
19
- assert_equal(@event.klass?, true)
20
- assert_equal(@event.klass, "PRIVATE")
21
-
22
- # Check that both dtstart and its alias start work correctly
23
- date = DateTime.new(2005, 02, 05, 23, 24, 25)
24
- @event.dtend = date
25
- assert_equal(@event.dtend.year, date.year)
26
-
27
- date2 = DateTime.new(2005, 02, 05, 23, 24, 26)
28
- @event.end = date2
29
- assert_equal(@event.end.year, date2.year)
30
- end
31
-
32
- def test_ical_multi_property
33
- # Query
34
- assert_equal(@event.comments?, false)
35
- @event.comments = []
36
- assert_equal(@event.comments?, true)
37
-
38
- # Should return an empty array, rather than nil
39
- assert_equal(@event.comments.size, 0)
40
-
41
- # Add and remove
42
- @event.add_comment "c1"
43
- @event.add_comment "c2"
44
- assert_equal(@event.comments.size, 2)
45
- assert_equal(@event.comments, ["c1", "c2"])
46
- @event.remove_comment "c1"
47
- assert_equal(@event.comments, ["c2"])
48
-
49
- # Set & get whole array
50
- foo = ["as", "df"]
51
- @event.comments = foo
52
- assert_equal(@event.comments, foo)
53
-
54
- foo = ["asdf", "qwer"]
55
- @event.comments(foo)
56
- assert_equal(@event.comments, foo)
57
-
58
- # Error cases
59
- assert_raise(ArgumentError) { @event.comments("asdf") }
60
- end
61
-
62
- def test_bad_args
63
- # Single property
64
- assert_raise(NotImplementedError) do
65
- @event.klass = {}
66
- end
67
-
68
- # Multi property
69
- assert_raise(NotImplementedError) do
70
- @event.comments = [{}]
71
- end
72
- end
9
+ # Create a calendar with an event for each test.
10
+ def setup
11
+ @cal = Icalendar::Calendar.new
12
+ @event = Icalendar::Event.new
13
+ end
14
+
15
+ def test_add_component
16
+ @cal.add_component @event
17
+ assert_equal(@cal.events.size, 1)
18
+ end
19
+
20
+ def test_ical_property
21
+ # No alias but it does have a prop_name
22
+ assert_equal(@event.klass?, false)
23
+ @event.klass = "PRIVATE"
24
+ assert_equal(@event.klass?, true)
25
+ assert_equal(@event.klass, "PRIVATE")
26
+
27
+ # Check that both dtstart and its alias start work correctly
28
+ date = DateTime.new(2005, 02, 05, 23, 24, 25)
29
+ @event.dtend = date
30
+ assert_equal(@event.dtend.year, date.year)
31
+
32
+ date2 = DateTime.new(2005, 02, 05, 23, 24, 26)
33
+ @event.end = date2
34
+ assert_equal(@event.end.year, date2.year)
35
+ end
36
+
37
+ def test_ical_multi_property
38
+ # Query
39
+ assert_equal(@event.comments?, false)
40
+ @event.comments = []
41
+ assert_equal(@event.comments?, true)
42
+
43
+ # Should return an empty array, rather than nil
44
+ assert_equal(@event.comments.size, 0)
45
+
46
+ # Add and remove
47
+ @event.add_comment "c1"
48
+ @event.add_comment "c2"
49
+ assert_equal(@event.comments.size, 2)
50
+ assert_equal(@event.comments, ["c1", "c2"])
51
+ @event.remove_comment "c1"
52
+ assert_equal(@event.comments, ["c2"])
53
+
54
+ # Set & get whole array
55
+ foo = ["as", "df"]
56
+ @event.comments = foo
57
+ assert_equal(@event.comments, foo)
58
+
59
+ foo = ["asdf", "qwer"]
60
+ @event.comments(foo)
61
+ assert_equal(@event.comments, foo)
62
+
63
+ # Error cases
64
+ assert_raise(ArgumentError) { @event.comments("asdf") }
65
+ end
66
+
67
+ def test_bad_args
68
+ # Single property
69
+ assert_raise(NotImplementedError) do
70
+ @event.klass = {}
71
+ end
72
+
73
+ # Multi property
74
+ assert_raise(NotImplementedError) do
75
+ @event.comments = [{}]
76
+ end
77
+ end
73
78
 
74
79
  end