Class: GmailContacts

Inherits:
Object
  • Object
show all
Defined in:
lib/gmail_contacts.rb,
lib/gmail_contacts/test_stub.rb

Overview

:stopdoc:

Defined Under Namespace

Classes: Contact, Error, TestStub

Constant Summary collapse

VERSION =
'2.0'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(authsub_token = nil, session_token = false) ⇒ GmailContacts

Creates a new GmailContacts using authsub_token. If you don’t yet have an AuthSub token, call #authsub_url.



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/gmail_contacts.rb', line 93

def initialize(authsub_token = nil, session_token = false)
  @authsub_token = authsub_token
  @session_token = session_token

  @id = nil
  @title = nil
  @author_email = nil
  @author_name = nil
  @contacts ||= []

  @google = URI.parse 'https://www.google.com'
  @http = Net::HTTP::Persistent.new "gmail_contacts_#{object_id}"
  @http.debug_output = $stderr
  @http.headers['Authorization'] = "AuthSub token=\"#{@authsub_token}\""
end

Instance Attribute Details

#author_emailObject (readonly)

Contact list author’s email



59
60
61
# File 'lib/gmail_contacts.rb', line 59

def author_email
  @author_email
end

#author_nameObject (readonly)

Contact list author’s name



64
65
66
# File 'lib/gmail_contacts.rb', line 64

def author_name
  @author_name
end

#authsub_tokenObject (readonly)

The current authsub token. If you upgrade a request token to a session token the value will change.



70
71
72
# File 'lib/gmail_contacts.rb', line 70

def authsub_token
  @authsub_token
end

#contactsObject (readonly)

Contact data

An Array with contact title, primary email and alternate emails



77
78
79
# File 'lib/gmail_contacts.rb', line 77

def contacts
  @contacts
end

#httpObject (readonly)

Returns the value of attribute http.



150
151
152
# File 'lib/gmail_contacts/test_stub.rb', line 150

def http
  @http
end

#idObject (readonly)

Contacts list identifier



82
83
84
# File 'lib/gmail_contacts.rb', line 82

def id
  @id
end

#titleObject (readonly)

Contacts list title



87
88
89
# File 'lib/gmail_contacts.rb', line 87

def title
  @title
end

Class Method Details

.stubObject



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/gmail_contacts/test_stub.rb', line 152

def self.stub
  return if @stubbed
  @stubbed = true

  alias old_get_token get_token

  def get_token
    if @authsub_token == 'recycled_authsub_token' then
      Net::HTTPForbidden.new(nil, '403', 'Forbidden').error!
    end

    @authsub_token = 'authsub_token-upgraded' if
      @authsub_token == 'authsub_token'
    @session_token = true

    @http.stub_reset
    @http.stub_data << GmailContacts::TestStub::CONTACTS
    @http.stub_data << GmailContacts::TestStub::CONTACTS2
  end

  alias old_fetch fetch

  def fetch(*args)
    if @authsub_token == 'wrong_user_authsub_token' then
      Net::HTTPForbidden.new(nil, '403', 'Forbidden').error!
    end

    old_fetch(*args)
  end

  alias old_fetch_photo fetch_photo

  def fetch_photo(contact)
    photo_url = if String === contact then
                  contact
                else
                  contact.photo_url
                end

    Net::HTTPNotFound.new(nil, '404', 'Not Found').error! if
      photo_url =~ /404/

    old_fetch_photo contact
  end

  alias old_revoke_token revoke_token

  def revoke_token
    @authsub_token = 'authsub_token-revoked'
    @session_token = false
  end

end

Instance Method Details

#authsub_url(next_url, secure = false, session = true, domain = nil) ⇒ Object

Returns a URL that will allow a user to authorize contact retrieval. Redirect the user to this URL and they will (should) approve your contact retrieval request.

next_url is where Google will redirect the user after they grant your request.

See code.google.com/apis/accounts/docs/AuthSub.html for more details.



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/gmail_contacts.rb', line 120

