vpim 0.16 → 0.17
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.
- 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
|
|