googlecontacts 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009 Pieter Noordhuis
1
+ Copyright (c) 2010 Pieter Noordhuis
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.3
1
+ 0.1.4
@@ -117,7 +117,7 @@ module GoogleContacts
117
117
 
118
118
  # Try to proxy missing method to one of the proxies
119
119
  def method_missing(sym, *args, &blk)
120
- if sym.to_s =~ /^(\w+)(=)?$/ && @proxies[$1.to_sym]
120
+ if sym.to_s =~ /^(\w+)(=)?$/ && @proxies[$1]
121
121
  if $2
122
122
  @proxies[$1].replace(args.first)
123
123
  else
@@ -25,6 +25,10 @@ module GoogleContacts
25
25
  properties[prop] = value
26
26
  end
27
27
 
28
+ def email
29
+ emails.primary.try(:address)
30
+ end
31
+
28
32
  def email=(address)
29
33
  emails[address].primary!
30
34
  end
@@ -10,9 +10,9 @@ module GoogleContacts
10
10
  end
11
11
 
12
12
  def reinitialize
13
- @current = @parent.xml.xpath("./#{@tag}").map do |entry|
13
+ @current = sanitize(@parent.xml.xpath("./#{@tag}").map do |entry|
14
14
  entry[@attr]
15
- end.compact.uniq.sort
15
+ end)
16
16
 
17
17
  # create a deep copy
18
18
  @new = @current.map { |item| item.dup }
@@ -29,17 +29,35 @@ module GoogleContacts
29
29
  end
30
30
  end
31
31
 
32
- def push(item)
33
- item = item.href if item.respond_to?(:href)
34
- method_missing(:push, item)
32
+ def replace(content)
33
+ @new = sanitize([content].flatten)
35
34
  end
36
- alias :<< :push
37
-
35
+
38
36
  private
37
+ # Extract href from arguments if the operation changes
38
+ # the contents of the array.
39
39
  def method_missing(sym, *args, &blk)
40
- ret = @new.send(sym, *args, &blk)
41
- @new = @new.compact.uniq.sort
42
- ret
40
+ if [:<<, :push, :+, :-, :concat].include?(sym)
41
+ args = href_from_items(*args)
42
+ end
43
+
44
+ result = @new.send(sym, *args, &blk)
45
+ @new = sanitize(@new)
46
+ result
47
+ end
48
+
49
+ def sanitize(array)
50
+ array.compact.uniq.sort
51
+ end
52
+
53
+ def href_from_items(*items)
54
+ items.map do |item|
55
+ if item.is_a?(::Array)
56
+ href_from_items(*item)
57
+ else
58
+ item.respond_to?(:href) ? item.href : item
59
+ end
60
+ end
43
61
  end
44
62
  end # class Array
45
63
  end # module Proxies
@@ -22,6 +22,10 @@ module GoogleContacts
22
22
  @current != @new
23
23
  end
24
24
 
25
+ def primary
26
+ @new.values.find { |email| email.primary? } || @new.values.first
27
+ end
28
+
25
29
  def primary!(address)
26
30
  @new.each do |key, email|
27
31
  if key == address
@@ -52,8 +52,21 @@ module GoogleContacts
52
52
  end
53
53
 
54
54
  def find(what, options = {}, &blk)
55
- xml = get("http://www.google.com/m8/feeds/#{what}/default/full", options)
56
- xml.xpath('/xmlns:feed/xmlns:entry').map(&blk)
55
+ options['max-results'] ||= 200
56
+ options['start-index'] = 1
57
+
58
+ result = []
59
+ begin
60
+ xml = get("http://www.google.com/m8/feeds/#{what}/default/full", options)
61
+ result.concat xml.xpath('/xmlns:feed/xmlns:entry').map(&blk)
62
+
63
+ total_results = xml.at('//openSearch:totalResults').text.to_i
64
+ start_index = xml.at('//openSearch:startIndex' ).text.to_i
65
+ per_page = xml.at('//openSearch:itemsPerPage').text.to_i
66
+ options['start-index'] = start_index + per_page
67
+ end while (options['start-index'] <= total_results)
68
+
69
+ result
57
70
  end
