aeden-contacts 0.2.19 → 0.2.20

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.
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :minor: 2
3
- :patch: 19
3
+ :patch: 20
4
4
  :major: 0
@@ -6,8 +6,8 @@ module Contacts
6
6
 
7
7
  # An object that represents a single contact
8
8
  class Contact
9
- attr_reader :emails, :ims, :phones, :addresses, :organizations, :firstname, :lastname
10
- attr_accessor :name, :username, :service_id, :note
9
+ attr_reader :organizations, :firstname, :lastname
10
+ attr_accessor :name, :username, :service_id, :note, :emails, :ims,:phones, :addresses
11
11
 
12
12
  def initialize(email, name = nil, username = nil, firstname = nil, lastname = nil)
13
13
  @emails = []
@@ -126,7 +126,7 @@ module Contacts
126
126
 
127
127
  response = nil
128
128
  http.start do |http|
129
- request = Net::HTTP::Get.new("/users/@L@#{@consent_token.locationid}/rest/invitationsbyemail", {"Authorization" => "DelegatedToken dt=\"#{@consent_token.delegationtoken}\""})
129
+ request = Net::HTTP::Get.new("/users/@L@#{@consent_token.locationid}/rest/LiveContacts", {"Authorization" => "DelegatedToken dt=\"#{@consent_token.delegationtoken}\""})
130
130
  response = http.request(request)
131
131
  end
132
132
 
@@ -141,32 +141,49 @@ module Contacts
141
141
  #
142
142
  def self.parse_xml(xml)
143
143
  doc = Hpricot::XML(xml)
144
-
145
144
  contacts = []
146
- doc.search('/livecontacts/contacts/contact').each do |contact|
147
- email = contact.at('/preferredemail').inner_text
148
- email.strip!
149
-
150
- first_name = last_name = nil
151
- if first_name = contact.at('/profiles/personal/firstname')
152
- first_name = first_name.inner_text.strip
153
- end
154
-
155
- if last_name = contact.at('/profiles/personal/lastname')
156
- last_name = last_name.inner_text.strip
157
- end
145
+ doc.search('/LiveContacts/Contacts/Contact') do |contact|
158
146
 
159
- name = nil
160
- if !first_name.nil? || !last_name.nil?
161
- name = "#{first_name} #{last_name}"
162
- name.strip!
163
- end
164
- new_contact = Contact.new(email, name, nil, first_name, last_name)
165
- contacts << new_contact
166
- end
147
+ contact_id = text_value contact, "ID"
148
+
149
+ first_name = text_value contact, "Profiles/Personal/FirstName"
150
+ last_name = text_value contact, "Profiles/Personal/LastName"
151
+ name = "#{first_name} #{last_name}".strip
152
+ emails = contact.search('Emails/Email').collect {|e| text_value e, "Address"}
153
+
154
+ phones = contact.search('Phones/Phone').collect do |e|
155
+ type=convert_type(text_value(e, "PhoneType"))
156
+ { "type" => type, "value" => (text_value e, "Number") }
157
+ end
158
+
159
+ addresses = contact.search('Locations/Location').collect do |e|
160
+ street = text_value(e, "StreetLine")
161
+ postal_code = text_value(e, "PostalCode")
162
+ sub_division = text_value(e, "Subdivision")
163
+ city = text_value(e, "PrimaryCity")
164
+ country_code = text_value(e, "CountryRegion")
165
+ formatted = [street, city, sub_division, postal_code, country_code].compact.join(", ")
166
+ type = convert_type(text_value(e, "LocationType"))
167
+ { "formatted" => formatted, "type" => type, "streetAddress" => street, "locality" => city, "region" => sub_division, "postalCode" => postal_code, "country" => country_code}
168
+ end
167
169
 
170
+ new_contact = Contact.new(nil, name, nil, first_name, last_name)
171
+ new_contact.emails = emails
172
+ new_contact.phones = phones
173
+ new_contact.addresses = addresses
174
+ new_contact.service_id = contact_id
175
+ contacts << new_contact
176
+ end
168
177
  return contacts
