google_contacts_api 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ gem "activesupport"
5
+ gem "json"
6
+ gem "hashie"
7
+
8
+ # Add dependencies to develop your gem here.
9
+ # Include everything needed to run rake, tests, features, etc.
10
+ group :development do
11
+ gem "shoulda", ">= 0"
12
+ gem "bundler", "~> 1.0.0"
13
+ gem "jeweler", "~> 1.5.2"
14
+ gem "rcov", ">= 0"
15
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Alvin Liang
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,42 @@
1
+ # google_contacts_api
2
+
3
+ An unofficial Google Contacts API for ruby. Might not be stable (but probably is). In active usage at [thirsst.com](http://thirsst.com/). (Shameless plug: We help you manage your RSS and group bookmarking!)
4
+
5
+ ## Usage
6
+
7
+ You need to provide an OAuth access token from one of the major OAuth libraries to this library.
8
+ Then you can instantiate a GoogleContactsApi::Api object for direct posting and parsing, or a
9
+ GoogleContactsApi::User object for easier stuff.
10
+
11
+ ```ruby
12
+ google_contacts_user = GoogleContactsApi::User(oauth_access_token_for_user)
13
+ contacts = google_contacts_user.contacts
14
+ groups = google_contacts_user.groups
15
+ groups.first.contacts
16
+ contacts.first.photo
17
+ contacts.first.title
18
+ contacts.first.id
19
+ ```
20
+
21
+ ## TODO
22
+
23
+ I welcome patches and pull requests, see the guidelines below (handily auto-generated
24
+ by jeweler).
25
+
26
+ * Tests! (using RSpec, please)
27
+ * Posting/putting/deleting groups, contacts and their photos
28
+ * Support ClientLogin
29
+
30
+ ## Contributing to google_contacts_api
31
+
32
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
33
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
34
+ * Fork the project
35
+ * Start a feature/bugfix branch
36
+ * Commit and push until you are happy with your contribution
37
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
38
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
39
+
40
+ ## Copyright
41
+
42
+ Copyright (c) 2011 Alvin Liang. See LICENSE.txt for further details.
data/Rakefile ADDED
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "google_contacts_api"
16
+ gem.homepage = "http://github.com/aliang/google_contacts_api"
17
+ gem.license = "MIT"
18
+ gem.summary = %Q{Lets you read from the Google Contacts API}
19
+ gem.description = %Q{Lets you read from the Google Contacts API. Posting to come later. Tests to come later.}
20
+ gem.email = "ayliang@gmail.com"
21
+ gem.authors = ["Alvin Liang"]
22
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
23
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
24
+ gem.add_runtime_dependency 'activesupport'
25
+ gem.add_runtime_dependency 'hashie'
26
+ gem.add_runtime_dependency 'json'
27
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
28
+ end
29
+ Jeweler::RubygemsDotOrgTasks.new
30
+
31
+ require 'rake/testtask'
32
+ Rake::TestTask.new(:test) do |test|
33
+ test.libs << 'lib' << 'test'
34
+ test.pattern = 'test/**/test_*.rb'
35
+ test.verbose = true
36
+ end
37
+
38
+ require 'rcov/rcovtask'
39
+ Rcov::RcovTask.new do |test|
40
+ test.libs << 'test'
41
+ test.pattern = 'test/**/test_*.rb'
42
+ test.verbose = true
43
+ end
44
+
45
+ task :default => :test
46
+
47
+ require 'rdoc/task'
48
+ Rake::RDocTask.new do |rdoc|
49
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
50
+
51
+ rdoc.rdoc_dir = 'rdoc'
52
+ rdoc.title = "google_contacts_api #{version}"
53
+ rdoc.rdoc_files.include('README*')
54
+ rdoc.rdoc_files.include('lib/**/*.rb')
55
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
@@ -0,0 +1,80 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{google_contacts_api}
8
+ s.version = "0.1.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Alvin Liang"]
12
+ s.date = %q{2011-07-08}
13
+ s.description = %q{Lets you read from the Google Contacts API. Posting to come later. Tests to come later.}
14
+ s.email = %q{ayliang@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.markdown"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "LICENSE.txt",
23
+ "README.markdown",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "google_contacts_api.gemspec",
27
+ "lib/google_contacts_api.rb",
28
+ "test/helper.rb",
29
+ "test/test_google_contacts_api.rb"
30
+ ]
31
+ s.homepage = %q{http://github.com/aliang/google_contacts_api}
32
+ s.licenses = ["MIT"]
33
+ s.require_paths = ["lib"]
34
+ s.rubygems_version = %q{1.6.2}
35
+ s.summary = %q{Lets you read from the Google Contacts API}
36
+ s.test_files = [
37
+ "test/helper.rb",
38
+ "test/test_google_contacts_api.rb"
39
+ ]
40
+
41
+ if s.respond_to? :specification_version then
42
+ s.specification_version = 3
43
+
44
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
45
+ s.add_runtime_dependency(%q<activesupport>, [">= 0"])
46
+ s.add_runtime_dependency(%q<json>, [">= 0"])
47
+ s.add_runtime_dependency(%q<hashie>, [">= 0"])
48
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
49
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
50
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
51
+ s.add_development_dependency(%q<rcov>, [">= 0"])
52
+ s.add_runtime_dependency(%q<activesupport>, [">= 0"])
53
+ s.add_runtime_dependency(%q<hashie>, [">= 0"])
54
+ s.add_runtime_dependency(%q<json>, [">= 0"])
55
+ else
56
+ s.add_dependency(%q<activesupport>, [">= 0"])
57
+ s.add_dependency(%q<json>, [">= 0"])
58
+ s.add_dependency(%q<hashie>, [">= 0"])
59
+ s.add_dependency(%q<shoulda>, [">= 0"])
60
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
61
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
62
+ s.add_dependency(%q<rcov>, [">= 0"])
63
+ s.add_dependency(%q<activesupport>, [">= 0"])
64
+ s.add_dependency(%q<hashie>, [">= 0"])
65
+ s.add_dependency(%q<json>, [">= 0"])
66
+ end
67
+ else
68
+ s.add_dependency(%q<activesupport>, [">= 0"])
69
+ s.add_dependency(%q<json>, [">= 0"])
70
+ s.add_dependency(%q<hashie>, [">= 0"])
71
+ s.add_dependency(%q<shoulda>, [">= 0"])
72
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
73
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
74
+ s.add_dependency(%q<rcov>, [">= 0"])
75
+ s.add_dependency(%q<activesupport>, [">= 0"])
76
+ s.add_dependency(%q<hashie>, [">= 0"])
77
+ s.add_dependency(%q<json>, [">= 0"])
78
+ end
79
+ end
80
+
@@ -0,0 +1,304 @@
1
+ # TODO: Use more than one file
2
+
3
+ require 'active_support/core_ext'
4
+ require 'json'
5
+ require 'hashie'
6
+
7
+ module GoogleContactsApi
8
+ class Api
9
+ # keep separate in case of new auth method
10
+ BASE_URL = "https://www.google.com/m8/feeds/"
11
+
12
+ attr_reader :oauth
13
+ def initialize(oauth)
14
+ # TODO: Later, accept ClientLogin
15
+ @oauth = oauth
16
+ end
17
+
18
+ # Get request to specified link, with query params
19
+ # For get, post, put, delete, always use JSON, it's simpler
20
+ # and lets us use Hashie::Mash. Note that in the JSON conversion from XML,
21
+ # ":" is replaced with $, element content is keyed with $t
22
+ def get(link, params = {}, headers = {})
23
+ params["alt"] = "json"
24
+ @oauth.get("#{BASE_URL}#{link}?#{params.to_query}", headers)
25
+ end
26
+
27
+ # Post request to specified link, with query params
28
+ # Not tried yet, might be issues with params
29
+ def post(link, params = {}, headers = {})
30
+ params["alt"] = "json"
31
+ @oauth.post("#{BASE_URL}#{link}?#{params.to_query}", headers)
32
+ end
33
+
34
+ # Put request to specified link, with query params
35
+ # Not tried yet
36
+ def put(link, params = {}, headers = {})
37
+ params["alt"] = "json"
38
+ @oauth.put("#{BASE_URL}#{link}?#{params.to_query}", headers)
39
+ end
40
+
41
+ # Delete request to specified link, with query params
42
+ # Not tried yet
43
+ def delete(link, params = {}, headers = {})
44
+ params["alt"] = "json"
45
+ @oauth.delete("#{BASE_URL}#{link}?#{params.to_query}", headers)
46
+ end
47
+ end
48
+
49
+ module Contacts
50
+ # Retrieve the contacts for this user or group
51
+ def contacts(params = {})
52
+ params = params.with_indifferent_access
53
+
54
+ # compose params into a string
55
+ # See http://code.google.com/apis/contacts/docs/3.0/reference.html#Parameters
56
+ # alt, q, max-results, start-index, updated-min,
57
+ # orderby, showdeleted, requirealldeleted, sortorder, group
58
+ params["max-results"] = 100000 unless params.key?("max-results")
59
+ url = "contacts/default/full"
60
+ response = @api.get(url, params)
61
+
62
+ # TODO: Define some fancy exceptions
63
+ case response.code
64
+ when 401; raise
65
+ when 403; raise
66
+ when 404; raise
67
+ when 400...500; raise
68
+ when 500...600; raise
69
+ end
70
+ ContactSet.new(response.body, @api)
71
+ end
72
+ end
73
+
74
+ class User
75
+ include Contacts
76
+
77
+ attr_reader :api
78
+ def initialize(oauth)
79
+ @api = GoogleContactsApi::Api.new(oauth)
80
+ end
81
+
82
+ # Retrieve the groups for this user
83
+ # TODO: Handle 403, 404, 401
84
+ def groups(params = {})
85
+ params = params.with_indifferent_access
86
+ # compose params into a string
87
+ # See http://code.google.com/apis/contacts/docs/3.0/reference.html#Parameters
88
+ # alt, q, max-results, start-index, updated-min,
89
+ # orderby, showdeleted, requirealldeleted, sortorder
90
+ params["max-results"] = 100000 unless params.key?("max-results")
91
+ url = "groups/default/full"
92
+ # TODO: So weird thing, version 3 doesn't return system groups
93
+ # When it does, uncomment this line and use it to request instead
94
+ # response = @api.get(url, params)
95
+ response = @api.get(url, params, {"GData-Version" => "2"})
96
+
97
+ case response.code
98
+ when 401; raise
99
+ when 403; raise
100
+ when 404; raise
101
+ when 400...500; raise
102
+ when 500...600; raise
103
+ end
104
+ GroupSet.new(response.body, @api)
105
+ end
106
+ end
107
+
108
+ # Base class for GroupSet and ContactSet
109
+ class ResultSet
110
+ include Enumerable
111
+ attr_reader :api
112
+ attr_accessor :total_results, :start_index, :items_per_page, :parsed
113
+
114
+ def initialize(response_body, api = nil)
115
+ @api = api
116
+ @parsed = Hashie::Mash.new(JSON.parse(response_body))
117
+ @total_results = @parsed.feed["openSearch$totalResults"]["$t"].to_i
118
+ @start_index = @parsed.feed["openSearch$startIndex"]["$t"].to_i
119
+ @items_per_page = @parsed.feed["openSearch$itemsPerPage"]["$t"].to_i
120
+ @results = []
121
+ end
122
+
123
+ def each
124
+ @results.each { |x| yield x }
125
+ end
126
+
127
+ def has_more?
128
+ # 1-based indexing
129
+ @start_index - 1 + @items_per_page <= @total_results
130
+ end
131
+
132
+ def inspect
133
+ "<#{self.class}: @start_index=#{@start_index}, @items_per_page=#{@items_per_page}, @total_results=#{@total_results}>"
134
+ end
135
+ end
136
+
137
+ class GroupSet < ResultSet
138
+ # Populate from a response that contains contacts
139
+ def initialize(response_body, api = nil)
140
+ super
141
+ @results = @parsed.feed.entry.map { |e| Group.new(e, nil, api) }
142
+ end
143
+ end
144
+
145
+ class ContactSet < ResultSet
146
+ # Populate from a response that contains contacts
147
+ def initialize(response_body, api = nil)
148
+ super
149
+ @results = @parsed.feed.entry.map { |e| Contact.new(e, nil, api) }
150
+ end
151
+ end
152
+
153
+ # Base class for Group and Contact
154
+ class Result < Hashie::Mash
155
+ # Note that the title is really just the (full) name
156
+ # ":" replaced with $, element content is keyed with $t
157
+
158
+ # These are the accessors we can write
159
+ # :id, :title, :updated, :content
160
+
161
+ attr_reader :api
162
+ # Populate from a single result Hash/Hashie
163
+ def initialize(source_hash = nil, default = nil, api = nil, &blk)
164
+ @api = api if api
165
+ super(source_hash, default, &blk)
166
+ end
167
+
168
+ # TODO: Conditional retrieval? There might not be an etag in the
169
+ # JSON representation, there is in the XML representation
170
+ def etag
171
+ end
172
+
173
+ def id
174
+ _id = self["id"]
175
+ _id ? _id["$t"] : nil
176
+ end
177
+
178
+ def title
179
+ _title = self["title"]
180
+ _title ? _title["$t"] : nil
181
+ end
182
+
183
+ def content
184
+ _content = self["content"]
185
+ _content ? content["$t"] : nil
186
+ end
187
+
188
+ def updated
189
+ _updated = self["updated"]
190
+ _updated ? DateTime.parse(_updated["$t"]) : nil
191
+ end
192
+
193
+ # Returns the array of categories, as category is an array for Hashie.
194
+ def categories
195
+ category
196
+ end
197
+
198
+ def deleted?
199
+ raise NotImplementedError
200
+ end
201
+
202
+ def inspect
203
+ "<#{self.class}: #{title}>"
204
+ end
205
+ end
206
+
207
+ class Group < Result
208
+ include Contacts
209
+ # Populate from a single entry element in the result response
210
+ # when requesting a set of Contacts
211
+
212
+ def system_group?
213
+ !self["gContact$systemGroup"].nil?
214
+ end
215
+
216
+ def contacts(params = {})
217
+ # contacts in this group
218
+ @contacts ||= super({"group" => self.id}.merge(params))
219
+ end
220
+
221
+ def contacts!(params = {})
222
+ # contacts in this group
223
+ @contacts = super({"group" => self.id}.merge(params))
224
+ end
225
+
226
+ # Returns the array of links, as link is an array for Hashie.
227
+ def links
228
+ self["link"].map { |l| l.href }
229
+ end
230
+
231
+ def self_link
232
+ _link = self["link"].find { |l| l.rel == "self" }
233
+ _link ? _link.href : nil
234
+ end
235
+
236
+ def edit_link
237
+ _link = self["link"].find { |l| l.rel == "edit" }
238
+ _link ? _link.href : nil
239
+ end
240
+ end
241
+
242
+ class Contact < Result
243
+ # :categories, (:content again), :links, (:title again), :email
244
+ # :extended_properties, :deleted, :im, :name,
245
+ # :organizations, :phone_numbers, :structured_postal_addresses, :where
246
+
247
+ # Returns the array of links, as link is an array for Hashie.
248
+ def links
249
+ self["link"].map { |l| l.href }
250
+ end
251
+
252
+ def self_link
253
+ _link = self["link"].find { |l| l.rel == "self" }
254
+ _link ? _link.href : nil
255
+ end
256
+
257
+ def alternate_link
258
+ _link = self["link"].find { |l| l.rel == "alternate" }
259
+ _link ? _link.href : nil
260
+ end
261
+
262
+ def photo_link
263
+ _link = self["link"].find { |l| l.rel == "http://schemas.google.com/contacts/2008/rel#photo" }
264
+ _link ? _link.href : nil
265
+ end
266
+
267
+ # Returns binary data for the photo. You can probably
268
+ # use it in a data-uri. This is in PNG format.
269
+ def photo
270
+ return nil unless photo_link
271
+ response = @api.oauth.get(photo_link)
272
+
273
+ case response.code
274
+ # maybe return a placeholder instead of nil
275
+ when 400; return nil
276
+ when 401; return nil
277
+ when 403; return nil
278
+ when 404; return nil
279
+ when 400...500; return nil
280
+ when 500...600; return nil
281
+ else; return response.body
282
+ end
283
+ end
284
+
285
+ def edit_photo_link
286
+ _link = self["link"].find { |l| l.rel == "http://schemas.google.com/contacts/2008/rel#edit_photo" }
287
+ _link ? _link.href : nil
288
+ end
289
+
290
+ def edit_link
291
+ _link = self["link"].find { |l| l.rel == "edit" }
292
+ _link ? _link.href : nil
293
+ end
294
+
295
+ def emails
296
+ self["gd$email"].map { |e| e.address }
297
+ end
298
+
299
+ def primary_email
300
+ _email = self["gd$email"].find { |e| e.primary == "true" }
301
+ _email ? _email.address : nil
302
+ end
303
+ end
304
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+ require 'shoulda'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ require 'google_contacts_api'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestGoogleContactsApi < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,179 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: google_contacts_api
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.1
6
+ platform: ruby
7
+ authors:
8
+ - Alvin Liang
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-07-08 00:00:00 -04:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: activesupport
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: "0"
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: hashie
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ type: :runtime
47
+ prerelease: false
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: shoulda
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: bundler
62
+ requirement: &id005 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ~>
66
+ - !ruby/object:Gem::Version
67
+ version: 1.0.0
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: *id005
71
+ - !ruby/object:Gem::Dependency
72
+ name: jeweler
73
+ requirement: &id006 !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ~>
77
+ - !ruby/object:Gem::Version
78
+ version: 1.5.2
79
+ type: :development
80
+ prerelease: false
81
+ version_requirements: *id006
82
+ - !ruby/object:Gem::Dependency
83
+ name: rcov
84
+ requirement: &id007 !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: "0"
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: *id007
93
+ - !ruby/object:Gem::Dependency
94
+ name: activesupport
95
+ requirement: &id008 !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: "0"
101
+ type: :runtime
102
+ prerelease: false
103
+ version_requirements: *id008
104
+ - !ruby/object:Gem::Dependency
105
+ name: hashie
106
+ requirement: &id009 !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: "0"
112
+ type: :runtime
113
+ prerelease: false
114
+ version_requirements: *id009
115
+ - !ruby/object:Gem::Dependency
116
+ name: json
117
+ requirement: &id010 !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: "0"
123
+ type: :runtime
124
+ prerelease: false
125
+ version_requirements: *id010
126
+ description: Lets you read from the Google Contacts API. Posting to come later. Tests to come later.
127
+ email: ayliang@gmail.com
128
+ executables: []
129
+
130
+ extensions: []
131
+
132
+ extra_rdoc_files:
133
+ - LICENSE.txt
134
+ - README.markdown
135
+ files:
136
+ - .document
137
+ - Gemfile
138
+ - LICENSE.txt
139
+ - README.markdown
140
+ - Rakefile
141
+ - VERSION
142
+ - google_contacts_api.gemspec
143
+ - lib/google_contacts_api.rb
144
+ - test/helper.rb
145
+ - test/test_google_contacts_api.rb
146
+ has_rdoc: true
147
+ homepage: http://github.com/aliang/google_contacts_api
148
+ licenses:
149
+ - MIT
150
+ post_install_message:
151
+ rdoc_options: []
152
+
153
+ require_paths:
154
+ - lib
155
+ required_ruby_version: !ruby/object:Gem::Requirement
156
+ none: false
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ hash: 3049273964521607278
161
+ segments:
162
+ - 0
163
+ version: "0"
164
+ required_rubygems_version: !ruby/object:Gem::Requirement
165
+ none: false
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: "0"
170
+ requirements: []
171
+
172
+ rubyforge_project:
173
+ rubygems_version: 1.6.2
174
+ signing_key:
175
+ specification_version: 3
176
+ summary: Lets you read from the Google Contacts API
177
+ test_files:
178
+ - test/helper.rb
179
+ - test/test_google_contacts_api.rb