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