58
71
 
59
72
  def save(instance)
@@ -66,8 +79,6 @@ module GoogleContacts
66
79
  def append_to_batch(entry)
67
80
  if @batching
68
81
  if @batch.present?
69
- # puts @batch.last.a
70
- # puts @batch.last.at('./category').inspect
71
82
  batch_term = @batch.last.at('./category')['term']
72
83
  entry_term = entry.at('./category')['term']
73
84
  raise "Cannot mix Contact and Group in one batch" if batch_term != entry_term
@@ -1,2 +1,2 @@
1
1
  # For convenience
2
- require './google_contacts'
2
+ require 'google_contacts'
@@ -0,0 +1,60 @@
1
+ <feed xmlns='http://www.w3.org/2005/Atom'
2
+ xmlns:openSearch='http://a9.com/-/spec/opensearch/1.1/'
3
+ xmlns:gContact='http://schemas.google.com/contact/2008'
4
+ xmlns:batch='http://schemas.google.com/gdata/batch'
5
+ xmlns:gd='http://schemas.google.com/g/2005'
6
+ gd:etag='W/"CUMBRHo_fip7ImA9WxRbGU0."'>
7
+ <id>liz@gmail.com</id>
8
+ <updated>2008-12-10T10:04:15.446Z</updated>
9
+ <category scheme='http://schemas.google.com/g/2005#kind'
10
+ term='http://schemas.google.com/contact/2008#contact' />
11
+ <title>Elizabeth Bennet's Contacts</title>
12
+ <link rel='http://schemas.google.com/g/2005#feed'
13
+ type='application/atom+xml'
14
+ href='http://www.google.com/m8/feeds/contacts/liz%40gmail.com/full' />
15
+ <link rel='http://schemas.google.com/g/2005#post'
16
+ type='application/atom+xml'
17
+ href='http://www.google.com/m8/feeds/contacts/liz%40gmail.com/full' />
18
+ <link rel='http://schemas.google.com/g/2005#batch'
19
+ type='application/atom+xml'
20
+ href='http://www.google.com/m8/feeds/contacts/liz%40gmail.com/full/batch' />
21
+ <link rel='self' type='application/atom+xml'
22
+ href='http://www.google.com/m8/feeds/contacts/liz%40gmail.com/full?max-results=25' />
23
+ <author>
24
+ <name>Elizabeth Bennet</name>
25
+ <email>liz@gmail.com</email>
26
+ </author>
27
+ <generator version='1.0' uri='http://www.google.com/m8/feeds'>
28
+ Contacts
29
+ </generator>
30
+ <openSearch:totalResults>40</openSearch:totalResults>
31
+ <openSearch:startIndex>1</openSearch:startIndex>
32
+ <openSearch:itemsPerPage>25</openSearch:itemsPerPage>
33
+ <entry gd:etag='"Qn04eTVSLyp7ImA9WxRbGEUORAQ."'>
34
+ <id>
35
+ http://www.google.com/m8/feeds/contacts/liz%40gmail.com/base/c9012de
36
+ </id>
37
+ <updated>2008-12-10T04:45:03.331Z</updated>
38
+ <app:edited xmlns:app='http://www.w3.org/2007/app'>2008-12-10T04:45:03.331Z</app:edited>
39
+ <category scheme='http://schemas.google.com/g/2005#kind'
40
+ term='http://schemas.google.com/contact/2008#contact' />
41
+ <title>Fitzwilliam Darcy</title>
42
+ <gd:name>
43
+ <gd:fullName>Fitzwilliam Darcy</gd:fullName>
44
+ </gd:name>
45
+ <link rel='http://schemas.google.com/contacts/2008/rel#photo' type='image/*'
46
+ href='http://www.google.com/m8/feeds/photos/media/liz%40gmail.com/c9012de'
47
+ gd:etag='"KTlcZWs1bCp7ImBBPV43VUV4LXEZCXERZAc."' />
48
+ <link rel='self' type='application/atom+xml'
49
+ href='http://www.google.com/m8/feeds/contacts/liz%40gmail.com/full/c9012de' />
50
+ <link rel='edit' type='application/atom+xml'
51
+ href='http://www.google.com/m8/feeds/contacts/liz%40gmail.com/full/c9012de' />
52
+ <gd:phoneNumber rel='http://schemas.google.com/g/2005#home'
53
+ primary='true'>
54
+ 456
55
+ </gd:phoneNumber>
56
+ <gd:extendedProperty name='pet' value='hamster' />
57
+ <gContact:groupMembershipInfo deleted='false'
58
+ href='http://www.google.com/m8/feeds/groups/liz%40gmail.com/base/270f' />
59
+ </entry>
60
+ </feed>
@@ -0,0 +1,60 @@
1
+ <feed xmlns='http://www.w3.org/2005/Atom'
2
+ xmlns:openSearch='http://a9.com/-/spec/opensearch/1.1/'
3
+ xmlns:gContact='http://schemas.google.com/contact/2008'
4
+ xmlns:batch='http://schemas.google.com/gdata/batch'
5
+ xmlns:gd='http://schemas.google.com/g/2005'
6
+ gd:etag='W/"CUMBRHo_fip7ImA9WxRbGU0."'>
7
+ <id>liz@gmail.com</id>
8
+ <updated>2008-12-10T10:04:15.446Z</updated>
9
+ <category scheme='http://schemas.google.com/g/2005#kind'
10
+ term='http://schemas.google.com/contact/2008#contact' />
11
+ <title>Elizabeth Bennet's Contacts</title>
12
+ <link rel='http://schemas.google.com/g/2005#feed'
13
+ type='application/atom+xml'
14
+ href='http://www.google.com/m8/feeds/contacts/liz%40gmail.com/full' />
15
+ <link rel='http://schemas.google.com/g/2005#post'
16
+ type='application/atom+xml'
17
+ href='http://www.google.com/m8/feeds/contacts/liz%40gmail.com/full' />
18
+ <link rel='http://schemas.google.com/g/2005#batch'
19
+ type='application/atom+xml'
20
+ href='http://www.google.com/m8/feeds/contacts/liz%40gmail.com/full/batch' />
21
+ <link rel='self' type='application/atom+xml'
22
+ href='http://www.google.com/m8/feeds/contacts/liz%40gmail.com/full?max-results=25' />
23
+ <author>
24
+ <name>Elizabeth Bennet</name>
25
+ <email>liz@gmail.com</email>
26
+ </author>
27
+ <generator version='1.0' uri='http://www.google.com/m8/feeds'>
28
+ Contacts
29
+ </generator>
30
+ <openSearch:totalResults>40</openSearch:totalResults>
31
+ <openSearch:startIndex>26</openSearch:startIndex>
32
+ <openSearch:itemsPerPage>25</openSearch:itemsPerPage>
33
+ <entry gd:etag='"Qn04eTVSLyp7ImA9WxRbGEUORAQ."'>
34
+ <id>
35
+ http://www.google.com/m8/feeds/contacts/liz%40gmail.com/base/c9012de
36
+ </id>
37
+ <updated>2008-12-10T04:45:03.331Z</updated>
38
+ <app:edited xmlns:app='http://www.w3.org/2007/app'>2008-12-10T04:45:03.331Z</app:edited>
39
+ <category scheme='http://schemas.google.com/g/2005#kind'
40
+ term='http://schemas.google.com/contact/2008#contact' />
41
+ <title>Fitzwilliam Darcy</title>
42
+ <gd:name>
43
+ <gd:fullName>Fitzwilliam Darcy</gd:fullName>
44
+ </gd:name>
45
+ <link rel='http://schemas.google.com/contacts/2008/rel#photo' type='image/*'
46
+ href='http://www.google.com/m8/feeds/photos/media/liz%40gmail.com/c9012de'
47
+ gd:etag='"KTlcZWs1bCp7ImBBPV43VUV4LXEZCXERZAc."' />
48
+ <link rel='self' type='application/atom+xml'
49
+ href='http://www.google.com/m8/feeds/contacts/liz%40gmail.com/full/c9012de' />
50
+ <link rel='edit' type='application/atom+xml'
51
+ href='http://www.google.com/m8/feeds/contacts/liz%40gmail.com/full/c9012de' />
52
+ <gd:phoneNumber rel='http://schemas.google.com/g/2005#home'
53
+ primary='true'>
54
+ 456
55
+ </gd:phoneNumber>
56
+ <gd:extendedProperty name='pet' value='hamster' />
57
+ <gContact:groupMembershipInfo deleted='false'
58
+ href='http://www.google.com/m8/feeds/groups/liz%40gmail.com/base/270f' />
59
+ </entry>
60
+ </feed>
@@ -84,12 +84,22 @@ describe GoogleContacts::Contact do
84
84
  @contact.changed?.should be_true
