pp-contacts 1.5.1 → 1.5.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ .DS_Store
2
+ pkg/*
3
+ test/accounts.yml
4
+ .rvmrc
5
+ .idea
6
+ *.swp
7
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ # A sample Gemfile
2
+ source "http://rubygems.org"
3
+
4
+ gem "rake"
5
+ gem 'gdata19', :git => 'git://github.com/paperlesspost/gdata.git'
6
+ gem 'hpricot'
7
+ gem 'nokogiri'
8
+ gem "json", '> 0.4.1'
9
+
10
+ group :development do
11
+ gem 'gem-release'
12
+ end
13
+
14
+ group :test do
15
+ gem 'shoulda'
16
+ end
@@ -0,0 +1,27 @@
1
+ GIT
2
+ remote: git://github.com/paperlesspost/gdata.git
3
+ revision: 91f3a5b2e701c879ae5dfcb48dec5bb039e37a7b
4
+ specs:
5
+ gdata19 (0.1.9.2)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ gem-release (0.3.1)
11
+ hpricot (0.8.4)
12
+ json (1.5.3)
13
+ nokogiri (1.5.2)
14
+ rake (0.9.2)
15
+ shoulda (2.11.3)
16
+
17
+ PLATFORMS
18
+ ruby
19
+
20
+ DEPENDENCIES
21
+ gdata19!
22
+ gem-release
23
+ hpricot
24
+ json (> 0.4.1)
25
+ nokogiri
26
+ rake
27
+ shoulda
@@ -0,0 +1,16 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "pp-contacts"
3
+ s.version = "1.5.2"
4
+ s.date = "2012-04-26"
5
+ s.summary = "A universal interface to grab contact list information from various providers including Outlook, Address Book, Yahoo, AOL, Gmail, Hotmail, and Plaxo."
6
+ s.homepage = "http://github.com/paperlesspost/contacts"
7
+ s.description = "A universal interface to grab contact list information from various providers including Outlook, Address Book, Yahoo, AOL, Gmail, Hotmail, and Plaxo."
8
+ s.has_rdoc = false
9
+ s.authors = ["Lucas Carlson", "Paperless"]
10
+ s.files = `git ls-files`.split($\)
11
+ s.add_dependency("json", ">= 1.1.1")
12
+ s.add_dependency('gdata19')
13
+ s.add_dependency('hpricot')
14
+ s.add_dependency('nokogiri')
15
+ s.add_dependency('encryptor')
16
+ end
@@ -0,0 +1,22 @@
1
+ # Project-specific configuration for CruiseControl.rb
2
+
3
+ Project.configure do |project|
4
+
5
+ # Send email notifications about broken and fixed builds to email1@your.site, email2@your.site (default: send to nobody)
6
+ # if building this on your own CI box, please remove!
7
+ project.email_notifier.emails = ['opensource@pivotallabs.com']
8
+
9
+ # Set email 'from' field to john@doe.com:
10
+ # project.email_notifier.from = 'john@doe.com'
11
+
12
+ # Build the project by invoking rake task 'custom'
13
+ # project.rake_task = 'custom'
14
+
15
+ # Build the project by invoking shell script "build_my_app.sh". Keep in mind that when the script is invoked, current working directory is
16
+ # [cruise]/projects/your_project/work, so if you do not keep build_my_app.sh in version control, it should be '../build_my_app.sh' instead
17
+ # project.build_command = 'build_my_app.sh'
18
+
19
+ # Ping Subversion for new revisions every 5 minutes (default: 30 seconds)
20
+ # project.scheduler.polling_interval = 5.minutes
21
+
22
+ end
@@ -0,0 +1,8 @@
1
+ ---
2
+ defaults:
3
+ install_options: --no-ri --no-rdoc
4
+ gems:
5
+ - name: json
6
+ version: >= 1.1.1
7
+ - name: gdata
8
+ version: >= 1.1.1
@@ -10,7 +10,6 @@ require "erb"
10
10
  class Contacts
11
11
  TYPES = {}
12
12
  FILETYPES = {}
13
- VERSION = "1.4.1"
14
13
 
15
14
  class Base
16
15
  def initialize(login, password, options={})
@@ -0,0 +1,59 @@
1
+ require 'csv'
2
+
3
+ class Contacts
4
+ class Outlook < Base
5
+
6
+ def initialize(file)
7
+ @contact_file = Array.new
8
+ file = file.respond_to?(:read) ? file.read : file
9
+ file.each_line do |line|
10
+ @contact_file << CSV.parse(line)[0]
11
+ end
12
+ @full_name = false
13
+ @header_indexes = Hash.new
14
+ @header_indexes[:email_address] = Array.new
15
+
16
+ headers = @contact_file[0]
17
+
18
+ @contact_file = @contact_file[1, @contact_file.length]
19
+
20
+ headers.each_with_index do |header, i|
21
+ if header.match(/^Name$/)
22
+ @full_name = true
23
+ @header_indexes[:full_name] = i
24
+ elsif header.match(/^First Name/)
25
+ @header_indexes[:first_name] = i
26
+ elsif header.match(/^Last Name/)
27
+ @header_indexes[:last_name] = i
28
+ elsif header.match(/E-mail/)
29
+ @header_indexes[:email_address] << i
30
+ end
31
+ end
32
+ end
33
+
34
+ def contacts
35
+
36
+ contacts = Array.new
37
+
38
+ @contact_file.each_with_index do |line, i|
39
+ contacts[i] = Array.new unless contacts[i]
40
+ if(@full_name)
41
+ contacts[i][0] = line[@header_indexes[:full_name]]
42
+ else
43
+ contacts[i][0] = "#{line[@header_indexes[:first_name]]} #{line[@header_indexes[:last_name]]}"
44
+ end
45
+ @header_indexes[:email_address].each do |index|
46
+ if line[index] && !contacts[i][1]
47
+ contacts[i][1] = line[index]
48
+ end
49
+ end
50
+ end
51
+
52
+ contacts
53
+ end
54
+
55
+ end
56
+
57
+ private
58
+ FILETYPES[:outlook] = Outlook
59
+ end
@@ -0,0 +1,25 @@
1
+ class Contacts
2
+ class Vcf < Base
3
+
4
+ def initialize(file)
5
+ @contact_file = file
6
+ end
7
+
8
+ def contacts
9
+ contacts = Array.new
10
+ i = 0
11
+ @contact_file.each do |line|
12
+ contacts[i] = Array.new unless contacts[i]
13
+ if line.match(/FN:/)
14
+ contacts[i] << line.gsub(/FN:/, '').strip
15
+ elsif line.match(/EMAIL;/)
16
+ contacts[i] << line.gsub(/^.*:/, '').strip
17
+ elsif line.match(/END:VCARD/)
18
+ i += 1
19
+ end
20
+ end
21
+ contacts
22
+ end
23
+ end
24
+ FILETYPES[:vcf] = Vcf
25
+ end
@@ -0,0 +1,40 @@
1
+ gmail:
2
+ username: <changeme>
3
+ password: <changeme>
4
+ contacts:
5
+ -
6
+ name: "FirstName1 LastName1"
7
+ email_address: "firstname1@example.com"
8
+ -
9
+ name: "FirstName2 LastName2"
10
+ email_address: "firstname2@example.com"
11
+ yahoo:
12
+ username: <changeme>
13
+ password: <changeme>
14
+ contacts:
15
+ -
16
+ name: "FirstName1 LastName1"
17
+ email_address: "firstname1@example.com"
18
+ -
19
+ name: "FirstName2 LastName2"
20
+ email_address: "firstname2@example.com"
21
+ hotmail:
22
+ username: <changeme>
23
+ password: <changeme>
24
+ contacts:
25
+ -
26
+ name: "FirstName1 LastName1"
27
+ email_address: "firstname1@example.com"
28
+ -
29
+ name: "FirstName2 LastName2"
30
+ email_address: "firstname2@example.com"
31
+ aol:
32
+ username: <changeme>
33
+ password: <changeme>
34
+ contacts:
35
+ -
36
+ name: "FirstName1 LastName1"
37
+ email_address: "firstname1@example.com"
38
+ -
39
+ name: "FirstName2 LastName2"
40
+ email_address: "firstname2@example.com"
@@ -0,0 +1,3 @@
1
+ First Name,Last Name,E-mail
2
+ FirstName1,LastName1,firstname1@example.com
3
+ FirstName2,LastName2,firstname2@example.com
@@ -0,0 +1,3 @@
1
+ First Name,Last Name,E-mail
2
+ FirstName1,LastName1,firstname1@example.com
3
+ FirstName2,LastName2,firstname2@example.com
@@ -0,0 +1,32 @@
1
+ dir = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift(dir + "/../lib/")
3
+ require 'test/unit'
4
+ require 'contacts'
5
+ require 'yaml'
6
+ require 'shoulda'
7
+
8
+ class ContactImporterTestCase < Test::Unit::TestCase
9
+ # Add more helper methods to be used by all tests here...
10
+ def default_test
11
+ assert true
12
+ end
13
+ end
14
+
15
+ class TestAccounts
16
+ def self.[](type)
17
+ load[type]
18
+ end
19
+
20
+ def self.load(file = File.dirname(__FILE__) + "/accounts.yml")
21
+ raise "/test/accounts.yml file not found, please create, see /test/example_accounts.yml for information" unless File.exist?(file)
22
+
23
+ accounts = {}
24
+ YAML::load(File.open(file)).each do |type, contents|
25
+ contacts = contents["contacts"].collect {|contact| [contact["name"], contact["email_address"]]}
26
+ accounts[type.to_sym] = Account.new(type.to_sym, contents["username"], contents["password"], contacts)
27
+ end
28
+ accounts
29
+ end
30
+
31
+ Account = Struct.new :type, :username, :password, :contacts
32
+ end
@@ -0,0 +1,4 @@
1
+ dir = File.dirname(__FILE__)
2
+ Dir["#{dir}/**/*_test.rb"].each do |file|
3
+ require file
4
+ end
@@ -0,0 +1,33 @@
1
+ dir = File.dirname(__FILE__)
2
+ require "#{dir}/../test_helper"
3
+ require 'contacts'
4
+
5
+ class AolContactImporterTest < ContactImporterTestCase
6
+ def setup
7
+ super
8
+ @account = TestAccounts[:aol]
9
+ end
10
+
11
+ def test_successful_login
12
+ Contacts.new(:aolImporter, @account.username, @account.password)
13
+ end
14
+
15
+ def test_importer_fails_with_blank_password
16
+ assert_raise(Contacts::AuthenticationError) do
17
+ Contacts.new(:aolImporter, @account.username, "")
18
+ end
19
+ end
20
+
21
+ def test_importer_fails_with_blank_username
22
+ assert_raise(Contacts::AuthenticationError) do
23
+ Contacts.new(:aolImporter, "", @account.password)
24
+ end
25
+ end
26
+
27
+ def test_fetch_contacts
28
+ contacts = Contacts.new(:aolImporter, @account.username, @account.password).contacts
29
+ @account.contacts.each do |contact|
30
+ assert contacts.include?(contact), "Could not find: #{contact.inspect} in #{contacts.inspect}"
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,39 @@
1
+ dir = File.dirname(__FILE__)
2
+ require "#{dir}/../test_helper"
3
+ require 'contacts'
4
+
5
+ class GmailContactImporterTest < ContactImporterTestCase
6
+ def setup
7
+ super
8
+ @account = TestAccounts[:gmail]
9
+ end
10
+
11
+ def test_successful_login
12
+ Contacts.new(:gmail, @account.username, @account.password)
13
+ end
14
+
15
+ def test_importer_fails_with_invalid_password
16
+ assert_raise(Contacts::AuthenticationError) do
17
+ Contacts.new(:gmail, @account.username, "wrong_password")
18
+ end
19
+ end
20
+
21
+ def test_importer_fails_with_blank_password
22
+ assert_raise(Contacts::AuthenticationError) do
23
+ Contacts.new(:gmail, @account.username, "")
24
+ end
25
+ end
26
+
27
+ def test_importer_fails_with_blank_username
28
+ assert_raise(Contacts::AuthenticationError) do
29
+ Contacts.new(:gmail, "", @account.password)
30
+ end
31
+ end
32
+
33
+ def test_fetch_contacts
34
+ contacts = Contacts.new(:gmail, @account.username, @account.password).contacts
35
+ @account.contacts.each do |contact|
36
+ assert contacts.include?(contact), "Could not find: #{contact.inspect} in #{contacts.inspect}"
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,41 @@
1
+ dir = File.dirname(__FILE__)
2
+ require "#{dir}/../test_helper"
3
+ require 'contacts'
4
+
5
+ class HotmailContactImporterTest < ContactImporterTestCase
6
+ def setup
7
+ super
8
+ @account = TestAccounts[:hotmail]
9
+ end
10
+
11
+ def test_successful_login
12
+ Contacts.new(:hotmail, @account.username, @account.password)
13
+ end
14
+
15
+ def test_importer_fails_with_invalid_msn_password
16
+ assert_raise(Contacts::AuthenticationError) do
17
+ Contacts.new(:hotmail, "test@msn.com","wrong_password")
18
+ end
19
+ end
20
+
21
+ def test_importer_fails_with_invalid_password
22
+ assert_raise(Contacts::AuthenticationError) do
23
+ Contacts.new(:hotmail, @account.username,"wrong_password")
24
+ end
25
+ end
26
+
27
+ def test_fetch_contacts
28
+ contacts = Contacts.new(:hotmail, @account.username, @account.password).contacts
29
+ @account.contacts.each do |contact|
30
+ assert contacts.include?(contact), "Could not find: #{contact.inspect} in #{contacts.inspect}"
31
+ end
32
+ end
33
+
34
+ # Since the hotmail scraper doesn't read names, test email
35
+ def test_fetch_email
36
+ contacts = Contacts.new(:hotmail, @account.username, @account.password).contacts
37
+ @account.contacts.each do |contact|
38
+ assert contacts.any?{|book_contact| book_contact.last == contact.last }, "Could not find: #{contact.inspect} in #{contacts.inspect}"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,23 @@
1
+ dir = File.dirname(__FILE__)
2
+ require "#{dir}/../test_helper"
3
+
4
+ class OutlookTest < ContactImporterTestCase
5
+ context 'An instance of Outlook' do
6
+ should 'open file properly' do
7
+ outlookobject = Contacts.new(:outlook, File.open(File.dirname(__FILE__) + '/../outlook_fullname.csv'))
8
+ assert_not_nil outlookobject
9
+ end
10
+
11
+ should 'get contacts from file with Full Name' do
12
+ outlookobject = Contacts.new(:outlook, File.open(File.dirname(__FILE__) + '/../outlook_fullname.csv'))
13
+ contacts = outlookobject.contacts
14
+ assert_not_nil contacts
15
+ end
16
+
17
+ should 'get contacts from file with split up names' do
18
+ outlookobject = Contacts.new(:outlook, File.open(File.dirname(__FILE__) + '/../outlook_splitname.csv'))
19
+ contacts = outlookobject.contacts
20
+ assert_not_nil contacts
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ dir = File.dirname(__FILE__)
2
+ require "#{dir}/../test_helper"
3
+
4
+ class TestAccountsTest < ContactImporterTestCase
5
+ def test_test_accounts_loads_data_from_example_accounts_file
6
+ account = TestAccounts.load(File.dirname(__FILE__) + "/../example_accounts.yml")[:gmail]
7
+
8
+ assert_equal :gmail, account.type
9
+ assert_equal "<changeme>", account.username
10
+ assert_equal "<changeme>", account.password
11
+ assert_equal [["FirstName1 LastName1", "firstname1@example.com"], ["FirstName2 LastName2", "firstname2@example.com"]], account.contacts
12
+ end
13
+
14
+ def test_test_accounts_blows_up_if_file_doesnt_exist
15
+ assert_raise(RuntimeError) do
16
+ TestAccounts.load("file_that_does_not_exist.yml")
17
+ end
18
+ end
19
+
20
+ def test_we_can_load_from_account_file
21
+ assert_not_nil TestAccounts[:gmail].username
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ dir = File.dirname(__FILE__)
2
+ require "#{dir}/../test_helper"
3
+
4
+ class VcfTest < ContactImporterTestCase
5
+ context 'An instance of Vcf' do
6
+ should 'open file properly' do
7
+ vcfobject = Contacts.new(:vcf, File.open(File.dirname(__FILE__) + '/../vCards.vcf'))
8
+ assert_not_nil vcfobject
9
+ end
10
+
11
+ should 'get contacts from file' do
12
+ vcfobject = Contacts.new(:vcf, File.open(File.dirname(__FILE__) + '/../vCards.vcf'))
13
+ contacts = vcfobject.contacts
14
+ assert_not_nil contacts
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,35 @@
1
+ dir = File.dirname(__FILE__)
2
+ require "#{dir}/../test_helper"
3
+ require 'contacts'
4
+
5
+ class YahooContactImporterTest < ContactImporterTestCase
6
+ def setup
7
+ super
8
+ @account = TestAccounts[:yahoo]
9
+ end
10
+
11
+ def test_a_successful_login
12
+ Contacts.new(:yahoo, @account.username, @account.password)
13
+ end
14
+
15
+ def test_importer_fails_with_invalid_password
16
+ assert_raise(Contacts::AuthenticationError) do
17
+ Contacts.new(:yahoo, @account.username, "wrong_password")
18
+ end
19
+ # run the "successful" login test to ensure we reset yahoo's failed login lockout counter
20
+ # See http://www.pivotaltracker.com/story/show/138210
21
+ # yahoo needs some time to unset the failed login state, apparently...
22
+ # ...1 sec and 5 secs still failed sporadically
23
+ sleep 10
24
+ assert_nothing_raised do
25
+ Contacts.new(:yahoo, @account.username, @account.password)
26
+ end
27
+ end
28
+
29
+ def test_a_fetch_contacts
30
+ contacts = Contacts.new(:yahoo, @account.username, @account.password).contacts
31
+ @account.contacts.each do |contact|
32
+ assert contacts.include?(contact), "Could not find: #{contact.inspect} in #{contacts.inspect}"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,16 @@
1
+ BEGIN:VCARD
2
+ VERSION:3.0
3
+ N:LastName1;FirstName1;;;
4
+ FN:FirstName1 LastName1
5
+ EMAIL;type=INTERNET;type=WORK;type=pref:firstname1@example.com
6
+ CATEGORIES:test
7
+ X-ABUID:3BCA01C2-3EF7-4BB8-BFD4-08ED31416C8F\:ABPerson
8
+ END:VCARD
9
+ BEGIN:VCARD
10
+ VERSION:3.0
11
+ N:LastName2;FirstName2;;;
12
+ FN:FirstName2 LastName2
13
+ EMAIL;type=INTERNET;type=WORK;type=pref:firstname2@example.com
14
+ CATEGORIES:test
15
+ X-ABUID:53F6702D-D2B6-4B46-8C41-CD31F743260B\:ABPerson
16
+ END:VCARD
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pp-contacts
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.1
4
+ version: 1.5.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -14,7 +14,7 @@ date: 2012-04-26 00:00:00.000000000Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: json
17
- requirement: &70264084117900 !ruby/object:Gem::Requirement
17
+ requirement: &70303491442280 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: 1.1.1
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *70264084117900
25
+ version_requirements: *70303491442280
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: gdata19
28
- requirement: &70264084117380 !ruby/object:Gem::Requirement
28
+ requirement: &70303491441860 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ! '>='
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: '0'
34
34
  type: :runtime
