Class: Contacts::Google
- Inherits:
-
Object
- Object
- Contacts::Google
- Defined in:
- lib/contacts/google.rb
Overview
Fetching Google Contacts
First, get the user to follow the following URL:
Contacts::Google.authentication_url('http://mysite.com/invite')
After he authenticates successfully to Google, it will redirect him back to the target URL (specified as argument above) and provide the token GET parameter. Use it to create a new instance of this class and request the contact list:
gmail = Contacts::Google.new(params[:token])
contacts = gmail.contacts
#-> [ ['Fitzgerald', '[email protected]', '[email protected]'],
['William Paginate', '[email protected]'], ...
]
Storing a session token
The basic token that you will get after the user has authenticated on Google is valid for only one request. However, you can specify that you want a session token which doesn't expire:
Contacts::Google.authentication_url('http://mysite.com/invite', :session => true)
When the user authenticates, he will be redirected back with a token that can be exchanged for a session token with the following method:
token = Contacts::Google.sesion_token(params[:token])
Now you have a permanent token. Store it with other user data so you can query the API on his behalf without him having to authenticate on Google each time.
Direct Known Subclasses
Constant Summary collapse
- DOMAIN =
'www.google.com'- AuthSubPath =
all variants go over HTTPS
'/accounts/AuthSub'- ClientLogin =
'/accounts/ClientLogin'- FeedsPath =
'/m8/feeds/contacts/'
Instance Attribute Summary collapse
-
#headers ⇒ Object
readonly
Returns the value of attribute headers.
-
#projection ⇒ Object
Returns the value of attribute projection.
-
#token ⇒ Object
readonly
Returns the value of attribute token.
-
#user ⇒ Object
readonly
Returns the value of attribute user.
Class Method Summary collapse
-
.authentication_url(target, options = {}) ⇒ Object
URL to Google site where user authenticates.
-
.authentication_url_options ⇒ Object
default options for #authentication_url.
- .authorization_header(token, client = false) ⇒ Object
-
.client_login(email, password) ⇒ Object
Alternative to AuthSub: using email and password.
-
.client_login_options ⇒ Object
default options for #client_login.
- .http_start(ssl = true) ⇒ Object
- .inspect_response(response, out = $stderr) ⇒ Object
-
.query_string(params) ⇒ Object
Constructs a query string from a Hash object.
-
.session_token(token) ⇒ Object
Makes an HTTPS request to exchange the given token with a session one.
Instance Method Summary collapse
-
#all_contacts(options = {}, chunk_size = 200) ⇒ Object
Fetches contacts using multiple API calls when necessary.
-
#contacts(options = {}) ⇒ Object
Fetches, parses and returns the contact list.
-
#get(params) ⇒ Object
:nodoc:.
-
#initialize(token, user_id = 'default', client = false) ⇒ Google
constructor
A token is required here.
-
#updated_at ⇒ Object
Timestamp of last update.
-
#updated_at_string ⇒ Object
Timestamp of last update as it appeared in the XML document.
Constructor Details
#initialize(token, user_id = 'default', client = false) ⇒ Google
A token is required here. By default, an AuthSub token from Google is one-time only, which means you can only make a single request with it.
117 118 119 120 121 122 123 124 125 |
# File 'lib/contacts/google.rb', line 117 def initialize(token, user_id = 'default', client = false) @user = user_id.to_s @token = token.to_s @headers = { 'Accept-Encoding' => 'gzip', 'User-Agent' => Identifier + ' (gzip)' }.update(self.class.(@token, client)) @projection = 'thin' end |
Instance Attribute Details
#headers ⇒ Object (readonly)
Returns the value of attribute headers.
112 113 114 |
# File 'lib/contacts/google.rb', line 112 def headers @headers end |
#projection ⇒ Object
Returns the value of attribute projection.
113 114 115 |
# File 'lib/contacts/google.rb', line 113 def projection @projection end |
#token ⇒ Object (readonly)
Returns the value of attribute token.
112 113 114 |
# File 'lib/contacts/google.rb', line 112 def token @token end |
#user ⇒ Object (readonly)
Returns the value of attribute user.
112 113 114 |
# File 'lib/contacts/google.rb', line 112 def user @user end |
Class Method Details
.authentication_url(target, options = {}) ⇒ Object
URL to Google site where user authenticates. Afterwards, Google redirects to your
site with the URL specified as target.
Options are:
- :scope -- the AuthSub scope in which the resulting token is valid (default: "http://www.google.com/m8/feeds/contacts/")
- :secure -- boolean indicating whether the token will be secure. Only available for registered domains. (default: false)
- :session -- boolean indicating if the token can be exchanged for a session token (default: false)
79 80 81 82 83 84 |
# File 'lib/contacts/google.rb', line 79 def self.authentication_url(target, = {}) params = .merge() params[:next] = target query = query_string(params) "https://#{DOMAIN}#{AuthSubPath}Request?#{query}" end |
.authentication_url_options ⇒ Object
default options for #authentication_url
51 52 53 54 55 56 57 |
# File 'lib/contacts/google.rb', line 51 def self. ||= { :scope => "http://#{DOMAIN}#{FeedsPath}", :secure => false, :session => false } end |
.authorization_header(token, client = false) ⇒ Object
259 260 261 262 |
# File 'lib/contacts/google.rb', line 259 def self.(token, client = false) type = client ? 'GoogleLogin auth' : 'AuthSub token' { 'Authorization' => %(#{type}="#{token}") } end |
.client_login(email, password) ⇒ Object
Alternative to AuthSub: using email and password.
101 102 103 104 105 106 107 108 109 110 |
# File 'lib/contacts/google.rb', line 101 def self.client_login(email, password) response = http_start do |google| query = query_string(.merge(:Email => email, :Passwd => password)) puts "posting #{query} to #{ClientLogin}" if Contacts::verbose? google.post(ClientLogin, query) end pair = response.body.split(/\n/).detect { |p| p.index('Auth=') == 0 } pair.split('=').last if pair end |
.client_login_options ⇒ Object
default options for #client_login
60 61 62 63 64 65 66 |
# File 'lib/contacts/google.rb', line 60 def self. ||= { :accountType => 'GOOGLE', :service => 'cp', :source => 'Contacts-Ruby' } end |
.http_start(ssl = true) ⇒ Object
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 |
# File 'lib/contacts/google.rb', line 264 def self.http_start(ssl = true) port = ssl ? Net::HTTP::https_default_port : Net::HTTP::http_default_port http = Net::HTTP.new(DOMAIN, port) redirects = 0 if ssl http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE end http.start begin response = yield(http) loop do inspect_response(response) if Contacts::verbose? case response when Net::HTTPSuccess break response when Net::HTTPRedirection if redirects == TooManyRedirects::MAX_REDIRECTS raise TooManyRedirects.new(response) end location = URI.parse response['Location'] puts "Redirected to #{location}" response = http.get(location.path) redirects += 1 else response.error! end end ensure http.finish end end |
.inspect_response(response, out = $stderr) ⇒ Object
300 301 302 303 304 305 306 |
# File 'lib/contacts/google.rb', line 300 def self.inspect_response(response, out = $stderr) out.puts response.inspect for name, value in response out.puts "#{name}: #{value}" end out.puts "----\n#{response_body response}\n----" unless response.body.empty? end |
.query_string(params) ⇒ Object
Constructs a query string from a Hash object
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/contacts/google.rb', line 215 def self.query_string(params) params.inject([]) do |all, pair| key, value = pair unless value.nil? value = case value when TrueClass; '1' when FalseClass; '0' else value end all << "#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}" end all end.join('&') end |
.session_token(token) ⇒ Object
Makes an HTTPS request to exchange the given token with a session one. Session tokens never expire, so you can store them in the database alongside user info.
Returns the new token as string or nil if the parameter couldn't be found in response body.
91 92 93 94 95 96 97 98 |
# File 'lib/contacts/google.rb', line 91 def self.session_token(token) response = http_start do |google| google.get(AuthSubPath + 'SessionToken', (token)) end pair = response.body.split(/\n/).detect { |p| p.index('Token=') == 0 } pair.split('=').last if pair end |
Instance Method Details
#all_contacts(options = {}, chunk_size = 200) ⇒ Object
Fetches contacts using multiple API calls when necessary
163 164 165 |
# File 'lib/contacts/google.rb', line 163 def all_contacts( = {}, chunk_size = 200) in_chunks(, :contacts, chunk_size) end |
#contacts(options = {}) ⇒ Object
Fetches, parses and returns the contact list.
Options
- :limit -- use a large number to fetch a bigger contact list (default: 200)
- :offset -- 0-based value, can be used for pagination
- :order -- currently the only value support by Google is "lastmodified"
- :descending -- boolean
- :updated_after -- string or time-like object, use to only fetch contacts that were updated after this date
156 157 158 159 160 |
# File 'lib/contacts/google.rb', line 156 def contacts( = {}) params = { :limit => 200 }.update() response = get(params) parse_contacts response_body(response) end |
#get(params) ⇒ Object
:nodoc:
127 128 129 130 131 132 133 134 |
# File 'lib/contacts/google.rb', line 127 def get(params) # :nodoc: self.class.http_start(false) do |google| path = FeedsPath + CGI.escape(@user) google_params = translate_parameters(params) query = self.class.query_string(google_params) google.get("#{path}/#{@projection}?#{query}", @headers) end end |
#updated_at ⇒ Object
Timestamp of last update. This value is available only after the XML document has been parsed; for instance after fetching the contact list.
138 139 140 |
# File 'lib/contacts/google.rb', line 138 def updated_at @updated_at ||= Time.parse @updated_string if @updated_string end |
#updated_at_string ⇒ Object
Timestamp of last update as it appeared in the XML document
143 144 145 |
# File 'lib/contacts/google.rb', line 143 def updated_at_string @updated_string end |