85
85
  end
86
86
 
87
+ it "should have no groups" do
88
+ @contact.groups.should be_empty
89
+ end
90
+
87
91
  it "should be possible to set the default email address" do
88
92
  @contact.email = 'foo@bar.com'
89
93
  @contact.emails['foo@bar.com'].should be_primary
90
94
  @contact.emails.size.should == 1
91
95
  end
92
96
 
97
+ it "should provide access to the contact's primary email address" do
98
+ @contact.email.should be_nil
99
+ @contact.email = 'foo@bar.com'
100
+ @contact.email.should == 'foo@bar.com'
101
+ end
102
+
93
103
  describe "when updating" do
94
104
  it "should update the title-tag" do
95
105
  @contact.xml.at('./xmlns:title').should be_nil
@@ -99,4 +109,43 @@ describe GoogleContacts::Contact do
99
109
  end
100
110
  end
101
111
  end
112
+
113
+ describe "operations" do
114
+ before(:each) do
115
+ @contact = GoogleContacts::Contact.new(wrapper)
116
+ @root = @contact.xml.document.root
117
+ end
118
+
119
+ describe "on groups" do
120
+ before(:each) do
121
+ @groups = [stub('group1', :href => 'foo'), stub('group2', :href => 'bar')]
122
+ end
123
+
124
+ it "should be possible to add an array of groups" do
125
+ @contact.groups += @groups
126
+ @contact.groups.should == ['foo', 'bar'].sort
127
+ end
128
+
129
+ it "should be possible to add an array of urls" do
130
+ @contact.groups += ['foo', 'bar']
131
+ @contact.groups.should == ['foo', 'bar'].sort
132
+ end
133
+
134
+ describe "with initial content" do
135
+ before(:each) do
136
+ @contact.groups = ['foo', 'bar', 'quux']
137
+ end
138
+
139
+ it "should be possible to remove an array of groups" do
140
+ @contact.groups -= @groups
141
+ @contact.groups.should == ['quux']
142
+ end
143
+
144
+ it "should be possible to remove an array of urls" do
145
+ @contact.groups -= ['foo', 'bar']
146
+ @contact.groups.should == ['quux']
147
+ end
148
+ end
149
+ end
150
+ end
102
151
  end
