vpim 0.16 → 0.17
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/vpim/agent/plist.rb +86 -0
- data/lib/vpim/date.rb +1 -3
- data/lib/vpim/date.rb~ +198 -0
- data/lib/vpim/dirinfo.rb +47 -15
- data/lib/vpim/dirinfo.rb~ +242 -0
- data/lib/vpim/duration.rb +1 -3
- data/lib/vpim/duration.rb~ +121 -0
- data/lib/vpim/enumerator.rb +1 -3
- data/lib/vpim/enumerator.rb~ +29 -0
- data/lib/vpim/field.rb +141 -56
- data/lib/vpim/field.rb~ +594 -0
- data/lib/vpim/icalendar.rb +10 -16
- data/lib/vpim/icalendar.rb~ +548 -0
- data/lib/vpim/maker/vcard.rb +124 -46
- data/lib/vpim/maker/vcard.rb~ +382 -0
- data/lib/vpim/rfc2425.rb +30 -17
- data/lib/vpim/rfc2425.rb~ +246 -0
- data/lib/vpim/rrule.rb +2 -4
- data/lib/vpim/rrule.rb~ +482 -0
- data/lib/vpim/time.rb +1 -3
- data/lib/vpim/time.rb~ +42 -0
- data/lib/vpim/vcard.rb +84 -18
- data/lib/vpim/vcard.rb~ +232 -0
- data/lib/vpim/vevent.rb +1 -3
- data/lib/vpim/vevent.rb~ +381 -0
- data/lib/vpim/vpim.rb +61 -29
- data/lib/vpim/vpim.rb~ +61 -29
- metadata +16 -2
data/lib/vpim/duration.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
=begin
|
2
|
-
|
3
|
-
|
4
|
-
Copyright (C) 2005 Sam Roberts
|
2
|
+
Copyright (C) 2006 Sam Roberts
|
5
3
|
|
6
4
|
This library is free software; you can redistribute it and/or modify it
|
7
5
|
under the same terms as the ruby language itself, see the file COPYING for
|
@@ -0,0 +1,121 @@
|
|
1
|
+
=begin
|
2
|
+
$Id: duration.rb,v 1.4 2004/11/17 05:06:27 sam Exp $
|
3
|
+
|
4
|
+
Copyright (C) 2005 Sam Roberts
|
5
|
+
|
6
|
+
This library is free software; you can redistribute it and/or modify it
|
7
|
+
under the same terms as the ruby language itself, see the file COPYING for
|
8
|
+
details.
|
9
|
+
=end
|
10
|
+
|
11
|
+
module Vpim
|
12
|
+
class Duration
|
13
|
+
SECS_HOUR = 60 * 60
|
14
|
+
SECS_DAY = 24 * SECS_HOUR
|
15
|
+
MINS_HOUR = 60
|
16
|
+
|
17
|
+
# Initialize from a number of seconds.
|
18
|
+
def initialize(secs)
|
19
|
+
@secs = secs
|
20
|
+
end
|
21
|
+
|
22
|
+
def Duration.secs(secs)
|
23
|
+
Duration.new(secs)
|
24
|
+
end
|
25
|
+
|
26
|
+
def Duration.mins(mins)
|
27
|
+
Duration.new(mins * 60)
|
28
|
+
end
|
29
|
+
|
30
|
+
def Duration.hours(hours)
|
31
|
+
Duration.new(hours * SECS_HOUR)
|
32
|
+
end
|
33
|
+
|
34
|
+
def Duration.days(days)
|
35
|
+
Duration.new(days * SECS_DAY)
|
36
|
+
end
|
37
|
+
|
38
|
+
def secs
|
39
|
+
@secs
|
40
|
+
end
|
41
|
+
|
42
|
+
def mins
|
43
|
+
(@secs/60).to_i
|
44
|
+
end
|
45
|
+
|
46
|
+
def hours
|
47
|
+
(@secs/SECS_HOUR).to_i
|
48
|
+
end
|
49
|
+
|
50
|
+
def days
|
51
|
+
(@secs/SECS_DAY).to_i
|
52
|
+
end
|
53
|
+
|
54
|
+
def weeks
|
55
|
+
(days/7).to_i
|
56
|
+
end
|
57
|
+
|
58
|
+
def by_hours
|
59
|
+
[ hours, mins % MINS_HOUR, secs % 60]
|
60
|
+
end
|
61
|
+
|
62
|
+
def by_days
|
63
|
+
[ days, hours % 24, mins % MINS_HOUR, secs % 60]
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_a
|
67
|
+
by_days
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_s
|
71
|
+
Duration.as_str(self.to_a)
|
72
|
+
end
|
73
|
+
|
74
|
+
def Duration.as_str(arr)
|
75
|
+
s = ""
|
76
|
+
case arr.length
|
77
|
+
when 4
|
78
|
+
if arr[0] > 0
|
79
|
+
s << "#{arr[0]} days"
|
80
|
+
end
|
81
|
+
if arr[1] > 0
|
82
|
+
if s.length > 0
|
83
|
+
s << ', '
|
84
|
+
end
|
85
|
+
s << "#{arr[1]} hours"
|
86
|
+
end
|
87
|
+
if arr[2] > 0
|
88
|
+
if s.length > 0
|
89
|
+
s << ', '
|
90
|
+
end
|
91
|
+
s << "#{arr[2]} mins"
|
92
|
+
end
|
93
|
+
if arr[3] > 0
|
94
|
+
if s.length > 0
|
95
|
+
s << ', '
|
96
|
+
end
|
97
|
+
s << "#{arr[3]} secs"
|
98
|
+
end
|
99
|
+
when 3
|
100
|
+
if arr[0] > 0
|
101
|
+
s << "#{arr[0]} hours"
|
102
|
+
end
|
103
|
+
if arr[1] > 0
|
104
|
+
if s.length > 0
|
105
|
+
s << ', '
|
106
|
+
end
|
107
|
+
s << "#{arr[1]} mins"
|
108
|
+
end
|
109
|
+
if arr[2] > 0
|
110
|
+
if s.length > 0
|
111
|
+
s << ', '
|
112
|
+
end
|
113
|
+
s << "#{arr[2]} secs"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
s
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
data/lib/vpim/enumerator.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
=begin
|
2
|
-
|
3
|
-
|
4
|
-
Copyright (C) 2005 Sam Roberts
|
2
|
+
Copyright (C) 2006 Sam Roberts
|
5
3
|
|
6
4
|
This library is free software; you can redistribute it and/or modify it
|
7
5
|
under the same terms as the ruby language itself, see the file COPYING for
|
@@ -0,0 +1,29 @@
|
|
1
|
+
=begin
|
2
|
+
$Id: enumerator.rb,v 1.2 2004/11/17 05:06:27 sam Exp $
|
3
|
+
|
4
|
+
Copyright (C) 2005 Sam Roberts
|
5
|
+
|
6
|
+
This library is free software; you can redistribute it and/or modify it
|
7
|
+
under the same terms as the ruby language itself, see the file COPYING for
|
8
|
+
details.
|
9
|
+
=end
|
10
|
+
|
11
|
+
module Vpim
|
12
|
+
# This is a way for an object to have multiple ways of being enumerated via
|
13
|
+
# argument to it's #each() method. An Enumerator mixes in Enumerable, so the
|
14
|
+
# standard APIS such as Enumerable#map(), Enumerable#to_a(), and
|
15
|
+
# Enumerable#find_all() can be used on it.
|
16
|
+
class Enumerator
|
17
|
+
include Enumerable
|
18
|
+
|
19
|
+
def initialize(obj, *args)
|
20
|
+
@obj = obj
|
21
|
+
@args = args
|
22
|
+
end
|
23
|
+
|
24
|
+
def each(&block)
|
25
|
+
@obj.each(*@args, &block)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
data/lib/vpim/field.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
=begin
|
2
|
-
|
3
|
-
|
4
|
-
Copyright (C) 2005 Sam Roberts
|
2
|
+
Copyright (C) 2006 Sam Roberts
|
5
3
|
|
6
4
|
This library is free software; you can redistribute it and/or modify it
|
7
5
|
under the same terms as the ruby language itself, see the file COPYING for
|
@@ -17,6 +15,18 @@ module Vpim
|
|
17
15
|
class DirectoryInfo
|
18
16
|
|
19
17
|
# A field in a directory info object.
|
18
|
+
#
|
19
|
+
# TODO
|
20
|
+
# - Field should know which param values and field vales are
|
21
|
+
# case-insensitive, configurably, so it can down case them
|
22
|
+
# - perhaps should have pvalue_set/del/add, perhaps case-insensitive, or
|
23
|
+
# pvalue_iset/idel/iadd, where set sets them all, add adds if not present,
|
24
|
+
# and del deletes any that are present
|
25
|
+
# - I really, really, need a case-insensitive string...
|
26
|
+
# - should allow nil as a field value, its not the same as '', if there is
|
27
|
+
# more than one pvalue, the empty string will show up. This isn't strictly
|
28
|
+
# disallowed, but its odd. Should also strip empty strings on decoding, if
|
29
|
+
# I don't already.
|
20
30
|
class Field
|
21
31
|
private_class_method :new
|
22
32
|
|
@@ -56,7 +66,7 @@ module Vpim
|
|
56
66
|
|
57
67
|
pvalues.each do |pvalue|
|
58
68
|
# check if we need to do any encoding
|
59
|
-
if pname
|
69
|
+
if Vpim::Methods.casecmp?(pname, 'ENCODING') && pvalue == :b64
|
60
70
|
pvalue = 'b' # the RFC definition of the base64 param value
|
61
71
|
value = [ value.to_str ].pack('m').gsub("\n", '')
|
62
72
|
end
|
@@ -100,11 +110,16 @@ module Vpim
|
|
100
110
|
raise Vpim::InvalidEncodingError, atline
|
101
111
|
end
|
102
112
|
|
103
|
-
atgroup = $1
|
104
|
-
atname = $2
|
113
|
+
atgroup = $1.upcase
|
114
|
+
atname = $2.upcase
|
105
115
|
paramslist = $3
|
106
116
|
atvalue = $~[-1]
|
107
117
|
|
118
|
+
# I've seen space that shouldn't be there, as in "BEGIN:VCARD ", so
|
119
|
+
# strip it. I'm not absolutely sure this is allowed... it certainly
|
120
|
+
# breaks round-trip encoding.
|
121
|
+
atvalue.strip!
|
122
|
+
|
108
123
|
if atgroup.length > 0
|
109
124
|
atgroup.chomp!('.')
|
110
125
|
else
|
@@ -120,7 +135,7 @@ module Vpim
|
|
120
135
|
paramslist.scan( %r{#{Bnf::PARAM}}i ) do
|
121
136
|
|
122
137
|
# param names are case-insensitive, and multi-valued
|
123
|
-
name = $1.
|
138
|
+
name = $1.upcase
|
124
139
|
params = $3
|
125
140
|
|
126
141
|
# v2.1 params have no '=' sign, figure out what kind of param it
|
@@ -131,13 +146,13 @@ module Vpim
|
|
131
146
|
params = $1
|
132
147
|
case $1
|
133
148
|
when /quoted-printable/i
|
134
|
-
name = '
|
149
|
+
name = 'ENCODING'
|
135
150
|
|
136
151
|
when /base64/i
|
137
|
-
name = '
|
152
|
+
name = 'ENCODING'
|
138
153
|
|
139
154
|
else
|
140
|
-
name = '
|
155
|
+
name = 'TYPE'
|
141
156
|
end
|
142
157
|
end
|
143
158
|
|
@@ -148,7 +163,7 @@ module Vpim
|
|
148
163
|
end
|
149
164
|
|
150
165
|
params.scan( %r{#{Bnf::PVALUE}} ) do
|
151
|
-
atparams[name] << ($1 || $2)
|
166
|
+
atparams[name] << ($1 || $2)
|
152
167
|
end
|
153
168
|
end
|
154
169
|
end
|
@@ -159,6 +174,10 @@ module Vpim
|
|
159
174
|
def initialize(line) # :nodoc:
|
160
175
|
@line = line.to_str
|
161
176
|
@group, @name, @params, @value = Field.decode0(@line)
|
177
|
+
|
178
|
+
@params.each do |pname,pvalues|
|
179
|
+
pvalues.freeze
|
180
|
+
end
|
162
181
|
self
|
163
182
|
end
|
164
183
|
|
@@ -206,12 +225,13 @@ module Vpim
|
|
206
225
|
# maximum line width of +width+, where +0+ means no wrapping, and nil is
|
207
226
|
# to accept the default wrapping (75, recommended by RFC2425).
|
208
227
|
#
|
209
|
-
# Note:
|
210
|
-
#
|
211
|
-
#
|
212
|
-
#
|
213
|
-
#
|
214
|
-
#
|
228
|
+
# Note: AddressBook.app 3.0.3 neither understands to unwrap lines when it
|
229
|
+
# imports vCards (it treats them as raw new-line characters), nor wraps
|
230
|
+
# long lines on export. This is mostly a cosmetic problem, but wrapping
|
231
|
+
# can be disabled by setting width to +0+, if desired.
|
232
|
+
#
|
233
|
+
# FIXME - breaks round-trip encoding, need to change this to not wrap
|
234
|
+
# fields that are already wrapped.
|
215
235
|
def encode(width=nil)
|
216
236
|
width = 75 unless width
|
217
237
|
l = @line
|
@@ -236,30 +256,24 @@ module Vpim
|
|
236
256
|
end
|
237
257
|
|
238
258
|
# An Array of all the param names.
|
239
|
-
def
|
259
|
+
def pnames
|
240
260
|
@params.keys
|
241
261
|
end
|
242
262
|
|
243
|
-
#
|
244
|
-
#
|
245
|
-
|
246
|
-
#
|
247
|
-
#
|
248
|
-
#
|
249
|
-
|
250
|
-
|
251
|
-
def param(name)
|
252
|
-
v = @params[name.downcase]
|
253
|
-
if v
|
254
|
-
v = v.collect do |p|
|
255
|
-
p.downcase.freeze
|
256
|
-
end
|
257
|
-
v.freeze
|
258
|
-
end
|
259
|
-
v
|
263
|
+
# FIXME - remove my own uses of #params
|
264
|
+
alias params pnames # :nodoc:
|
265
|
+
|
266
|
+
# The Array of all values of the param +name+, nil if there is no such
|
267
|
+
# param, [] if the param has no values. If the Field isn't frozen, the
|
268
|
+
# Array is mutable.
|
269
|
+
def pvalues(name)
|
270
|
+
@params[name.upcase]
|
260
271
|
end
|
261
272
|
|
262
|
-
|
273
|
+
# FIXME - remove my own uses of #param
|
274
|
+
alias param pvalues # :nodoc:
|
275
|
+
|
276
|
+
alias [] pvalues
|
263
277
|
|
264
278
|
# Yield once for each param, +name+ is the parameter name, +value+ is an
|
265
279
|
# array of the parameter values.
|
@@ -287,21 +301,20 @@ module Vpim
|
|
287
301
|
|
288
302
|
when 'quoted-printable' then @value.unpack('M*').first
|
289
303
|
|
290
|
-
else
|
304
|
+
else
|
305
|
+
raise Vpim::InvalidEncodingError, "unrecognized encoding (#{encoding})"
|
291
306
|
end
|
292
307
|
end
|
293
308
|
|
294
309
|
# Is the #name of this Field +name+? Names are case insensitive.
|
295
310
|
def name?(name)
|
296
|
-
|
311
|
+
Vpim::Methods.casecmp?(@name, name)
|
297
312
|
end
|
298
313
|
|
299
314
|
# Is the #group of this field +group+? Group names are case insensitive.
|
300
315
|
# A +group+ of nil matches if the field has no group.
|
301
316
|
def group?(group)
|
302
|
-
|
303
|
-
g2 = group ? group.downcase : nil
|
304
|
-
g1 == g2
|
317
|
+
Vpim::Methods.casecmp?(@group, group)
|
305
318
|
end
|
306
319
|
|
307
320
|
# Is the value of this field of type +kind+? RFC2425 allows the type of
|
@@ -321,7 +334,7 @@ module Vpim
|
|
321
334
|
# - boolean:
|
322
335
|
# - float:
|
323
336
|
def kind?(kind)
|
324
|
-
kind
|
337
|
+
Vpim::Methods.casecmp?(self.kind == kind)
|
325
338
|
end
|
326
339
|
|
327
340
|
# Is one of the values of the TYPE parameter of this field +type+? The
|
@@ -331,10 +344,12 @@ module Vpim
|
|
331
344
|
# TYPE parameters are used for general categories, such as
|
332
345
|
# distinguishing between an email address used at home or at work.
|
333
346
|
def type?(type)
|
347
|
+
type = type.to_str
|
348
|
+
|
334
349
|
types = param('type')
|
335
350
|
|
336
351
|
if types
|
337
|
-
types = types.
|
352
|
+
types = types.detect { |t| Vpim::Methods.casecmp?(t, type) }
|
338
353
|
end
|
339
354
|
end
|
340
355
|
|
@@ -342,12 +357,22 @@ module Vpim
|
|
342
357
|
# #type?('pref'). This method is not necessarily meaningful for
|
343
358
|
# non-vCard profiles.
|
344
359
|
def pref?
|
345
|
-
type?
|
360
|
+
type? 'pref'
|
361
|
+
end
|
362
|
+
|
363
|
+
# Set whether a field is marked as preferred. See #pref?
|
364
|
+
def pref=(ispref)
|
365
|
+
if ispref
|
366
|
+
pvalue_iadd('type', 'pref')
|
367
|
+
else
|
368
|
+
pvalue_idel('type', 'pref')
|
369
|
+
end
|
346
370
|
end
|
347
371
|
|
348
372
|
# Is the value of this field +value+? The check is case insensitive.
|
373
|
+
# FIXME - it shouldn't be insensitive, make a #casevalue? method.
|
349
374
|
def value?(value)
|
350
|
-
@value
|
375
|
+
Vpim::Methods.casecmp?(@value, value.to_str)
|
351
376
|
end
|
352
377
|
|
353
378
|
# The value of the ENCODING parameter, if present, or nil if not
|
@@ -359,7 +384,7 @@ module Vpim
|
|
359
384
|
if e.length > 1
|
360
385
|
raise Vpim::InvalidEncodingError, "multi-valued param 'encoding' (#{e})"
|
361
386
|
end
|
362
|
-
e = e.first
|
387
|
+
e = e.first.downcase
|
363
388
|
end
|
364
389
|
e
|
365
390
|
end
|
@@ -374,7 +399,7 @@ module Vpim
|
|
374
399
|
end
|
375
400
|
v = v.first
|
376
401
|
end
|
377
|
-
v
|
402
|
+
v.downcase
|
378
403
|
end
|
379
404
|
|
380
405
|
# The value as an array of Time objects (all times and dates in
|
@@ -382,7 +407,7 @@ module Vpim
|
|
382
407
|
# birthday). The time will be UTC if marked as so (with a timezone of
|
383
408
|
# "Z"), and in localtime otherwise.
|
384
409
|
#
|
385
|
-
# TODO
|
410
|
+
# TODO - support timezone offsets
|
386
411
|
#
|
387
412
|
# TODO - if year is before 1970, this won't work... but some people
|
388
413
|
# are generating calendars saying Canada Day started in 1753!
|
@@ -452,10 +477,6 @@ module Vpim
|
|
452
477
|
@value
|
453
478
|
end
|
454
479
|
|
455
|
-
def inspect # :nodoc:
|
456
|
-
@line
|
457
|
-
end
|
458
|
-
|
459
480
|
# TODO def pretty_print() ...
|
460
481
|
|
461
482
|
# Set the group of this field to +group+.
|
@@ -477,21 +498,85 @@ module Vpim
|
|
477
498
|
def text=(text)
|
478
499
|
end
|
479
500
|
|
480
|
-
# Set a the param +
|
481
|
-
# Field.create()
|
482
|
-
|
501
|
+
# Set a the param +pname+'s value to +pvalue+, replacing any value it
|
502
|
+
# currently has. See Field.create() for a description of +pvalue+.
|
503
|
+
#
|
504
|
+
# Example:
|
505
|
+
# if field['type']
|
506
|
+
# field['type'] << 'home'
|
507
|
+
# else
|
508
|
+
# field['type'] = [ 'home'
|
509
|
+
# end
|
510
|
+
#
|
511
|
+
# TODO - this could be an alias to #pvalue_set
|
512
|
+
def []=(pname,pvalue)
|
483
513
|
unless pvalue.respond_to?(:to_ary)
|
484
514
|
pvalue = [ pvalue ]
|
485
515
|
end
|
486
516
|
|
487
517
|
h = @params.dup
|
488
518
|
|
489
|
-
h[
|
519
|
+
h[pname.upcase] = pvalue
|
490
520
|
|
491
521
|
mutate(@group, @name, h, @value)
|
492
522
|
pvalue
|
493
523
|
end
|
494
524
|
|
525
|
+
# Add +pvalue+ to the param +pname+'s value. The values are treated as a
|
526
|
+
# set so duplicate values won't occur, and String values are case
|
527
|
+
# insensitive. See Field.create() for a description of +pvalue+.
|
528
|
+
def pvalue_iadd(pname, pvalue)
|
529
|
+
pname = pname.upcase
|
530
|
+
|
531
|
+
# Get a uniq set, where strings are compared case-insensitively.
|
532
|
+
values = [ pvalue, @params[pname] ].flatten.compact
|
533
|
+
values = values.collect do |v|
|
534
|
+
if v.respond_to? :to_str
|
535
|
+
v = v.to_str.upcase
|
536
|
+
end
|
537
|
+
v
|
538
|
+
end
|
539
|
+
values.uniq!
|
540
|
+
|
541
|
+
h = @params.dup
|
542
|
+
|
543
|
+
h[pname] = values
|
544
|
+
|
545
|
+
mutate(@group, @name, h, @value)
|
546
|
+
values
|
547
|
+
end
|
548
|
+
|
549
|
+
# Delete +pvalue+ from the param +pname+'s value. The values are treated
|
550
|
+
# as a set so duplicate values won't occur, and String values are case
|
551
|
+
# insensitive. +pvalue+ must be a single String or Symbol.
|
552
|
+
def pvalue_idel(pname, pvalue)
|
553
|
+
pname = pname.upcase
|
554
|
+
if pvalue.respond_to? :to_str
|
555
|
+
pvalue = pvalue.to_str.downcase
|
556
|
+
end
|
557
|
+
|
558
|
+
# Get a uniq set, where strings are compared case-insensitively.
|
559
|
+
values = [ nil, @params[pname] ].flatten.compact
|
560
|
+
values = values.collect do |v|
|
561
|
+
if v.respond_to? :to_str
|
562
|
+
v = v.to_str.downcase
|
563
|
+
end
|
564
|
+
v
|
565
|
+
end
|
566
|
+
values.uniq!
|
567
|
+
values.delete pvalue
|
568
|
+
|
569
|
+
h = @params.dup
|
570
|
+
|
571
|
+
h[pname] = values
|
572
|
+
|
573
|
+
mutate(@group, @name, h, @value)
|
574
|
+
values
|
575
|
+
end
|
576
|
+
|
577
|
+
# FIXME - should change this so it doesn't assign to @line here, so @line
|
578
|
+
# is used to preserve original encoding. That way, #encode can only wrap
|
579
|
+
# new fields, not old fields.
|
495
580
|
def mutate(g, n, p, v) #:nodoc:
|
496
581
|
line = Field.encode0(g, n, p, v)
|
497
582
|
|