ruby-vobject 0.1.0 → 1.0.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.
- checksums.yaml +5 -5
- data/.hound.yml +3 -0
- data/.rubocop.tb.yml +650 -0
- data/.rubocop.yml +1077 -0
- data/.travis.yml +16 -3
- data/Gemfile +1 -1
- data/LICENSE.txt +21 -17
- data/README.adoc +142 -0
- data/Rakefile +1 -1
- data/lib/c.rb +173 -0
- data/lib/error.rb +19 -0
- data/lib/vcalendar.rb +77 -0
- data/lib/vcard.rb +67 -0
- data/lib/vobject.rb +13 -170
- data/lib/vobject/component.rb +87 -36
- data/lib/vobject/parameter.rb +116 -0
- data/lib/vobject/parametervalue.rb +26 -0
- data/lib/vobject/property.rb +134 -55
- data/lib/vobject/propertyvalue.rb +46 -0
- data/lib/vobject/vcalendar/component.rb +106 -0
- data/lib/vobject/vcalendar/grammar.rb +595 -0
- data/lib/vobject/vcalendar/paramcheck.rb +259 -0
- data/lib/vobject/vcalendar/propertyparent.rb +98 -0
- data/lib/vobject/vcalendar/propertyvalue.rb +606 -0
- data/lib/vobject/vcalendar/typegrammars.rb +605 -0
- data/lib/vobject/vcard/v3_0/component.rb +40 -0
- data/lib/vobject/vcard/v3_0/grammar.rb +176 -0
- data/lib/vobject/vcard/v3_0/paramcheck.rb +111 -0
- data/lib/vobject/vcard/v3_0/parameter.rb +17 -0
- data/lib/vobject/vcard/v3_0/property.rb +18 -0
- data/lib/vobject/vcard/v3_0/propertyvalue.rb +401 -0
- data/lib/vobject/vcard/v3_0/typegrammars.rb +426 -0
- data/lib/vobject/vcard/v4_0/component.rb +40 -0
- data/lib/vobject/vcard/v4_0/grammar.rb +225 -0
- data/lib/vobject/vcard/v4_0/paramcheck.rb +270 -0
- data/lib/vobject/vcard/v4_0/parameter.rb +18 -0
- data/lib/vobject/vcard/v4_0/property.rb +63 -0
- data/lib/vobject/vcard/v4_0/propertyvalue.rb +404 -0
- data/lib/vobject/vcard/v4_0/typegrammars.rb +540 -0
- data/lib/vobject/vcard/version.rb +3 -0
- data/lib/vobject/version.rb +1 -1
- data/ruby-vobject.gemspec +19 -11
- metadata +93 -17
- data/README.md +0 -94
@@ -0,0 +1,426 @@
|
|
1
|
+
require "rsec"
|
2
|
+
require "set"
|
3
|
+
require "uri"
|
4
|
+
require "date"
|
5
|
+
include Rsec::Helpers
|
6
|
+
require "vobject/vcard/version"
|
7
|
+
require "vobject"
|
8
|
+
require_relative "./propertyvalue"
|
9
|
+
|
10
|
+
module Vcard::V3_0
|
11
|
+
class Typegrammars
|
12
|
+
class << self
|
13
|
+
# property value types, each defining their own parser
|
14
|
+
|
15
|
+
def binary
|
16
|
+
binary = seq(/[a-zA-Z0-9+\/]*/.r, /={0,2}/.r) do |b, q|
|
17
|
+
if (b.length + q.length) % 4 == 0
|
18
|
+
PropertyValue::Binary.new(b + q)
|
19
|
+
else
|
20
|
+
{ error: "Malformed binary coding" }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
binary.eof
|
24
|
+
end
|
25
|
+
|
26
|
+
def phone_number
|
27
|
+
# This is on the lax side; there should be up to 15 digits
|
28
|
+
# Will allow letters
|
29
|
+
phone_number = /[0-9() +A-Z-]+/i.r.map { |p| PropertyValue::Phonenumber.new p }
|
30
|
+
phone_number.eof
|
31
|
+
end
|
32
|
+
|
33
|
+
def geovalue
|
34
|
+
float = prim(:double)
|
35
|
+
geovalue = seq(float << ";".r, float) do |a, b|
|
36
|
+
if a <= 180.0 && a >= -180.0 && b <= 180 && b > -180
|
37
|
+
PropertyValue::Geovalue.new(lat: a, long: b)
|
38
|
+
else
|
39
|
+
{ error: "Latitude/Longitude outside of range -180..180" }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
geovalue.eof
|
43
|
+
end
|
44
|
+
|
45
|
+
def classvalue
|
46
|
+
iana_token = /[a-zA-Z\d\-]+/.r
|
47
|
+
xname = seq(/[xX]-/, /[a-zA-Z0-9-]+/.r).map(&:join)
|
48
|
+
classvalue = (/PUBLIC/i.r | /PRIVATE/i.r | /CONFIDENTIAL/i.r | iana_token | xname).map do |m|
|
49
|
+
PropertyValue::ClassValue.new m
|
50
|
+
end
|
51
|
+
classvalue.eof
|
52
|
+
end
|
53
|
+
|
54
|
+
def integer
|
55
|
+
integer = prim(:int32).map { |i| PropertyValue::Integer.new i }
|
56
|
+
integer.eof
|
57
|
+
end
|
58
|
+
|
59
|
+
def float_t
|
60
|
+
float_t = prim(:double).map { |f| PropertyValue::Float.new f }
|
61
|
+
float_t.eof
|
62
|
+
end
|
63
|
+
|
64
|
+
def iana_token
|
65
|
+
iana_token = /[a-zA-Z\d\-]+/.r.map { |x| PropertyValue::Ianatoken.new x }
|
66
|
+
iana_token.eof
|
67
|
+
end
|
68
|
+
|
69
|
+
def versionvalue
|
70
|
+
versionvalue = "3.0".r.map { |v| PropertyValue::Version.new v }
|
71
|
+
versionvalue.eof
|
72
|
+
end
|
73
|
+
|
74
|
+
def profilevalue
|
75
|
+
profilevalue = /VCARD/i.r.map { |v| PropertyValue::Profilevalue.new v }
|
76
|
+
profilevalue.eof
|
77
|
+
end
|
78
|
+
|
79
|
+
def uri
|
80
|
+
uri = /\S+/.r.map do |s|
|
81
|
+
if s =~ URI::DEFAULT_PARSER.make_regexp
|
82
|
+
PropertyValue::Uri.new(s)
|
83
|
+
else
|
84
|
+
{ error: "Invalid URI" }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
uri.eof
|
88
|
+
end
|
89
|
+
|
90
|
+
def text_t
|
91
|
+
text_t = C::TEXT3.map { |t| PropertyValue::Text.new(unescape(t)) }
|
92
|
+
text_t.eof
|
93
|
+
end
|
94
|
+
|
95
|
+
def textlist
|
96
|
+
text = C::TEXT3
|
97
|
+
textlist1 =
|
98
|
+
seq(text << ",".r, lazy { textlist1 }) { |a, b| [unescape(a), b].flatten } |
|
99
|
+
text.map { |t| [unescape(t)] }
|
100
|
+
textlist = textlist1.map { |m| PropertyValue::Textlist.new m }
|
101
|
+
textlist.eof
|
102
|
+
end
|
103
|
+
|
104
|
+
def org
|
105
|
+
text = C::TEXT3
|
106
|
+
org1 =
|
107
|
+
seq(text << ";".r, lazy { org1 }) { |a, b| [unescape(a), b].flatten } |
|
108
|
+
text.map { |t| [unescape(t)] }
|
109
|
+
org = org1.map { |o| PropertyValue::Org.new o }
|
110
|
+
org.eof
|
111
|
+
end
|
112
|
+
|
113
|
+
def date_t
|
114
|
+
date_t = seq(/[0-9]{4}/.r, /-/.r._? >> /[0-9]{2}/.r, /-/.r._? >> /[0-9]{2}/.r) do |yy, mm, dd|
|
115
|
+
PropertyValue::Date.new(year: yy, month: mm, day: dd)
|
116
|
+
end
|
117
|
+
date_t.eof
|
118
|
+
end
|
119
|
+
|
120
|
+
def time_t
|
121
|
+
utc_offset = seq(C::SIGN, /[0-9]{2}/.r << /:/.r._?, /[0-9]{2}/.r) do |s, h, m|
|
122
|
+
{ sign: s, hour: h, min: m }
|
123
|
+
end
|
124
|
+
zone = utc_offset.map { |u| u } |
|
125
|
+
/Z/i.r.map { "Z" }
|
126
|
+
hour = /[0-9]{2}/.r
|
127
|
+
minute = /[0-9]{2}/.r
|
128
|
+
second = /[0-9]{2}/.r
|
129
|
+
secfrac = seq(",".r >> /[0-9]+/)
|
130
|
+
time_t = seq(hour << /:/._?, minute << /:/._?, second, secfrac._?, zone._?) do |h, m, s, f, z|
|
131
|
+
h = { hour: h, min: m, sec: s }
|
132
|
+
h[:zone] = z[0] unless z.empty?
|
133
|
+
h[:secfrac] = f[0] unless f.empty?
|
134
|
+
PropertyValue::Time.new(h)
|
135
|
+
end
|
136
|
+
time_t.eof
|
137
|
+
end
|
138
|
+
|
139
|
+
def date_time
|
140
|
+
utc_offset = seq(C::SIGN, /[0-9]{2}/.r << /:/.r._?, /[0-9]{2}/.r) do |s, h, m|
|
141
|
+
{ sign: s, hour: h, min: m }
|
142
|
+
end
|
143
|
+
zone = utc_offset.map { |u| u } |
|
144
|
+
/Z/i.r.map { "Z" }
|
145
|
+
hour = /[0-9]{2}/.r
|
146
|
+
minute = /[0-9]{2}/.r
|
147
|
+
second = /[0-9]{2}/.r
|
148
|
+
secfrac = seq(",".r >> /[0-9]+/)
|
149
|
+
date = seq(/[0-9]{4}/.r, /-/.r._?, /[0-9]{2}/.r, /-/.r._?, /[0-9]{2}/.r) do |yy, _, mm, _, dd|
|
150
|
+
{ year: yy, month: mm, day: dd }
|
151
|
+
end
|
152
|
+
time = seq(hour << /:/.r._?, minute << /:/.r._?, second, secfrac._?, zone._?) do |h, m, s, f, z|
|
153
|
+
h = { hour: h, min: m, sec: s }
|
154
|
+
h[:zone] = if z.empty?
|
155
|
+
""
|
156
|
+
else
|
157
|
+
z[0]
|
158
|
+
end
|
159
|
+
h[:secfrac] = f[0] unless f.empty?
|
160
|
+
h
|
161
|
+
end
|
162
|
+
date_time = seq(date << "T".r, time) do |d, t|
|
163
|
+
PropertyValue::DateTimeLocal.new(d.merge(t))
|
164
|
+
end
|
165
|
+
date_time.eof
|
166
|
+
end
|
167
|
+
|
168
|
+
def date_or_date_time
|
169
|
+
utc_offset = seq(C::SIGN, /[0-9]{2}/.r << /:/.r._?, /[0-9]{2}/.r) do |s, h, m|
|
170
|
+
{ sign: s, hour: h, min: m }
|
171
|
+
end
|
172
|
+
zone = utc_offset.map { |u| u } |
|
173
|
+
/Z/i.r.map { "Z" }
|
174
|
+
hour = /[0-9]{2}/.r
|
175
|
+
minute = /[0-9]{2}/.r
|
176
|
+
second = /[0-9]{2}/.r
|
177
|
+
secfrac = seq(",".r >> /[0-9]+/)
|
178
|
+
date = seq(/[0-9]{4}/.r << /-/.r._?, /[0-9]{2}/.r << /-/.r._?, /[0-9]{2}/.r) do |yy, mm, dd|
|
179
|
+
{ year: yy, month: mm, day: dd }
|
180
|
+
end
|
181
|
+
time = seq(hour << /:/.r._?, minute << /:/.r._?, second, secfrac._?, zone._?) do |h, m, s, f, z|
|
182
|
+
h = { hour: h, min: m, sec: s }
|
183
|
+
h[:zone] = z[0] unless z.empty?
|
184
|
+
h[:secfrac] = f[0] unless f.empty?
|
185
|
+
h
|
186
|
+
end
|
187
|
+
date_or_date_time = seq(date << "T".r, time) do |d, t|
|
188
|
+
PropertyValue::DateTimeLocal.new(d.merge(t))
|
189
|
+
end | date.map { |d| PropertyValue::Date.new(d) }
|
190
|
+
date_or_date_time.eof
|
191
|
+
end
|
192
|
+
|
193
|
+
def utc_offset
|
194
|
+
utc_offset = seq(C::SIGN, /[0-9]{2}/.r, /:/.r._?, /[0-9]{2}/.r) do |s, h, _, m|
|
195
|
+
PropertyValue::Utcoffset.new(sign: s, hour: h, min: m)
|
196
|
+
end
|
197
|
+
utc_offset.eof
|
198
|
+
end
|
199
|
+
|
200
|
+
def kindvalue
|
201
|
+
iana_token = /[a-zA-Z\d\-]+/.r
|
202
|
+
xname = seq(/[xX]-/, /[a-zA-Z0-9-]+/.r).map(&:join)
|
203
|
+
kindvalue = (/individual/i.r | /group/i.r | /org/i.r | /location/i.r |
|
204
|
+
iana_token | xname).map do |k|
|
205
|
+
PropertyValue::Kindvalue.new(k)
|
206
|
+
end
|
207
|
+
kindvalue.eof
|
208
|
+
end
|
209
|
+
|
210
|
+
def fivepartname
|
211
|
+
text = C::TEXT3
|
212
|
+
component = seq(text << ",".r, lazy { component }) do |a, b|
|
213
|
+
[unescape(a), b].flatten
|
214
|
+
end | text.map { |t| [unescape(t)] }
|
215
|
+
fivepartname1 = seq(component << ";".r, component << ";".r, component << ";".r,
|
216
|
+
component << ";".r, component) do |a, b, c, d, e|
|
217
|
+
a = a[0] if a.length == 1
|
218
|
+
b = b[0] if b.length == 1
|
219
|
+
c = c[0] if c.length == 1
|
220
|
+
d = d[0] if d.length == 1
|
221
|
+
e = e[0] if e.length == 1
|
222
|
+
{ surname: a, givenname: b, middlename: c, honprefix: d, honsuffix: e }
|
223
|
+
end | seq(component << ";".r, component << ";".r, component << ";".r, component) do |a, b, c, d|
|
224
|
+
a = a[0] if a.length == 1
|
225
|
+
b = b[0] if b.length == 1
|
226
|
+
c = c[0] if c.length == 1
|
227
|
+
d = d[0] if d.length == 1
|
228
|
+
{ surname: a, givenname: b, middlename: c, honprefix: d, honsuffix: "" }
|
229
|
+
end | seq(component << ";".r, component << ";".r, component) do |a, b, c|
|
230
|
+
a = a[0] if a.length == 1
|
231
|
+
b = b[0] if b.length == 1
|
232
|
+
c = c[0] if c.length == 1
|
233
|
+
{ surname: a, givenname: b, middlename: c, honprefix: "", honsuffix: "" }
|
234
|
+
end | seq(component << ";".r, component) do |a, b|
|
235
|
+
a = a[0] if a.length == 1
|
236
|
+
b = b[0] if b.length == 1
|
237
|
+
{ surname: a, givenname: b, middlename: "", honprefix: "", honsuffix: "" }
|
238
|
+
end | component.map do |a|
|
239
|
+
a = a[0] if a.length == 1
|
240
|
+
{ surname: a, givenname: "", middlename: "", honprefix: "", honsuffix: "" }
|
241
|
+
end
|
242
|
+
fivepartname = fivepartname1.map { |n| PropertyValue::Fivepartname.new(n) }
|
243
|
+
fivepartname.eof
|
244
|
+
end
|
245
|
+
|
246
|
+
def address
|
247
|
+
text = C::TEXT3
|
248
|
+
component = seq(text << ",".r, lazy { component }) do |a, b|
|
249
|
+
[unescape(a), b].flatten
|
250
|
+
end | text.map { |t| [unescape(t)] }
|
251
|
+
address1 = seq(component << ";".r, component << ";".r, component << ";".r, component << ";".r,
|
252
|
+
component << ";".r, component << ";".r, component) do |a, b, c, d, e, f, g|
|
253
|
+
a = a[0] if a.length == 1
|
254
|
+
b = b[0] if b.length == 1
|
255
|
+
c = c[0] if c.length == 1
|
256
|
+
d = d[0] if d.length == 1
|
257
|
+
e = e[0] if e.length == 1
|
258
|
+
f = f[0] if f.length == 1
|
259
|
+
g = g[0] if g.length == 1
|
260
|
+
{ pobox: a, ext: b, street: c,
|
261
|
+
locality: d, region: e, code: f, country: g }
|
262
|
+
end | seq(component << ";".r, component << ";".r, component << ";".r, component << ";".r,
|
263
|
+
component << ";".r, component) do |a, b, c, d, e, f|
|
264
|
+
a = a[0] if a.length == 1
|
265
|
+
b = b[0] if b.length == 1
|
266
|
+
c = c[0] if c.length == 1
|
267
|
+
d = d[0] if d.length == 1
|
268
|
+
e = e[0] if e.length == 1
|
269
|
+
f = f[0] if f.length == 1
|
270
|
+
{ pobox: a, ext: b, street: c,
|
271
|
+
locality: d, region: e, code: f, country: "" }
|
272
|
+
end | seq(component << ";".r, component << ";".r, component << ";".r,
|
273
|
+
component << ";".r, component) do |a, b, c, d, e|
|
274
|
+
a = a[0] if a.length == 1
|
275
|
+
b = b[0] if b.length == 1
|
276
|
+
c = c[0] if c.length == 1
|
277
|
+
d = d[0] if d.length == 1
|
278
|
+
e = e[0] if e.length == 1
|
279
|
+
{ pobox: a, ext: b, street: c,
|
280
|
+
locality: d, region: e, code: "", country: "" }
|
281
|
+
end | seq(component << ";".r, component << ";".r, component << ";".r, component) do |a, b, c, d|
|
282
|
+
a = a[0] if a.length == 1
|
283
|
+
b = b[0] if b.length == 1
|
284
|
+
c = c[0] if c.length == 1
|
285
|
+
d = d[0] if d.length == 1
|
286
|
+
{ pobox: a, ext: b, street: c,
|
287
|
+
locality: d, region: "", code: "", country: "" }
|
288
|
+
end | seq(component << ";".r, component << ";".r, component) do |a, b, c|
|
289
|
+
a = a[0] if a.length == 1
|
290
|
+
b = b[0] if b.length == 1
|
291
|
+
c = c[0] if c.length == 1
|
292
|
+
{ pobox: a, ext: b, street: c,
|
293
|
+
locality: "", region: "", code: "", country: "" }
|
294
|
+
end | seq(component << ";".r, component) do |a, b|
|
295
|
+
a = a[0] if a.length == 1
|
296
|
+
b = b[0] if b.length == 1
|
297
|
+
{ pobox: a, ext: b, street: "",
|
298
|
+
locality: "", region: "", code: "", country: "" }
|
299
|
+
end | component.map do |a|
|
300
|
+
a = a[0] if a.length == 1
|
301
|
+
{ pobox: a, ext: "", street: "",
|
302
|
+
locality: "", region: "", code: "", country: "" }
|
303
|
+
end
|
304
|
+
address = address1.map { |n| PropertyValue::Address.new(n) }
|
305
|
+
address.eof
|
306
|
+
end
|
307
|
+
|
308
|
+
def registered_propname
|
309
|
+
registered_propname = C::NAME_VCARD
|
310
|
+
registered_propname.eof
|
311
|
+
end
|
312
|
+
|
313
|
+
def registered_propname?(x)
|
314
|
+
p = registered_propname.parse(x)
|
315
|
+
not(Rsec::INVALID[p])
|
316
|
+
end
|
317
|
+
|
318
|
+
# text escapes: \\ \; \, \N \n
|
319
|
+
def unescape(x)
|
320
|
+
# temporarily escape \\ as \007f, which is disallowed in any text
|
321
|
+
x.gsub(/\\\\/, "\u007f").gsub(/\\;/, ";").gsub(/\\,/, ",").gsub(/\\[Nn]/, "\n").tr("\u007f", "\\")
|
322
|
+
end
|
323
|
+
|
324
|
+
# Enforce type restrictions on values of particular properties.
|
325
|
+
# If successful, return typed interpretation of string
|
326
|
+
def typematch(strict, key, params, _component, value, ctx)
|
327
|
+
errors = []
|
328
|
+
params[:VALUE] = params[:VALUE].downcase if params && params[:VALUE]
|
329
|
+
ctx1 = Rsec::ParseContext.new value, "source"
|
330
|
+
case key
|
331
|
+
when :VERSION
|
332
|
+
ret = versionvalue._parse ctx1
|
333
|
+
when :SOURCE, :URL, :IMPP, :FBURL, :CALURI, :CALADRURI, :CAPURI
|
334
|
+
ret = uri._parse ctx1
|
335
|
+
# not imposing filename restrictions on calendar URIs
|
336
|
+
when :NAME, :FN, :LABEL, :EMAIL, :MAILER, :TITLE, :ROLE, :NOTE, :PRODID, :SORT_STRING, :UID
|
337
|
+
ret = text_t._parse ctx1
|
338
|
+
when :CLASS
|
339
|
+
ret = classvalue._parse ctx1
|
340
|
+
when :CATEGORIES, :NICKNAME
|
341
|
+
ret = textlist._parse ctx1
|
342
|
+
when :ORG
|
343
|
+
ret = org._parse ctx1
|
344
|
+
when :PROFILE
|
345
|
+
ret = profilevalue._parse ctx1
|
346
|
+
when :N
|
347
|
+
ret = fivepartname._parse ctx1
|
348
|
+
when :PHOTO, :LOGO, :SOUND
|
349
|
+
ret = if params && params[:VALUE] == "uri"
|
350
|
+
uri._parse ctx1
|
351
|
+
else
|
352
|
+
binary._parse ctx1
|
353
|
+
end
|
354
|
+
when :KEY
|
355
|
+
ret = if params && params[:ENCODING] == "b"
|
356
|
+
binary._parse ctx1
|
357
|
+
else
|
358
|
+
text_t._parse ctx1
|
359
|
+
end
|
360
|
+
when :BDAY
|
361
|
+
ret = if params && params[:VALUE] == "date-time"
|
362
|
+
date_time._parse ctx1
|
363
|
+
elsif params && params[:VALUE] == "date"
|
364
|
+
date_t._parse ctx1
|
365
|
+
else
|
366
|
+
# unlike VCARD 4, can have either date || date_time without explicit value switch
|
367
|
+
date_or_date_time._parse ctx1
|
368
|
+
end
|
369
|
+
when :REV
|
370
|
+
ret = if params && params[:VALUE] == "date"
|
371
|
+
date_t._parse ctx1
|
372
|
+
elsif params && params[:VALUE] == "date-time"
|
373
|
+
date_time._parse ctx1
|
374
|
+
else
|
375
|
+
# unlike VCARD 4, can have either date || date_time without explicit value switch
|
376
|
+
ret = date_or_date_time._parse ctx1
|
377
|
+
end
|
378
|
+
when :ADR
|
379
|
+
ret = address._parse ctx1
|
380
|
+
when :TEL
|
381
|
+
ret = phone_number._parse ctx1
|
382
|
+
when :TZ
|
383
|
+
ret = if params && params[:VALUE] == "text"
|
384
|
+
text_t._parse ctx1
|
385
|
+
else
|
386
|
+
utc_offset._parse ctx1
|
387
|
+
end
|
388
|
+
when :GEO
|
389
|
+
ret = geovalue._parse ctx1
|
390
|
+
when :AGENT
|
391
|
+
if params && params[:VALUE] == "uri"
|
392
|
+
ret = uri._parse ctx1
|
393
|
+
else
|
394
|
+
# unescape
|
395
|
+
value = value.gsub(/\\n/, "\n").gsub(/\\;/, ";").gsub(/\\,/, ",").gsub(/\\:/, ":")
|
396
|
+
# spec says that colons need to be escaped, but none of the examples do so
|
397
|
+
value = value.gsub(/BEGIN:VCARD\n/, "BEGIN:VCARD\nVERSION:3.0\n") unless value =~ /\nVERSION:3\.0/
|
398
|
+
ctx1 = Rsec::ParseContext.new value, "source"
|
399
|
+
ret = PropertyValue::Agent.new(Grammar.new(strict).vobject_grammar._parse(ctx1))
|
400
|
+
# TODO same strictness as grammar
|
401
|
+
end
|
402
|
+
else
|
403
|
+
ret = text_t._parse ctx1
|
404
|
+
end
|
405
|
+
if ret.is_a?(Hash) && ret[:error]
|
406
|
+
parse_err(strict, errors, "#{ret[:error]} for property #{key}, value #{value}", ctx)
|
407
|
+
end
|
408
|
+
if Rsec::INVALID[ret]
|
409
|
+
parse_err(strict, errors, "Type mismatch for property #{key}, value #{value}", ctx)
|
410
|
+
end
|
411
|
+
Rsec::Fail.reset
|
412
|
+
[ret, errors]
|
413
|
+
end
|
414
|
+
|
415
|
+
private
|
416
|
+
|
417
|
+
def parse_err(strict, errors, msg, ctx)
|
418
|
+
if strict
|
419
|
+
raise ctx.report_error msg, "source"
|
420
|
+
else
|
421
|
+
errors << ctx.report_error(msg, "source")
|
422
|
+
end
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "vobject/component"
|
2
|
+
require "vobject/vcard/v4_0/property"
|
3
|
+
require "vobject/vcard/v4_0/grammar"
|
4
|
+
require "pp"
|
5
|
+
|
6
|
+
module Vcard::V4_0
|
7
|
+
class Component < Vobject::Component
|
8
|
+
class << self
|
9
|
+
def parse(vcf, strict)
|
10
|
+
hash = Vcard::V4_0::Grammar.new(strict).parse(vcf)
|
11
|
+
comp_name = hash.keys.first
|
12
|
+
new comp_name, hash[comp_name], hash[:errors]
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def raise_invalid_parsing
|
18
|
+
raise "vCard parse failed"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def property_base_class
|
25
|
+
version_class.const_get(:Property)
|
26
|
+
end
|
27
|
+
|
28
|
+
def component_base_class
|
29
|
+
version_class.const_get(:Component)
|
30
|
+
end
|
31
|
+
|
32
|
+
def parameter_base_class
|
33
|
+
version_class.const_get(:Parameter)
|
34
|
+
end
|
35
|
+
|
36
|
+
def version_class
|
37
|
+
Vcard::V4_0
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|