169
178
  end
179
+
180
+ def self.text_value(elem,path)
181
+ elem.at(path).inner_text rescue nil
182
+ end
183
+
184
+ def self.convert_type(t)
185
+ {"personal" => "home", "business" => "work"}[t.downcase] || "other"
186
+ end
170
187
  end
171
188
 
172
189
  end
@@ -58,7 +58,7 @@ module Contacts
58
58
  AUTH_PATH = "/WSLogin/V1/wslogin?appid=#appid&ts=#ts"
59
59
  CREDENTIAL_PATH = "/WSLogin/V1/wspwtoken_login?appid=#appid&ts=#ts&token=#token"
60
60
  ADDRESS_BOOK_DOMAIN = "address.yahooapis.com"
61
- ADDRESS_BOOK_PATH = "/v1/searchContacts?format=json&fields=name,email&appid=#appid&WSSID=#wssid"
61
+ ADDRESS_BOOK_PATH = "/v1/searchContacts?format=json&fields=all&appid=#appid&WSSID=#wssid"
62
62
  CONFIG_FILE = File.dirname(__FILE__) + '/../config/contacts.yml'
63
63
 
64
64
  attr_reader :appid, :secret, :token, :wssid, :cookie
@@ -213,34 +213,114 @@ module Contacts
213
213
  #
214
214
  # ==== Parameters
215
215
  # * json <String>:: A String of user's contacts in JSON format
216
- #
216
+ # "fields": [
217
+ # {
218
+ # "type": "phone",
219
+ # "data": "808 123 1234",
220
+ # "home": true,
221
+ # },
222
+ # {
223
+ # "type": "email",
224
+ # "data": "martin.berner@mail.com",
225
+ # },
226
+ #
227
+ # {
228
+ # "type": "otherid",
229
+ # "data": "windowslive@msn.com",
230
+ # "msn": true,
231
+ # }
232
+ # ]
233
+ #
217
234
  def self.parse_contacts(json)
218
235
  contacts = []
219
236
  people = JSON.parse(json)
237
+
220
238
  people['contacts'].each do |contact|
221
239
  name = nil
222
240
  email = nil
223
241
  firstname = nil
224
242
  lastname = nil
225
- contact['fields'].each do |field|
226
- case field['type']
227
- when 'email'
228
- email = field['data']
229
- email.strip!
230
- when 'name'
231
- name = "#{field['first']} #{field['last']}"
232
- name.strip!
233
- lastname = field['last']
234
- firstname = field['first']
243
+
244
+ contact_fields=Yahoo.array_to_hash contact['fields']
245
+
246
+ emails = (contact_fields['email'] || []).collect {|e| e['data']}
247
+ ims = (contact_fields['otherid'] || []).collect { |im| get_type_value(im) }
248
+ phones = (contact_fields['phone'] || []).collect { |phone| get_type_value(phone) }
249
+ addresses = (contact_fields['address'] || []).collect do |address|
250
+ type=get_type(address)
251
+ type = {"home" => "home", "work" => "work"}[type.downcase] || "other"
252
+ value = [address['street'], address['city'], address['state'], address['zip'], address['country']].compact.join(", ")
253
+ {"type" => type, "value" => value}
254
+ end
255
+
256
+ name_field=(contact_fields['name'] || [])
257
+
258
+ # if name is blank, try go for the yahoo id, and if that's blank too, ignore the record altogether (probably a mailing list)
259
+ if (name_field.empty?)
260
+ if contact_fields['yahooid']
261
+ name = contact_fields['yahooid'][0]['data']
262
+ else
263
+ next
235
264
  end
265
+ else
266
+ name_field = name_field[0]
267
+ name = "#{name_field['first']} #{name_field['last']}"
268
+ name.strip!
269
+ lastname = name_field['last']
270
+ firstname = name_field['first']
236
271
  end
