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.



117
118
119
120
121
122
123
124
125
126
# 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)',
    '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.



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

def headers
  @headers
end

#projectionObject

Returns the value of attribute projection.



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

def projection
  @projection
end

#tokenObject (readonly)

Returns the value of attribute token.



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

def token
  @token
end

#userObject (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: “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, 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



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

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.



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

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



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

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

.response_body(response) ⇒ Object



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

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.



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', 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



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

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



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

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

#get(params = {}) ⇒ Object

:nodoc:



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

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



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

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



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

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

#response_body(response) ⇒ Object



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

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.



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

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



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

def updated_at_string
  @updated_string
end