icalendar 0.96 → 0.96.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +3 -3
- data/Rakefile +3 -4
- data/docs/api/classes/Array.html +148 -0
- data/docs/api/classes/Date.html +159 -0
- data/docs/api/classes/DateTime.html +180 -0
- data/docs/api/classes/Fixnum.html +148 -0
- data/docs/api/classes/Float.html +148 -0
- data/docs/api/classes/HashAttrs.html +199 -0
- data/docs/api/classes/Icalendar/Alarm.html +228 -0
- data/docs/api/classes/Icalendar/Base.html +120 -0
- data/docs/api/classes/Icalendar/Calendar.html +365 -0
- data/docs/api/classes/Icalendar/Component.html +689 -0
- data/docs/api/classes/Icalendar/DateProp.html +187 -0
- data/docs/api/classes/Icalendar/DateProp/ClassMethods.html +195 -0
- data/docs/api/classes/Icalendar/Daylight.html +156 -0
- data/docs/api/classes/Icalendar/Event.html +650 -0
- data/docs/api/classes/Icalendar/Freebusy.html +297 -0
- data/docs/api/classes/Icalendar/InvalidPropertyValue.html +117 -0
- data/docs/api/classes/Icalendar/Journal.html +459 -0
- data/docs/api/classes/Icalendar/Parameter.html +166 -0
- data/docs/api/classes/Icalendar/Parser.html +447 -0
- data/docs/api/classes/Icalendar/Standard.html +156 -0
- data/docs/api/classes/Icalendar/Timezone.html +351 -0
- data/docs/api/classes/Icalendar/Todo.html +496 -0
- data/docs/api/classes/Icalendar/UnknownComponentClass.html +117 -0
- data/docs/api/classes/Icalendar/UnknownPropertyMethod.html +117 -0
- data/docs/api/classes/Object.html +270 -0
- data/docs/api/classes/String.html +148 -0
- data/docs/api/classes/Time.html +163 -0
- data/docs/api/classes/URI.html +111 -0
- data/docs/api/classes/URI/Generic.html +148 -0
- data/docs/api/created.rid +1 -0
- data/docs/api/files/COPYING.html +163 -0
- data/docs/api/files/GPL.html +531 -0
- data/docs/api/files/README.html +254 -0
- data/docs/api/files/lib/hash_attrs_rb.html +107 -0
- data/docs/api/files/lib/icalendar/base_rb.html +108 -0
- data/docs/api/files/lib/icalendar/calendar_rb.html +101 -0
- data/docs/api/files/lib/icalendar/component/alarm_rb.html +101 -0
- data/docs/api/files/lib/icalendar/component/event_rb.html +101 -0
- data/docs/api/files/lib/icalendar/component/freebusy_rb.html +101 -0
- data/docs/api/files/lib/icalendar/component/journal_rb.html +101 -0
- data/docs/api/files/lib/icalendar/component/timezone_rb.html +101 -0
- data/docs/api/files/lib/icalendar/component/todo_rb.html +101 -0
- data/docs/api/files/lib/icalendar/component_rb.html +101 -0
- data/docs/api/files/lib/icalendar/conversions_rb.html +109 -0
- data/docs/api/files/lib/icalendar/helpers_rb.html +101 -0
- data/docs/api/files/lib/icalendar/parameter_rb.html +101 -0
- data/docs/api/files/lib/icalendar/parser_rb.html +109 -0
- data/docs/api/files/lib/icalendar_rb.html +120 -0
- data/docs/api/files/lib/meta_rb.html +107 -0
- data/docs/api/fr_class_index.html +55 -0
- data/docs/api/fr_file_index.html +45 -0
- data/docs/api/fr_method_index.html +79 -0
- data/docs/api/index.html +24 -0
- data/docs/api/rdoc-style.css +208 -0
- data/{docs/examples → examples}/create_cal.rb +6 -2
- data/{docs/examples → examples}/parse_cal.rb +3 -2
- data/{docs/examples → examples}/single_event.ics +0 -0
- data/lib/hash_attrs.rb +29 -0
- data/lib/icalendar.rb +11 -2
- data/lib/icalendar/base.rb +6 -2
- data/lib/icalendar/calendar.rb +1 -1
- data/lib/icalendar/component.rb +98 -71
- data/lib/icalendar/component/alarm.rb +5 -3
- data/lib/icalendar/component/event.rb +1 -1
- data/lib/icalendar/component/freebusy.rb +23 -23
- data/lib/icalendar/component/journal.rb +1 -5
- data/lib/icalendar/component/timezone.rb +7 -0
- data/lib/icalendar/component/todo.rb +1 -6
- data/lib/icalendar/conversions.rb +10 -0
- data/lib/icalendar/parser.rb +82 -85
- data/lib/meta.rb +32 -0
- data/test/component_test.rb +69 -64
- metadata +74 -12
- data/lib/icalendar/foo.rb +0 -394
- 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 = ""
|
data/lib/icalendar/parser.rb
CHANGED
@@ -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
|
-
|
190
|
-
|
191
|
-
|
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
|
196
|
-
|
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
|
-
|
202
|
-
val = [val]
|
198
|
+
name = name.downcase
|
203
199
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
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
|
-
|
215
|
-
component.property_params[name] = params
|
213
|
+
raise(UnknownPropertyMethod, "Unknown property type: #{adder}")
|
216
214
|
end
|
217
|
-
|
218
215
|
else
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
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
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
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
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
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
|
data/lib/meta.rb
ADDED
@@ -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
|
data/test/component_test.rb
CHANGED
@@ -6,69 +6,74 @@ require 'icalendar'
|
|
6
6
|
|
7
7
|
class TestComponent < Test::Unit::TestCase
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|