237
- yahoo_contact = Contact.new(email, name, nil, firstname, lastname)
272
+
273
+ yahoo_contact = Contact.new(nil, name, nil, firstname, lastname)
274
+ yahoo_contact.emails = emails
275
+ yahoo_contact.ims = ims
276
+ yahoo_contact.phones = phones
277
+ yahoo_contact.addresses = addresses
238
278
  yahoo_contact.service_id = contact['cid']
239
279
 
240
280
  contacts.push yahoo_contact
241
281
  end
242
282
  return contacts
243
283
  end
284
+
285
+ #
286
+ # grab the type field from each array item
287
+ # and turn it into a "email"=>{}, "phone"=>{} array
288
+ #
289
+ private
290
+ def self.array_to_hash(a)
291
+ (a || []).inject({}) {|x,y|
292
+ x[y['type']] ||= []
293
+ x[y['type']] << y
294
+ x
295
+ }
296
+ end
297
+
298
+ #
299
+ # return type/value from a datastructure like
300
+ # {
301
+ # "data": "808 456 7890",
302
+ # "mobile": true
303
+ # }
304
+ # -----> "type"=>"mobile", "value"=>"808 456 7890"
305
+ #
306
+ def self.get_type_value(hash)
307
+ type_field = hash.find{ |x| x[1] == true }
308
+ type = type_field ? type_field[0] : nil
309
+ {"type" => type, "value" => hash["data"]}
310
+ end
311
+
312
+ #
313
+ # return just the type from a datastructure like
314
+ # {
315
+ # "data": "808 456 7890",
316
+ # "mobile": true
317
+ # }
318
+ # -----> "mobile"
319
+ #
320
+ def self.get_type(hash)
321
+ type_field = hash.find{ |x| x[1] == true }
322
+ type = type_field ? type_field[0] : nil
323
+ end
244
324
 
245
325
  end
246
326
  class YahooToken
@@ -0,0 +1,78 @@
1
+ <?xml version="1.0"?>
2
+ <LiveContacts>
3
+
4
+ <Contacts>
5
+ <Contact>
6
+ <ID>abc</ID>
7
+ <WindowsLiveID>beenthere@hotmail.com</WindowsLiveID>
8
+
9
+ <Profiles>
10
+ <Personal>
11
+ <FirstName>Mia</FirstName>
12
+ <LastName>Pia</LastName>
13
+ <UniqueName>mia</UniqueName>
14
+ </Personal>
15
+ </Profiles>
16
+ <Emails>
17
+ <Email>
18
+ <ID>1</ID>
19
+ <EmailType>Personal</EmailType>
20
+ <Address>mia@hotmail.com</Address>
21
+ <IsIMEnabled>false</IsIMEnabled>
22
+ <IsDefault>false</IsDefault>
23
+ </Email>
24
+ <Email>
25
+ <ID>8</ID>
26
+ <EmailType>WindowsLiveID</EmailType>
27
+ <Address>othermia@yahoo.com</Address>
28
+ <IsIMEnabled>true</IsIMEnabled>
29
+ <IsDefault>false</IsDefault>
30
+ </Email>
31
+ </Emails>
32
+ <Phones>
33
+ <Phone>
34
+ <ID>1</ID>
35
+ <PhoneType>Personal</PhoneType>
36
+ <Number>(123) 123 1234</Number>
37
+ <IsIMEnabled>false</IsIMEnabled>
38
+ <IsDefault>false</IsDefault>
39
+ </Phone>
40
+ <Phone>
41
+ <ID>3</ID>
42
+ <PhoneType>Mobile</PhoneType>
43
+ <Number>(321) 555 1234</Number>
44
+ <IsIMEnabled>false</IsIMEnabled>
45
+ <IsDefault>false</IsDefault>
46
+ </Phone>
47
+ </Phones>
48
+ <Locations>
49
+ <Location>
50
+ <ID>1</ID>
51
+ <LocationType>Personal</LocationType>
52
+ <StreetLine>123 Green St</StreetLine>
53
+ <PrimaryCity>Middleville</PrimaryCity>
54
+ <Subdivision>CA</Subdivision>
55
+ <PostalCode>92123</PostalCode>
56
+ <CountryRegion>USA</CountryRegion>
57
+ <IsDefault>false</IsDefault>
58
+ </Location>
59
+ </Locations>
60
+ </Contact>
61
+ <Contact>
62
+ <ID>def</ID>
63
+ <WindowsLiveID>marcus@hotmail.com</WindowsLiveID>
64
+ <Profiles>
65
+ <Personal>
66
+ <UniqueName>marcus</UniqueName>
67
+ <DisplayName>marietta_ru@hotmail.com</DisplayName>
68
+ </Personal>
69
+ </Profiles>
70
+ <Emails>
71
+ <Email>
72
+ <EmailType>WindowsLiveID</EmailType>
73
+ <Address>marcus@hotmail.com</Address>
74
+ </Email>
75
+ </Emails>
76
+ </Contact>
77
+ </Contacts>
78
+ </LiveContacts>
@@ -34,86 +34,45 @@
34
34
  "categories":[
35
35
  ]