def authsub_url next_url, secure = false, session = true, domain = nil
  query = {
    'next'    => CGI.escape(next_url),
    'scope'   => 'http%3A%2F%2Fwww.google.com%2Fm8%2Ffeeds%2F',
    'secure'  => (secure  ? 1 : 0),
    'session' => (session ? 1 : 0),
  }

  query['hd'] = CGI.escape domain if domain

  query = query.map do |key, value|
    "#{key}=#{value}"
  end.sort.join '&'

  "https://www.google.com/accounts/AuthSubRequest?#{query}"
end

#fetch(*args) ⇒ Object

Fetches contacts from google for email.



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/gmail_contacts.rb', line 140

def fetch(email, revoke = true)
  get_token

  uri = URI.parse "http://www.google.com/m8/feeds/contacts/#{email}/full"

  loop do
    res = request uri

    xml = Nokogiri::XML res.body

    parse xml

    next_uri = xml.xpath('//xmlns:feed/xmlns:link[@rel="next"]').first
    break unless next_uri

    uri += next_uri['href']
  end

  yield if block_given?
ensure
  revoke_token if revoke and token?
end

#fetch_photo(contact) ⇒ Object

Fetches the photo data for contact which may be a photo URL



166
167
168
169
170
171
172
173
174
175
# File 'lib/gmail_contacts.rb', line 166

def fetch_photo(contact)
  photo_url = if String === contact then
                contact
              else
                contact.photo_url
              end
  response = request URI.parse photo_url

  response.body
end

#get_tokenObject

Fetches an AuthSub session token. Changes the value of #authsub_token so you can store it in a database or wherever.



181
182
183
184
185
186
187
188
189
190
191
# File 'lib/gmail_contacts.rb', line 181

def get_token
  return if @session_token

  response = request @google + '/accounts/AuthSubSessionToken'

  response.body =~ /^Token=(.*)/

  @authsub_token = $1
  @http.headers['Authorization'] = "AuthSub token=\"#{@authsub_token}\""
  @session_token = true
end

#parse(xml) ⇒ Object

Extracts contact information from xml, appending it to the current contact information



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/gmail_contacts.rb', line 197

def parse(xml)
  @id    = xml.xpath('//xmlns:feed/xmlns:id').first.text
  @title = xml.xpath('//xmlns:feed/xmlns:title').first.text

  @author_email =
    xml.xpath('//xmlns:feed/xmlns:author/xmlns:email').first.text
  @author_name = xml.xpath('//xmlns:feed/xmlns:author/xmlns:name').first.text

  xml.xpath('//xmlns:feed/xmlns:entry').each do |entry|
    title = entry.xpath('.//xmlns:title').first.text
    emails = []
    primary = entry.xpath('.//gd:email[@primary]')
    next unless primary.first
    emails << primary.first['address']
    alternates = entry.xpath('.//gd:email[not(@primary)]')

    emails.push(*alternates.map { |e| e['address'] })

    ims = []
    entry.xpath('.//gd:im').each do |im|
      ims << [im['address'], im['protocol']]
    end

    phones = []
    entry.xpath('.//gd:phoneNumber').each do |phone|
      phones << [phone.text, phone['rel']]
    end

    addresses = []
    entry.xpath('.//gd:postalAddress').each do |address|
      addresses << [address.text, address['rel']]
    end

    photo_link = entry.xpath('.//xmlns:link[@rel="http://schemas.google.com/contacts/2008/rel#photo"]').first
    photo_url = photo_link['href'] if photo_link

    contact = Contact.new title, emails, ims, phones, addresses, photo_url

    @contacts << contact
  end

  self
end

#request(uri) ⇒ Object

Performs a GET for uri and returns the Net::HTTPResponse. Raises Net::HTTPError if the response wasn’t a success (OK, Created, Found).



245
246
247
248
249
250
251
252
253
254
# File 'lib/gmail_contacts.rb', line 245

def request uri
  response = @http.request uri

  case response
  when Net::HTTPOK, Net::HTTPCreated, Net::HTTPFound then
    response
  else
    response.error!
  end
end

#revoke_tokenObject

Revokes our AuthSub session token



259
260
261
262
263
# File 'lib/gmail_contacts.rb', line 259

def revoke_token
  request @google + '/accounts/AuthSubRevokeToken'

  @session_token = false
end

#token?Boolean

Do we have an AuthSub session token?

Returns:

  • (Boolean)


268
269
270
# File 'lib/gmail_contacts.rb', line 268

def token?
  @session_token
end