Class: GoogleDriveV0::Session

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

Overview

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

Constant Summary collapse

UPLOAD_CHUNK_SIZE =
512 * 1024

Constants included from Util

Util::DOCS_BASE_URL, Util::EXT_TO_CONTENT_TYPE

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 GoogleDriveV0.restore_session instead.



86
87
88
89
90
91
92
# File 'lib/google_drive_v0/session.rb', line 86

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

Class Method Details

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

DEPRECATED: Will be removed in the next version.

The same as GoogleDriveV0.login.



38
39
40
41
42
43
44
45
# File 'lib/google_drive_v0/session.rb', line 38

def self.(mail, password, proxy = nil)
  warn(
      "WARNING: GoogleDriveV0.login is deprecated and will be removed in the next version. " +
      "Use GoogleDriveV0.login_with_oauth instead.")
  session = Session.new(nil, ClientLoginFetcher.new({}, proxy))
  session.(mail, password)
  return session
end

.login_with_oauth(access_token, proxy = nil) ⇒ Object

The same as GoogleDriveV0.login_with_oauth.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/google_drive_v0/session.rb', line 48

def self.(access_token, proxy = nil)
  if proxy
    warn(
      "WARNING: Specifying a proxy object is deprecated and will not work in the next version. " +
      "Set ENV[\"http_proxy\"] instead.")
  end
  case access_token
    when OAuth::AccessToken
      warn(
        "WARNING: Authorization with OAuth1 is deprecated and will not work in the next version. " +
        "Use OAuth2 instead.")
      raise(GoogleDriveV0::Error, "proxy is not supported with OAuth1.") if proxy
      fetcher = OAuth1Fetcher.new(access_token)
    when OAuth2::AccessToken
      fetcher = OAuth2Fetcher.new(access_token.token, proxy)
    when String
      fetcher = OAuth2Fetcher.new(access_token, proxy)
    else
      raise(GoogleDriveV0::Error,
          "access_token is neither String, OAuth2::Token nor OAuth::Token: %p" % access_token)
  end
  return Session.new(nil, fetcher)
end

.new_dummyObject

Creates a dummy GoogleDriveV0::Session object for testing.



81
82
83
# File 'lib/google_drive_v0/session.rb', line 81

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

.restore_session(auth_tokens, proxy = nil) ⇒ Object

The same as GoogleDriveV0.restore_session.



73
74
75
76
77
78
# File 'lib/google_drive_v0/session.rb', line 73

def self.restore_session(auth_tokens, proxy = nil)
  warn(
      "WARNING: GoogleDriveV0.restore_session is deprecated and will be removed in the next version. " +
      "Use GoogleDriveV0.login_with_oauth instead.")
  return Session.new(auth_tokens, nil, proxy)
end

Instance Method Details

#auth_token(auth = :wise) ⇒ Object

Authentication token.



126
127
128
129
130
# File 'lib/google_drive_v0/session.rb', line 126

def auth_token(auth = :wise)
  warn(
      "WARNING: GoogleDriveV0::Session\#auth_token is deprecated and will be removed in the next version.")
  return self.auth_tokens[auth]
end

#auth_tokensObject

Authentication tokens.



114
115
116
117
118
119
120
121
122
123
# File 'lib/google_drive_v0/session.rb', line 114

def auth_tokens
  warn(
      "WARNING: GoogleDriveV0::Session\#auth_tokens is deprecated and will be removed in the next version.")
  if !@fetcher.is_a?(ClientLoginFetcher)
    raise(GoogleDriveV0::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 GoogleDriveV0::Collection. Returns nil if not found. If multiple collections with the title are found, returns one of them.



263
264
265
# File 'lib/google_drive_v0/session.rb', line 263

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

#collection_by_url(url) ⇒ Object

Returns GoogleDriveV0::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")


280
281
282
283
284
285
286
287
288
# File 'lib/google_drive_v0/session.rb', line 280

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, to_v3_url(url))
end

#collectionsObject

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



255
256
257
# File 'lib/google_drive_v0/session.rb', line 255

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 GoogleDriveV0::Spreadsheet.

e.g.

session.create_spreadsheet("My new sheet")


294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/google_drive_v0/session.rb', line 294

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:



436
437
438
439
440
441
442
443
444
445
446
447
448
449
# File 'lib/google_drive_v0/session.rb', line 436

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 GoogleDriveV0::File.new(self, entry)
  end