@@ -27,6 +27,10 @@ describe GoogleContacts::Proxies::Emails do
27
27
  @proxy['fubar@gmail.com' ] .should be_primary
28
28
  end
29
29
 
30
+ it "should be able to return the primary address" do
31
+ @proxy.primary.should == @proxy['fubar@gmail.com']
32
+ end
33
+
30
34
  it "should initially be unchanged" do
31
35
  @proxy.changed?.should be_false
32
36
  end
@@ -2,23 +2,43 @@ require File.dirname(__FILE__) + '/spec_helper'
2
2
 
3
3
  describe GoogleContacts::Wrapper do
4
4
  describe "fetching" do
5
+ before(:each) do
6
+ @options = { 'max-results' => 200, 'start-index' => 1 }
7
+ end
8
+
9
+ def params(overrides)
10
+ @options.merge(overrides).map do |tuple|
11
+ tuple.join('=')
12
+ end.join('&')
13
+ end
14
+
15
+ def register(type, options = {})
16
+ url = "http://www.google.com/m8/feeds/#{type}/default/full?#{params(options)}"
17
+ FakeWeb.register_uri(:get, url, :body => yield)
18
+ end
19
+
5
20
  it "should be able to get list of contacts" do
6
- FakeWeb.register_uri(:get,
7
- 'http://www.google.com/m8/feeds/contacts/default/full',
8
- :body => asset('contacts_full')
9
- )
10
-
21
+ register(:contacts) { asset('contacts_full') }
11
22
  result = wrapper.contacts.find(:all)
