vcard_parser 0.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.
- data/.gitignore +6 -0
- data/.travis.yml +4 -0
- data/Gemfile +17 -0
- data/Guardfile +8 -0
- data/LICENSE +22 -0
- data/README.md +36 -0
- data/Rakefile +21 -0
- data/lib/vcard_parser.rb +4 -0
- data/lib/vcard_parser/3.0/field.rb +83 -0
- data/lib/vcard_parser/vcard.rb +93 -0
- data/lib/vcard_parser/version.rb +3 -0
- data/specs/data/two_vcard3.0.vcf +21 -0
- data/specs/data/vcard2.1.vcf +15 -0
- data/specs/data/vcard3.0.vcf +14 -0
- data/specs/spec_helper.rb +25 -0
- data/specs/unit/3.0/field_spec.rb +76 -0
- data/specs/unit/vcard_spec.rb +190 -0
- data/vcard_parser.gemspec +16 -0
- metadata +69 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gemspec
|
4
|
+
|
5
|
+
gem 'rake'
|
6
|
+
|
7
|
+
group(:development) do
|
8
|
+
gem 'simplecov'
|
9
|
+
gem 'guard-bacon', '~> 1.1.0'
|
10
|
+
gem 'rb-fsevent'
|
11
|
+
gem 'growl'
|
12
|
+
end
|
13
|
+
|
14
|
+
group(:test) do
|
15
|
+
gem 'schmurfy-bacon', '~> 1.4.2'
|
16
|
+
gem 'mocha', '~> 0.10.0'
|
17
|
+
end
|
data/Guardfile
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
|
2
|
+
# parameters:
|
3
|
+
# output => the formatted to use
|
4
|
+
# backtrace => number of lines, nil = everything
|
5
|
+
guard 'bacon', :output => "BetterOutput", :backtrace => nil do
|
6
|
+
watch(%r{^lib/vcard_parser/(.+)\.rb$}) { |m| "specs/unit/#{m[1]}_spec.rb" }
|
7
|
+
watch(%r{specs/.+\.rb$})
|
8
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Julien Ammous
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
# What ?
|
3
|
+
|
4
|
+
This is a vCard parser/generator released under te MIT license
|
5
|
+
|
6
|
+
# Support
|
7
|
+
For now only v3.0 is supported and the testing was done mostly with Apple
|
8
|
+
Addressbook and iOS contacts.
|
9
|
+
|
10
|
+
# Continuous integration ([](https://secure.travis-ci.org/schmurfy/vcard_parser.png))
|
11
|
+
|
12
|
+
This gem is tested against these ruby by travis-ci.org:
|
13
|
+
|
14
|
+
- mri 1.9.3
|
15
|
+
|
16
|
+
# Usage
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
require 'vcard_parser'
|
20
|
+
|
21
|
+
cards = VCardParser:parse(...)
|
22
|
+
|
23
|
+
puts cards[0].fields
|
24
|
+
puts cards[0]['N']
|
25
|
+
|
26
|
+
# dump back the vcard
|
27
|
+
puts cards[0].to_s
|
28
|
+
```
|
29
|
+
|
30
|
+
# Setting up development environmeent
|
31
|
+
|
32
|
+
```bash
|
33
|
+
# clone the repository and:
|
34
|
+
$ bundle
|
35
|
+
$ bundle exec guard
|
36
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
|
5
|
+
task :default => :test
|
6
|
+
|
7
|
+
task :test do
|
8
|
+
require 'bacon'
|
9
|
+
|
10
|
+
# do not generate coverage report from travis
|
11
|
+
unless ENV['TRAVIS']
|
12
|
+
ENV['COVERAGE'] = "1"
|
13
|
+
end
|
14
|
+
|
15
|
+
Dir[File.expand_path('../specs/**/*_spec.rb', __FILE__)].each do |file|
|
16
|
+
puts "File: #{file}:"
|
17
|
+
load(file)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
data/lib/vcard_parser.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
module VCardParser
|
4
|
+
module V30
|
5
|
+
|
6
|
+
class Field
|
7
|
+
attr_reader :name, :group, :value, :params
|
8
|
+
|
9
|
+
FORMAT = /^
|
10
|
+
(?:(?<group>[a-zA-Z0-9-]+)\.)?
|
11
|
+
(?<name>[a-zA-Z-]+)
|
12
|
+
(?<params> (?:;[A-Za-z]+=[^;]+)+ )?
|
13
|
+
:(?<value>.*)
|
14
|
+
$/x
|
15
|
+
|
16
|
+
def initialize(name, value, group = nil, params = {})
|
17
|
+
@name = name
|
18
|
+
@group = group
|
19
|
+
@value = convert_value(value)
|
20
|
+
@params = params
|
21
|
+
end
|
22
|
+
|
23
|
+
def full_name
|
24
|
+
ret = ""
|
25
|
+
ret << "#{@group}." if group
|
26
|
+
ret << @name
|
27
|
+
ret
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.parse(line)
|
31
|
+
m = FORMAT.match(line.strip)
|
32
|
+
if m
|
33
|
+
params = {}
|
34
|
+
if m[:params]
|
35
|
+
m[:params].split(';').reject{|s| s.empty? }.each do |p|
|
36
|
+
pname, pvalue = p.split('=')
|
37
|
+
params[pname] ||= []
|
38
|
+
params[pname] << pvalue
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
new(m[:name], m[:value], m[:group], params)
|
43
|
+
else
|
44
|
+
raise MalformedInput, line
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
ret = full_name
|
50
|
+
|
51
|
+
@params.each do |name, values|
|
52
|
+
values.each do |v|
|
53
|
+
ret << ";#{name}=#{v}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
ret << ":#{dump_value(value)}"
|
58
|
+
|
59
|
+
ret
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
def convert_value(value)
|
64
|
+
case name
|
65
|
+
when "REV", "BDAY" then Time.parse(value)
|
66
|
+
else
|
67
|
+
value
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def dump_value(value)
|
72
|
+
case name
|
73
|
+
when "REV" then value.iso8601
|
74
|
+
when "BDAY" then value.strftime("%Y-%m-%d")
|
75
|
+
else
|
76
|
+
value
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module VCardParser
|
2
|
+
ParsingError = Class.new(RuntimeError)
|
3
|
+
|
4
|
+
UnsupportedVersion = Class.new(ParsingError)
|
5
|
+
MalformedInput = Class.new(ParsingError)
|
6
|
+
|
7
|
+
class VCard
|
8
|
+
attr_reader :version, :fields
|
9
|
+
|
10
|
+
VCARD_FORMAT = /
|
11
|
+
BEGIN:VCARD\s+
|
12
|
+
(.+?)
|
13
|
+
END:VCARD\s*
|
14
|
+
/xm
|
15
|
+
|
16
|
+
def initialize(version, fields = [])
|
17
|
+
@version = version
|
18
|
+
@fields = fields
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.parse(data)
|
22
|
+
data.scan(VCARD_FORMAT).map do |vcard_data|
|
23
|
+
# fetch the version to choose the correct parser
|
24
|
+
lines = vcard_data[0].each_line.map(&:strip)
|
25
|
+
|
26
|
+
key, version = lines[0].split(':')
|
27
|
+
if key != "VERSION"
|
28
|
+
raise MalformedInput, "VERSION expected, got #{key}"
|
29
|
+
end
|
30
|
+
|
31
|
+
# remove begin and version
|
32
|
+
lines.slice!(0, 1)
|
33
|
+
|
34
|
+
new(version).tap do |card|
|
35
|
+
lines.each do |line|
|
36
|
+
card.add_field_from_string(line)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_field_from_string(line)
|
44
|
+
f = field_class.parse(line)
|
45
|
+
@fields << f
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_field(*args)
|
49
|
+
f = field_class.new(*args)
|
50
|
+
@fields << f
|
51
|
+
end
|
52
|
+
|
53
|
+
def values(key, group = nil)
|
54
|
+
@fields.select do |f|
|
55
|
+
(f.name == key) &&
|
56
|
+
(!group || (f.group == group))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def [](key, group = nil)
|
61
|
+
v = values(key, group)
|
62
|
+
v.empty? ? nil : v[0]
|
63
|
+
end
|
64
|
+
|
65
|
+
def each_field(&block)
|
66
|
+
@fields.each(&block)
|
67
|
+
end
|
68
|
+
|
69
|
+
def vcard
|
70
|
+
ret = ["BEGIN:VCARD", "VERSION:#{@version}"]
|
71
|
+
|
72
|
+
@fields.each do |f|
|
73
|
+
ret << f.to_s
|
74
|
+
end
|
75
|
+
|
76
|
+
ret << "END:VCARD\n"
|
77
|
+
ret.join("\n")
|
78
|
+
end
|
79
|
+
|
80
|
+
alias :to_s :vcard
|
81
|
+
|
82
|
+
|
83
|
+
private
|
84
|
+
def field_class
|
85
|
+
@field_class ||= case version
|
86
|
+
when '3.0' then V30::Field
|
87
|
+
else
|
88
|
+
raise UnsupportedVersion, version
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
BEGIN:VCARD
|
2
|
+
VERSION:3.0
|
3
|
+
PRODID:-//Apple Inc.//iOS 5.0.1//EN
|
4
|
+
N:Durand;Christophe;;;
|
5
|
+
FN:Christophe Durand
|
6
|
+
ORG:Op;
|
7
|
+
item1.TEL;type=pref:2 56 38 54
|
8
|
+
item2.TEL;type=pref:5 66
|
9
|
+
TEL;type=CELL;type=VOICE:3 55
|
10
|
+
item2.ADR;type=HOME;type=pref:;;3 rue du chat;Dris;;90880;FRANCE
|
11
|
+
BDAY;value=date:1900-01-01
|
12
|
+
REV:2012-10-31T16:08:22Z
|
13
|
+
UID:11
|
14
|
+
END:VCARD
|
15
|
+
END:VCARD
|
16
|
+
BEGIN:VCARD
|
17
|
+
VERSION:3.0
|
18
|
+
N:Jean;René;;;
|
19
|
+
FN:Jean René
|
20
|
+
UID:12
|
21
|
+
END:VCARD
|
@@ -0,0 +1,15 @@
|
|
1
|
+
BEGIN:VCARD
|
2
|
+
VERSION:2.1
|
3
|
+
N:Gump;Forrest
|
4
|
+
FN:Forrest Gump
|
5
|
+
ORG:Bubba Gump Shrimp Co.
|
6
|
+
TITLE:Shrimp Man
|
7
|
+
TEL;WORK;VOICE:(111) 555-1212
|
8
|
+
TEL;HOME;VOICE:(404) 555-1212
|
9
|
+
ADR;WORK:;;100 Waters Edge;Baytown;LA;30314;United States of America
|
10
|
+
LABEL;WORK;ENCODING=QUOTED-PRINTABLE:100 Waters Edge=0D=0ABaytown, LA 30314=0D=0AUnited States of America
|
11
|
+
ADR;HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America
|
12
|
+
LABEL;HOME;ENCODING=QUOTED-PRINTABLE:42 Plantation St.=0D=0ABaytown, LA 30314=0D=0AUnited States of America
|
13
|
+
EMAIL;PREF;INTERNET:forrestgump@example.com
|
14
|
+
REV:20080424T195243Z
|
15
|
+
END:VCARD
|
@@ -0,0 +1,14 @@
|
|
1
|
+
BEGIN:VCARD
|
2
|
+
VERSION:3.0
|
3
|
+
PRODID:-//Apple Inc.//iOS 5.0.1//EN
|
4
|
+
N:Durand;Christophe;;;
|
5
|
+
FN:Christophe Durand
|
6
|
+
ORG:Op;
|
7
|
+
item1.TEL;type=pref:2 56 38 54
|
8
|
+
item2.TEL;type=pref:5 66
|
9
|
+
TEL;type=CELL;type=VOICE:3 55
|
10
|
+
item2.ADR;type=HOME;type=pref:;;3 rue du chat;Dris;;90880;FRANCE
|
11
|
+
BDAY;value=date:1900-01-01
|
12
|
+
REV:2012-10-31T16:08:22Z
|
13
|
+
UID:11
|
14
|
+
END:VCARD
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
require 'bacon'
|
5
|
+
|
6
|
+
if ENV['COVERAGE']
|
7
|
+
Bacon.allow_focused_run = false
|
8
|
+
|
9
|
+
require 'simplecov'
|
10
|
+
SimpleCov.start do
|
11
|
+
add_filter ".*_spec"
|
12
|
+
add_filter "/helpers/"
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
$LOAD_PATH.unshift( File.expand_path('../../lib' , __FILE__) )
|
18
|
+
require 'vcard_parser'
|
19
|
+
|
20
|
+
# require 'bacon/ext/mocha'
|
21
|
+
# require 'bacon/ext/em'
|
22
|
+
|
23
|
+
# Thread.abort_on_exception = true
|
24
|
+
|
25
|
+
Bacon.summary_on_exit()
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
describe 'Field 3.0' do
|
4
|
+
should 'parse grouped field' do
|
5
|
+
f = VCardParser::V30::Field.parse("item1.TEL;type=pref:1 24 54 36")
|
6
|
+
f.group.should == "item1"
|
7
|
+
f.name.should == "TEL"
|
8
|
+
f.value.should == "1 24 54 36"
|
9
|
+
f.params.should == {'type' => ['pref']}
|
10
|
+
|
11
|
+
f.full_name.should == "item1.TEL"
|
12
|
+
end
|
13
|
+
|
14
|
+
should 'parse empty field' do
|
15
|
+
f = VCardParser::V30::Field.parse("FN:")
|
16
|
+
f.group.should == nil
|
17
|
+
f.name.should == "FN"
|
18
|
+
f.value.should == ""
|
19
|
+
f.params.should == {}
|
20
|
+
end
|
21
|
+
|
22
|
+
should 'parse extension field' do
|
23
|
+
f = VCardParser::V30::Field.parse("X-ABShowAs:COMPANY")
|
24
|
+
f.group.should == nil
|
25
|
+
f.name.should == "X-ABShowAs"
|
26
|
+
f.value.should == "COMPANY"
|
27
|
+
f.params.should == {}
|
28
|
+
end
|
29
|
+
|
30
|
+
should 'parse simple line' do
|
31
|
+
f = VCardParser::V30::Field.parse("PRODID:-//Apple Inc.//Mac OS X 10.8.2//EN")
|
32
|
+
f.group.should == nil
|
33
|
+
f.name.should == "PRODID"
|
34
|
+
f.value.should == "-//Apple Inc.//Mac OS X 10.8.2//EN"
|
35
|
+
f.params.should == {}
|
36
|
+
end
|
37
|
+
|
38
|
+
should 'parse dat field' do
|
39
|
+
f = VCardParser::V30::Field.parse("REV:2012-10-29T21:23:09Z")
|
40
|
+
f.group.should == nil
|
41
|
+
f.name.should == "REV"
|
42
|
+
f.value.should == Time.parse("2012-10-29T21:23:09Z")
|
43
|
+
f.params.should == {}
|
44
|
+
end
|
45
|
+
|
46
|
+
should 'parse line with parameters' do
|
47
|
+
f = VCardParser::V30::Field.parse("TEL;type=WORK;type=VOICE;type=pref:1234")
|
48
|
+
f.group.should == nil
|
49
|
+
f.name.should == "TEL"
|
50
|
+
f.value.should == "1234"
|
51
|
+
f.params.should == {
|
52
|
+
'type' => %w(WORK VOICE pref)
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
should 'generate vcf line from data' do
|
57
|
+
line = "TEL;type=WORK;type=VOICE;type=pref:1234"
|
58
|
+
f = VCardParser::V30::Field.parse(line)
|
59
|
+
f.to_s.should == line
|
60
|
+
end
|
61
|
+
|
62
|
+
should 'generate vcf line from converted data' do
|
63
|
+
line = "REV:2012-10-29T21:23:09Z"
|
64
|
+
f = VCardParser::V30::Field.parse(line)
|
65
|
+
f.to_s.should == line
|
66
|
+
end
|
67
|
+
|
68
|
+
should 'generate vcf line from data with group' do
|
69
|
+
line = "item1.TEL;type=pref:1 24 54 36"
|
70
|
+
f = VCardParser::V30::Field.parse(line)
|
71
|
+
f.to_s.should == line
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require_relative '../spec_helper'
|
3
|
+
|
4
|
+
def data_path(path)
|
5
|
+
File.expand_path(File.join(
|
6
|
+
File.expand_path('../../data', __FILE__),
|
7
|
+
path
|
8
|
+
))
|
9
|
+
end
|
10
|
+
|
11
|
+
def data_file(path)
|
12
|
+
File.read( data_path(path) )
|
13
|
+
end
|
14
|
+
|
15
|
+
describe 'VCard' do
|
16
|
+
|
17
|
+
should 'add a new field without parameters' do
|
18
|
+
v = VCardParser::VCard.new("3.0")
|
19
|
+
v.add_field("N", "Georges")
|
20
|
+
|
21
|
+
v['N'].value.should == 'Georges'
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'vCard 3.0' do
|
25
|
+
should 'parse simple card' do
|
26
|
+
vcards = VCardParser::VCard.parse( data_file('vcard3.0.vcf') )
|
27
|
+
vcards.size.should == 1
|
28
|
+
|
29
|
+
vcards[0].version.should == "3.0"
|
30
|
+
vcards[0]["PRODID"].value.should == "-//Apple Inc.//iOS 5.0.1//EN"
|
31
|
+
vcards[0]["N"].value.should == "Durand;Christophe;;;"
|
32
|
+
vcards[0]["FN"].value.should == "Christophe Durand"
|
33
|
+
vcards[0]["UID"].value.should == "11"
|
34
|
+
end
|
35
|
+
|
36
|
+
should 'parse multiple cards' do
|
37
|
+
vcards = VCardParser::VCard.parse( data_file('two_vcard3.0.vcf') )
|
38
|
+
vcards.size.should == 2
|
39
|
+
|
40
|
+
vcards[0].version.should == "3.0"
|
41
|
+
vcards[0]["PRODID"].value.should == "-//Apple Inc.//iOS 5.0.1//EN"
|
42
|
+
vcards[0]["N"].value.should == "Durand;Christophe;;;"
|
43
|
+
vcards[0]["FN"].value.should == "Christophe Durand"
|
44
|
+
vcards[0]["UID"].value.should == "11"
|
45
|
+
|
46
|
+
|
47
|
+
vcards[1].version.should == "3.0"
|
48
|
+
vcards[1]["N"].value.should == "Jean;René;;;"
|
49
|
+
vcards[1]["FN"].value.should == "Jean René"
|
50
|
+
vcards[1]["UID"].value.should == "12"
|
51
|
+
end
|
52
|
+
|
53
|
+
should 'enumerate attributes' do
|
54
|
+
vcards = VCardParser::VCard.parse( data_file('vcard3.0.vcf') )
|
55
|
+
vcards.size.should == 1
|
56
|
+
|
57
|
+
attrs = []
|
58
|
+
|
59
|
+
vcards[0].each_field do |a|
|
60
|
+
attrs << a
|
61
|
+
end
|
62
|
+
|
63
|
+
attrs.size.should == 11
|
64
|
+
end
|
65
|
+
|
66
|
+
should 'enumerate attributes' do
|
67
|
+
vcards = VCardParser::VCard.parse( data_file('vcard3.0.vcf') )
|
68
|
+
vcards.size.should == 1
|
69
|
+
|
70
|
+
attrs = []
|
71
|
+
|
72
|
+
vcards[0].each_field.sort_by(&:name).each do |a|
|
73
|
+
attrs << a
|
74
|
+
end
|
75
|
+
|
76
|
+
attrs[0].tap do |a|
|
77
|
+
a.group.should == "item2"
|
78
|
+
a.name.should == "ADR"
|
79
|
+
a.value.should == ";;3 rue du chat;Dris;;90880;FRANCE"
|
80
|
+
a.params.should == {'type' => ['HOME', 'pref']}
|
81
|
+
end
|
82
|
+
|
83
|
+
attrs[1].tap do |a|
|
84
|
+
a.name.should == "BDAY"
|
85
|
+
a.value.should == Time.parse('1900-01-01')
|
86
|
+
a.params.should == {'value' => ['date']}
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
attrs[2].tap do |a|
|
91
|
+
a.name.should == "FN"
|
92
|
+
a.value.should == "Christophe Durand"
|
93
|
+
a.params.should == {}
|
94
|
+
end
|
95
|
+
|
96
|
+
attrs[3].tap do |a|
|
97
|
+
a.name.should == "N"
|
98
|
+
a.value.should == "Durand;Christophe;;;"
|
99
|
+
a.params.should == {}
|
100
|
+
end
|
101
|
+
|
102
|
+
attrs[4].tap do |a|
|
103
|
+
a.name.should == "ORG"
|
104
|
+
a.value.should == "Op;"
|
105
|
+
a.params.should == {}
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
attrs[5].tap do |a|
|
110
|
+
a.name.should == "PRODID"
|
111
|
+
a.value.should == "-//Apple Inc.//iOS 5.0.1//EN"
|
112
|
+
a.params.should == {}
|
113
|
+
end
|
114
|
+
|
115
|
+
attrs[6].tap do |a|
|
116
|
+
a.name.should == "REV"
|
117
|
+
a.value.should == Time.parse("2012-10-31T16:08:22Z")
|
118
|
+
a.params.should == {}
|
119
|
+
end
|
120
|
+
|
121
|
+
attrs[7].tap do |a|
|
122
|
+
a.group.should == nil
|
123
|
+
a.name.should == "TEL"
|
124
|
+
a.value.should == "3 55"
|
125
|
+
a.params.should == {'type' => %w(CELL VOICE)}
|
126
|
+
end
|
127
|
+
|
128
|
+
attrs[8].tap do |a|
|
129
|
+
a.group.should == "item1"
|
130
|
+
a.name.should == "TEL"
|
131
|
+
a.value.should == "2 56 38 54"
|
132
|
+
a.params.should == {'type' => %w(pref)}
|
133
|
+
end
|
134
|
+
|
135
|
+
attrs[9].tap do |a|
|
136
|
+
a.group.should == "item2"
|
137
|
+
a.name.should == "TEL"
|
138
|
+
a.value.should == "5 66"
|
139
|
+
a.params.should == {'type' => %w(pref)}
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
attrs[10].tap do |a|
|
144
|
+
a.name.should == "UID"
|
145
|
+
a.value.should == "11"
|
146
|
+
a.params.should == {}
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
should "raise an error if version is not supported" do
|
152
|
+
->{
|
153
|
+
VCardParser::VCard.parse( data_file('vcard2.1.vcf') )
|
154
|
+
}.should.raise(VCardParser::UnsupportedVersion)
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
should 'parse and rebuild a full vcard' do
|
159
|
+
vcard_data = <<-EOS
|
160
|
+
BEGIN:VCARD
|
161
|
+
VERSION:3.0
|
162
|
+
PRODID:-//Apple Inc.//iOS 5.0.1//EN
|
163
|
+
N:Durand;Christophe;;;
|
164
|
+
FN:Christophe Durand
|
165
|
+
ORG:Op;
|
166
|
+
item1.TEL;type=pref:2 56 38 54
|
167
|
+
item2.TEL;type=pref:5 66
|
168
|
+
TEL;type=CELL;type=VOICE:3 55
|
169
|
+
item2.ADR;type=HOME;type=pref:;;3 rue du chat;Dris;;90880;FRANCE
|
170
|
+
BDAY;value=date:1900-01-01
|
171
|
+
REV:2012-10-31T16:08:22Z
|
172
|
+
UID:12
|
173
|
+
END:VCARD
|
174
|
+
EOS
|
175
|
+
|
176
|
+
cards = VCardParser::VCard.parse(vcard_data)
|
177
|
+
cards.size.should == 1
|
178
|
+
cards[0]['TEL', 'item1'].value.should == '2 56 38 54'
|
179
|
+
cards[0].to_s.should == vcard_data
|
180
|
+
end
|
181
|
+
|
182
|
+
should 'generate vcard from data' do
|
183
|
+
data = data_file('vcard3.0.vcf')
|
184
|
+
vcard = VCardParser::VCard.parse(data).first
|
185
|
+
vcard.vcard.should == data
|
186
|
+
vcard.to_s.should == data
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/vcard_parser/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Julien Ammous"]
|
6
|
+
gem.email = ["schmurfy@gmail.com"]
|
7
|
+
gem.description = %q{A vCard parser}
|
8
|
+
gem.summary = %q{A simple vCard parser}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.name = "vcard_parser"
|
14
|
+
gem.require_paths = ["lib"]
|
15
|
+
gem.version = VcardParser::VERSION
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vcard_parser
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Julien Ammous
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-02 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: A vCard parser
|
15
|
+
email:
|
16
|
+
- schmurfy@gmail.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- .gitignore
|
22
|
+
- .travis.yml
|
23
|
+
- Gemfile
|
24
|
+
- Guardfile
|
25
|
+
- LICENSE
|
26
|
+
- README.md
|
27
|
+
- Rakefile
|
28
|
+
- lib/vcard_parser.rb
|
29
|
+
- lib/vcard_parser/3.0/field.rb
|
30
|
+
- lib/vcard_parser/vcard.rb
|
31
|
+
- lib/vcard_parser/version.rb
|
32
|
+
- specs/data/two_vcard3.0.vcf
|
33
|
+
- specs/data/vcard2.1.vcf
|
34
|
+
- specs/data/vcard3.0.vcf
|
35
|
+
- specs/spec_helper.rb
|
36
|
+
- specs/unit/3.0/field_spec.rb
|
37
|
+
- specs/unit/vcard_spec.rb
|
38
|
+
- vcard_parser.gemspec
|
39
|
+
homepage: ''
|
40
|
+
licenses: []
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options: []
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
none: false
|
47
|
+
requirements:
|
48
|
+
- - ! '>='
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
51
|
+
segments:
|
52
|
+
- 0
|
53
|
+
hash: -4310828650863439750
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
hash: -4310828650863439750
|
63
|
+
requirements: []
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 1.8.24
|
66
|
+
signing_key:
|
67
|
+
specification_version: 3
|
68
|
+
summary: A simple vCard parser
|
69
|
+
test_files: []
|