googlecontacts 0.1.3 → 0.1.4

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.
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