Class: GoogleDrive::Session

Inherits:
Object
  • Object
show all
Extended by:
Util
Includes:
Util
Defined in:
lib/google_drive/session.rb

Overview

Use GoogleDrive.login or GoogleDrive.saved_session to get GoogleDrive::Session object.

Constant Summary collapse

UPLOAD_CHUNK_SIZE =
512 * 1024

Constants included from Util

Util::DOCS_BASE_URL, Util::EXT_TO_CONTENT_TYPE

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Util

concat_url, encode_query, h, to_v3_url

Constructor Details

#initialize(auth_tokens = nil, fetcher = nil, proxy = nil) ⇒ Session

DEPRECATED: Use GoogleDrive.restore_session instead.



65
66
67
68
69
70
71
# File 'lib/google_drive/session.rb', line 65

def initialize(auth_tokens = nil, fetcher = nil, proxy = nil)
  if fetcher
    @fetcher = fetcher
  else
    @fetcher = ClientLoginFetcher.new(auth_tokens || {}, proxy)
  end
end

Instance Attribute Details

#on_auth_failObject

Proc or Method called when authentication has failed. When this function returns true, it tries again.



109
110
111
# File 'lib/google_drive/session.rb', line 109

def on_auth_fail
  @on_auth_fail
end

Class Method Details

.login(mail, password, proxy = nil) ⇒ Object

The same as GoogleDrive.login.



34
35
36
37
38
# File 'lib/google_drive/session.rb', line 34

def self.(mail, password, proxy = nil)
  session = Session.new(nil, ClientLoginFetcher.new({}, proxy))
  session.(mail, password)
  return session
end

.login_with_oauth(oauth_token) ⇒ Object

The same as GoogleDrive.login_with_oauth.



41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/google_drive/session.rb', line 41

def self.(oauth_token)
  case oauth_token
    when OAuth::AccessToken
      fetcher = OAuth1Fetcher.new(oauth_token)
    when OAuth2::AccessToken
      fetcher = OAuth2Fetcher.new(oauth_token)
    else
      raise(GoogleDrive::Error,
          "oauth_token is neither OAuth::Token nor OAuth2::Token: %p" % oauth_token)
  end
  return Session.new(nil, fetcher)
end

.new_dummyObject

Creates a dummy GoogleDrive::Session object for testing.



60
61
62
# File 'lib/google_drive/session.rb', line 60

def self.new_dummy()
  return Session.new(nil, Object.new())
end

.restore_session(auth_tokens, proxy = nil) ⇒ Object

The same as GoogleDrive.restore_session.



55
56
57
# File 'lib/google_drive/session.rb', line 55

def self.restore_session(auth_tokens, proxy = nil)
  return Session.new(auth_tokens, nil, proxy)
end

Instance Method Details

#auth_token(auth = :wise) ⇒ Object

Authentication token.



103
104
105
# File 'lib/google_drive/session.rb', line 103

def auth_token(auth = :wise)
  return self.auth_tokens[auth]
end

#auth_tokensObject

Authentication tokens.



93
94
95
96
97
98
99
100
# File 'lib/google_drive/session.rb', line 93

def auth_tokens
  if !@fetcher.is_a?(ClientLoginFetcher)
    raise(GoogleDrive::Error,
        "Cannot call auth_tokens for session created by " +
        "login_with_oauth.")
  end
  return @fetcher.auth_tokens
end

#collection_by_title(title) ⇒ Object

Returns a top-level collection whose title exactly matches title as GoogleDrive::Collection. Returns nil if not found. If multiple collections with the title are found, returns one of them.



220
221
222
# File 'lib/google_drive/session.rb', line 220

def collection_by_title(title)
  return self.root_collection.subcollection_by_title(title)
end

#collection_by_url(url) ⇒ Object

Returns GoogleDrive::Collection with given url. You must specify either of:

  • URL of the page you get when you go to docs.google.com/ with your browser and open a collection

  • URL of collection (folder) feed

e.g.

session.collection_by_url(
  "https://drive.google.com/#folders/" +
  "0B9GfDpQ2pBVUODNmOGE0NjIzMWU3ZC00NmUyLTk5NzEtYaFkZjY1MjAyxjMc")
session.collection_by_url(
  "http://docs.google.com/feeds/default/private/full/folder%3A" +
  "0B9GfDpQ2pBVUODNmOGE0NjIzMWU3ZC00NmUyLTk5NzEtYaFkZjY1MjAyxjMc")


237
238
239
240
241
242
243
244
245
# File 'lib/google_drive/session.rb', line 237

def collection_by_url(url)
  uri = URI.parse(url)
  if ["docs.google.com", "drive.google.com"].include?(uri.host) &&
      uri.fragment =~ /^folders\/(.+)$/
    # Looks like a URL of human-readable collection page. Converts to collection feed URL.
    url = "#{DOCS_BASE_URL}/folder%3A#{$1}"
  end
  return Collection.new(self, url)
end

#collectionsObject

Returns the top-level collections (direct children of the root collection).



212
213
214
# File 'lib/google_drive/session.rb', line 212

def collections
  return self.root_collection.subcollections
end

#create_spreadsheet(title = "Untitled", feed_url = "https://docs.google.com/feeds/documents/private/full") ⇒ Object

Creates new spreadsheet and returns the new GoogleDrive::Spreadsheet.

e.g.

session.create_spreadsheet("My new sheet")


251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/google_drive/session.rb', line 251

def create_spreadsheet(
    title = "Untitled",
    feed_url = "https://docs.google.com/feeds/documents/private/full")
  
  xml = <<-"EOS"
    <atom:entry
        xmlns:atom="http://www.w3.org/2005/Atom"
        xmlns:docs="http://schemas.google.com/docs/2007">
      <atom:category
          scheme="http://schemas.google.com/g/2005#kind"
          term="http://schemas.google.com/docs/2007#spreadsheet"
          label="spreadsheet"/>
      <atom:title>#{h(title)}</atom:title>
    </atom:entry>
  EOS

  doc = request(:post, feed_url, :data => xml, :auth => :writely)
  ss_url = doc.css(
    "link[rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed']")[0]["href"]
  return Spreadsheet.new(self, ss_url, title)
  
end

#entry_element_to_file(entry) ⇒ Object

:nodoc:



392
393
394
395
396
397
398
399
400
401
402
403
404
405
# File 'lib/google_drive/session.rb', line 392

def entry_element_to_file(entry) #:nodoc:
  type, resource_id = entry.css("gd|resourceId").text.split(/:/)
  title = entry.css("title").text
  case type
    when "folder"
      return Collection.new(self, entry)
    when "spreadsheet"
      worksheets_feed_link = entry.css(
        "link[rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed']")[0]
      return Spreadsheet.new(self, worksheets_feed_link["href"], title)
    else
      return GoogleDrive::File.new(self, entry)
  end
end

#file_by_title(title) ⇒ Object

Returns GoogleDrive::File or its subclass whose title exactly matches title. Returns nil if not found. If multiple files with the title are found, returns one of them.



130
131
132
# File 'lib/google_drive/session.rb', line 130

def file_by_title(title)
  return files("title" => title, "title-exact" => "true")[0]
end

#files(params = {}) ⇒ Object

Returns list of files for the user as array of GoogleDrive::File or its subclass. You can specify query parameters described at developers.google.com/google-apps/documents-list/#getting_a_list_of_documents_and_files

files doesn’t return collections unless “showfolders” => true is specified.

e.g.

session.files
session.files("title" => "hoge", "title-exact" => "true")


120
121
122
123
124
125
# File 'lib/google_drive/session.rb', line 120

def files(params = {})
  url = concat_url(
      "#{DOCS_BASE_URL}?v=3", "?" + encode_query(params))
  doc = request(:get, url, :auth => :writely)
  return doc.css("feed > entry").map(){ |e| entry_element_to_file(e) }
end

#inspectObject



438
439
440
# File 'lib/google_drive/session.rb', line 438

def inspect
  return "#<%p:0x%x>" % [self.class, self.object_id]
end

#login(mail, password) ⇒ Object

Authenticates with given mail and password, and updates current session object if succeeds. Raises GoogleDrive::AuthenticationError if fails. Google Apps account is supported.



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/google_drive/session.rb', line 76

def (mail, password)
  if !@fetcher.is_a?(ClientLoginFetcher)
    raise(GoogleDrive::Error,
        "Cannot call login for session created by login_with_oauth.")
  end
  begin
    @fetcher.auth_tokens = {
      :wise => authenticate(mail, password, :wise),
      :writely => authenticate(mail, password, :writely),
    }
  rescue GoogleDrive::Error => ex
    return true if @on_auth_fail && @on_auth_fail.call()
    raise(AuthenticationError, "Authentication failed for #{mail}: #{ex.message}")
  end
end

#request(method, url, params = {}) ⇒ Object

:nodoc:



407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
# File 'lib/google_drive/session.rb', line 407

def request(method, url, params = {}) #:nodoc:
  
  # Always uses HTTPS.
  url = url.gsub(%r{^http://}, "https://")
  data = params[:data]
  auth = params[:auth] || :wise
  if params[:header]
    extra_header = params[:header]
  elsif data
    extra_header = {"Content-Type" => "application/atom+xml"}
  else
    extra_header = {}
  end
  response_type = params[:response_type] || :xml

  while true
    response = @fetcher.request_raw(method, url, data, extra_header, auth)
    if response.code == "401" && @on_auth_fail && @on_auth_fail.call()
      next
    end
    if !(response.code =~ /^[23]/)
      raise(
        response.code == "401" ? AuthenticationError : GoogleDrive::Error,
        "Response code #{response.code} for #{method} #{url}: " +
        CGI.unescapeHTML(response.body))
    end
    return convert_response(response, response_type)
  end
  
end

#root_collectionObject

Returns the root collection.



207
208
209
# File 'lib/google_drive/session.rb', line 207

def root_collection
  return Collection.new(self, Collection::ROOT_URL)
end

#spreadsheet_by_key(key) ⇒ Object

Returns GoogleDrive::Spreadsheet with given key.

e.g.

# http://spreadsheets.google.com/ccc?key=pz7XtlQC-PYx-jrVMJErTcg&hl=ja
session.spreadsheet_by_key("pz7XtlQC-PYx-jrVMJErTcg")


160
161
162
163
# File 'lib/google_drive/session.rb', line 160

def spreadsheet_by_key(key)
  url = "https://spreadsheets.google.com/feeds/worksheets/#{key}/private/full"
  return Spreadsheet.new(self, url)
end

#spreadsheet_by_title(title) ⇒ Object

Returns GoogleDrive::Spreadsheet with given title. Returns nil if not found. If multiple spreadsheets with the title are found, returns one of them.



191
192
193
# File 'lib/google_drive/session.rb', line 191

def spreadsheet_by_title(title)
  return spreadsheets({"title" => title, "title-exact" => "true"})[0]
end

#spreadsheet_by_url(url) ⇒ Object

Returns GoogleDrive::Spreadsheet with given url. You must specify either of:

  • URL of the page you open to access the spreadsheet in your browser

  • URL of worksheet-based feed of the spreadseet

e.g.

session.spreadsheet_by_url(
  "https://docs.google.com/spreadsheet/ccc?key=pz7XtlQC-PYx-jrVMJErTcg")
session.spreadsheet_by_url(
  "https://spreadsheets.google.com/feeds/" +
  "worksheets/pz7XtlQC-PYx-jrVMJErTcg/private/full")


175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/google_drive/session.rb', line 175

def spreadsheet_by_url(url)
  # Tries to parse it as URL of human-readable spreadsheet.
  uri = URI.parse(url)
  if ["spreadsheets.google.com", "docs.google.com"].include?(uri.host) &&
      uri.path =~ /\/ccc$/
    if (uri.query || "").split(/&/).find(){ |s| s=~ /^key=(.*)$/ }
      return spreadsheet_by_key($1)
    end
  end
  # Assumes the URL is worksheets feed URL.
  return Spreadsheet.new(self, url)
end

#spreadsheets(params = {}) ⇒ Object

Returns list of spreadsheets for the user as array of GoogleDrive::Spreadsheet. You can specify query parameters e.g. “title”, “title-exact”.

e.g.

session.spreadsheets
session.spreadsheets("title" => "hoge")
session.spreadsheets("title" => "hoge", "title-exact" => "true")


141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/google_drive/session.rb', line 141

def spreadsheets(params = {})
  query = encode_query(params)
  doc = request(
      :get, "https://spreadsheets.google.com/feeds/spreadsheets/private/full?#{query}")
  result = []
  doc.css("feed > entry").each() do |entry|
    title = entry.css("title").text
    url = entry.css(
      "link[rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed']")[0]["href"]
    result.push(Spreadsheet.new(self, url, title))
  end
  return result
end

#upload_from_file(path, title = nil, params = {}) ⇒ Object

Uploads a local file. Returns a GoogleSpreadsheet::File object.

e.g.

# Uploads a text file and converts to a Google Docs document:
session.upload_from_file("/path/to/hoge.txt")

# Uploads without conversion:
session.upload_from_file("/path/to/hoge.txt", "Hoge", :convert => false)

# Uploads with explicit content type:
session.upload_from_file("/path/to/hoge", "Hoge", :content_type => "text/plain")

# Uploads a text file and converts to a Google Spreadsheet:
session.upload_from_file("/path/to/hoge.tsv", "Hoge")
session.upload_from_file("/path/to/hoge.csv", "Hoge")
session.upload_from_file("/path/to/hoge", "Hoge", :content_type => "text/tab-separated-values")
session.upload_from_file("/path/to/hoge", "Hoge", :content_type => "text/tsv")


311
312
313
314
315
316
317
# File 'lib/google_drive/session.rb', line 311

def upload_from_file(path, title = nil, params = {})
  file_name = ::File.basename(path)
  params = {:file_name => file_name}.merge(params)
  open(path, "rb") do |f|
    return upload_from_io(f, title || file_name, params)
  end
end

#upload_from_io(io, title = "Untitled", params = {}) ⇒ Object

Uploads a file. Reads content from io. Returns a GoogleSpreadsheet::File object.



321
322
323
324
325
326
327
# File 'lib/google_drive/session.rb', line 321

def upload_from_io(io, title = "Untitled", params = {})
  doc = request(:get, "#{DOCS_BASE_URL}?v=3",
      :auth => :writely)
  initial_url = doc.css(
      "link[rel='http://schemas.google.com/g/2005#resumable-create-media']")[0]["href"]
  return upload_raw(:post, initial_url, io, title, params)
end

#upload_from_string(content, title = "Untitled", params = {}) ⇒ Object

Uploads a file with the given title and content. Returns a GoogleSpreadsheet::File object.

e.g.

# Uploads and converts to a Google Docs document:
session.upload_from_string(
    "Hello world.", "Hello", :content_type => "text/plain")

# Uploads without conversion:
session.upload_from_string(
    "Hello world.", "Hello", :content_type => "text/plain", :convert => false)

# Uploads and converts to a Google Spreadsheet:
session.upload_from_string("hoge\tfoo\n", "Hoge", :content_type => "text/tab-separated-values")
session.upload_from_string("hoge,foo\n", "Hoge", :content_type => "text/tsv")


289
290
291
# File 'lib/google_drive/session.rb', line 289

def upload_from_string(content, title = "Untitled", params = {})
  return upload_from_io(StringIO.new(content), title, params)
end

#upload_raw(method, url, io, title = "Untitled", params = {}) ⇒ Object

:nodoc:



329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/google_drive/session.rb', line 329

def upload_raw(method, url, io, title = "Untitled", params = {}) #:nodoc:
  
  params = {:convert => true}.merge(params)
  pos = io.pos
  io.seek(0, IO::SEEK_END)
  total_bytes = io.pos - pos
  io.pos = pos
  content_type = params[:content_type]
  if !content_type && params[:file_name]
    content_type = EXT_TO_CONTENT_TYPE[::File.extname(params[:file_name]).downcase]
  end
  if !content_type
    content_type = "application/octet-stream"
  end
  
  initial_xml = <<-"EOS"
    <entry xmlns="http://www.w3.org/2005/Atom" 
        xmlns:docs="http://schemas.google.com/docs/2007">
      <title>#{h(title)}</title>
    </entry>
  EOS
  
  default_initial_header = {
      "Content-Type" => "application/atom+xml",
      "X-Upload-Content-Type" => content_type,
      "X-Upload-Content-Length" => total_bytes.to_s(),
  }
  initial_full_url = concat_url(url, params[:convert] ? "?convert=true" : "?convert=false")
  initial_response = request(method, initial_full_url,
      :header => default_initial_header.merge(params[:header] || {}),
      :data => initial_xml,
      :auth => :writely,
      :response_type => :response)
  upload_url = initial_response["location"]
  
  if total_bytes > 0
    sent_bytes = 0
    while data = io.read(UPLOAD_CHUNK_SIZE)
      content_range = "bytes %d-%d/%d" % [
          sent_bytes,
          sent_bytes + data.bytesize - 1,
          total_bytes,
      ]
      upload_header = {
          "Content-Type" => content_type,
          "Content-Range" => content_range,
      }
      doc = request(
          :put, upload_url, :header => upload_header, :data => data, :auth => :writely)
      sent_bytes += data.bytesize
    end
  else
    upload_header = {
        "Content-Type" => content_type,
    }
    doc = request(
        :put, upload_url, :header => upload_header, :data => "", :auth => :writely)
  end
  
  return entry_element_to_file(doc.root)
  
end

#worksheet_by_url(url) ⇒ Object

Returns GoogleDrive::Worksheet with given url. You must specify URL of cell-based feed of the worksheet.

e.g.

session.worksheet_by_url(
  "http://spreadsheets.google.com/feeds/" +
  "cells/pz7XtlQC-PYxNmbBVgyiNWg/od6/private/full")


202
203
204
# File 'lib/google_drive/session.rb', line 202

def worksheet_by_url(url)
  return Worksheet.new(self, nil, url)
end