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.

Direct Known Subclasses

GoogleOAuth

Constant Summary collapse

DOMAIN =
'www.google.com'
AuthSubPath =

all variants go over HTTPS

'/accounts/AuthSub'
ClientLogin =
'/accounts/ClientLogin'
FeedsPath =
'/m8/feeds/contacts/'
GroupsPath =
'/m8/feeds/groups/'

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.



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

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)',
    'GData-Version' => '3.0'
  }.update(self.class.authorization_header(@token, client))
  @projection = 'thin'
end

Instance Attribute Details

#headersObject (readonly)

Returns the value of attribute headers.



113
114
115
# File 'lib/contacts/google.rb', line 113

def headers
  @headers
end

#projectionObject

Returns the value of attribute projection.



114
115
116
# File 'lib/contacts/google.rb', line 114

def projection
  @projection
end

#tokenObject (readonly)

Returns the value of attribute token.



113
114
115
# File 'lib/contacts/google.rb', line 113

def token
  @token
end

#userObject (readonly)

Returns the value of attribute user.



113
114
115
# File 'lib/contacts/google.rb', line 113

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
# File 'lib/contacts/google.rb', line 80

def self.authentication_url(target, options = {})
  params = authentication_url_options.merge(options)
  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 => "http://#{DOMAIN}#{FeedsPath}",
    :secure => false,
    :session => false
  }
end

.client_login(email, password) ⇒ Object

Alternative to AuthSub: using email and password.



102
103
104
105
106
107
108
109
110
111
# File 'lib/contacts/google.rb', line 102

def self.(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_optionsObject

default options for #client_login



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

def self.
   ||= {
    :accountType => 'GOOGLE',
    :service => 'cp',
    :source => 'Contacts-Ruby'
  }
end

.response_body(response) ⇒ Object



191
192
193
194
195
196
197
198
# File 'lib/contacts/google.rb', line 191

def self.response_body(response)
  unless response['Content-Encoding'] == 'gzip'
    response.body
  else
    gzipped = StringIO.new(response.body)
    Zlib::GzipReader.new(gzipped).read
  end
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.



92
93
94
95
96
97
98
99
# File 'lib/contacts/google.rb', line 92

def self.session_token(token)
  response = http_start do |google|
    google.get(AuthSubPath + 'SessionToken', authorization_header(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



177
178
179
# File 'lib/contacts/google.rb', line 177

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



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

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

#get(params = {}) ⇒ Object

:nodoc:



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

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)
    #puts "get query: #{query}"
    google.get("#{path}/#{@projection}?#{query}", @headers)
  end
end

#get_groups(params = {}) ⇒ Object



139
140
141
142
143
144
145
146
147
148
# File 'lib/contacts/google.rb', line 139

def get_groups(params={})
  self.class.http_start(false) do |google|
    path = GroupsPath + CGI.escape(@user)
    google_params = translate_parameters(params)
    query = self.class.query_string(google_params)
    url = "#{path}/full?#{query}"
    #puts "get_groups url: #{url}"
    google.get(url, @headers)
  end
end

#groups(options = {}) ⇒ Object



181
182
183
184
185
# File 'lib/contacts/google.rb', line 181

def groups(options = {})
  params = {}.update(options)
  response = get_groups(params)
  parse_groups response_body(response)
end

#response_body(response) ⇒ Object



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

def response_body(response)
  self.class.response_body(response)
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.



152
153
154
# File 'lib/contacts/google.rb', line 152

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



157
158
159
# File 'lib/contacts/google.rb', line 157

def updated_at_string
  @updated_string
end