aeden-contacts 0.2.19 → 0.2.20

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