aeden-contacts 0.2.15

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.
Files changed (49) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +51 -0
  3. data/Rakefile +71 -0
  4. data/VERSION.yml +4 -0
  5. data/lib/config/contacts.yml +10 -0
  6. data/lib/contacts/flickr.rb +133 -0
  7. data/lib/contacts/google.rb +387 -0
  8. data/lib/contacts/google_oauth.rb +91 -0
  9. data/lib/contacts/version.rb +9 -0
  10. data/lib/contacts/windows_live.rb +164 -0
  11. data/lib/contacts/yahoo.rb +236 -0
  12. data/lib/contacts.rb +55 -0
  13. data/spec/contact_spec.rb +61 -0
  14. data/spec/feeds/contacts.yml +10 -0
  15. data/spec/feeds/flickr/auth.getFrob.xml +4 -0
  16. data/spec/feeds/flickr/auth.getToken.xml +5 -0
  17. data/spec/feeds/google-many.xml +48 -0
  18. data/spec/feeds/google-single.xml +46 -0
  19. data/spec/feeds/wl_contacts.xml +29 -0
  20. data/spec/feeds/yh_contacts.txt +119 -0
  21. data/spec/feeds/yh_credential.xml +28 -0
  22. data/spec/flickr/auth_spec.rb +80 -0
  23. data/spec/gmail/auth_spec.rb +70 -0
  24. data/spec/gmail/fetching_spec.rb +198 -0
  25. data/spec/rcov.opts +2 -0
  26. data/spec/spec.opts +2 -0
  27. data/spec/spec_helper.rb +84 -0
  28. data/spec/windows_live/windows_live_spec.rb +34 -0
  29. data/spec/yahoo/yahoo_spec.rb +83 -0
  30. data/vendor/fakeweb/CHANGELOG +80 -0
  31. data/vendor/fakeweb/LICENSE.txt +281 -0
  32. data/vendor/fakeweb/README.rdoc +160 -0
  33. data/vendor/fakeweb/Rakefile +57 -0
  34. data/vendor/fakeweb/fakeweb.gemspec +13 -0
  35. data/vendor/fakeweb/lib/fake_web/ext/net_http.rb +58 -0
  36. data/vendor/fakeweb/lib/fake_web/registry.rb +78 -0
  37. data/vendor/fakeweb/lib/fake_web/responder.rb +88 -0
  38. data/vendor/fakeweb/lib/fake_web/response.rb +10 -0
  39. data/vendor/fakeweb/lib/fake_web/socket_delegator.rb +24 -0
  40. data/vendor/fakeweb/lib/fake_web.rb +152 -0
  41. data/vendor/fakeweb/test/fixtures/test_example.txt +1 -0
  42. data/vendor/fakeweb/test/fixtures/test_request +21 -0
  43. data/vendor/fakeweb/test/test_allow_net_connect.rb +41 -0
  44. data/vendor/fakeweb/test/test_fake_web.rb +453 -0
  45. data/vendor/fakeweb/test/test_fake_web_open_uri.rb +62 -0
  46. data/vendor/fakeweb/test/test_helper.rb +52 -0
  47. data/vendor/fakeweb/test/test_query_string.rb +37 -0
  48. data/vendor/windowslivelogin.rb +1151 -0
  49. metadata +108 -0
