contact-list 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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: []