36
36
  },
37
- {
38
- "type":"name",
39
- "first":"Nina",
40
- "last":"Benchimol",
41
- "fid":4,
42
- "categories":[
43
- ]
44
- }
45
- ],
46
- "cid":5,
47
- "categories":[
48
- ]
49
- },
50
- {
51
- "type":"contact",
52
- "fields":[
53
- {
37
+ {
54
38
  "type":"email",
55
- "data":"and@yahoo.com",
56
- "fid":7,
39
+ "data":"nina2@yahoo.com",
40
+ "fid":5,
57
41
  "categories":[
58
42
  ]
59
43
  },
60
44
  {
61
45
  "type":"name",
62
- "first":"Andrea",
63
- "last":"Dimitri",
64
- "fid":6,
65
- "categories":[
66
- ]
67
- }
68
- ],
69
- "cid":1,
70
- "categories":[
71
- ]
72
- },
73
- {
74
- "type":"contact",
75
- "fields":[
76
- {
77
- "type":"email",
78
- "data":"ricardo@poli.usp.br",
79
- "fid":11,
46
+ "first":"Nina",
47
+ "last":"Benchimol",
48
+ "fid":4,
80
49
  "categories":[
81
50
  ]
82
51
  },
83
- {
84
- "type":"name",
85
- "first":"Ricardo",
86
- "last":"Fiorelli",
87
- "fid":10,
88
- "categories":[
89
- ]
90
- }
52
+ {
53
+ "type": "otherid",
54
+ "data": "windowslive@msn.com",
55
+ "msn": true
56
+ },
57
+ {
58
+ "type": "phone",
59
+ "data": "808 456 7890",
60
+ "mobile": true
61
+ },
62
+ {
63
+ "type": "address",
64
+ "street": "123 Home Street",
65
+ "city": "Super City",
66
+ "state": "HI",
67
+ "zip": "96815",
68
+ "country": "United States",
69
+ "home": true
70
+ }
91
71
  ],
92
- "cid":3,
72
+ "cid":5,
93
73
  "categories":[
94
74
  ]
95
75
  },
96
- {
97
- "type":"contact",
98
- "fields":[
99
- {
100
- "type":"email",
101
- "data":"pizinha@yahoo.com.br",
102
- "fid":14,
103
- "categories":[
104
- ]
105
- },
106
- {
107
- "type":"name",
108
- "first":"Priscila",
109
- "fid":13,
110
- "categories":[
111
- ]
112
- }
113
- ],
114
- "cid":2,
115
- "categories":[
116
- ]
117
- }
76
+ {"fields": [{"fid": 9, "data": "carl_larsson", "type": "yahooid", "categories": []}], "type": "contact", "categories": [], "cid": 30}
118
77
  ]
119
78
  }
