contact_sport 1.0.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 +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +65 -0
- data/Rakefile +8 -0
- data/contact_sport.gemspec +23 -0
- data/lib/contact_sport/contact.rb +8 -0
- data/lib/contact_sport/encoding.rb +25 -0
- data/lib/contact_sport/outlook_reader.rb +145 -0
- data/lib/contact_sport/vcard_reader.rb +97 -0
- data/lib/contact_sport/version.rb +3 -0
- data/lib/contact_sport.rb +32 -0
- data/test/fixtures/iso_8859_1_commas.csv +5 -0
- data/test/fixtures/utf_16_le_tabs.csv +0 -0
- data/test/fixtures/vcards.vcf +26 -0
- data/test/outlook_reader_test.rb +74 -0
- data/test/test_helper.rb +7 -0
- data/test/vcard_reader_test.rb +45 -0
- metadata +107 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2013 Andy Stewart
|
|
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,65 @@
|
|
|
1
|
+
# ContactSport
|
|
2
|
+
|
|
3
|
+
Simple importing of contacts from Outlook and vCard.
|
|
4
|
+
|
|
5
|
+
`ContactSport` provides a simple way to read contacts exported from Outlook or vCard files. It came about because I needed to enable users of a webapp to import contacts from their existing desktop address books and email clients.
|
|
6
|
+
|
|
7
|
+
Outlook contacts should be exported as [Comma Separated Values (Windows)][outlook].
|
|
8
|
+
|
|
9
|
+
vCards can be exported from most other address books or email clients. Here are [instructions for Apple's Address Book][osx].
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
There is precisely one method in the API:
|
|
15
|
+
|
|
16
|
+
Assuming `contacts_file` is the name and path of your contacts file:
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
ContactSport.contacts(contacts_file)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
This returns an array of contacts, where a contact is an object responding to:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
:first_name
|
|
26
|
+
:last_name
|
|
27
|
+
:name
|
|
28
|
+
:email
|
|
29
|
+
:url
|
|
30
|
+
:office_phone
|
|
31
|
+
:mobile_phone
|
|
32
|
+
:fax
|
|
33
|
+
:company
|
|
34
|
+
:address1
|
|
35
|
+
:address2
|
|
36
|
+
:city
|
|
37
|
+
:region
|
|
38
|
+
:postcode
|
|
39
|
+
:country
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
All responses are strings. If a value is not set (e.g. a contact has no fax number) an empty string is returned.
|
|
43
|
+
|
|
44
|
+
Where there are work and home values, e.g. a work phone and a home phone, `ContactSport` will prefer the work phone.
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
$ gem install contact_sport
|
|
50
|
+
|
|
51
|
+
The code requires Ruby 1.9.
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
## Intellectual Property
|
|
55
|
+
|
|
56
|
+
Copyright [Andrew Stewart][boss], AirBlade Software Ltd.
|
|
57
|
+
|
|
58
|
+
Released under the MIT licence.
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
[outlook]: http://office.microsoft.com/en-us/outlook-help/export-contacts-HA101870639.aspx
|
|
62
|
+
[osx]: http://support.apple.com/kb/PH4655
|
|
63
|
+
[contact_csv]: https://github.com/mwhuss/contact_csv
|
|
64
|
+
[contactgems]: https://rubygems.org/search?query=contact
|
|
65
|
+
[boss]: mailto:boss@airbladesoftware.com
|
data/Rakefile
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'contact_sport/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |gem|
|
|
7
|
+
gem.name = "contact_sport"
|
|
8
|
+
gem.version = ContactSport::VERSION
|
|
9
|
+
gem.authors = ["Andy Stewart"]
|
|
10
|
+
gem.email = ["boss@airbladesoftware.com"]
|
|
11
|
+
gem.description = 'Simple importing of contacts from Outlook and vCard.'
|
|
12
|
+
gem.summary = gem.description
|
|
13
|
+
gem.homepage = ""
|
|
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
|
+
|
|
20
|
+
gem.add_dependency 'vcard', '~> 0.2'
|
|
21
|
+
|
|
22
|
+
gem.add_development_dependency 'rake', '~> 0.9.2'
|
|
23
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module ContactSport
|
|
2
|
+
module Encoding
|
|
3
|
+
|
|
4
|
+
# https://github.com/Manfred/Ensure-encoding/blob/master/lib/ensure/encoding.rb
|
|
5
|
+
# https://github.com/oleander/rchardet/blob/master/lib/rchardet/universaldetector.rb
|
|
6
|
+
BYTE_ORDER_MARKS = {
|
|
7
|
+
::Encoding::UTF_32BE => [0x00, 0x00, 0xfe, 0xff],
|
|
8
|
+
::Encoding::UTF_32LE => [0xff, 0xfe, 0x00, 0x00],
|
|
9
|
+
::Encoding::UTF_8 => [0xef, 0xbb, 0xbf],
|
|
10
|
+
::Encoding::UTF_16BE => [0xfe, 0xff],
|
|
11
|
+
::Encoding::UTF_16LE => [0xff, 0xfe],
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
FALLBACK_ENCODING = ::Encoding::ISO_8859_1
|
|
15
|
+
|
|
16
|
+
# Guesses the encoding of the given file.
|
|
17
|
+
def self.guess_encoding(file)
|
|
18
|
+
# First look for a byte order mark (BOM).
|
|
19
|
+
first_bytes = IO.read(file, 4).bytes.to_a
|
|
20
|
+
bom = BYTE_ORDER_MARKS.detect { |encoding, bytes| first_bytes[0...bytes.length] == bytes }
|
|
21
|
+
bom ? bom.first : FALLBACK_ENCODING
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
require 'csv'
|
|
2
|
+
require 'contact_sport/contact'
|
|
3
|
+
require 'contact_sport/encoding'
|
|
4
|
+
|
|
5
|
+
module ContactSport
|
|
6
|
+
|
|
7
|
+
# To read an Outlook "Comma Separated Values (Windows)" file we need to guess:
|
|
8
|
+
#
|
|
9
|
+
# * the file's encoding
|
|
10
|
+
# * the column separator
|
|
11
|
+
# * the column headings
|
|
12
|
+
#
|
|
13
|
+
# These all vary by Outlook version, platform, and phase of the moon.
|
|
14
|
+
#
|
|
15
|
+
# Some example contact headings are given here:
|
|
16
|
+
#
|
|
17
|
+
# * http://stackoverflow.com/questions/4847596/what-are-the-csv-headers-in-outlook-contact-export
|
|
18
|
+
# * http://lists.lrug.org/pipermail/chat-lrug.org/2013-February/008474.html
|
|
19
|
+
class OutlookReader
|
|
20
|
+
attr_reader :file, :file_encoding, :column_separator
|
|
21
|
+
|
|
22
|
+
def initialize(file)
|
|
23
|
+
@file = file
|
|
24
|
+
@file_encoding = ContactSport::Encoding.guess_encoding file
|
|
25
|
+
@column_separator = guess_column_separator
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def contacts
|
|
29
|
+
begin
|
|
30
|
+
@contacts ||= begin
|
|
31
|
+
results = []
|
|
32
|
+
CSV.foreach(file, headers: true,
|
|
33
|
+
col_sep: column_separator,
|
|
34
|
+
encoding: "#{file_encoding.to_s}:UTF-8") do |row|
|
|
35
|
+
# Headers:
|
|
36
|
+
results << Contact.new(
|
|
37
|
+
first_name: first_name(row),
|
|
38
|
+
last_name: last_name(row),
|
|
39
|
+
name: name(row),
|
|
40
|
+
|
|
41
|
+
email: email(row),
|
|
42
|
+
url: web_page(row),
|
|
43
|
+
|
|
44
|
+
company: company(row),
|
|
45
|
+
office_phone: office_phone(row),
|
|
46
|
+
mobile_phone: mobile_phone(row),
|
|
47
|
+
fax: fax(row),
|
|
48
|
+
|
|
49
|
+
address1: street(row),
|
|
50
|
+
address2: street2(row),
|
|
51
|
+
city: city(row),
|
|
52
|
+
region: state(row),
|
|
53
|
+
postcode: postcode(row),
|
|
54
|
+
country: country(row)
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
results
|
|
58
|
+
end
|
|
59
|
+
rescue EncodingError => e
|
|
60
|
+
raise ContactSport::EncodingError, e.message
|
|
61
|
+
rescue StandardError => e
|
|
62
|
+
raise ContactSport::FormatError, e.message
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def guess_column_separator
|
|
69
|
+
first_line = File.open file, encoding: "#{file_encoding.to_s}:UTF-8", &:readline
|
|
70
|
+
comma_count = first_line.count ','
|
|
71
|
+
tab_count = first_line.count '\t'
|
|
72
|
+
comma_count > tab_count ? ',' : "\t"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def find_first(row, *fields)
|
|
76
|
+
fields.each do |field|
|
|
77
|
+
if result = row[field]
|
|
78
|
+
return result
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
EMPTY_FIELD
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def first_name(row)
|
|
85
|
+
find_first row, 'First Name'
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def last_name(row)
|
|
89
|
+
find_first row, 'Last Name'
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def street(row)
|
|
93
|
+
find_first row, 'Business Street', 'Home Street', 'Work Street Address', 'Home Street Address'
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def street2(row)
|
|
97
|
+
find_first row, 'Business Street 2', 'Home Street 2', 'Work Street Address 2', 'Home Street Address 2'
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def city(row)
|
|
101
|
+
find_first row, 'Business City', 'Home City', 'Work City'
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def state(row)
|
|
105
|
+
find_first row, 'Business State', 'Home State', 'Work State'
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def postcode(row)
|
|
109
|
+
find_first row, 'Business Postal Code', 'Home Postal Code', 'Work Zip', 'Home Zip'
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def country(row)
|
|
113
|
+
find_first row, 'Business Country', 'Home Country', 'Business Country/Region', 'Work Country/Region', 'Home Country/Region'
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def fax(row)
|
|
117
|
+
find_first row, 'Business Fax', 'Work Fax', 'Home Fax'
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def web_page(row)
|
|
121
|
+
find_first row, 'Web Page'
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def email(row)
|
|
125
|
+
find_first row, 'E-mail Address', 'Email Address 1'
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def office_phone(row)
|
|
129
|
+
find_first row, 'Work Phone 1', 'Home Phone 1', 'Business Phone', 'Home Phone'
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def mobile_phone(row)
|
|
133
|
+
find_first row, 'Mobile Phone'
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def company(row)
|
|
137
|
+
find_first row, 'Company'
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def name(row)
|
|
141
|
+
[ row['First Name'], row['Last Name'] ].compact.join(' ') || EMPTY_FIELD
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require 'vcard'
|
|
2
|
+
require 'contact_sport/contact'
|
|
3
|
+
|
|
4
|
+
module ContactSport
|
|
5
|
+
|
|
6
|
+
class VcardReader
|
|
7
|
+
attr_reader :file
|
|
8
|
+
|
|
9
|
+
def initialize(file)
|
|
10
|
+
@file = file
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def contacts
|
|
14
|
+
begin
|
|
15
|
+
@contacts ||= Vcard::Vcard.decode(contents).map do |card|
|
|
16
|
+
Contact.new(
|
|
17
|
+
first_name: card.name.given,
|
|
18
|
+
last_name: card.name.family,
|
|
19
|
+
name: card.name.fullname,
|
|
20
|
+
|
|
21
|
+
email: email(card),
|
|
22
|
+
url: url(card),
|
|
23
|
+
|
|
24
|
+
company: company(card),
|
|
25
|
+
office_phone: work_phone(card),
|
|
26
|
+
mobile_phone: mobile_phone(card),
|
|
27
|
+
fax: fax(card),
|
|
28
|
+
|
|
29
|
+
address1: address_first_line(card),
|
|
30
|
+
address2: address_second_line(card),
|
|
31
|
+
city: address(:locality, card),
|
|
32
|
+
region: address(:region, card),
|
|
33
|
+
postcode: address(:postalcode, card),
|
|
34
|
+
country: address(:country, card)
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
rescue Vcard::InvalidEncodingError, Vcard::UnsupportedError, Vcard::Unencodeable => e
|
|
38
|
+
raise ContactSport::FormatError, e.message
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def contents
|
|
45
|
+
File.read file
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def company(card)
|
|
49
|
+
card.org ? card.org.first : EMPTY_FIELD
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def address(field, card)
|
|
53
|
+
card.address ? card.address.send(field) : EMPTY_FIELD
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def address_first_line(card)
|
|
57
|
+
if card.address && card.address.street
|
|
58
|
+
card.address.street.split("\n").first
|
|
59
|
+
else
|
|
60
|
+
EMPTY_FIELD
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def address_second_line(card)
|
|
65
|
+
if card.address && card.address.street
|
|
66
|
+
card.address.street.split("\n")[1..-1].join "\n"
|
|
67
|
+
else
|
|
68
|
+
EMPTY_FIELD
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def email(card)
|
|
73
|
+
card.email ? card.email.to_s : EMPTY_FIELD
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def url(card)
|
|
77
|
+
card.url ? card.url.uri.sub('http\\','http') : EMPTY_FIELD
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def work_phone(card)
|
|
81
|
+
tel = card.telephones.detect { |t| t.location.any? { |l| l =~ /work/i } } || card.telephone
|
|
82
|
+
tel ? tel.to_s : EMPTY_FIELD
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def mobile_phone(card)
|
|
86
|
+
tel = card.telephones.detect { |t| t.location.any? { |l| l =~ /cell/i } }
|
|
87
|
+
tel ? tel.to_s : EMPTY_FIELD
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def fax(card)
|
|
91
|
+
fax = card.telephones.detect { |t| t.capability.any? { |c| c =~ /fax/i } }
|
|
92
|
+
fax ? fax.to_s : EMPTY_FIELD
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require "contact_sport/version"
|
|
2
|
+
require 'contact_sport/vcard_reader'
|
|
3
|
+
require 'contact_sport/outlook_reader'
|
|
4
|
+
|
|
5
|
+
module ContactSport
|
|
6
|
+
|
|
7
|
+
# Raised when a contacts file doesn't appear to be Outlook CSV or vCard.
|
|
8
|
+
UnknownProviderError = Class.new RuntimeError
|
|
9
|
+
|
|
10
|
+
# Raised when a contacts file cannot be parsed.
|
|
11
|
+
FormatError = Class.new RuntimeError
|
|
12
|
+
|
|
13
|
+
# Raised when a contacts file is in an unknown encoding.
|
|
14
|
+
EncodingError = Class.new RuntimeError
|
|
15
|
+
|
|
16
|
+
EMPTY_FIELD = ''
|
|
17
|
+
|
|
18
|
+
def self.contacts(file)
|
|
19
|
+
reader(file).contacts
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def self.reader(file)
|
|
25
|
+
case File.extname(file)
|
|
26
|
+
when /[.]vcf$/i; VcardReader.new(file)
|
|
27
|
+
when /[.]csv$/i; OutlookReader.new(file)
|
|
28
|
+
else raise UnknownProviderError
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"Title","First Name","Middle Name","Last Name","Suffix","Company","Department","Job Title","Business Street","Business Street 2","Business Street 3","Business City","Business State","Business Postal Code","Business Country/Region","Home Street","Home Street 2","Home Street 3","Home City","Home State","Home Postal Code","Home Country/Region","Other Street","Other Street 2","Other Street 3","Other City","Other State","Other Postal Code","Other Country/Region","Assistant's Phone","Business Fax","Business Phone","Business Phone 2","Callback","Car Phone","Company Main Phone","Home Fax","Home Phone","Home Phone 2","ISDN","Mobile Phone","Other Fax","Other Phone","Pager","Primary Phone","Radio Phone","TTY/TDD Phone","Telex","Account","Anniversary","Assistant's Name","Billing Information","Birthday","Business Address PO Box","Categories","Children","Directory Server","E-mail Address","E-mail Type","E-mail Display Name","E-mail 2 Address","E-mail 2 Type","E-mail 2 Display Name","E-mail 3 Address","E-mail 3 Type","E-mail 3 Display Name","Gender","Government ID Number","Hobby","Home Address PO Box","Initials","Internet Free Busy","Keywords","Language","Location","Manager's Name","Mileage","Notes","Office Location","Organizational ID Number","Other Address PO Box","Priority","Private","Profession","Referred By","Sensitivity","Spouse","User 1","User 2","User 3","User 4","Web Page"
|
|
2
|
+
"","Bruce","","Wayne",,"Wayne Enterprises",,,"1st Floor","1 High Street",,"London","Middlesex","W1 1AB","United Kingdom",,,,,,,,,,,,,,,,"020 7123 4560","020 7123 4567",,,,,,,,,"07979 797 979",,,,,,,,,,,,,,,,,"b.wayne@example.com","SMTP",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"http://example.com"
|
|
3
|
+
"","James","","Bond",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,""
|
|
4
|
+
"","Clarke","","Kent",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"
|
|
5
|
+
",,,,,,,,,""
|
|
Binary file
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
BEGIN:VCARD
|
|
2
|
+
VERSION:3.0
|
|
3
|
+
N:;;;;
|
|
4
|
+
FN:Wayne Enterprises
|
|
5
|
+
ORG:Wayne Enterprises;
|
|
6
|
+
EMAIL;type=INTERNET;type=WORK;type=pref:info@wayneenterprises.com
|
|
7
|
+
X-ABShowAs:COMPANY
|
|
8
|
+
CATEGORIES:Test
|
|
9
|
+
X-ABUID:C9C08B97-1889-4D67-9595-C12482BF3B5F\:ABPerson
|
|
10
|
+
END:VCARD
|
|
11
|
+
BEGIN:VCARD
|
|
12
|
+
VERSION:3.0
|
|
13
|
+
N:Wayne;Bruce;;;
|
|
14
|
+
FN:Bruce Wayne
|
|
15
|
+
ORG:Wayne Enterprises;
|
|
16
|
+
EMAIL;type=INTERNET;type=WORK;type=pref:bruce@wayneenterprises.com
|
|
17
|
+
TEL;type=WORK;type=pref:+44 20 7123 4567
|
|
18
|
+
TEL;type=CELL:07777 999 888
|
|
19
|
+
TEL;type=HOME;type=FAX:+44 20 71234 4560
|
|
20
|
+
item1.ADR;type=WORK;type=pref:;;The Batcave;Gotham;NY;90210;USA
|
|
21
|
+
item1.X-ABADR:gb
|
|
22
|
+
item2.URL;type=pref:www.batman.com
|
|
23
|
+
item2.X-ABLabel:_$!<HomePage>!$_
|
|
24
|
+
CATEGORIES:Test
|
|
25
|
+
X-ABUID:3852D6F6-6DDB-441C-8CE7-6594B0112C3D\:ABPerson
|
|
26
|
+
END:VCARD
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
require 'minitest/autorun'
|
|
2
|
+
require 'test_helper'
|
|
3
|
+
require 'contact_sport'
|
|
4
|
+
|
|
5
|
+
class OutlookReaderTest < MiniTest::Unit::TestCase
|
|
6
|
+
|
|
7
|
+
def test_iso_8859_1_commas
|
|
8
|
+
contacts = ContactSport.contacts fixture('iso_8859_1_commas.csv')
|
|
9
|
+
assert_equal 3, contacts.length
|
|
10
|
+
|
|
11
|
+
contact = contacts.shift
|
|
12
|
+
assert_equal 'Bruce', contact.first_name
|
|
13
|
+
assert_equal 'Wayne', contact.last_name
|
|
14
|
+
assert_equal 'Bruce Wayne', contact.name
|
|
15
|
+
assert_equal 'b.wayne@example.com', contact.email
|
|
16
|
+
assert_equal 'http://example.com', contact.url
|
|
17
|
+
assert_equal '020 7123 4567', contact.office_phone
|
|
18
|
+
assert_equal '07979 797 979', contact.mobile_phone
|
|
19
|
+
assert_equal '020 7123 4560', contact.fax
|
|
20
|
+
assert_equal 'Wayne Enterprises', contact.company
|
|
21
|
+
assert_equal '1st Floor', contact.address1
|
|
22
|
+
assert_equal '1 High Street', contact.address2
|
|
23
|
+
assert_equal 'London', contact.city
|
|
24
|
+
assert_equal 'Middlesex', contact.region
|
|
25
|
+
assert_equal 'W1 1AB', contact.postcode
|
|
26
|
+
assert_equal 'United Kingdom', contact.country
|
|
27
|
+
|
|
28
|
+
contact = contacts.shift
|
|
29
|
+
assert_equal 'James Bond', contact.name
|
|
30
|
+
|
|
31
|
+
contact = contacts.shift
|
|
32
|
+
assert_equal 'Clarke Kent', contact.name
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_utf_16_le_tabs
|
|
36
|
+
contacts = ContactSport.contacts fixture('utf_16_le_tabs.csv')
|
|
37
|
+
assert_equal 6, contacts.length
|
|
38
|
+
|
|
39
|
+
contact = contacts.shift
|
|
40
|
+
assert_equal 'Andrew', contact.first_name
|
|
41
|
+
assert_equal 'Stewart', contact.last_name
|
|
42
|
+
assert_equal 'Andrew Stewart', contact.name
|
|
43
|
+
assert_equal 'andrew@home.de', contact.email
|
|
44
|
+
assert_equal 'http://www.mcdonalds.com/staff/Andrew', contact.url
|
|
45
|
+
assert_equal '4420812345', contact.office_phone
|
|
46
|
+
assert_equal '49123456789', contact.mobile_phone
|
|
47
|
+
assert_equal '', contact.fax
|
|
48
|
+
assert_equal 'McDonalds Corporation', contact.company
|
|
49
|
+
assert_equal '1 Leicester Square', contact.address1
|
|
50
|
+
assert_equal '', contact.address2
|
|
51
|
+
assert_equal 'London', contact.city
|
|
52
|
+
assert_equal '', contact.region
|
|
53
|
+
assert_equal 'WC1A 1AA', contact.postcode
|
|
54
|
+
assert_equal '', contact.country
|
|
55
|
+
|
|
56
|
+
contact = contacts.pop
|
|
57
|
+
assert_equal '', contact.first_name
|
|
58
|
+
assert_equal '', contact.last_name
|
|
59
|
+
assert_equal '', contact.name
|
|
60
|
+
assert_equal 'info@mcdonalds.com', contact.email
|
|
61
|
+
assert_equal 'http://www.mcdonalds.com', contact.url
|
|
62
|
+
assert_equal '', contact.office_phone
|
|
63
|
+
assert_equal '', contact.mobile_phone
|
|
64
|
+
assert_equal '800234567', contact.fax
|
|
65
|
+
assert_equal 'McDonalds Corporation', contact.company
|
|
66
|
+
assert_equal '1 Canary Wharf', contact.address1
|
|
67
|
+
assert_equal '', contact.address2
|
|
68
|
+
assert_equal 'London', contact.city
|
|
69
|
+
assert_equal 'E1 1AA', contact.region # data-entry mistake
|
|
70
|
+
assert_equal '', contact.postcode # data-entry mistake
|
|
71
|
+
assert_equal '', contact.country
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
end
|
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'minitest/autorun'
|
|
2
|
+
require 'test_helper'
|
|
3
|
+
require 'contact_sport'
|
|
4
|
+
|
|
5
|
+
class VcardReaderTest < MiniTest::Unit::TestCase
|
|
6
|
+
|
|
7
|
+
def test_foo
|
|
8
|
+
contacts = ContactSport.contacts fixture('vcards.vcf')
|
|
9
|
+
assert_equal 2, contacts.length
|
|
10
|
+
|
|
11
|
+
contact = contacts.shift
|
|
12
|
+
assert_equal '', contact.first_name
|
|
13
|
+
assert_equal '', contact.last_name
|
|
14
|
+
assert_equal 'Wayne Enterprises', contact.name
|
|
15
|
+
assert_equal 'info@wayneenterprises.com', contact.email
|
|
16
|
+
assert_equal '', contact.url
|
|
17
|
+
assert_equal '', contact.office_phone
|
|
18
|
+
assert_equal '', contact.mobile_phone
|
|
19
|
+
assert_equal '', contact.fax
|
|
20
|
+
assert_equal 'Wayne Enterprises', contact.company
|
|
21
|
+
assert_equal '', contact.address1
|
|
22
|
+
assert_equal '', contact.address2
|
|
23
|
+
assert_equal '', contact.city
|
|
24
|
+
assert_equal '', contact.region
|
|
25
|
+
assert_equal '', contact.postcode
|
|
26
|
+
assert_equal '', contact.country
|
|
27
|
+
|
|
28
|
+
contact = contacts.shift
|
|
29
|
+
assert_equal 'Bruce', contact.first_name
|
|
30
|
+
assert_equal 'Wayne', contact.last_name
|
|
31
|
+
assert_equal 'Bruce Wayne', contact.name
|
|
32
|
+
assert_equal 'bruce@wayneenterprises.com', contact.email
|
|
33
|
+
assert_equal 'www.batman.com', contact.url
|
|
34
|
+
assert_equal '+44 20 7123 4567', contact.office_phone
|
|
35
|
+
assert_equal '07777 999 888', contact.mobile_phone
|
|
36
|
+
assert_equal '+44 20 71234 4560', contact.fax
|
|
37
|
+
assert_equal 'Wayne Enterprises', contact.company
|
|
38
|
+
assert_equal 'The Batcave', contact.address1
|
|
39
|
+
assert_equal '', contact.address2
|
|
40
|
+
assert_equal 'Gotham', contact.city
|
|
41
|
+
assert_equal 'NY', contact.region
|
|
42
|
+
assert_equal '90210', contact.postcode
|
|
43
|
+
assert_equal 'USA', contact.country
|
|
44
|
+
end
|
|
45
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: contact_sport
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Andy Stewart
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2013-02-25 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: vcard
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ~>
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '0.2'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
26
|
+
requirements:
|
|
27
|
+
- - ~>
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '0.2'
|
|
30
|
+
- !ruby/object:Gem::Dependency
|
|
31
|
+
name: rake
|
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
|
33
|
+
none: false
|
|
34
|
+
requirements:
|
|
35
|
+
- - ~>
|
|
36
|
+
- !ruby/object:Gem::Version
|
|
37
|
+
version: 0.9.2
|
|
38
|
+
type: :development
|
|
39
|
+
prerelease: false
|
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
41
|
+
none: false
|
|
42
|
+
requirements:
|
|
43
|
+
- - ~>
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: 0.9.2
|
|
46
|
+
description: Simple importing of contacts from Outlook and vCard.
|
|
47
|
+
email:
|
|
48
|
+
- boss@airbladesoftware.com
|
|
49
|
+
executables: []
|
|
50
|
+
extensions: []
|
|
51
|
+
extra_rdoc_files: []
|
|
52
|
+
files:
|
|
53
|
+
- .gitignore
|
|
54
|
+
- Gemfile
|
|
55
|
+
- LICENSE.txt
|
|
56
|
+
- README.md
|
|
57
|
+
- Rakefile
|
|
58
|
+
- contact_sport.gemspec
|
|
59
|
+
- lib/contact_sport.rb
|
|
60
|
+
- lib/contact_sport/contact.rb
|
|
61
|
+
- lib/contact_sport/encoding.rb
|
|
62
|
+
- lib/contact_sport/outlook_reader.rb
|
|
63
|
+
- lib/contact_sport/vcard_reader.rb
|
|
64
|
+
- lib/contact_sport/version.rb
|
|
65
|
+
- test/fixtures/iso_8859_1_commas.csv
|
|
66
|
+
- test/fixtures/utf_16_le_tabs.csv
|
|
67
|
+
- test/fixtures/vcards.vcf
|
|
68
|
+
- test/outlook_reader_test.rb
|
|
69
|
+
- test/test_helper.rb
|
|
70
|
+
- test/vcard_reader_test.rb
|
|
71
|
+
homepage: ''
|
|
72
|
+
licenses: []
|
|
73
|
+
post_install_message:
|
|
74
|
+
rdoc_options: []
|
|
75
|
+
require_paths:
|
|
76
|
+
- lib
|
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
78
|
+
none: false
|
|
79
|
+
requirements:
|
|
80
|
+
- - ! '>='
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
segments:
|
|
84
|
+
- 0
|
|
85
|
+
hash: -3767443409390475510
|
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
87
|
+
none: false
|
|
88
|
+
requirements:
|
|
89
|
+
- - ! '>='
|
|
90
|
+
- !ruby/object:Gem::Version
|
|
91
|
+
version: '0'
|
|
92
|
+
segments:
|
|
93
|
+
- 0
|
|
94
|
+
hash: -3767443409390475510
|
|
95
|
+
requirements: []
|
|
96
|
+
rubyforge_project:
|
|
97
|
+
rubygems_version: 1.8.23
|
|
98
|
+
signing_key:
|
|
99
|
+
specification_version: 3
|
|
100
|
+
summary: Simple importing of contacts from Outlook and vCard.
|
|
101
|
+
test_files:
|
|
102
|
+
- test/fixtures/iso_8859_1_commas.csv
|
|
103
|
+
- test/fixtures/utf_16_le_tabs.csv
|
|
104
|
+
- test/fixtures/vcards.vcf
|
|
105
|
+
- test/outlook_reader_test.rb
|
|
106
|
+
- test/test_helper.rb
|
|
107
|
+
- test/vcard_reader_test.rb
|