@@ -0,0 +1,91 @@
1
+ require 'oauth'
2
+ require 'contacts/google'
3
+
4
+ # An extension to the standard OAuth library so we can nicely use Google APIs
5
+ module GoogleOAuth
6
+ class RequestToken < OAuth::RequestToken
7
+ def authorize_url(params={})
8
+ params.merge! :oauth_token => token
9
+ params = params.map { |k,v| "%s=%s" % [CGI.escape(k.to_s), CGI.escape(v)] }
10
+ consumer.authorize_url + "?" + params.join("&")
11
+ end
12
+ end
13
+
14
+ class Consumer < OAuth::Consumer
15
+ def initialize(consumer_key, consumer_secret)
16
+ super(consumer_key,
17
+ consumer_secret,
18
+ {:site => "https://www.google.com",
19
+ :request_token_path => "/accounts/OAuthGetRequestToken",
20
+ :access_token_path => "/accounts/OAuthGetAccessToken",
21
+ :authorize_path => "/accounts/OAuthAuthorizeToken"})
22
+ end
23
+
24
+ def marshal_load(data)
25
+ initialize(data[:key], data[:secret])
26
+ end
27
+
28
+ def marshal_dump
29
+ {:key => self.key, :secret => self.secret}
30
+ end
31
+
32
+ def get_request_token(params={})
33
+ params_str = params.map { |k,v| "%s=%s" % [CGI.escape(k.to_s), CGI.escape(v)] }.join("&")
34
+ uri = URI.parse(request_token_url? ? request_token_url : request_token_path)
35
+ if !uri.query || uri.query == ''
36
+ uri.query = params_str
37
+ else
38
+ uri.query = uri.query + "&" + params_str
39
+ end
40
+
41
+ response=token_request(http_method, uri.to_s, nil, {})
42
+ GoogleOAuth::RequestToken.new(self, response[:oauth_token], response[:oauth_token_secret])
43
+ end
44
+ end
45
+ end
46
+
47
+ module Contacts
48
+ class GoogleOAuth < Google
49
+ def initialize(consumer_key, consumer_secret, user_id = 'default')
50
+ @consumer = ::GoogleOAuth::Consumer.new(consumer_key, consumer_secret)
51
+ @request_token = @consumer.get_request_token :scope => "https://www.google.com/m8/feeds/"
52
+ @projection = 'thin'
53
+ @user = user_id.to_s
54
+ end
55
+
56
+ def marshal_load(data)
57
+ @consumer = data[:consumer]
58
+ @request_token = data[:request_token]
59
+ @projection = 'thin'
60
+ @user = data[:user]
61
+ end
62
+
63
+ def marshal_dump
64
+ {:consumer => @consumer,
65
+ :request_token => @request_token,
66
+ :user => @user}
67
+ end
68
+
69
+ # Available parameters:
70
+ # - hd: Google Apps domain that should be requested (default nil)
71
+ # - oauth_callback: The URL that the user should be redirected to when he successfully authorized us.
72
+ def authentication_url(params={})
73
+ @request_token.authorize_url params
74
+ end
75
+
76
+ def access_token
77
+ return @access_token if @access_token
78
+ begin
79
+ @access_token = @request_token.get_access_token
80
+ rescue Net::HTTPServerException
81
+ end
82
+ end
83
+
84
+ def get(params={})
85
+ path = FeedsPath + CGI.escape(@user)
86
+ google_params = translate_parameters(params)
87
+ query = self.class.query_string(google_params)
88
+ access_token.get("#{path}/#{@projection}?#{query}")
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,9 @@
1
+ module Contacts
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 2
5
+ TINY = 6
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,164 @@
1
+ require 'contacts'
2
+ require File.join(File.dirname(__FILE__), %w{.. .. vendor windowslivelogin})
3
+
4
+ require 'rubygems'
5
+ require 'hpricot'
6
+ require 'uri'
7
+ require 'yaml'
8
+
9
+ module Contacts
10
+ # = How I can fetch Windows Live Contacts?
11
+ # To gain access to a Windows Live user's data in the Live Contacts service,
12
+ # a third-party developer first must ask the owner for permission. You must
13
+ # do that through Windows Live Delegated Authentication.
14
+ #
15
+ # This library give you access to Windows Live Delegated Authentication System
16
+ # and Windows Live Contacts API. Just follow the steps below and be happy!
17
+ #
18
+ # === Registering your app
19
+ # First of all, follow the steps in this
20
+ # page[http://msdn.microsoft.com/en-us/library/cc287659.aspx] to register your
21
+ # app.
22
+ #
23
+ # === Configuring your Windows Live YAML
24
+ # After registering your app, you will have an *appid*, a <b>secret key</b> and
25
+ # a <b>return URL</b>. Use their values to fill in the config/contacts.yml file.
26
+ # The policy URL field inside the YAML config file must contain the URL
27
+ # of the privacy policy of your Web site for Delegated Authentication.
28
+ #
29
+ # === Authenticating your user and fetching his contacts
30
+ #
31
+ # wl = Contacts::WindowsLive.new
32
+ # auth_url = wl.get_authentication_url
33
+ #
34
+ # Use that *auth_url* to redirect your user to Windows Live. He will authenticate
35
+ # there and Windows Live will POST to your return URL. You have to get the
36
+ # body of that POST, let's call it post_body. (if you're using Rails, you can
37
+ # get the POST body through request.raw_post, in the context of an action inside
38
+ # ActionController)
39
+ #
40
+ # Now, to fetch his contacts, just do this:
41
+ #
42
+ # contacts = wl.contacts(post_body)
43
+ # #-> [ ['Fitzgerald', 'fubar@gmail.com', 'fubar@example.com'],
44
+ # ['William Paginate', 'will.paginate@gmail.com'], ...
45
+ # ]
46
+ #--
47
+ # This class has two responsibilities:
48
+ # 1. Access the Windows Live Contacts API through Delegated Authentication
49
+ # 2. Import contacts from Windows Live and deliver it inside an Array
50
+ #
51
+ class WindowsLive
52
+ CONFIG_FILE = File.dirname(__FILE__) + '/../config/contacts.yml'
53
+
54
+ # Initialize a new WindowsLive object.
55
+ #
56
+ # ==== Paramaters
57
+ # * config_file <String>:: The contacts YAML config file name
58
+ #--
59
+ # You can check an example of a config file inside config/ directory
60
+ #
61
+ def initialize(config_file=CONFIG_FILE)
62
+ confs = YAML.load_file(config_file)['windows_live']
63
+ @wll = WindowsLiveLogin.new(confs['appid'], confs['secret'], confs['security_algorithm'],
64
+ nil, confs['policy_url'], confs['return_url'])
65
+ end
66
+
67
+
68
+ # Windows Live Contacts API need to authenticate the user that is giving you
69
+ # access to his contacts. To do that, you must give him a URL. That method
70
+ # generates that URL. The user must access that URL, and after he has done
71
+ # authentication, hi will be redirected to your application.
72
+ #
73
+ def get_authentication_url
74
+ @wll.getConsentUrl("Contacts.Invite")
75
+ end
76
+
77
+ # After the user has been authenticaded, Windows Live Delegated Authencation
78
+ # Service redirects to your application, through a POST HTTP method. Along
79
+ # with the POST, Windows Live send to you a Consent that you must process
80
+ # to access the user's contacts. This method process the Consent
81
+ # to you.
82
+ #
83
+ # ==== Paramaters
84
+ # * consent <String>:: A string containing the Consent given to you inside
85
+ # the redirection POST from Windows Live
86
+ #
87
+ def process_consent(consent)
88
+ consent.strip!
89
+ consent = URI.unescape(consent)
90
+ @consent_token = @wll.processConsent(consent)
91
+ end
92
+
93
+ # This method return the user's contacts inside an Array in the following
94
+ # format:
95
+ #
96
+ # [
97
+ # ['Brad Fitzgerald', 'fubar@gmail.com'],
98
+ # [nil, 'nagios@hotmail.com'],
99
+ # ['William Paginate', 'will.paginate@yahoo.com'] ...
100
+ # ]
101
+ #
102
+ # ==== Paramaters
103
+ # * consent <String>:: A string containing the Consent given to you inside
104
+ # the redirection POST from Windows Live
105
+ #
106
+ def contacts(consent)
107
+ process_consent(consent)
108
+ contacts_xml = access_live_contacts_api()
109
+ contacts_list = WindowsLive.parse_xml(contacts_xml)
110
+ end
111
+
112
+ # This method access the Windows Live Contacts API Web Service to get
113
+ # the XML contacts document
114
+ #
115
+ def access_live_contacts_api
116
+ http = http = Net::HTTP.new('livecontacts.services.live.com', 443)
117
+ http.use_ssl = true
118
+
119
+ response = nil
120
+ http.start do |http|
121
+ request = Net::HTTP::Get.new("/users/@L@#{@consent_token.locationid}/rest/invitationsbyemail", {"Authorization" => "DelegatedToken dt=\"#{@consent_token.delegationtoken}\""})
122
+ response = http.request(request)
123
+ end
124
+
125
+ return response.body
126
+ end
127
+
128
+ # This method parses the XML Contacts document and returns the contacts
129
+ # inside an Array
130
+ #
131
+ # ==== Paramaters
132
+ # * xml <String>:: A string containing the XML contacts document
133
+ #
134
+ def self.parse_xml(xml)
135
+ doc = Hpricot::XML(xml)
136
+
137
+ contacts = []
138
+ doc.search('/livecontacts/contacts/contact').each do |contact|
139
+ email = contact.at('/preferredemail').inner_text
140
+ email.strip!
141
+
142
+ first_name = last_name = nil
143
+ if first_name = contact.at('/profiles/personal/firstname')
144
+ first_name = first_name.inner_text.strip
145
+ end
146
+
147
+ if last_name = contact.at('/profiles/personal/lastname')
148
+ last_name = last_name.inner_text.strip
149
+ end
150
+
151
+ name = nil
152
+ if !first_name.nil? || !last_name.nil?
153
+ name = "#{first_name} #{last_name}"
154
+ name.strip!
155
+ end
156
+ new_contact = Contact.new(email, name)
157
+ contacts << new_contact
158
+ end
159
+
160
+ return contacts
161
+ end
162
+ end
163
+
164
+ end
@@ -0,0 +1,236 @@
1
+ require 'contacts'
2
+
3
+ require 'rubygems'
4
+ require 'hpricot'
5
+ require 'md5'
6
+ require 'net/https'
7
+ require 'uri'
8
+ require 'yaml'
9
+ require 'json'
10
+
11
+ module Contacts
12
+ # = How I can fetch Yahoo Contacts?
13
+ # To gain access to a Yahoo user's data in the Yahoo Address Book Service,
14
+ # a third-party developer first must ask the owner for permission. You must
15
+ # do that through Yahoo Browser Based Authentication (BBAuth).
16
+ #
17
+ # This library give you access to Yahoo BBAuth and Yahoo Address Book API.
18
+ # Just follow the steps below and be happy!
19
+ #
20
+ # === Registering your app
21
+ # First of all, follow the steps in this
22
+ # page[http://developer.yahoo.com/wsregapp/] to register your app. If you need
23
+ # some help with that form, you can get it
24
+ # here[http://developer.yahoo.com/auth/appreg.html]. Just two tips: inside
25
+ # <b>Required access scopes</b> in that registration form, choose
26
+ # <b>Yahoo! Address Book with Read Only access</b>. Inside
27
+ # <b>Authentication method</b> choose <b>Browser Based Authentication</b>.
28
+ #
29
+ # === Configuring your Yahoo YAML
30
+ # After registering your app, you will have an <b>application id</b> and a
31
+ # <b>shared secret</b>. Use their values to fill in the config/contacts.yml
32
+ # file.
33
+ #
34
+ # === Authenticating your user and fetching his contacts
35
+ #
36
+ # yahoo = Contacts::Yahoo.new
37
+ # auth_url = yahoo.get_authentication_url
38
+ #
39
+ # Use that *auth_url* to redirect your user to Yahoo BBAuth. He will authenticate
40
+ # there and Yahoo will redirect to your application entrypoint URL (that you provided
41
+ # while registering your app with Yahoo). You have to get the path of that
42
+ # redirect, let's call it path (if you're using Rails, you can get it through
43
+ # request.request_uri, in the context of an action inside ActionController)
44
+ #
45
+ # Now, to fetch his contacts, just do this:
46
+ #
47
+ # contacts = wl.contacts(path)
48
+ # #-> [ ['Fitzgerald', 'fubar@gmail.com', 'fubar@example.com'],
49
+ # ['William Paginate', 'will.paginate@gmail.com'], ...
50
+ # ]
51
+ #--
52
+ # This class has two responsibilities:
53
+ # 1. Access the Yahoo Address Book API through Delegated Authentication
54
+ # 2. Import contacts from Yahoo Mail and deliver it inside an Array
55
+ #
56
+ class Yahoo
57
+ AUTH_DOMAIN = "https://api.login.yahoo.com"
58
+ AUTH_PATH = "/WSLogin/V1/wslogin?appid=#appid&ts=#ts"
59
+ CREDENTIAL_PATH = "/WSLogin/V1/wspwtoken_login?appid=#appid&ts=#ts&token=#token"
60
+ ADDRESS_BOOK_DOMAIN = "address.yahooapis.com"
61
+ ADDRESS_BOOK_PATH = "/v1/searchContacts?format=json&fields=name,email&appid=#appid&WSSID=#wssid"
62
+ CONFIG_FILE = File.dirname(__FILE__) + '/../config/contacts.yml'
63
+
64
+ attr_reader :appid, :secret, :token, :wssid, :cookie
65
+
66
+ # Initialize a new Yahoo object.
67
+ #
68
+ # ==== Paramaters
69
+ # * config_file <String>:: The contacts YAML config file name
70
+ #--
71
+ # You can check an example of a config file inside config/ directory
72
+ #
73
+ def initialize(config_file=CONFIG_FILE)
74
+ confs = YAML.load_file(config_file)['yahoo']
75
+ @appid = confs['appid']
76
+ @secret = confs['secret']
77
+ end
78
+
79
+ # Yahoo Address Book API need to authenticate the user that is giving you
80
+ # access to his contacts. To do that, you must give him a URL. This method
81
+ # generates that URL. The user must access that URL, and after he has done
82
+ # authentication, hi will be redirected to your application.
83
+ #
84
+ def get_authentication_url
85
+ path = AUTH_PATH.clone
86
+ path.sub!(/#appid/, @appid)
87
+
88
+ timestamp = Time.now.utc.to_i
89
+ path.sub!(/#ts/, timestamp.to_s)
90
+
91
+ signature = MD5.hexdigest(path + @secret)
92
+ return AUTH_DOMAIN + "#{path}&sig=#{signature}"
93
+ end
94
+
95
+ # This method return the user's contacts inside an Array in the following
96
+ # format:
97
+ #
98
+ # [
99
+ # ['Brad Fitzgerald', 'fubar@gmail.com'],
100
+ # [nil, 'nagios@hotmail.com'],
101
+ # ['William Paginate', 'will.paginate@yahoo.com'] ...
102
+ # ]
103
+ #
104
+ # ==== Paramaters
105
+ # * path <String>:: The path of the redirect request that Yahoo sent to you
106
+ # after authenticating the user
107
+ #
108
+ def contacts(path)
109
+ begin
110
+ validate_signature(path)
111
+ credentials = access_user_credentials()
112
+ parse_credentials(credentials)
113
+ contacts_json = access_address_book_api()
114
+ Yahoo.parse_contacts(contacts_json)
115
+ rescue Exception => e
116
+ "Error #{e.class}: #{e.message}."
117
+ end
118
+ end
119
+
120
+ # This method processes and validates the redirect request that Yahoo send to
121
+ # you. Validation is done to verify that the request was really made by
122
+ # Yahoo. Processing is done to get the token.
123
+ #
124
+ # ==== Paramaters
125
+ # * path <String>:: The path of the redirect request that Yahoo sent to you
126
+ # after authenticating the user
127
+ #
128
+ def validate_signature(path)
129
+ path.match(/^(.+)&sig=(\w{32})$/)
130
+ path_without_sig = $1
131
+ sig = $2
132
+
133
+ if sig == MD5.hexdigest(path_without_sig + @secret)
134
+ path.match(/token=(.+?)&/)
135
+ @token = $1
136
+ return true
137
+ else
138
+ raise 'Signature not valid. This request may not have been sent from Yahoo.'
139
+ end
140
+ end
141
+
142
+ # This method accesses Yahoo to retrieve the user's credentials.
143
+ #
144
+ def access_user_credentials
145
+ url = get_credential_url()
146
+ uri = URI.parse(url)
147
+
148
+ http = http = Net::HTTP.new(uri.host, uri.port)
149
+ http.use_ssl = true
150
+
151
+ response = nil
152
+ http.start do |http|
153
+ request = Net::HTTP::Get.new("#{uri.path}?#{uri.query}")
154
+ response = http.request(request)
155
+ end
156
+
157
+ return response.body
158
+ end
159
+
160
+ # This method generates the URL that you must access to get user's
161
+ # credentials.
162
+ #
163
+ def get_credential_url
164
+ path = CREDENTIAL_PATH.clone
165
+ path.sub!(/#appid/, @appid)
166
+
167
+ path.sub!(/#token/, @token)
168
+
169
+ timestamp = Time.now.utc.to_i
170
+ path.sub!(/#ts/, timestamp.to_s)
171
+
172
+ signature = MD5.hexdigest(path + @secret)
173
+ return AUTH_DOMAIN + "#{path}&sig=#{signature}"
174
+ end
175
+
176
+ # This method parses the user's credentials to generate the WSSID and
177
+ # Coookie that are needed to give you access to user's address book.
178
+ #
179
+ # ==== Paramaters
180
+ # * xml <String>:: A String containing the user's credentials
181
+ #
182
+ def parse_credentials(xml)
183
+ doc = Hpricot::XML(xml)
184
+ @wssid = doc.at('/BBAuthTokenLoginResponse/Success/WSSID').inner_text.strip
185
+ @cookie = doc.at('/BBAuthTokenLoginResponse/Success/Cookie').inner_text.strip
186
+ end
187
+
188
+ # This method accesses the Yahoo Address Book API and retrieves the user's
189
+ # contacts in JSON.
190
+ #
191
+ def access_address_book_api
192
+ http = http = Net::HTTP.new(ADDRESS_BOOK_DOMAIN, 80)
193
+
194
+ response = nil
195
+ http.start do |http|
196
+ path = ADDRESS_BOOK_PATH.clone
197
+ path.sub!(/#appid/, @appid)
198
+ path.sub!(/#wssid/, @wssid)
199
+
200
+ request = Net::HTTP::Get.new(path, {'Cookie' => @cookie})
201
+ response = http.request(request)
202
+ end
203
+
204
+ return response.body
205
+ end
206
+
207
+ # This method parses the JSON contacts document and returns an array
208
+ # contaning all the user's contacts.
209
+ #
210
+ # ==== Parameters
211
+ # * json <String>:: A String of user's contacts in JSON format
212
+ #
213
+ def self.parse_contacts(json)
214
+ contacts = []
215
+ people = JSON.parse(json)
216
+
217
+ people['contacts'].each do |contact|
218
+ name = nil
219
+ email = nil
220
+ contact['fields'].each do |field|
221
+ case field['type']
222
+ when 'email'
223
+ email = field['data']
224
+ email.strip!
225
+ when 'name'
226
+ name = "#{field['first']} #{field['last']}"
227
+ name.strip!
228
+ end
229
+ end
230
+ contacts.push Contact.new(email, name)
231
+ end
232
+ return contacts
233
+ end
234
+
235
+ end
236
+ end
data/lib/contacts.rb ADDED
@@ -0,0 +1,55 @@
1
+ require 'contacts/version'
2
+
3
+ module Contacts
4
+
5
+ Identifier = 'Ruby Contacts v' + VERSION::STRING
6
+
7
+ # An object that represents a single contact
8
+ class Contact
9
+ attr_reader :emails, :ims, :phones, :addresses, :organizations
10
+ attr_accessor :name, :username, :service_id, :note
11
+
12
+ def initialize(email, name = nil, username = nil)
13
+ @emails = []
14
+ @emails << email if email
15
+ @ims = []
16
+ @phones = []
17
+ @addresses = []
18
+ @organizations = []
19
+ @name = name
20
+ @username = username
21
+ end
22
+
23
+ def email
24
+ @emails.first
25
+ end
26
+
27
+ def inspect
28
+ %!#<Contacts::Contact "#{name}"#{email ? " (#{email})" : ''}>!
29
+ end
30
+ end
31
+
32
+ def self.verbose=(verbose)
33
+ @verbose = verbose
34
+ end
35
+
36
+ def self.verbose?
37
+ @verbose || 'irb' == $0
38
+ end
39
+
40
+ class Error < StandardError
41
+ end
42
+
43
+ class TooManyRedirects < Error
44
+ attr_reader :response, :location
45
+
46
+ MAX_REDIRECTS = 2
47
+
48
+ def initialize(response)
49
+ @response = response
50
+ @location = @response['Location']
51
+ super "exceeded maximum of #{MAX_REDIRECTS} redirects (Location: #{location})"
52
+ end
53
+ end
54
+
55
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+ require 'contacts'
3
+
4
+ describe Contacts::Contact do
5
+ describe 'instance' do
6
+ before do
7
+ @contact = Contacts::Contact.new('max@example.com', 'Max Power', 'maxpower')
8
+ end
9
+
10
+ it "should have email" do
11
+ @contact.email.should == 'max@example.com'
12
+ end
13
+
14
+ it "should have name" do
15
+ @contact.name.should == 'Max Power'
16
+ end
17
+
18
+ it "should support a service id" do
19
+ @contact.service_id = 'service identifier'
20
+ @contact.service_id.should == 'service identifier'
21
+ end
22
+
23
+ it "should support multiple emails" do
24
+ @contact.emails << 'maxpower@example.com'
25
+ @contact.email.should == 'max@example.com'
26
+ @contact.emails.should == ['max@example.com', 'maxpower@example.com']
27
+ end
28
+
29
+ it "should support multiple ims" do
30
+ @contact.ims << {'value' => 'max', 'type' => 'skype'}
31
+ @contact.ims.should == [{'value' => 'max', 'type' => 'skype'}]
32
+ end
33
+
34
+ it "should support multiple phones" do
35
+ @contact.phones << {'value' => '111 111 1111', 'type' => 'home'}
36
+ @contact.phones.should == [{'value' => '111 111 1111', 'type' => 'home'}]
37
+ end
38
+
39
+ it "should support multiple addresses" do
40
+ @contact.addresses << {'formatted' => '111 SW 1st Street, New York, NY'}
41
+ @contact.addresses.should == [{'formatted' => '111 SW 1st Street, New York, NY'}]
42
+ end
43
+
44
+ it "should have username" do
45
+ @contact.username.should == 'maxpower'
46
+ end
47
+ end
48
+
49
+ describe '#inspect' do
50
+ it "should be nice" do
51
+ @contact = Contacts::Contact.new('max@example.com', 'Max Power', 'maxpower')
52
+ @contact.inspect.should == '#<Contacts::Contact "Max Power" (max@example.com)>'
53
+ end
54
+
55
+ it "should be nice without email" do
56
+ @contact = Contacts::Contact.new(nil, 'Max Power', 'maxpower')
57
+ @contact.inspect.should == '#<Contacts::Contact "Max Power">'
58
+ end
59
+ end
60
+
61
+ end
@@ -0,0 +1,10 @@
1
+ windows_live:
2
+ appid: your_app_id
3
+ secret: your_app_secret_key
4
+ security_algorithm: wsignin1.0
5
+ return_url: http://yourserver.com/your_return_url
6
+ policy_url: http://yourserver.com/you_policy_url
7
+
8
+ yahoo:
9
+ appid: i%3DB%26p%3DUw70JGIdHWVRbpqYItcMw--
10
+ secret: a34f389cbd135de4618eed5e23409d34450
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="utf-8" ?>
2
+ <rsp stat="ok">
3
+ <frob>934-746563215463214621</frob>
4
+ </rsp>
@@ -0,0 +1,5 @@
1
+ <auth>
2
+ <token>45-76598454353455</token>
3
+ <perms>read</perms>
4
+ <user nsid="12037949754@N01" username="Bees" fullname="Cal H" />
5
+ </auth>
@@ -0,0 +1,48 @@
1
+ <feed>
2
+ <entry xmlns='http://www.w3.org/2005/Atom' xmlns:gd='http://schemas.google.com/g/2005'>
3
+ <category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/contact/2008#contact'/>
4
+ <title>Elizabeth Bennet</title>
5
+ <content>My good friend, Liz. A little quick to judge sometimes, but nice girl.</content>
6
+ <gd:email rel='http://schemas.google.com/g/2005#work' primary='true' address='liz@gmail.com'/>
7
+ <gd:email rel='http://schemas.google.com/g/2005#home' address='liz@example.org'/>
8
+ <gd:phoneNumber rel='http://schemas.google.com/g/2005#work' primary='true'>
9
+ (206)555-1212
10
+ </gd:phoneNumber>
11
+ <gd:phoneNumber rel='http://schemas.google.com/g/2005#home'>
12
+ (206)555-1213
13
+ </gd:phoneNumber>
14
+ <gd:phoneNumber rel='http://schemas.google.com/g/2005#mobile'>
15
+ (206) 555-1212
16
+ </gd:phoneNumber>
17
+ <gd:im rel='http://schemas.google.com/g/2005#home'
18
+ protocol='http://schemas.google.com/g/2005#GOOGLE_TALK'
19
+ address='liz@gmail.com'/>
20
+ <gd:postalAddress rel='http://schemas.google.com/g/2005#work' primary='true'>
21
+ 1600 Amphitheatre Pkwy
22
+ Mountain View, CA 94043
23
+ </gd:postalAddress>
24
+ <gd:postalAddress rel='http://schemas.google.com/g/2005#home'>
25
+ 800 Main Street
26
+ Mountain View, CA 94041
27
+ </gd:postalAddress>
28
+ <gd:organization>
29
+ <gd:orgName>Google, Inc.</gd:orgName>
30
+ <gd:orgTitle>Tech Writer</gd:orgTitle>
31
+ </gd:organization>
32
+ </entry>
33
+
34
+ <entry>
35
+ <title>Poor Jack</title>
36
+ <content>Poor Jack doesn't have an e-mail address</content>
37
+ </entry>
38
+
39
+ <entry>
40
+ <title>William Paginate</title>
41
+ <gd:email address='will_paginate@googlegroups.com' />
42
+ </entry>
43
+
44
+ <entry>
45
+ <content>This guy doesn't have a name</content>
46
+ <gd:email address='anonymous@example.com' />
47
+ </entry>
48
+ </feed>