@@ -10,16 +10,22 @@ describe Contacts::WindowsLive do
10
10
 
11
11
  it 'parse the XML contacts document' do
12
12
  contacts = Contacts::WindowsLive.parse_xml(contacts_xml)
13
+ contacts.size.should == 2
14
+ contacts[0].service_id == "abc"
15
+ contacts[0].name.should == "Mia Pia"
16
+ contacts[0].firstname.should == "Mia"
17
+ contacts[0].lastname.should == "Pia"
18
+ contacts[0].emails.should include("mia@hotmail.com", "othermia@yahoo.com")
19
+ contacts[0].phones.should include({"value"=>"(123) 123 1234", "type"=>"home"}, {"value"=>"(321) 555 1234", "type"=>"other"})
20
+ contacts[0].addresses.should include({"region"=>"CA", "country"=>"USA", "postalCode"=>"92123", "streetAddress"=>"123 Green St", "type"=>"home", "locality"=>"Middleville", "formatted"=>"123 Green St, Middleville, CA, 92123, USA"})
13
21
 
14
- contacts[0].name.should be_nil
15
- contacts[0].email.should == 'froz@gmail.com'
16
- contacts[1].firstname.should == "Rafael"
17
- contacts[1].lastname.should == "Timbo"
18
- contacts[1].name.should == 'Rafael Timbo'
19
- contacts[1].email.should == 'timbo@hotmail.com'
20
- contacts[2].name.should be_nil
21
- contacts[2].email.should == 'betinho@hotmail.com'
22
-
22
+ contacts[0].service_id == "def"
23
+ contacts[1].name.should == ""
24
+ contacts[1].firstname.should == nil
25
+ contacts[1].lastname.should == nil
26
+ contacts[1].emails.should include("marcus@hotmail.com")
27
+ contacts[1].phones.should be_empty
28
+ contacts[1].addresses.should be_empty
23
29
  end
24
30
 
25
31
  it 'should can be initialized by a YAML file' do
@@ -31,6 +37,6 @@ describe Contacts::WindowsLive do
31
37
  end
32
38
 
33
39
  def contacts_xml
34
- File.open(@path + 'wl_contacts.xml', 'r+').read
40
+ File.open(@path + 'wl_full_contacts.xml', 'r+').read
35
41
  end
36
42
  end
@@ -20,12 +20,7 @@ describe Contacts::Yahoo do
20
20
  redirect_path = '/?appid=i%3DB%26p%3DUw70JGIdHWVRbpqYItcMw--&token=AB.KoEg8vBwvJKFkwfcDTJEMKhGeAD6KhiDe0aZLCvoJzMeQG00-&appdata=&ts=1218501215&sig=d381fba89c7e9d3c14788720733c3fbf'
21
21
 
22
22
  results = @yahoo.contacts(redirect_path)
23
- results.should have_contact('Hugo Barauna', 'hugo.barauna@gmail.com', 4)
24
- results.should have_contact('Nina Benchimol', 'nina@hotmail.com', 5)
25
- results.should have_contact('Andrea Dimitri', 'and@yahoo.com',1)
26
- results.should have_contact('Ricardo Fiorelli', 'ricardo@poli.usp.br',3)
27
- results.should have_contact('Priscila', 'pizinha@yahoo.com.br', 2)
28
- end
23
+ results.size.should == 3 end
29
24
 
30
25
  it 'should validate yahoo redirect signature' do
31
26
  redirect_path = '/?appid=i%3DB%26p%3DUw70JGIdHWVRbpqYItcMw--&token=AB.KoEg8vBwvJKFkwfcDTJEMKhGeAD6KhiDe0aZLCvoJzMeQG00-&appdata=&ts=1218501215&sig=d381fba89c7e9d3c14788720733c3fbf'
@@ -58,10 +53,14 @@ describe Contacts::Yahoo do
58
53
  json = read_file('yh_contacts.txt')
59
54
 