end

#file_by_title(title) ⇒ Object

Returns GoogleDriveV0::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.

If given an Array, traverses collections by title. e.g.

session.file_by_title(["myfolder", "mysubfolder/even/w/slash", "myfile"])


168
169
170
171
172
173
174
# File 'lib/google_drive_v0/session.rb', line 168

def file_by_title(title)
  if title.is_a?(Array)
    return self.root_collection.file_by_title(title)
  else
    return files("title" => title, "title-exact" => "true")[0]
  end
end

#files(params = {}) ⇒ Object

Returns list of files for the user as array of GoogleDriveV0::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")


155
156
157
158
159
160
# File 'lib/google_drive_v0/session.rb', line 155

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



482
483
484
# File 'lib/google_drive_v0/session.rb', line 482

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 GoogleDriveV0::AuthenticationError if fails. Google Apps account is supported.



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/google_drive_v0/session.rb', line 97

def (mail, password)
  if !@fetcher.is_a?(ClientLoginFetcher)
    raise(GoogleDriveV0::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 GoogleDriveV0::Error => ex
    return true if @on_auth_fail && @on_auth_fail.call()
    raise(AuthenticationError, "Authentication failed for #{mail}: #{ex.message}")
  end
end

#on_auth_failObject

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



134
135
136
137
138
# File 'lib/google_drive_v0/session.rb', line 134

def on_auth_fail
  warn(
      "WARNING: GoogleDriveV0::Session\#on_auth_fail is deprecated and will be removed in the next version.")
  return @on_auth_fail
end

#on_auth_fail=(func) ⇒ Object



140
141
142
143
144
# File 'lib/google_drive_v0/session.rb', line 140

def on_auth_fail=(func)
  warn(
      "WARNING: GoogleDriveV0::Session\#on_auth_fail is deprecated and will be removed in the next version.")
  @on_auth_fail = func
end

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

:nodoc:



451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
# File 'lib/google_drive_v0/session.rb', line 451

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;charset=utf-8"}
  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 : GoogleDriveV0::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.



250
251
252
# File 'lib/google_drive_v0/session.rb', line 250

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

#spreadsheet_by_key(key) ⇒ Object

Returns GoogleDriveV0::Spreadsheet with given key.

e.g.

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


199
200
201
202
# File 'lib/google_drive_v0/session.rb', line 199

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 GoogleDriveV0::Spreadsheet with given title. Returns nil if not found. If multiple spreadsheets with the title are found, returns one of them.



234
235
236
# File 'lib/google_drive_v0/session.rb', line 234

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

#spreadsheet_by_url(url) ⇒ Object

Returns GoogleDriveV0::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")


214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/google_drive_v0/session.rb', line 214

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)
    case uri.path
      when /\/d\/([^\/]+)/
        return spreadsheet_by_key($1)
      when /\/ccc$/
        if (uri.query || "").split(/&/).find(){ |s| s=~ /^key=(.*)$/ }
          return spreadsheet_by_key($1)
        end
    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 GoogleDriveV0::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")


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

def spreadsheets(params = {})
  url = concat_url(
      "#{DOCS_BASE_URL}/-/spreadsheet?v=3", "?" + encode_query(params))
  doc = request(:get, url, :auth => :writely)
  # The API may return non-spreadsheets too when title-exact is specified.
  # Probably a bug. For workaround, only returns Spreadsheet instances.
  return doc.css("feed > entry").
      map(){ |e| entry_element_to_file(e) }.
      select(){ |f| f.is_a?(Spreadsheet) }
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")


354
355
356
357
358
359
360
# File 'lib/google_drive_v0/session.rb', line 354

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.



364
365
366
367
368
369
370
371
# File 'lib/google_drive_v0/session.rb', line 364

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"]
  entry = upload_raw(:post, initial_url, io, title, params)
  return entry_element_to_file(entry)
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")


332
333
334
# File 'lib/google_drive_v0/session.rb', line 332

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:



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
398
399
400
401
402
403
404
405
406
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
# File 'lib/google_drive_v0/session.rb', line 373

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;charset=utf-8",
      "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 doc.root
  
end

#worksheet_by_url(url) ⇒ Object

Returns GoogleDriveV0::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")


245
246
247
# File 'lib/google_drive_v0/session.rb', line 245

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