contact-list 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/README ADDED
File without changes
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+
3
+ require 'active_support/core_ext/hash/slice'
4
+ require 'active_support/core_ext/class/inheritable_attributes'
5
+ require 'active_support/core_ext/module/delegation'
6
+ require 'active_support/core_ext/object/blank'
7
+ require 'active_support/inflector'
8
+
9
+ class Hash
10
+ alias_method :only, :slice
11
+ alias_method :only!, :slice!
12
+ end
13
+ require File.expand_path('../contacts', __FILE__)
@@ -0,0 +1,11 @@
1
+ class Contacts::Contact::Facebook < Struct.new(:uid, :name)
2
+
3
+ def self.parse hash
4
+ new hash['id'], hash['name']
5
+ end
6
+
7
+ def image_url
8
+ "http://graph.facebook.com/#{uid}/picture"
9
+ end
10
+
11
+ end
@@ -0,0 +1,11 @@
1
+ class Contacts::Contact::Google < Struct.new(:email, :name)
2
+
3
+ def self.parse xml
4
+ name = xml.css("title").text
5
+ email_node = xml.xpath("gd:email").first
6
+ return nil unless email_node.present?
7
+ email = email_node['address']
8
+ new(email, name)
9
+ end
10
+
11
+ end
@@ -0,0 +1,7 @@
1
+ class Contacts::Contact::Linkedin < Struct.new(:uid, :name, :first_name, :last_name, :image_url)
2
+
3
+ def self.parse hash
4
+ new hash['id'], "#{hash['firstName']} #{hash['lastName']}", hash['firstName'], hash['lastName'], hash['pictureUrl']
5
+ end
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ class Contacts::Contact::Twitter < Struct.new(:uid, :screen_name, :name, :image_url)
2
+
3
+ def self.parse hash
4
+ new hash['id'].to_s, hash['screen_name'], hash['name'], hash['profile_image_url']
5
+ end
6
+
7
+ end
@@ -0,0 +1,17 @@
1
+ class Contacts::Contact::Yahoo < Struct.new(:email, :name)
2
+
3
+ def self.parse hash
4
+ email, name = '', nil
5
+ hash['fields'].each do |field|
6
+ case field['type']
7
+ when 'email'
8
+ email = field['value']
9
+ when 'name'
10
+ name = [field['value']['givenName'], field['value']['familyName']].join(' ') rescue ''
11
+ end
12
+ end
13
+ name ||= email
14
+ new(email, name) unless email.blank?
15
+ end
16
+
17
+ end
@@ -0,0 +1,9 @@
1
+ module Contacts
2
+ module Contact
3
+ autoload :Google, 'contacts/contact/google'
4
+ autoload :Yahoo, 'contacts/contact/yahoo'
5
+ autoload :Facebook, 'contacts/contact/facebook'
6
+ autoload :Twitter, 'contacts/contact/twitter'
7
+ autoload :Linkedin, 'contacts/contact/linkedin'
8
+ end
9
+ end
@@ -0,0 +1,73 @@
1
+ module Contacts
2
+ module Provider
3
+ class Base
4
+
5
+ class_inheritable_accessor :site_url
6
+ self.site_url = nil
7
+ delegate :site_url, :to => "self.class"
8
+
9
+ class_inheritable_accessor :valid_attributes
10
+ self.valid_attributes = []
11
+ delegate :import_type, :valid_attributes, :to => "self.class"
12
+
13
+ def self.set_site_url url
14
+ self.site_url = url
15
+ end
16
+
17
+ def self.set_valid_attributes *attrs
18
+ self.valid_attributes = attrs.map!(&:to_sym)
19
+ attr_accessor *attrs
20
+ end
21
+
22
+ def self.provider_id
23
+ self.name.demodulize.underscore
24
+ end
25
+
26
+ def self.all attrs={}
27
+ new(attrs).contacts
28
+ end
29
+
30
+ def initialize attrs={}
31
+ parse_attributes attrs
32
+ end
33
+
34
+ def connect!
35
+ raise "implement in subclass"
36
+ end
37
+
38
+ def connection
39
+ @connection ||= connect!
40
+ end
41
+
42
+ def retrieve_contacts!
43
+ raise "implement in subclass"
44
+ end
45
+
46
+ def contacts_response
47
+ @contacts_response ||= retrieve_contacts!
48
+ end
49
+
50
+ def parse_contacts!
51
+ raise "implement in subclass"
52
+ end
53
+
54
+ def contacts
55
+ @contacts ||= parse_contacts!
56
+ end
57
+
58
+ def parse_attributes attrs = {}
59
+ self.attributes = attrs.only(*valid_attributes)
60
+ end
61
+
62
+ def attributes= attrs = {}
63
+ attrs.each do |attr_name, attr_value|
64
+ send("#{attr_name}=", attr_value)
65
+ end
66
+ end
67
+
68
+ def attributes
69
+ valid_attributes.inject({}) {|result, attr| result.merge attr => send(attr) }
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,13 @@
1
+ require 'multi_json'
2
+ class Contacts::Provider::Facebook < Contacts::Provider::OAuth2
3
+ set_site_url 'https://graph.facebook.com'
4
+
5
+ def retrieve_contacts!
6
+ @contacts_response = connection.get("/me/friends")
7
+ end
8
+
9
+ def parse_contacts!
10
+ @contacts = MultiJson.decode(contacts_response)['data'].map { |contact| Contacts::Contact::Facebook.parse(contact) }.compact
11
+ end
12
+
13
+ end
@@ -0,0 +1,18 @@
1
+ require 'nokogiri'
2
+
3
+ class Contacts::Provider::Google < Contacts::Provider::OAuth
4
+ set_site_url 'https://www.google.com'
5
+
6
+ def retrieve_contacts!
7
+ @contacts_response = connection.get("/m8/feeds/contacts/#{uid||'default'}/full?max-results=999")
8
+ end
9
+
10
+ def parse_contacts!
11
+ if contacts_response.code.to_i == 401
12
+ raise "Error 401".inspect
13
+ else
14
+ @contacts = Nokogiri::XML(contacts_response.body).css("entry").map {|entry| Contacts::Contact::Google.parse(entry)}.compact
15
+ end
16
+ end
17
+
18
+ end
@@ -0,0 +1,13 @@
1
+ require 'multi_json'
2
+ class Contacts::Provider::Linkedin < Contacts::Provider::OAuth
3
+ set_site_url 'http://api.linkedin.com'
4
+
5
+ def retrieve_contacts!
6
+ @connections_response = connection.get("/v1/people/~/connections:(first-name,last-name,id,headline,picture-url)")
7
+ end
8
+
9
+ def parse_contacts!
10
+ MultiJson.decode(contacts_response.body)['values'].map { |contact| Contacts::Contact::Linkedin.parse(contact) }.compact
11
+ end
12
+
13
+ end
@@ -0,0 +1,18 @@
1
+ require 'oauth'
2
+
3
+ module Contacts
4
+ module Provider
5
+ class OAuth < Contacts::Provider::Base
6
+ set_valid_attributes :token, :secret, :consumer_key, :consumer_secret, :uid
7
+
8
+ def consumer
9
+ @consumer ||= ::OAuth::Consumer.new(consumer_key, consumer_secret, :site => site_url)
10
+ end
11
+
12
+ def connect!
13
+ ::OAuth::AccessToken.new(consumer, token, secret)
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ require 'oauth2'
2
+
3
+ module Contacts
4
+ module Provider
5
+ class OAuth2 < Contacts::Provider::Base
6
+ set_valid_attributes :token, :consumer_key, :consumer_secret
7
+
8
+ def client
9
+ @client ||= ::OAuth2::Client.new(consumer_key, consumer_secret, :site => site_url)
10
+ end
11
+
12
+ def connect!
13
+ ::OAuth2::AccessToken.new(client, token)
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,33 @@
1
+ require 'multi_json'
2
+ class Contacts::Provider::Twitter < Contacts::Provider::OAuth
3
+ set_site_url 'http://api.twitter.com'
4
+
5
+ def retrieve_contacts!
6
+ {:followers_response => followers_response, :following_response => following_response}
7
+ end
8
+
9
+ def parse_contacts!
10
+ followers & following
11
+ end
12
+
13
+ def followers_response
14
+ @followers_response ||= connection.get("/1/statuses/followers.json")
15
+ end
16
+
17
+ def following_response
18
+ @following_response ||= connection.get("/1/statuses/friends.json")
19
+ end
20
+
21
+ def followers
22
+ @followers ||= parse_response!(followers_response)
23
+ end
24
+
25
+ def following
26
+ @following ||= parse_response!(following_response)
27
+ end
28
+
29
+ def parse_response! response
30
+ MultiJson.decode(response.body).map { |contact| Contacts::Contact::Twitter.parse(contact) }.compact
31
+ end
32
+
33
+ end
@@ -0,0 +1,13 @@
1
+ require 'multi_json'
2
+ class Contacts::Provider::Yahoo < Contacts::Provider::OAuth
3
+ set_site_url 'http://social.yahooapis.com'
4
+
5
+ def retrieve_contacts!
6
+ @contacts_response = connection.get("/v1/user/me/contacts?format=json")
7
+ end
8
+
9
+ def parse_contacts!
10
+ @contacts = ((MultiJson.decode(contacts_response.body)['contacts']||{})['contact']||[]).map { |contact| Contacts::Contact::Yahoo.parse(contact) }.compact
11
+ end
12
+
13
+ end
@@ -0,0 +1,21 @@
1
+ module Contacts
2
+ module Provider
3
+ autoload :Base, 'contacts/provider/base'
4
+ autoload :OAuth, 'contacts/provider/oauth'
5
+ autoload :Google, 'contacts/provider/google'
6
+ autoload :Yahoo, 'contacts/provider/yahoo'
7
+ autoload :Twitter, 'contacts/provider/twitter'
8
+ autoload :OAuth2, 'contacts/provider/oauth2'
9
+ autoload :Facebook, 'contacts/provider/facebook'
10
+ autoload :Linkedin, 'contacts/provider/linkedin'
11
+
12
+ def self.all
13
+ [Google, Yahoo, Twitter, Facebook, Linkedin]
14
+ end
15
+
16
+ def self.find provider_id
17
+ all.find { |provider| provider.provider_id == provider_id.to_s }
18
+ end
19
+
20
+ end
21
+ end
data/lib/contacts.rb ADDED
@@ -0,0 +1,14 @@
1
+ module Contacts
2
+ autoload :Provider, 'contacts/provider'
3
+ autoload :Contact, 'contacts/contact'
4
+
5
+ def self.[] provider_id
6
+ Provider.find(provider_id.to_s) || error_with!(provider_id)
7
+ end
8
+
9
+ private
10
+
11
+ def self.error_with! provider_id
12
+ raise("Invalid provider_id #{provider_id.to_s.inspect}, available provider_id; #{Provider.all.map(&:provider_id).join(', ')}")
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,189 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: contact-list
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Alex Neill
9
+ - Tom Warner
10
+ - Paul Bowsher
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2012-02-14 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: contact-list
18
+ requirement: &70198657387760 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ! '>='
22
+ - !ruby/object:Gem::Version
23
+ version: '0'
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: *70198657387760
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: &70198657387140 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: '2.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: *70198657387140
38
+ - !ruby/object:Gem::Dependency
39
+ name: rspec
40
+ requirement: &70198657386660 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '2.0'
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: *70198657386660
49
+ - !ruby/object:Gem::Dependency
50
+ name: rspec
51
+ requirement: &70198657386040 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ version: '2.0'
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: *70198657386040
60
+ - !ruby/object:Gem::Dependency
61
+ name: rspec
62
+ requirement: &70198657385400 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ~>
66
+ - !ruby/object:Gem::Version
67
+ version: '2.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: *70198657385400
71
+ - !ruby/object:Gem::Dependency
72
+ name: activesupport
73
+ requirement: &70198657384880 !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: 2.1.0
79
+ type: :runtime
80
+ prerelease: false
81
+ version_requirements: *70198657384880
82
+ - !ruby/object:Gem::Dependency
83
+ name: multi_json
84
+ requirement: &70198657384340 !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: 1.0.4
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: *70198657384340
93
+ - !ruby/object:Gem::Dependency
94
+ name: nokogiri
95
+ requirement: &70198657383640 !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ~>
99
+ - !ruby/object:Gem::Version
100
+ version: 1.5.0
101
+ type: :runtime
102
+ prerelease: false
103
+ version_requirements: *70198657383640
104
+ - !ruby/object:Gem::Dependency
105
+ name: oauth
106
+ requirement: &70198657383040 !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ~>
110
+ - !ruby/object:Gem::Version
111
+ version: 0.4.5
112
+ type: :runtime
113
+ prerelease: false
114
+ version_requirements: *70198657383040
115
+ - !ruby/object:Gem::Dependency
116
+ name: oauth2
117
+ requirement: &70198657382440 !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ~>
121
+ - !ruby/object:Gem::Version
122
+ version: 0.5.2
123
+ type: :runtime
124
+ prerelease: false
125
+ version_requirements: *70198657382440
126
+ - !ruby/object:Gem::Dependency
127
+ name: rspec
128
+ requirement: &70198657381900 !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ~>
132
+ - !ruby/object:Gem::Version
133
+ version: '2.0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: *70198657381900
137
+ description: Find contacts from multiple providers
138
+ email:
139
+ - alex@rawnet.com
140
+ - tom@rawnet.com
141
+ - paul.bowsher@gmail.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files:
145
+ - README
146
+ files:
147
+ - lib/contact_list.rb
148
+ - lib/contacts.rb
149
+ - lib/contacts/contact.rb
150
+ - lib/contacts/contact/facebook.rb
151
+ - lib/contacts/contact/google.rb
152
+ - lib/contacts/contact/linkedin.rb
153
+ - lib/contacts/contact/twitter.rb
154
+ - lib/contacts/contact/yahoo.rb
155
+ - lib/contacts/provider.rb
156
+ - lib/contacts/provider/base.rb
157
+ - lib/contacts/provider/facebook.rb
158
+ - lib/contacts/provider/google.rb
159
+ - lib/contacts/provider/linkedin.rb
160
+ - lib/contacts/provider/oauth.rb
161
+ - lib/contacts/provider/oauth2.rb
162
+ - lib/contacts/provider/twitter.rb
163
+ - lib/contacts/provider/yahoo.rb
164
+ - README
165
+ homepage:
166
+ licenses: []
167
+ post_install_message:
168
+ rdoc_options: []
169
+ require_paths:
170
+ - lib
171
+ required_ruby_version: !ruby/object:Gem::Requirement
172
+ none: false
173
+ requirements:
174
+ - - ! '>='
175
+ - !ruby/object:Gem::Version
176
+ version: '0'
177
+ required_rubygems_version: !ruby/object:Gem::Requirement
178
+ none: false
179
+ requirements:
180
+ - - ! '>='
181
+ - !ruby/object:Gem::Version
182
+ version: '0'
183
+ requirements: []
184
+ rubyforge_project:
185
+ rubygems_version: 1.8.15
186
+ signing_key:
187
+ specification_version: 3
188
+ summary: Find contacts from multiple providers
189
+ test_files: []