60
55
  Contacts::Yahoo.parse_contacts(json).should have_contact('Hugo Barauna', 'hugo.barauna@gmail.com', 4)
61
- Contacts::Yahoo.parse_contacts(json).should have_contact('Nina Benchimol', 'nina@hotmail.com', 5)
62
- Contacts::Yahoo.parse_contacts(json).should have_contact('Andrea Dimitri', 'and@yahoo.com', 1)
63
- Contacts::Yahoo.parse_contacts(json).should have_contact('Ricardo Fiorelli', 'ricardo@poli.usp.br', 3)
64
- Contacts::Yahoo.parse_contacts(json).should have_contact('Priscila', 'pizinha@yahoo.com.br', 2)
56
+ Contacts::Yahoo.parse_contacts(json).should have_contact('Nina Benchimol',
57
+ ['nina@hotmail.com','nina2@yahoo.com'],
58
+ 5,
59
+ [{"type" => "msn", "value" => "windowslive@msn.com"}],
60
+ [{"type" => "mobile", "value" => "808 456 7890"}],
61
+ [{"type" => "home", "value" => "123 Home Street, Super City, HI, 96815, United States"}]
62
+ )
63
+ Contacts::Yahoo.parse_contacts(json).should have_contact('carl_larsson','',30)
65
64
  end
66
65
 
67
66
  it 'should can be initialized by a YAML file' do
@@ -73,10 +72,19 @@ describe Contacts::Yahoo do
73
72
  File.open(@path + file, 'r+').read
74
73
  end
75
74
 
76
- def have_contact(name, email, cid)
75
+ def have_contact(name, email, cid, ims = [], phones = [], addresses = [])
76
+ email = [email] if email.is_a? String
77
77
  matcher_class = Class.new()
78
78
  matcher_class.instance_eval do
79
- define_method(:matches?) {|some_contacts| some_contacts.any? {|a_contact| a_contact.name == name && a_contact.emails.include?(email) && a_contact.service_id == cid}}
79
+ define_method(:matches?) {|some_contacts| some_contacts.any? {|a_contact|
80
+ a_contact.name == name && \
81
+ ((a_contact.emails - email).empty?) && \
82
+ ((a_contact.ims - ims).empty?) && \
83
+ ((a_contact.phones - phones).empty?) && \
84
+ ((a_contact.addresses - addresses).empty?) && \
85
+ a_contact.service_id == cid
86
+ }
87
+ }
80
88
  end
81
89
  matcher_class.new
82
90
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aeden-contacts
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.19
4
+ version: 0.2.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - "Mislav Marohni\xC6\x92\xC3\xA1"
@@ -11,7 +11,7 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
 
14
- date: 2009-07-23 00:00:00 -07:00
14
+ date: 2009-07-29 00:00:00 -07:00
15
15
  default_executable:
16
16
  dependencies: []
17
17
 
@@ -43,6 +43,7 @@ files:
43
43
  - spec/feeds/google-many.xml
44
44
  - spec/feeds/google-single.xml
45
45
  - spec/feeds/wl_contacts.xml
46
+ - spec/feeds/wl_full_contacts.xml
46
47
  - spec/feeds/yh_contacts.txt
47
48
  - spec/feeds/yh_credential.xml
48
49
  - spec/flickr/auth_spec.rb
@@ -74,6 +75,7 @@ files:
74
75
  - vendor/windowslivelogin.rb
75
76
  has_rdoc: true
76
77
  homepage: http://github.com/aeden/contacts
78
+ licenses:
77
79
  post_install_message:
78
80
  rdoc_options:
79
81
  - --charset=UTF-8
@@ -94,7 +96,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
96
  requirements: []
95
97
 
96
98
  rubyforge_project:
97
- rubygems_version: 1.2.0
99
+ rubygems_version: 1.3.5
98
100
  signing_key:
99
101
  specification_version: 2
100
102
  summary: Ruby library for consuming Google, Yahoo!, Flickr and Windows Live contact APIs