googlecontacts 0.1.8 → 0.2.0
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/.gitignore +4 -0
- data/Gemfile +9 -0
- data/LICENSE +1 -1
- data/Rakefile +4 -47
- data/googlecontacts.gemspec +23 -0
- data/lib/google_contacts.rb +13 -15
- data/lib/google_contacts/auth.rb +5 -5
- data/lib/google_contacts/base.rb +38 -30
- data/lib/google_contacts/contact.rb +16 -5
- data/lib/google_contacts/group.rb +4 -2
- data/lib/google_contacts/proxies/emails.rb +15 -8
- data/lib/google_contacts/proxies/hash.rb +2 -2
- data/lib/google_contacts/proxies/tag.rb +1 -1
- data/lib/google_contacts/version.rb +3 -0
- data/lib/google_contacts/wrapper.rb +16 -16
- data/lib/googlecontacts.rb +1 -1
- data/spec/base_spec.rb +35 -35
- data/spec/contact_spec.rb +30 -30
- data/spec/group_spec.rb +5 -5
- data/spec/proxies/array_spec.rb +19 -19
- data/spec/proxies/emails_spec.rb +54 -52
- data/spec/proxies/hash_spec.rb +14 -14
- data/spec/proxies/tag_spec.rb +9 -8
- data/spec/spec_helper.rb +8 -15
- data/spec/wrapper_spec.rb +25 -25
- metadata +43 -88
- data/VERSION +0 -1
- data/spec/spec.opts +0 -2
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
CHANGED
data/Rakefile
CHANGED
@@ -1,51 +1,8 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
Jeweler::Tasks.new do |gem|
|
7
|
-
gem.name = "googlecontacts"
|
8
|
-
gem.summary = %Q{Contacts API on steroids}
|
9
|
-
gem.description = %Q{Google Contacts API implementation}
|
10
|
-
gem.email = "pcnoordhuis@gmail.com"
|
11
|
-
gem.homepage = "http://github.com/pietern/googlecontacts"
|
12
|
-
gem.authors = ["Pieter Noordhuis"]
|
13
|
-
gem.add_development_dependency "rspec", ">= 1.2.9"
|
14
|
-
gem.add_development_dependency "fakeweb", ">= 1.2.8"
|
15
|
-
gem.add_development_dependency "mocha", ">= 0.9.8"
|
16
|
-
gem.add_runtime_dependency "activesupport", ">= 2.3.4"
|
17
|
-
gem.add_runtime_dependency "nokogiri", ">= 1.4.1"
|
18
|
-
gem.add_runtime_dependency "oauth", ">= 0.3.6"
|
19
|
-
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
20
|
-
end
|
21
|
-
Jeweler::GemcutterTasks.new
|
22
|
-
rescue LoadError
|
23
|
-
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
4
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
5
|
+
t.rspec_opts = ['--backtrace', '--color', '--format nested']
|
24
6
|
end
|
25
7
|
|
26
|
-
require 'spec/rake/spectask'
|
27
|
-
Spec::Rake::SpecTask.new(:spec) do |spec|
|
28
|
-
spec.libs << 'lib' << 'spec'
|
29
|
-
spec.spec_files = FileList['spec/**/*_spec.rb']
|
30
|
-
end
|
31
|
-
|
32
|
-
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
33
|
-
spec.libs << 'lib' << 'spec'
|
34
|
-
spec.pattern = 'spec/**/*_spec.rb'
|
35
|
-
spec.rcov = true
|
36
|
-
spec.rcov_opts += ['--exclude', ENV['GEM_HOME']]
|
37
|
-
end
|
38
|
-
|
39
|
-
task :spec => :check_dependencies
|
40
|
-
|
41
8
|
task :default => :spec
|
42
|
-
|
43
|
-
require 'rake/rdoctask'
|
44
|
-
Rake::RDocTask.new do |rdoc|
|
45
|
-
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
46
|
-
|
47
|
-
rdoc.rdoc_dir = 'rdoc'
|
48
|
-
rdoc.title = "googlecontacts #{version}"
|
49
|
-
rdoc.rdoc_files.include('README*')
|
50
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
51
|
-
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "google_contacts/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "googlecontacts"
|
7
|
+
s.version = GoogleContacts::VERSION
|
8
|
+
s.authors = ["Pieter Noordhuis"]
|
9
|
+
s.email = ["pcnoordhuis@gmail.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Google Contacts API implementation}
|
12
|
+
|
13
|
+
s.rubyforge_project = "googlecontacts"
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_runtime_dependency "activesupport", "~> 3.0"
|
21
|
+
s.add_runtime_dependency "nokogiri", "~> 1.5.0"
|
22
|
+
s.add_runtime_dependency "oauth", "~> 0.4.5"
|
23
|
+
end
|
data/lib/google_contacts.rb
CHANGED
@@ -1,18 +1,16 @@
|
|
1
1
|
$:.unshift File.dirname(__FILE__)
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require 'nokogiri'
|
2
|
+
require "rubygems"
|
3
|
+
require "active_support/basic_object"
|
4
|
+
require "active_support/core_ext/hash"
|
5
|
+
require "oauth"
|
6
|
+
require "nokogiri"
|
8
7
|
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require 'google_contacts/group'
|
8
|
+
require "google_contacts/auth"
|
9
|
+
require "google_contacts/wrapper"
|
10
|
+
require "google_contacts/contact"
|
11
|
+
require "google_contacts/group"
|
14
12
|
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
18
|
-
require
|
13
|
+
require "google_contacts/proxies/array"
|
14
|
+
require "google_contacts/proxies/hash"
|
15
|
+
require "google_contacts/proxies/emails"
|
16
|
+
require "google_contacts/proxies/tag"
|
data/lib/google_contacts/auth.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
module GoogleContacts
|
2
2
|
class Auth
|
3
3
|
GOOGLE_OAUTH = {
|
4
|
-
:site =>
|
5
|
-
:request_token_path =>
|
6
|
-
:authorize_path =>
|
7
|
-
:access_token_path =>
|
4
|
+
:site => "https://www.google.com",
|
5
|
+
:request_token_path => "/accounts/OAuthGetRequestToken",
|
6
|
+
:authorize_path => "/accounts/OAuthAuthorizeToken",
|
7
|
+
:access_token_path => "/accounts/OAuthGetAccessToken",
|
8
8
|
}.freeze
|
9
9
|
|
10
10
|
class << self
|
@@ -21,7 +21,7 @@ module GoogleContacts
|
|
21
21
|
self.class.consumer.get_request_token({
|
22
22
|
:oauth_callback => options[:callback]
|
23
23
|
}, {
|
24
|
-
:scope =>
|
24
|
+
:scope => "http://www.google.com/m8/feeds/"
|
25
25
|
})
|
26
26
|
end
|
27
27
|
end
|
data/lib/google_contacts/base.rb
CHANGED
@@ -1,27 +1,29 @@
|
|
1
|
+
require "time"
|
2
|
+
|
1
3
|
module GoogleContacts
|
2
4
|
class Base
|
3
5
|
NAMESPACES = {
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
"atom" => "http://www.w3.org/2005/Atom",
|
7
|
+
"openSearch" => "http://a9.com/-/spec/opensearch/1.1/",
|
8
|
+
"gContact" => "http://schemas.google.com/contact/2008",
|
9
|
+
"batch" => "http://schemas.google.com/gdata/batch",
|
10
|
+
"gd" => "http://schemas.google.com/g/2005",
|
9
11
|
}.freeze
|
10
12
|
|
11
13
|
attr_reader :xml
|
12
14
|
def initialize(wrapper, xml = nil)
|
13
|
-
raise "Cannot create instance of Base" if self.class.name.split(/::/).last ==
|
15
|
+
raise "Cannot create instance of Base" if self.class.name.split(/::/).last == "Base"
|
14
16
|
@wrapper = wrapper
|
15
17
|
|
16
18
|
# If a root node is given, create a new XML document based on
|
17
19
|
# a deep copy. Otherwise, initialize a new XML document.
|
18
|
-
@xml = if xml
|
20
|
+
@xml = if xml
|
19
21
|
self.class.new_xml_document(xml).root
|
20
22
|
else
|
21
23
|
self.class.initialize_xml_document.root
|
22
24
|
end
|
23
25
|
|
24
|
-
@proxies =
|
26
|
+
@proxies = Hash.new
|
25
27
|
register_base_proxies
|
26
28
|
end
|
27
29
|
|
@@ -40,40 +42,40 @@ module GoogleContacts
|
|
40
42
|
end
|
41
43
|
|
42
44
|
def self.feed_for_batch
|
43
|
-
new_xml_document(
|
45
|
+
new_xml_document("feed").root
|
44
46
|
end
|
45
47
|
|
46
48
|
# Create new XML::Document that can be used in a
|
47
49
|
# Google Contacts batch operation.
|
48
50
|
def entry_for_batch(operation)
|
49
51
|
root = self.class.new_xml_document(xml).root
|
50
|
-
root.xpath(
|
51
|
-
root.xpath(
|
52
|
+
root.xpath("./xmlns:link" ).remove
|
53
|
+
root.xpath("./xmlns:updated").remove
|
52
54
|
|
53
55
|
if operation == :update || operation == :delete
|
54
|
-
root.at(
|
56
|
+
root.at("./xmlns:id").content = url(:edit)
|
55
57
|
end
|
56
58
|
|
57
|
-
self.class.insert_xml(root,
|
58
|
-
self.class.insert_xml(root,
|
59
|
+
self.class.insert_xml(root, "batch:id")
|
60
|
+
self.class.insert_xml(root, "batch:operation", :type => operation)
|
59
61
|
|
60
62
|
root
|
61
63
|
end
|
62
64
|
|
63
65
|
def new?
|
64
|
-
xml.at_xpath(
|
66
|
+
xml.at_xpath("./xmlns:id").nil?
|
65
67
|
end
|
66
68
|
|
67
69
|
def href
|
68
|
-
xml.at_xpath(
|
70
|
+
xml.at_xpath("./xmlns:id").text.strip unless new?
|
69
71
|
end
|
70
72
|
|
71
73
|
def updated_at
|
72
|
-
Time.parse xml.at_xpath(
|
74
|
+
Time.parse xml.at_xpath("./xmlns:updated").text.strip unless new?
|
73
75
|
end
|
74
76
|
|
75
77
|
def url(rel)
|
76
|
-
rel =
|
78
|
+
rel = "http://schemas.google.com/contacts/2008/rel#photo" if rel == :photo
|
77
79
|
xml.at_xpath(%{xmlns:link[@rel="#{rel}"]})[:href]
|
78
80
|
end
|
79
81
|
|
@@ -101,12 +103,18 @@ module GoogleContacts
|
|
101
103
|
properties[prop] = value
|
102
104
|
end
|
103
105
|
|
104
|
-
# Alias
|
105
|
-
|
106
|
+
# Alias "name" to "title"
|
107
|
+
def name
|
108
|
+
method_missing(:title)
|
109
|
+
end
|
110
|
+
|
111
|
+
def name=(v)
|
112
|
+
method_missing(:title=, v)
|
113
|
+
end
|
106
114
|
|
107
115
|
protected
|
108
116
|
def register_proxy(name, proxy)
|
109
|
-
@proxies[name.
|
117
|
+
@proxies[name.to_s] = proxy
|
110
118
|
end
|
111
119
|
|
112
120
|
def synchronize_proxies
|
@@ -114,11 +122,11 @@ module GoogleContacts
|
|
114
122
|
end
|
115
123
|
|
116
124
|
def register_base_proxies
|
117
|
-
register_proxy :title, Proxies::Tag.new(self, :tag =>
|
125
|
+
register_proxy :title, Proxies::Tag.new(self, :tag => "xmlns:title")
|
118
126
|
register_proxy :properties, Proxies::Hash.new(self,
|
119
|
-
:tag =>
|
120
|
-
:key =>
|
121
|
-
:value =>
|
127
|
+
:tag => "gd:extendedProperty",
|
128
|
+
:key => "name",
|
129
|
+
:value => "value")
|
122
130
|
end
|
123
131
|
|
124
132
|
# Try to proxy missing method to one of the proxies
|
@@ -143,7 +151,7 @@ module GoogleContacts
|
|
143
151
|
def self.insert_xml(parent, tag, attributes = {}, &blk)
|
144
152
|
# Construct new node with the right namespace
|
145
153
|
matches = tag.match /^((\w+):)?(\w+)$/
|
146
|
-
ns = matches[2] ==
|
154
|
+
ns = matches[2] == "xmlns" ? "atom" : (matches[2] || "atom")
|
147
155
|
tag = matches[3]
|
148
156
|
node = Nokogiri::XML::Node.new(tag, parent)
|
149
157
|
node.namespace = namespace(parent, ns) || raise("Unknown namespace: #{ns}")
|
@@ -169,16 +177,16 @@ module GoogleContacts
|
|
169
177
|
end
|
170
178
|
|
171
179
|
def self.initialize_xml_document
|
172
|
-
doc = new_xml_document(
|
173
|
-
insert_xml(doc.root,
|
174
|
-
:scheme =>
|
180
|
+
doc = new_xml_document("entry")
|
181
|
+
insert_xml(doc.root, "atom:category", {
|
182
|
+
:scheme => "http://schemas.google.com/g/2005#kind",
|
175
183
|
:term => const_get(:CATEGORY_TERM)
|
176
184
|
})
|
177
185
|
doc
|
178
186
|
end
|
179
187
|
|
180
188
|
def self.decorate_document_with_namespaces(doc)
|
181
|
-
doc.root.default_namespace = NAMESPACES[
|
189
|
+
doc.root.default_namespace = NAMESPACES["atom"]
|
182
190
|
NAMESPACES.each_pair do |prefix, href|
|
183
191
|
doc.root.add_namespace(prefix, href)
|
184
192
|
end
|
@@ -1,18 +1,29 @@
|
|
1
|
+
require "google_contacts/base"
|
2
|
+
|
1
3
|
module GoogleContacts
|
2
4
|
class Contact < Base
|
3
|
-
CATEGORY_TERM =
|
5
|
+
CATEGORY_TERM = "http://schemas.google.com/contact/2008#contact"
|
4
6
|
|
5
|
-
alias_attribute :name, :title
|
6
7
|
def initialize(*args)
|
7
8
|
super
|
8
9
|
register_proxy :emails, Proxies::Emails.new(self)
|
9
10
|
register_proxy :groups, Proxies::Array.new(self,
|
10
|
-
:tag =>
|
11
|
-
:attr =>
|
11
|
+
:tag => "gContact:groupMembershipInfo",
|
12
|
+
:attr => "href")
|
13
|
+
end
|
14
|
+
|
15
|
+
# Alias "name" to "title"
|
16
|
+
def name
|
17
|
+
method_missing(:title)
|
18
|
+
end
|
19
|
+
|
20
|
+
def name=(v)
|
21
|
+
method_missing(:title=, v)
|
12
22
|
end
|
13
23
|
|
14
24
|
def email
|
15
|
-
emails.primary
|
25
|
+
primary = emails.primary
|
26
|
+
primary && primary.address
|
16
27
|
end
|
17
28
|
|
18
29
|
def email=(address)
|
@@ -1,9 +1,11 @@
|
|
1
|
+
require "google_contacts/base"
|
2
|
+
|
1
3
|
module GoogleContacts
|
2
4
|
class Group < Base
|
3
|
-
CATEGORY_TERM =
|
5
|
+
CATEGORY_TERM = "http://schemas.google.com/contact/2008#group"
|
4
6
|
|
5
7
|
def system_group?
|
6
|
-
@xml.xpath(
|
8
|
+
@xml.xpath("./gContact:systemGroup").size > 0
|
7
9
|
end
|
8
10
|
|
9
11
|
def inspect
|
@@ -48,7 +48,7 @@ module GoogleContacts
|
|
48
48
|
def synchronize
|
49
49
|
@parent.remove_xml("./gd:email")
|
50
50
|
@new.each_pair do |address, email|
|
51
|
-
@parent.insert_xml(
|
51
|
+
@parent.insert_xml("gd:email", email)
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
@@ -74,11 +74,9 @@ module GoogleContacts
|
|
74
74
|
|
75
75
|
class Email < ::HashWithIndifferentAccess
|
76
76
|
DEFAULTS = {
|
77
|
-
:rel =>
|
77
|
+
:rel => "http://schemas.google.com/g/2005#home"
|
78
78
|
}.freeze
|
79
79
|
|
80
|
-
alias_attribute :name, :displayName
|
81
|
-
|
82
80
|
def initialize(parent, attributes = {})
|
83
81
|
super(DEFAULTS)
|
84
82
|
@parent = parent
|
@@ -88,6 +86,15 @@ module GoogleContacts
|
|
88
86
|
end
|
89
87
|
end
|
90
88
|
|
89
|
+
# Alias "name" to "displayName"
|
90
|
+
def name
|
91
|
+
method_missing(:displayName)
|
92
|
+
end
|
93
|
+
|
94
|
+
def name=(v)
|
95
|
+
method_missing(:displayName=, v)
|
96
|
+
end
|
97
|
+
|
91
98
|
def primary!
|
92
99
|
@parent.primary! self[:address]
|
93
100
|
end
|
@@ -103,7 +110,7 @@ module GoogleContacts
|
|
103
110
|
end
|
104
111
|
|
105
112
|
def []=(key, value)
|
106
|
-
if "#{key}" ==
|
113
|
+
if "#{key}" == "address" && self[key]
|
107
114
|
raise "Cannot modify email address"
|
108
115
|
end
|
109
116
|
super(key, value.to_s)
|
@@ -116,10 +123,10 @@ module GoogleContacts
|
|
116
123
|
def method_missing(sym, *args, &blk)
|
117
124
|
if sym.to_s =~ /^(\w+)(=|\?)?$/
|
118
125
|
case $2
|
119
|
-
when
|
126
|
+
when "="
|
120
127
|
send(:[]=, $1, *args)
|
121
|
-
when
|
122
|
-
send(:[], $1) ==
|
128
|
+
when "?"
|
129
|
+
send(:[], $1) == "true"
|
123
130
|
else
|
124
131
|
send(:[], $1)
|
125
132
|
end
|
@@ -11,13 +11,13 @@ module GoogleContacts
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def reinitialize
|
14
|
-
@current = HashWithIndifferentAccess.new
|
14
|
+
@current = ::HashWithIndifferentAccess.new
|
15
15
|
@parent.xml.xpath("./#{@tag}").map do |entry|
|
16
16
|
@current[entry[@key]] = entry[@value]
|
17
17
|
end
|
18
18
|
|
19
19
|
# create a deep copy
|
20
|
-
@new = HashWithIndifferentAccess.new
|
20
|
+
@new = ::HashWithIndifferentAccess.new
|
21
21
|
@current.each { |k,v| @new[k.dup] = v.dup }
|
22
22
|
end
|
23
23
|
|