Class: Contacts::Google

Inherits:
Object
  • Object
show all
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.

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

Class Method Summary collapse

Instance Method Summary collapse

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.



143
144
145
146
147
148
149
150
151
152
# File 'lib/contacts/google.rb', line 143

def initialize(token, user_id = 'default', client = false)
  @user    = user_id.to_s
  @token   = token.to_s
  @client  = client
  @headers = {
    'Accept-Encoding' => 'gzip',
    'User-Agent' => Identifier + ' (gzip)'
  }.update(self.class.authorization_header(@token, client))
  @projection = 'thin'
end

Instance Attribute Details

#authorObject (readonly)

Returns the value of attribute author.



138
139
140
# File 'lib/contacts/google.rb', line 138

def author
  @author
end

#headersObject (readonly)

Returns the value of attribute headers.



138
139
140
# File 'lib/contacts/google.rb', line 138

def headers
  @headers
end

#projectionObject

Returns the value of attribute projection.



139
140
141
# File 'lib/contacts/google.rb', line 139

def projection
  @projection
end

#tokenObject (readonly)

Returns the value of attribute token.



138
139
140
# File 'lib/contacts/google.rb', line 138

def token
  @token
end

#userObject (readonly)

Returns the value of attribute user.



138
139
140
# File 'lib/contacts/google.rb', line 138

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: “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)



80
81
82
83
84
85
86
87
88
89
# File 'lib/contacts/google.rb', line 80

def self.authentication_url(target, options = {})
  params = authentication_url_options.merge(options)
  if key = params.delete(:key)
    params[:secure] = true
    set_private_key(key)
  end
  params[:next] = target
  query = query_string(params)
  "https://#{DOMAIN}#{AuthSubPath}Request?#{query}"
end

.authentication_url_optionsObject

default options for #authentication_url



52
53
54
55
56
57
58
# File 'lib/contacts/google.rb', line 52

def self.authentication_url_options
  @authentication_url_options ||= {
    :scope => "https://#{DOMAIN}#{FeedsPath}",
    :secure => false,
    :session => false
  }
end

.client_login(email, password) ⇒ Object

Alternative to AuthSub: using email and password.



128
129
130
131
132
133
134
135
136
# File 'lib/contacts/google.rb', line 128

def self.(email, password)
  response = http_start do |google|
    query = query_string(.merge(:Email => email, :Passwd => password))
    google.post(ClientLogin, query)
  end

  pair = response.body.split(/\n/).detect { |p| p.index('Auth=') == 0 }
  pair.split('=').last if pair
end

.client_login_optionsObject

default options for #client_login



61
62
63
64
65
66
67
# File 'lib/contacts/google.rb', line 61

def self.
  @client_login_options ||= {
    :accountType => 'GOOGLE',
    :service => 'cp',
    :source => 'Contacts-Ruby'
  }
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.



117
118
119
120
121
122
123
124
125
# File 'lib/contacts/google.rb', line 117

def self.session_token(token)
  response = http_start(true) do |google|
    uri = AuthSubPath + 'SessionToken'
    header = authorization_header(token, false, uri)
    google.get(uri, header)
  end
  pair = response.body.split(/\n/).detect { |p| p.index('Token=') == 0 }
  pair.split('=').last if pair
end

.set_private_key(key) ⇒ Object

Sets the private key for a secure AuthSub request. key may be an IO, String or OpenSSL::PKey::RSA. Stolen from github.com/stuart/google-authsub/lib/googleauthsub.rb



94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/contacts/google.rb', line 94

def self.set_private_key(key)
  case key
  when OpenSSL::PKey::RSA
    @@pkey = key
  when File
    @@pkey = OpenSSL::PKey::RSA.new(key.read)
  when String
    @@pkey = OpenSSL::PKey::RSA.new(key)
  else
    raise "Private Key in wrong format. Require IO, String or OpenSSL::PKey::RSA, you gave me #{key.class}"
  end
end

.unset_private_keyObject

Unsets the private key. Only used for test teardowns.



108
109
110
# File 'lib/contacts/google.rb', line 108

def self.unset_private_key
  @@pkey = nil
end

Instance Method Details

#all_contacts(options = {}, chunk_size = 200) ⇒ Object

Fetches contacts using multiple API calls when necessary



192
193
194
# File 'lib/contacts/google.rb', line 192

def all_contacts(options = {}, chunk_size = 200)
  in_chunks(options, :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



185
186
187
188
189
# File 'lib/contacts/google.rb', line 185

def contacts(options = {})
  params = { :limit => 200 }.update(options)
  response = get(params)
  parse_contacts response_body(response)
end

#get(params) ⇒ Object

:nodoc:



154
155
156
157
158
159
160
161
162
163
# File 'lib/contacts/google.rb', line 154

def get(params) # :nodoc:
  self.class.http_start(true) do |google|
    path = FeedsPath + CGI.escape(@user)
    google_params = translate_parameters(params)
    query = self.class.query_string(google_params)
    uri = "#{path}/#{@projection}?#{query}"
    headers = @headers.update(self.class.authorization_header(@token, @client, uri))
    google.get(uri, headers)
  end
end

#updated_atObject

Timestamp of last update. This value is available only after the XML document has been parsed; for instance after fetching the contact list.



167
168
169
# File 'lib/contacts/google.rb', line 167

def updated_at
  @updated_at ||= Time.parse @updated_string if @updated_string
end

#updated_at_stringObject

Timestamp of last update as it appeared in the XML document



172
173
174
# File 'lib/contacts/google.rb', line 172

def updated_at_string
  @updated_string
end