12
23
  result.should have(1).contact
13
24
  result.first.should be_a GoogleContacts::Contact
14
25
  end
15
26
 
27
+ it "should be able to get list of contacts when result is paginated" do
28
+ register(:contacts, 'start-index' => 1) { asset('contacts_full_page1') }
29
+ register(:contacts, 'start-index' => 26) { asset('contacts_full_page2') }
30
+ result = wrapper.contacts.find(:all)
31
+ result.should have(2).contacts
32
+ end
33
+
34
+ it "should be possible to specify the max-results parameter" do
35
+ register(:contacts, 'max-results' => 25) { asset('contacts_full') }
36
+ result = wrapper.contacts.find(:all, 'max-results' => 25)
37
+ result.should have(1).contact
38
+ end
39
+
16
40
  it "should be able to get list of groups" do
17
- FakeWeb.register_uri(:get,
18
- 'http://www.google.com/m8/feeds/groups/default/full',
19
- :body => asset('groups_full')
20
- )
21
-
41
+ register(:groups) { asset('groups_full') }
22
42
  result = wrapper.groups.find(:all)
23
43
  result.should have(2).groups
24
44
  result.first.should be_a GoogleContacts::Group
@@ -53,6 +73,11 @@ describe GoogleContacts::Wrapper do
53
73
  end
54
74
  end
55
75
 
76
+ it "should not flush when there are no operations to execute" do
77
+ wrapper.expects(:post).never
78
+ wrapper.batch {}
79
+ end
80
+
56
81
  it "should raise when mixing contacts and groups in one batch" do