35
35
  prerelease: false
36
- version_requirements: *70264084117380
36
+ version_requirements: *70303491441860
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: hpricot
39
- requirement: &70264084116800 !ruby/object:Gem::Requirement
39
+ requirement: &70303491441400 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ! '>='
@@ -44,10 +44,10 @@ dependencies:
44
44
  version: '0'
45
45
  type: :runtime
46
46
  prerelease: false
47
- version_requirements: *70264084116800
47
+ version_requirements: *70303491441400
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: nokogiri
50
- requirement: &70264084116260 !ruby/object:Gem::Requirement
50
+ requirement: &70303491440980 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
53
  - - ! '>='
@@ -55,10 +55,10 @@ dependencies:
55
55
  version: '0'
56
56
  type: :runtime
57
57
  prerelease: false
58
- version_requirements: *70264084116260
58
+ version_requirements: *70303491440980
59
59
  - !ruby/object:Gem::Dependency
60
60
  name: encryptor
61
- requirement: &70264084115760 !ruby/object:Gem::Requirement
61
+ requirement: &70303491440560 !ruby/object:Gem::Requirement
62
62
  none: false
63
63
  requirements:
64
64
  - - ! '>='
@@ -66,7 +66,7 @@ dependencies:
66
66
  version: '0'
67
67
  type: :runtime
