vcard 0.1.1 → 0.2.0
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/.gitignore +15 -5
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +17 -0
- data/Rakefile +4 -52
- data/{LICENSE → VPIM-LICENSE.txt} +2 -2
- data/lib/vcard.rb +304 -28
- data/lib/vcard/attachment.rb +10 -12
- data/lib/vcard/bnf.rb +66 -0
- data/lib/vcard/dirinfo.rb +24 -26
- data/lib/vcard/enumerator.rb +5 -7
- data/lib/vcard/errors.rb +23 -0
- data/lib/vcard/field.rb +56 -58
- data/lib/vcard/vcard.rb +210 -240
- data/lib/vcard/version.rb +3 -0
- data/test/field_test.rb +55 -55
- data/test/fixtures/bday_decode.vcard +3 -0
- data/test/fixtures/bday_decode_2.vcard +6 -0
- data/test/fixtures/empty_tel.vcard +3 -0
- data/test/fixtures/ex1.vcard +7 -0
- data/test/fixtures/ex2.vcard +9 -0
- data/test/fixtures/ex3.vcard +30 -0
- data/test/fixtures/ex_21.vcard +16 -0
- data/test/fixtures/ex_21_case0.vcard +15 -0
- data/test/fixtures/ex_apple1.vcard +13 -0
- data/test/fixtures/ex_attach.vcard +16 -0
- data/test/fixtures/ex_bdays.vcard +8 -0
- data/test/fixtures/ex_encode_1.vcard +10 -0
- data/test/fixtures/ex_ical_1.vcal +47 -0
- data/test/fixtures/gmail.vcard +27 -0
- data/test/fixtures/highrise.vcard +41 -0
- data/test/fixtures/multiple_occurences_of_type.vcard +17 -0
- data/test/fixtures/nickname0.vcard +2 -0
- data/test/fixtures/nickname1.vcard +3 -0
- data/test/fixtures/nickname2.vcard +3 -0
- data/test/fixtures/nickname3.vcard +3 -0
- data/test/fixtures/nickname4.vcard +4 -0
- data/test/fixtures/nickname5.vcard +5 -0
- data/test/fixtures/slash_in_field_name.vcard +3 -0
- data/test/fixtures/tst1.vcard +9 -0
- data/test/fixtures/url_decode.vcard +4 -0
- data/test/test_helper.rb +34 -6
- data/test/vcard_test.rb +87 -577
- data/vcard.gemspec +19 -0
- metadata +88 -43
- data/.document +0 -5
- data/README.rdoc +0 -7
- data/VERSION +0 -1
- data/lib/vcard/rfc2425.rb +0 -367
data/vcard.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'vcard/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "vcard"
|
8
|
+
gem.version = Vcard::VERSION
|
9
|
+
gem.authors = ["Kuba Kuźma"]
|
10
|
+
gem.email = ["kuba@jah.pl"]
|
11
|
+
gem.description = %q{Vcard extracted from Vpim}
|
12
|
+
gem.summary = %q{Vcard extracted from Vpim}
|
13
|
+
gem.homepage = "http://github.com/qoobaa/vcard"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
end
|
metadata
CHANGED
@@ -1,73 +1,118 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: vcard
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
prerelease:
|
5
6
|
platform: ruby
|
6
|
-
authors:
|
7
|
-
-
|
7
|
+
authors:
|
8
|
+
- Kuba Kuźma
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
|
12
|
-
date: 2009-10-20 00:00:00 +02:00
|
13
|
-
default_executable:
|
12
|
+
date: 2012-11-26 00:00:00.000000000 Z
|
14
13
|
dependencies: []
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
description: Vcard extracted from Vpim
|
15
|
+
email:
|
16
|
+
- kuba@jah.pl
|
18
17
|
executables: []
|
19
|
-
|
20
18
|
extensions: []
|
21
|
-
|
22
|
-
|
23
|
-
- LICENSE
|
24
|
-
- README.rdoc
|
25
|
-
files:
|
26
|
-
- .document
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
27
21
|
- .gitignore
|
28
|
-
-
|
29
|
-
-
|
22
|
+
- Gemfile
|
23
|
+
- LICENSE.txt
|
24
|
+
- README.md
|
30
25
|
- Rakefile
|
31
|
-
-
|
26
|
+
- VPIM-LICENSE.txt
|
32
27
|
- lib/vcard.rb
|
33
28
|
- lib/vcard/attachment.rb
|
29
|
+
- lib/vcard/bnf.rb
|
34
30
|
- lib/vcard/dirinfo.rb
|
35
31
|
- lib/vcard/enumerator.rb
|
32
|
+
- lib/vcard/errors.rb
|
36
33
|
- lib/vcard/field.rb
|
37
|
-
- lib/vcard/rfc2425.rb
|
38
34
|
- lib/vcard/vcard.rb
|
35
|
+
- lib/vcard/version.rb
|
39
36
|
- test/field_test.rb
|
37
|
+
- test/fixtures/bday_decode.vcard
|
38
|
+
- test/fixtures/bday_decode_2.vcard
|
39
|
+
- test/fixtures/empty_tel.vcard
|
40
|
+
- test/fixtures/ex1.vcard
|
41
|
+
- test/fixtures/ex2.vcard
|
42
|
+
- test/fixtures/ex3.vcard
|
43
|
+
- test/fixtures/ex_21.vcard
|
44
|
+
- test/fixtures/ex_21_case0.vcard
|
45
|
+
- test/fixtures/ex_apple1.vcard
|
46
|
+
- test/fixtures/ex_attach.vcard
|
47
|
+
- test/fixtures/ex_bdays.vcard
|
48
|
+
- test/fixtures/ex_encode_1.vcard
|
49
|
+
- test/fixtures/ex_ical_1.vcal
|
50
|
+
- test/fixtures/gmail.vcard
|
51
|
+
- test/fixtures/highrise.vcard
|
52
|
+
- test/fixtures/multiple_occurences_of_type.vcard
|
53
|
+
- test/fixtures/nickname0.vcard
|
54
|
+
- test/fixtures/nickname1.vcard
|
55
|
+
- test/fixtures/nickname2.vcard
|
56
|
+
- test/fixtures/nickname3.vcard
|
57
|
+
- test/fixtures/nickname4.vcard
|
58
|
+
- test/fixtures/nickname5.vcard
|
59
|
+
- test/fixtures/slash_in_field_name.vcard
|
60
|
+
- test/fixtures/tst1.vcard
|
61
|
+
- test/fixtures/url_decode.vcard
|
40
62
|
- test/test_helper.rb
|
41
63
|
- test/vcard_test.rb
|
42
|
-
|
64
|
+
- vcard.gemspec
|
43
65
|
homepage: http://github.com/qoobaa/vcard
|
44
66
|
licenses: []
|
45
|
-
|
46
67
|
post_install_message:
|
47
|
-
rdoc_options:
|
48
|
-
|
49
|
-
require_paths:
|
68
|
+
rdoc_options: []
|
69
|
+
require_paths:
|
50
70
|
- lib
|
51
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ! '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
63
83
|
requirements: []
|
64
|
-
|
65
84
|
rubyforge_project:
|
66
|
-
rubygems_version: 1.
|
85
|
+
rubygems_version: 1.8.24
|
67
86
|
signing_key:
|
68
87
|
specification_version: 3
|
69
|
-
summary: Vcard
|
70
|
-
test_files:
|
88
|
+
summary: Vcard extracted from Vpim
|
89
|
+
test_files:
|
71
90
|
- test/field_test.rb
|
91
|
+
- test/fixtures/bday_decode.vcard
|
92
|
+
- test/fixtures/bday_decode_2.vcard
|
93
|
+
- test/fixtures/empty_tel.vcard
|
94
|
+
- test/fixtures/ex1.vcard
|
95
|
+
- test/fixtures/ex2.vcard
|
96
|
+
- test/fixtures/ex3.vcard
|
97
|
+
- test/fixtures/ex_21.vcard
|
98
|
+
- test/fixtures/ex_21_case0.vcard
|
99
|
+
- test/fixtures/ex_apple1.vcard
|
100
|
+
- test/fixtures/ex_attach.vcard
|
101
|
+
- test/fixtures/ex_bdays.vcard
|
102
|
+
- test/fixtures/ex_encode_1.vcard
|
103
|
+
- test/fixtures/ex_ical_1.vcal
|
104
|
+
- test/fixtures/gmail.vcard
|
105
|
+
- test/fixtures/highrise.vcard
|
106
|
+
- test/fixtures/multiple_occurences_of_type.vcard
|
107
|
+
- test/fixtures/nickname0.vcard
|
108
|
+
- test/fixtures/nickname1.vcard
|
109
|
+
- test/fixtures/nickname2.vcard
|
110
|
+
- test/fixtures/nickname3.vcard
|
111
|
+
- test/fixtures/nickname4.vcard
|
112
|
+
- test/fixtures/nickname5.vcard
|
113
|
+
- test/fixtures/slash_in_field_name.vcard
|
114
|
+
- test/fixtures/tst1.vcard
|
115
|
+
- test/fixtures/url_decode.vcard
|
72
116
|
- test/test_helper.rb
|
73
117
|
- test/vcard_test.rb
|
118
|
+
has_rdoc:
|
data/.document
DELETED
data/README.rdoc
DELETED
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
0.1.1
|
data/lib/vcard/rfc2425.rb
DELETED
@@ -1,367 +0,0 @@
|
|
1
|
-
=begin
|
2
|
-
Copyright (C) 2008 Sam Roberts
|
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
|
-
require "date"
|
10
|
-
|
11
|
-
module Vpim
|
12
|
-
# Contains regular expression strings for the EBNF of RFC 2425.
|
13
|
-
module Bnf #:nodoc:
|
14
|
-
|
15
|
-
# 1*(ALPHA / DIGIT / "-")
|
16
|
-
# Note: I think I can add A-Z here, and get rid of the "i" matches elsewhere.
|
17
|
-
# Note: added '_' to allowed because its produced by Notes (X-LOTUS-CHILD_UID:)
|
18
|
-
# Note: added '/' to allowed because its produced by KAddressBook (X-messaging/xmpp-All:)
|
19
|
-
# Note: added ' ' to allowed because its produced by highrisehq.com (X-GOOGLE TALK:)
|
20
|
-
NAME = '[-a-z0-9_/][-a-z0-9_/ ]*'
|
21
|
-
|
22
|
-
# <"> <Any character except CTLs, DQUOTE> <">
|
23
|
-
QSTR = '"([^"]*)"'
|
24
|
-
|
25
|
-
# *<Any character except CTLs, DQUOTE, ";", ":", ",">
|
26
|
-
PTEXT = '([^";:,]+)'
|
27
|
-
|
28
|
-
# param-value = ptext / quoted-string
|
29
|
-
PVALUE = "(?:#{QSTR}|#{PTEXT})"
|
30
|
-
|
31
|
-
# param = name "=" param-value *("," param-value)
|
32
|
-
# Note: v2.1 allows a type or encoding param-value to appear without the type=
|
33
|
-
# or the encoding=. This is hideous, but we try and support it, if there
|
34
|
-
# is no "=", then $2 will be "", and we will treat it as a v2.1 param.
|
35
|
-
PARAM = ";(#{NAME})(=?)((?:#{PVALUE})?(?:,#{PVALUE})*)"
|
36
|
-
|
37
|
-
# V3.0: contentline = [group "."] name *(";" param) ":" value
|
38
|
-
# V2.1: contentline = *( group "." ) name *(";" param) ":" value
|
39
|
-
#
|
40
|
-
# We accept the V2.1 syntax for backwards compatibility.
|
41
|
-
#LINE = "((?:#{NAME}\\.)*)?(#{NAME})([^:]*)\:(.*)"
|
42
|
-
LINE = "^((?:#{NAME}\\.)*)?(#{NAME})((?:#{PARAM})*):(.*)$"
|
43
|
-
|
44
|
-
# date = date-fullyear ["-"] date-month ["-"] date-mday
|
45
|
-
# date-fullyear = 4 DIGIT
|
46
|
-
# date-month = 2 DIGIT
|
47
|
-
# date-mday = 2 DIGIT
|
48
|
-
DATE = '(\d\d\d\d)-?(\d\d)-?(\d\d)'
|
49
|
-
|
50
|
-
# time = time-hour [":"] time-minute [":"] time-second [time-secfrac] [time-zone]
|
51
|
-
# time-hour = 2 DIGIT
|
52
|
-
# time-minute = 2 DIGIT
|
53
|
-
# time-second = 2 DIGIT
|
54
|
-
# time-secfrac = "," 1*DIGIT
|
55
|
-
# time-zone = "Z" / time-numzone
|
56
|
-
# time-numzone = sign time-hour [":"] time-minute
|
57
|
-
TIME = '(\d\d):?(\d\d):?(\d\d)(\.\d+)?(Z|[-+]\d\d:?\d\d)?'
|
58
|
-
|
59
|
-
# integer = (["+"] / "-") 1*DIGIT
|
60
|
-
INTEGER = '[-+]?\d+'
|
61
|
-
|
62
|
-
# QSAFE-CHAR = WSP / %x21 / %x23-7E / NON-US-ASCII
|
63
|
-
# ; Any character except CTLs and DQUOTE
|
64
|
-
QSAFECHAR = '[ \t\x21\x23-\x7e\x80-\xff]'
|
65
|
-
|
66
|
-
# SAFE-CHAR = WSP / %x21 / %x23-2B / %x2D-39 / %x3C-7E / NON-US-ASCII
|
67
|
-
# ; Any character except CTLs, DQUOTE, ";", ":", ","
|
68
|
-
SAFECHAR = '[ \t\x21\x23-\x2b\x2d-\x39\x3c-\x7e\x80-\xff]'
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
module Vpim
|
73
|
-
# Split on \r\n or \n to get the lines, unfold continued lines (they
|
74
|
-
# start with ' ' or \t), and return the array of unfolded lines.
|
75
|
-
#
|
76
|
-
# This also supports the (invalid) encoding convention of allowing empty
|
77
|
-
# lines to be inserted for readability - it does this by dropping zero-length
|
78
|
-
# lines.
|
79
|
-
def Vpim.unfold(card) #:nodoc:
|
80
|
-
unfolded = []
|
81
|
-
|
82
|
-
card.lines do |line|
|
83
|
-
line.chomp!
|
84
|
-
# If it's a continuation line, add it to the last.
|
85
|
-
# If it's an empty line, drop it from the input.
|
86
|
-
if( line =~ /^[ \t]/ )
|
87
|
-
unfolded[-1] << line[1, line.size-1]
|
88
|
-
elsif( line =~ /^$/ )
|
89
|
-
else
|
90
|
-
unfolded << line
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
unfolded
|
95
|
-
end
|
96
|
-
|
97
|
-
# Convert a +sep+-seperated list of values into an array of values.
|
98
|
-
def Vpim.decode_list(value, sep = ',') # :nodoc:
|
99
|
-
list = []
|
100
|
-
|
101
|
-
value.split(sep).each do |item|
|
102
|
-
item.chomp!(sep)
|
103
|
-
list << yield(item)
|
104
|
-
end
|
105
|
-
list
|
106
|
-
end
|
107
|
-
|
108
|
-
# Convert a RFC 2425 date into an array of [year, month, day].
|
109
|
-
def Vpim.decode_date(v) # :nodoc:
|
110
|
-
unless v =~ %r{^\s*#{Bnf::DATE}\s*$}
|
111
|
-
raise Vpim::InvalidEncodingError, "date not valid (#{v})"
|
112
|
-
end
|
113
|
-
[$1.to_i, $2.to_i, $3.to_i]
|
114
|
-
end
|
115
|
-
|
116
|
-
# Convert a RFC 2425 date into a Date object.
|
117
|
-
def self.decode_date_to_date(v)
|
118
|
-
Date.new(*decode_date(v))
|
119
|
-
end
|
120
|
-
|
121
|
-
# Note in the following the RFC2425 allows yyyy-mm-ddThh:mm:ss, but RFC2445
|
122
|
-
# does not. I choose to encode to the subset that is valid for both.
|
123
|
-
|
124
|
-
# Encode a Date object as "yyyymmdd".
|
125
|
-
def Vpim.encode_date(d) # :nodoc:
|
126
|
-
"%0.4d%0.2d%0.2d" % [ d.year, d.mon, d.day ]
|
127
|
-
end
|
128
|
-
|
129
|
-
# Encode a Date object as "yyyymmdd".
|
130
|
-
def Vpim.encode_time(d) # :nodoc:
|
131
|
-
"%0.4d%0.2d%0.2d" % [ d.year, d.mon, d.day ]
|
132
|
-
end
|
133
|
-
|
134
|
-
# Encode a Time or DateTime object as "yyyymmddThhmmss"
|
135
|
-
def Vpim.encode_date_time(d) # :nodoc:
|
136
|
-
"%0.4d%0.2d%0.2dT%0.2d%0.2d%0.2d" % [ d.year, d.mon, d.day, d.hour, d.min, d.sec ]
|
137
|
-
end
|
138
|
-
|
139
|
-
# Convert a RFC 2425 time into an array of [hour,min,sec,secfrac,timezone]
|
140
|
-
def Vpim.decode_time(v) # :nodoc:
|
141
|
-
unless match = %r{^\s*#{Bnf::TIME}\s*$}.match(v)
|
142
|
-
raise Vpim::InvalidEncodingError, "time '#{v}' not valid"
|
143
|
-
end
|
144
|
-
hour, min, sec, secfrac, tz = match.to_a[1..5]
|
145
|
-
|
146
|
-
[hour.to_i, min.to_i, sec.to_i, secfrac ? secfrac.to_f : 0, tz]
|
147
|
-
end
|
148
|
-
|
149
|
-
def self.array_datetime_to_time(dtarray) #:nodoc:
|
150
|
-
# We get [ year, month, day, hour, min, sec, usec, tz ]
|
151
|
-
begin
|
152
|
-
tz = (dtarray.pop == "Z") ? :gm : :local
|
153
|
-
Time.send(tz, *dtarray)
|
154
|
-
rescue ArgumentError => e
|
155
|
-
raise Vpim::InvalidEncodingError, "#{tz} #{e} (#{dtarray.join(', ')})"
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
# Convert a RFC 2425 time into an array of Time objects.
|
160
|
-
def Vpim.decode_time_to_time(v) # :nodoc:
|
161
|
-
array_datetime_to_time(decode_date_time(v))
|
162
|
-
end
|
163
|
-
|
164
|
-
# Convert a RFC 2425 date-time into an array of [year,mon,day,hour,min,sec,secfrac,timezone]
|
165
|
-
def Vpim.decode_date_time(v) # :nodoc:
|
166
|
-
unless match = %r{^\s*#{Bnf::DATE}T#{Bnf::TIME}\s*$}.match(v)
|
167
|
-
raise Vpim::InvalidEncodingError, "date-time '#{v}' not valid"
|
168
|
-
end
|
169
|
-
year, month, day, hour, min, sec, secfrac, tz = match.to_a[1..8]
|
170
|
-
|
171
|
-
[
|
172
|
-
# date
|
173
|
-
year.to_i, month.to_i, day.to_i,
|
174
|
-
# time
|
175
|
-
hour.to_i, min.to_i, sec.to_i, secfrac ? secfrac.to_f : 0, tz
|
176
|
-
]
|
177
|
-
end
|
178
|
-
|
179
|
-
def Vpim.decode_date_time_to_datetime(v) #:nodoc:
|
180
|
-
year, month, day, hour, min, sec, secfrac, tz = Vpim.decode_date_time(v)
|
181
|
-
# TODO - DateTime understands timezones, so we could decode tz and use it.
|
182
|
-
DateTime.civil(year, month, day, hour, min, sec, 0)
|
183
|
-
end
|
184
|
-
|
185
|
-
# Vpim.decode_boolean
|
186
|
-
#
|
187
|
-
# float
|
188
|
-
#
|
189
|
-
# float_list
|
190
|
-
=begin
|
191
|
-
=end
|
192
|
-
|
193
|
-
# Convert an RFC2425 INTEGER value into an Integer
|
194
|
-
def Vpim.decode_integer(v) # :nodoc:
|
195
|
-
unless match = %r{\s*#{Bnf::INTEGER}\s*}.match(v)
|
196
|
-
raise Vpim::InvalidEncodingError, "integer not valid (#{v})"
|
197
|
-
end
|
198
|
-
v.to_i
|
199
|
-
end
|
200
|
-
|
201
|
-
#
|
202
|
-
# integer_list
|
203
|
-
|
204
|
-
# Convert a RFC2425 date-list into an array of dates.
|
205
|
-
def Vpim.decode_date_list(v) # :nodoc:
|
206
|
-
Vpim.decode_list(v) do |date|
|
207
|
-
date.strip!
|
208
|
-
if date.length > 0
|
209
|
-
Vpim.decode_date(date)
|
210
|
-
end
|
211
|
-
end.compact
|
212
|
-
end
|
213
|
-
|
214
|
-
# Convert a RFC 2425 time-list into an array of times.
|
215
|
-
def Vpim.decode_time_list(v) # :nodoc:
|
216
|
-
Vpim.decode_list(v) do |time|
|
217
|
-
time.strip!
|
218
|
-
if time.length > 0
|
219
|
-
Vpim.decode_time(time)
|
220
|
-
end
|
221
|
-
end.compact
|
222
|
-
end
|
223
|
-
|
224
|
-
# Convert a RFC 2425 date-time-list into an array of date-times.
|
225
|
-
def Vpim.decode_date_time_list(v) # :nodoc:
|
226
|
-
Vpim.decode_list(v) do |datetime|
|
227
|
-
datetime.strip!
|
228
|
-
if datetime.length > 0
|
229
|
-
Vpim.decode_date_time(datetime)
|
230
|
-
end
|
231
|
-
end.compact
|
232
|
-
end
|
233
|
-
|
234
|
-
# Convert RFC 2425 text into a String.
|
235
|
-
# \\ -> \
|
236
|
-
# \n -> NL
|
237
|
-
# \N -> NL
|
238
|
-
# \, -> ,
|
239
|
-
# \; -> ;
|
240
|
-
#
|
241
|
-
# I've seen double-quote escaped by iCal.app. Hmm. Ok, if you aren't supposed
|
242
|
-
# to escape anything but the above, everything else is ambiguous, so I'll
|
243
|
-
# just support it.
|
244
|
-
def Vpim.decode_text(v) # :nodoc:
|
245
|
-
# FIXME - I think this should trim leading and trailing space
|
246
|
-
v.gsub(/\\(.)/) do
|
247
|
-
case $1
|
248
|
-
when 'n', 'N'
|
249
|
-
"\n"
|
250
|
-
else
|
251
|
-
$1
|
252
|
-
end
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
|
-
def Vpim.encode_text(v) #:nodoc:
|
257
|
-
v.to_str.gsub(/([\\,;\n])/) { $1 == "\n" ? "\\n" : "\\"+$1 }
|
258
|
-
end
|
259
|
-
|
260
|
-
# v is an Array of String, or just a single String
|
261
|
-
def Vpim.encode_text_list(v, sep = ",") #:nodoc:
|
262
|
-
begin
|
263
|
-
v.to_ary.map{ |t| Vpim.encode_text(t) }.join(sep)
|
264
|
-
rescue
|
265
|
-
Vpim.encode_text(v)
|
266
|
-
end
|
267
|
-
end
|
268
|
-
|
269
|
-
# Convert a +sep+-seperated list of TEXT values into an array of values.
|
270
|
-
def Vpim.decode_text_list(value, sep = ',') # :nodoc:
|
271
|
-
# Need to do in two stages, as best I can find.
|
272
|
-
list = value.scan(/([^#{sep}\\]*(?:\\.[^#{sep}\\]*)*)#{sep}/).map do |v|
|
273
|
-
Vpim.decode_text(v.first)
|
274
|
-
end
|
275
|
-
if value.match(/([^#{sep}\\]*(?:\\.[^#{sep}\\]*)*)$/)
|
276
|
-
list << $1
|
277
|
-
end
|
278
|
-
list
|
279
|
-
end
|
280
|
-
|
281
|
-
# param-value = paramtext / quoted-string
|
282
|
-
# paramtext = *SAFE-CHAR
|
283
|
-
# quoted-string = DQUOTE *QSAFE-CHAR DQUOTE
|
284
|
-
def Vpim.encode_paramtext(value)
|
285
|
-
case value
|
286
|
-
when %r{\A#{Bnf::SAFECHAR}*\z}
|
287
|
-
value
|
288
|
-
else
|
289
|
-
raise Vpim::Unencodable, "paramtext #{value.inspect}"
|
290
|
-
end
|
291
|
-
end
|
292
|
-
|
293
|
-
def Vpim.encode_paramvalue(value)
|
294
|
-
case value
|
295
|
-
when %r{\A#{Bnf::SAFECHAR}*\z}
|
296
|
-
value
|
297
|
-
when %r{\A#{Bnf::QSAFECHAR}*\z}
|
298
|
-
'"' + value + '"'
|
299
|
-
else
|
300
|
-
raise Vpim::Unencodable, "param-value #{value.inspect}"
|
301
|
-
end
|
302
|
-
end
|
303
|
-
|
304
|
-
|
305
|
-
# Unfold the lines in +card+, then return an array of one Field object per
|
306
|
-
# line.
|
307
|
-
def Vpim.decode(card) #:nodoc:
|
308
|
-
content = Vpim.unfold(card).collect { |line| DirectoryInfo::Field.decode(line) }
|
309
|
-
end
|
310
|
-
|
311
|
-
|
312
|
-
# Expand an array of fields into its syntactic entities. Each entity is a sequence
|
313
|
-
# of fields where the sequences is delimited by a BEGIN/END field. Since
|
314
|
-
# BEGIN/END delimited entities can be nested, we build a tree. Each entry in
|
315
|
-
# the array is either a Field or an array of entries (where each entry is
|
316
|
-
# either a Field, or an array of entries...).
|
317
|
-
def Vpim.expand(src) #:nodoc:
|
318
|
-
# output array to expand the src to
|
319
|
-
dst = []
|
320
|
-
# stack used to track our nesting level, as we see begin/end we start a
|
321
|
-
# new/finish the current entity, and push/pop that entity from the stack
|
322
|
-
current = [ dst ]
|
323
|
-
|
324
|
-
for f in src
|
325
|
-
if f.name? 'BEGIN'
|
326
|
-
e = [ f ]
|
327
|
-
|
328
|
-
current.last.push(e)
|
329
|
-
current.push(e)
|
330
|
-
|
331
|
-
elsif f.name? 'END'
|
332
|
-
current.last.push(f)
|
333
|
-
|
334
|
-
unless current.last.first.value? current.last.last.value
|
335
|
-
raise "BEGIN/END mismatch (#{current.last.first.value} != #{current.last.last.value})"
|
336
|
-
end
|
337
|
-
|
338
|
-
current.pop
|
339
|
-
|
340
|
-
else
|
341
|
-
current.last.push(f)
|
342
|
-
end
|
343
|
-
end
|
344
|
-
|
345
|
-
dst
|
346
|
-
end
|
347
|
-
|
348
|
-
# Split an array into an array of all the fields at the outer level, and
|
349
|
-
# an array of all the inner arrays of fields. Return the array [outer,
|
350
|
-
# inner].
|
351
|
-
def Vpim.outer_inner(fields) #:nodoc:
|
352
|
-
# TODO - use Enumerable#partition
|
353
|
-
# seperate into the outer-level fields, and the arrays of component
|
354
|
-
# fields
|
355
|
-
outer = []
|
356
|
-
inner = []
|
357
|
-
fields.each do |line|
|
358
|
-
case line
|
359
|
-
when Array; inner << line
|
360
|
-
else; outer << line
|
361
|
-
end
|
362
|
-
end
|
363
|
-
return outer, inner
|
364
|
-
end
|
365
|
-
|
366
|
-
end
|
367
|
-
|