57
82
  lambda {
58
83
  wrapper.batch {
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: googlecontacts
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 4
9
+ version: 0.1.4
5
10
  platform: ruby
6
11
  authors:
7
12
  - Pieter Noordhuis
@@ -9,69 +14,93 @@ autorequire:
9
14
  bindir: bin
10
15
  cert_chain: []
11
16
 
12
- date: 2010-01-27 00:00:00 +01:00
17
+ date: 2010-03-14 00:00:00 +01:00
13
18
  default_executable:
14
19
  dependencies:
15
20
  - !ruby/object:Gem::Dependency
16
21
  name: rspec
17
- type: :development
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
20
24
  requirements:
21
25
  - - ">="
22
26
  - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 2
30
+ - 9
23
31
  version: 1.2.9
24
- version:
32
+ type: :development
33
+ version_requirements: *id001
25
34
  - !ruby/object:Gem::Dependency
26
35
  name: fakeweb
27
- type: :development
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
30
38
  requirements:
31
39
  - - ">="
32
40
  - !ruby/object:Gem::Version
41
+ segments:
42
+ - 1
43
+ - 2
44
+ - 8
33
45
  version: 1.2.8
34
- version:
46
+ type: :development
47
+ version_requirements: *id002
35
48
  - !ruby/object:Gem::Dependency
36
49
  name: mocha
37
- type: :development
38
- version_requirement:
39
- version_requirements: !ruby/object:Gem::Requirement
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
40
52
  requirements:
41
53
  - - ">="
42
54
  - !ruby/object:Gem::Version
55
+ segments:
56
+ - 0
57
+ - 9
58
+ - 8
43
59
  version: 0.9.8
44
- version:
60
+ type: :development
61
+ version_requirements: *id003
45
62
  - !ruby/object:Gem::Dependency
46
63
  name: activesupport
47
- type: :runtime
48
- version_requirement:
49
- version_requirements: !ruby/object:Gem::Requirement
64
+ prerelease: false
65
+ requirement: &id004 !ruby/object:Gem::Requirement
50
66
  requirements:
51
67
  - - ">="
52
68
  - !ruby/object:Gem::Version
69
+ segments:
70
+ - 2
71
+ - 3
72
+ - 4
53
73
  version: 2.3.4
54
- version:
74
+ type: :runtime
75
+ version_requirements: *id004
55
76
  - !ruby/object:Gem::Dependency
56
77
  name: nokogiri
57
- type: :runtime
58
- version_requirement:
59
- version_requirements: !ruby/object:Gem::Requirement
78
+ prerelease: false
79
+ requirement: &id005 !ruby/object:Gem::Requirement
60
80
  requirements:
61
81
  - - ">="
62
82
  - !ruby/object:Gem::Version
83
+ segments:
84
+ - 1
85
+ - 4
86
+ - 1
63
87
  version: 1.4.1
64
- version:
88
+ type: :runtime
89
+ version_requirements: *id005
65
90
  - !ruby/object:Gem::Dependency
66
91
  name: oauth
67
- type: :runtime
68
- version_requirement:
69
- version_requirements: !ruby/object:Gem::Requirement
92
+ prerelease: false
93
+ requirement: &id006 !ruby/object:Gem::Requirement
70
94
  requirements:
71
95
  - - ">="
72
96
  - !ruby/object:Gem::Version
97
+ segments:
98
+ - 0
99
+ - 3
100
+ - 6
73
101
  version: 0.3.6
74
- version:
102
+ type: :runtime
103
+ version_requirements: *id006
75
104
  description: Google Contacts API implementation
76
105
  email: pcnoordhuis@gmail.com
77
106
  executables: []
@@ -101,6 +130,8 @@ files:
101
130
  - lib/google_contacts/wrapper.rb
102
131
  - lib/googlecontacts.rb
103
132
  - spec/assets/contacts_full.xml
133
+ - spec/assets/contacts_full_page1.xml
134
+ - spec/assets/contacts_full_page2.xml
104
135
  - spec/assets/groups_full.xml
105
136
  - spec/base_spec.rb
106
137
  - spec/contact_spec.rb
@@ -126,18 +157,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
126
157
  requirements:
127
158
  - - ">="
128
159
  - !ruby/object:Gem::Version
160
+ segments:
161
+ - 0
129
162
  version: "0"
130
- version:
131
163
  required_rubygems_version: !ruby/object:Gem::Requirement
132
164
  requirements:
133
165
  - - ">="
134
166
  - !ruby/object:Gem::Version
167
+ segments:
168
+ - 0
135
169
  version: "0"
136
- version:
137
170
  requirements: []
138
171
 
139
172
  rubyforge_project:
140
- rubygems_version: 1.3.5
173
+ rubygems_version: 1.3.6
141
174
  signing_key:
142
175
  specification_version: 3
143
176
  summary: Contacts API on steroids