68
68
  prerelease: false
69
- version_requirements: *70264084115760
69
+ version_requirements: *70303491440560
70
70
  description: A universal interface to grab contact list information from various providers
71
71
  including Outlook, Address Book, Yahoo, AOL, Gmail, Hotmail, and Plaxo.
72
72
  email:
@@ -74,18 +74,39 @@ executables: []
74
74
  extensions: []
75
75
  extra_rdoc_files: []
76
76
  files:
77
+ - .gitignore
78
+ - Gemfile
79
+ - Gemfile.lock
77
80
  - LICENSE
78
- - Rakefile
79
81
  - README.md
82
+ - Rakefile
83
+ - contacts.gemspec
84
+ - cruise_config.rb
80
85
  - examples/grab_contacts.rb
86
+ - geminstaller.yml
81
87
  - lib/contacts.rb
88
+ - lib/contacts/aol_importer.rb
82
89
  - lib/contacts/base.rb
83
- - lib/contacts/json_picker.rb
84
90
  - lib/contacts/gmail.rb
85
- - lib/contacts/aol_importer.rb
86
91
  - lib/contacts/hotmail.rb
92
+ - lib/contacts/json_picker.rb
93
+ - lib/contacts/outlook.rb
87
94
  - lib/contacts/plaxo.rb
95
+ - lib/contacts/vcf.rb
88
96
  - lib/contacts/yahoo.rb
97
+ - test/example_accounts.yml
98
+ - test/outlook_fullname.csv
99
+ - test/outlook_splitname.csv
100
+ - test/test_helper.rb
101
+ - test/test_suite.rb
102
+ - test/unit/aol_contact_importer_test.rb
103
+ - test/unit/gmail_contact_importer_test.rb
104
+ - test/unit/hotmail_contact_importer_test.rb
105
+ - test/unit/outlook_test.rb
106
+ - test/unit/test_accounts_test.rb
107
+ - test/unit/vcf_test.rb
108
+ - test/unit/yahoo_csv_contact_importer_test.rb
109
+ - test/vCards.vcf
89
110
  homepage: http://github.com/paperlesspost/contacts
90
111
  licenses: []
91
112
  post_install_message: