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.



227
228
229
# File 'lib/google_drive/session.rb', line 227

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")


244
245
246
247
248
249
250
251
252
# File 'lib/google_drive/session.rb', line 244

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).



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

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")


258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/google_drive/session.rb', line 258

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:



399
400
401
402
403
404
405
406
407
408
409
410
411
412
# File 'lib/google_drive/session.rb', line 399

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

#find_by_id(resource_id) ⇒ Object

Returns GoogleDrive::File. Returns nil if not found



136
137
138
139
# File 'lib/google_drive/session.rb', line 136

def find_by_id(resource_id)
  doc = request(:get, "https://docs.google.com/feeds/documents/private/full/#{resource_id}", :auth => :writely)
  return entry_element_to_file(doc)
end

#inspectObject



445
446
447
# File 'lib/google_drive/session.rb', line 445

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:



414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
# File 'lib/google_drive/session.rb', line 414

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.



214
215
216
# File 'lib/google_drive/session.rb', line 214

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")


167
168
169
170
# File 'lib/google_drive/session.rb', line 167

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.



198
199
200
# File 'lib/google_drive/session.rb', line 198

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")


182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/google_drive/session.rb', line 182

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")


148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/google_drive/session.rb', line 148

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/csv")


318
319
320
321
322
323
324
# File 'lib/google_drive/session.rb', line 318

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.



328
329
330
331
332
333
334
# File 'lib/google_drive/session.rb', line 328

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")


296
297
298
# File 'lib/google_drive/session.rb', line 296

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:



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
391
392
393
394
395
396
397
# File 'lib/google_drive/session.rb', line 336

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")


209
210
211
# File 'lib/google_drive/session.rb', line 209

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