google_drive 0.3.11 → 1.0.0.pre1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. checksums.yaml +7 -7
  2. data/README.rdoc +27 -10
  3. data/lib/google_drive/acl.rb +40 -58
  4. data/lib/google_drive/acl_entry.rb +76 -56
  5. data/lib/google_drive/api_client_fetcher.rb +49 -0
  6. data/lib/google_drive/collection.rb +69 -71
  7. data/lib/google_drive/file.rb +171 -128
  8. data/lib/google_drive/session.rb +234 -268
  9. data/lib/google_drive/spreadsheet.rb +19 -163
  10. data/lib/google_drive/util.rb +126 -17
  11. data/lib/google_drive/worksheet.rb +108 -80
  12. data/lib/google_drive.rb +63 -57
  13. data/lib/google_drive_v1/acl.rb +115 -0
  14. data/lib/google_drive_v1/acl_entry.rb +100 -0
  15. data/lib/google_drive_v1/api_client_fetcher.rb +47 -0
  16. data/lib/google_drive_v1/authentication_error.rb +14 -0
  17. data/lib/{google_drive → google_drive_v1}/basic_fetcher.rb +1 -1
  18. data/lib/{google_drive → google_drive_v1}/client_login_fetcher.rb +2 -2
  19. data/lib/google_drive_v1/collection.rb +167 -0
  20. data/lib/google_drive_v1/error.rb +12 -0
  21. data/lib/google_drive_v1/file.rb +258 -0
  22. data/lib/google_drive_v1/list.rb +119 -0
  23. data/lib/google_drive_v1/list_row.rb +88 -0
  24. data/lib/{google_drive → google_drive_v1}/oauth1_fetcher.rb +1 -1
  25. data/lib/{google_drive → google_drive_v1}/oauth2_fetcher.rb +2 -2
  26. data/lib/google_drive_v1/record.rb +31 -0
  27. data/lib/google_drive_v1/session.rb +522 -0
  28. data/lib/google_drive_v1/spreadsheet.rb +248 -0
  29. data/lib/google_drive_v1/table.rb +60 -0
  30. data/lib/google_drive_v1/util.rb +73 -0
  31. data/lib/google_drive_v1/worksheet.rb +498 -0
  32. data/lib/google_drive_v1.rb +148 -0
  33. metadata +112 -77
  34. data/doc_src/google_drive/acl_entry.rb +0 -33
@@ -0,0 +1,498 @@
1
+ # Author: Hiroshi Ichikawa <http://gimite.net/>
2
+ # The license of this source is "New BSD Licence"
3
+
4
+ require "set"
5
+
6
+ require "google_drive_v1/util"
7
+ require "google_drive_v1/error"
8
+ require "google_drive_v1/table"
9
+ require "google_drive_v1/list"
10
+
11
+
12
+ module GoogleDriveV1
13
+
14
+ # A worksheet (i.e. a tab) in a spreadsheet.
15
+ # Use GoogleDriveV1::Spreadsheet#worksheets to get GoogleDriveV1::Worksheet object.
16
+ class Worksheet
17
+
18
+ include(Util)
19
+
20
+ def initialize(session, spreadsheet, cells_feed_url, title = nil, updated = nil) #:nodoc:
21
+
22
+ @session = session
23
+ @spreadsheet = spreadsheet
24
+ @cells_feed_url = cells_feed_url
25
+ @title = title
26
+ @updated = updated
27
+
28
+ @cells = nil
29
+ @input_values = nil
30
+ @numeric_values = nil
31
+ @modified = Set.new()
32
+ @list = nil
33
+
34
+ end
35
+
36
+ # URL of cell-based feed of the worksheet.
37
+ attr_reader(:cells_feed_url)
38
+
39
+ # URL of worksheet feed URL of the worksheet.
40
+ def worksheet_feed_url
41
+ # I don't know good way to get worksheet feed URL from cells feed URL.
42
+ # Probably it would be cleaner to keep worksheet feed URL and get cells feed URL
43
+ # from it.
44
+ if !(@cells_feed_url =~
45
+ %r{^https?://spreadsheets.google.com/feeds/cells/(.*)/(.*)/private/full((\?.*)?)$})
46
+ raise(GoogleDriveV1::Error,
47
+ "Cells feed URL is in unknown format: #{@cells_feed_url}")
48
+ end
49
+ return "https://spreadsheets.google.com/feeds/worksheets/#{$1}/private/full/#{$2}#{$3}"
50
+ end
51
+
52
+ # GoogleDriveV1::Spreadsheet which this worksheet belongs to.
53
+ def spreadsheet
54
+ if !@spreadsheet
55
+ if !(@cells_feed_url =~
56
+ %r{^https?://spreadsheets.google.com/feeds/cells/(.*)/(.*)/private/full(\?.*)?$})
57
+ raise(GoogleDriveV1::Error,
58
+ "Cells feed URL is in unknown format: #{@cells_feed_url}")
59
+ end
60
+ @spreadsheet = @session.spreadsheet_by_key($1)
61
+ end
62
+ return @spreadsheet
63
+ end
64
+
65
+ # Returns content of the cell as String. Arguments must be either
66
+ # (row number, column number) or cell name. Top-left cell is [1, 1].
67
+ #
68
+ # e.g.
69
+ # worksheet[2, 1] #=> "hoge"
70
+ # worksheet["A2"] #=> "hoge"
71
+ def [](*args)
72
+ (row, col) = parse_cell_args(args)
73
+ return self.cells[[row, col]] || ""
74
+ end
75
+
76
+ # Updates content of the cell.
77
+ # Arguments in the bracket must be either (row number, column number) or cell name.
78
+ # Note that update is not sent to the server until you call save().
79
+ # Top-left cell is [1, 1].
80
+ #
81
+ # e.g.
82
+ # worksheet[2, 1] = "hoge"
83
+ # worksheet["A2"] = "hoge"
84
+ # worksheet[1, 3] = "=A1+B1"
85
+ def []=(*args)
86
+ (row, col) = parse_cell_args(args[0...-1])
87
+ value = args[-1].to_s()
88
+ reload() if !@cells
89
+ @cells[[row, col]] = value
90
+ @input_values[[row, col]] = value
91
+ @numeric_values[[row, col]] = nil
92
+ @modified.add([row, col])
93
+ self.max_rows = row if row > @max_rows
94
+ self.max_cols = col if col > @max_cols
95
+ if value.empty?
96
+ @num_rows = nil
97
+ @num_cols = nil
98
+ else
99
+ @num_rows = row if row > num_rows
100
+ @num_cols = col if col > num_cols
101
+ end
102
+ end
103
+
104
+ # Updates cells in a rectangle area by a two-dimensional Array.
105
+ # +top_row+ and +left_col+ specifies the top-left corner of the area.
106
+ #
107
+ # e.g.
108
+ # worksheet.update_cells(2, 3, [["1", "2"], ["3", "4"]])
109
+ def update_cells(top_row, left_col, darray)
110
+ darray.each_with_index() do |array, y|
111
+ array.each_with_index() do |value, x|
112
+ self[top_row + y, left_col + x] = value
113
+ end
114
+ end
115
+ end
116
+
117
+ # Returns the value or the formula of the cell. Arguments must be either
118
+ # (row number, column number) or cell name. Top-left cell is [1, 1].
119
+ #
120
+ # If user input "=A1+B1" to cell [1, 3]:
121
+ # worksheet[1, 3] #=> "3" for example
122
+ # worksheet.input_value(1, 3) #=> "=RC[-2]+RC[-1]"
123
+ def input_value(*args)
124
+ (row, col) = parse_cell_args(args)
125
+ reload() if !@cells
126
+ return @input_values[[row, col]] || ""
127
+ end
128
+
129
+ # Returns the numeric value of the cell. Arguments must be either
130
+ # (row number, column number) or cell name. Top-left cell is [1, 1].
131
+ #
132
+ # e.g.
133
+ # worksheet[1, 3] #=> "3,0" # it depends on locale, currency...
134
+ # worksheet.numeric_value(1, 3) #=> 3.0
135
+ #
136
+ # Returns nil if the cell is empty or contains non-number.
137
+ #
138
+ # If you modify the cell, its numeric_value is nil until you call save() and reload().
139
+ #
140
+ # For details, see:
141
+ # https://developers.google.com/google-apps/spreadsheets/#working_with_cell-based_feeds
142
+ def numeric_value(*args)
143
+ (row, col) = parse_cell_args(args)
144
+ reload() if !@cells
145
+ return @numeric_values[[row, col]]
146
+ end
147
+
148
+ # Row number of the bottom-most non-empty row.
149
+ def num_rows
150
+ reload() if !@cells
151
+ # Memoizes it because this can be bottle-neck.
152
+ # https://github.com/gimite/google-drive-ruby/pull/49
153
+ return @num_rows ||= @input_values.select(){ |(r, c), v| !v.empty? }.map(){ |(r, c), v| r }.max || 0
154
+ end
155
+
156
+ # Column number of the right-most non-empty column.
157
+ def num_cols
158
+ reload() if !@cells
159
+ # Memoizes it because this can be bottle-neck.
160
+ # https://github.com/gimite/google-drive-ruby/pull/49
161
+ return @num_cols ||= @input_values.select(){ |(r, c), v| !v.empty? }.map(){ |(r, c), v| c }.max || 0
162
+ end
163
+
164
+ # Number of rows including empty rows.
165
+ def max_rows
166
+ reload() if !@cells
167
+ return @max_rows
168
+ end
169
+
170
+ # Updates number of rows.
171
+ # Note that update is not sent to the server until you call save().
172
+ def max_rows=(rows)
173
+ reload() if !@cells
174
+ @max_rows = rows
175
+ @meta_modified = true
176
+ end
177
+
178
+ # Number of columns including empty columns.
179
+ def max_cols
180
+ reload() if !@cells
181
+ return @max_cols
182
+ end
183
+
184
+ # Updates number of columns.
185
+ # Note that update is not sent to the server until you call save().
186
+ def max_cols=(cols)
187
+ reload() if !@cells
188
+ @max_cols = cols
189
+ @meta_modified = true
190
+ end
191
+
192
+ # Title of the worksheet (shown as tab label in Web interface).
193
+ def title
194
+ reload() if !@title
195
+ return @title
196
+ end
197
+
198
+ # Date updated of the worksheet (shown as tab label in Web interface).
199
+ def updated
200
+ reload() if !@updated
201
+ return @updated
202
+ end
203
+
204
+ # Updates title of the worksheet.
205
+ # Note that update is not sent to the server until you call save().
206
+ def title=(title)
207
+ reload() if !@cells
208
+ @title = title
209
+ @meta_modified = true
210
+ end
211
+
212
+ def cells #:nodoc:
213
+ reload() if !@cells
214
+ return @cells
215
+ end
216
+
217
+ # An array of spreadsheet rows. Each row contains an array of
218
+ # columns. Note that resulting array is 0-origin so:
219
+ #
220
+ # worksheet.rows[0][0] == worksheet[1, 1]
221
+ def rows(skip = 0)
222
+ nc = self.num_cols
223
+ result = ((1 + skip)..self.num_rows).map() do |row|
224
+ (1..nc).map(){ |col| self[row, col] }.freeze()
225
+ end
226
+ return result.freeze()
227
+ end
228
+
229
+ # Reloads content of the worksheets from the server.
230
+ # Note that changes you made by []= etc. is discarded if you haven't called save().
231
+ def reload()
232
+
233
+ doc = @session.request(:get, @cells_feed_url)
234
+ @max_rows = doc.css("gs|rowCount").text.to_i()
235
+ @max_cols = doc.css("gs|colCount").text.to_i()
236
+ @title = doc.css("feed > title")[0].text
237
+
238
+ @num_cols = nil
239
+ @num_rows = nil
240
+
241
+ @cells = {}
242
+ @input_values = {}
243
+ @numeric_values = {}
244
+ doc.css("feed > entry").each() do |entry|
245
+ cell = entry.css("gs|cell")[0]
246
+ row = cell["row"].to_i()
247
+ col = cell["col"].to_i()
248
+ @cells[[row, col]] = cell.inner_text
249
+ @input_values[[row, col]] = cell["inputValue"] || cell.inner_text
250
+ numeric_value = cell["numericValue"]
251
+ @numeric_values[[row, col]] = numeric_value ? numeric_value.to_f() : nil
252
+ end
253
+ @modified.clear()
254
+ @meta_modified = false
255
+ return true
256
+
257
+ end
258
+
259
+ # Saves your changes made by []=, etc. to the server.
260
+ def save()
261
+
262
+ sent = false
263
+
264
+ if @meta_modified
265
+
266
+ ws_doc = @session.request(:get, self.worksheet_feed_url)
267
+ edit_url = ws_doc.css("link[rel='edit']")[0]["href"]
268
+ xml = <<-"EOS"
269
+ <entry xmlns='http://www.w3.org/2005/Atom'
270
+ xmlns:gs='http://schemas.google.com/spreadsheets/2006'>
271
+ <title>#{h(self.title)}</title>
272
+ <gs:rowCount>#{h(self.max_rows)}</gs:rowCount>
273
+ <gs:colCount>#{h(self.max_cols)}</gs:colCount>
274
+ </entry>
275
+ EOS
276
+
277
+ @session.request(
278
+ :put, edit_url, :data => xml,
279
+ :header => {"Content-Type" => "application/atom+xml;charset=utf-8", "If-Match" => "*"})
280
+
281
+ @meta_modified = false
282
+ sent = true
283
+
284
+ end
285
+
286
+ if !@modified.empty?
287
+
288
+ # Gets id and edit URL for each cell.
289
+ # Note that return-empty=true is required to get those info for empty cells.
290
+ cell_entries = {}
291
+ rows = @modified.map(){ |r, c| r }
292
+ cols = @modified.map(){ |r, c| c }
293
+ url = concat_url(@cells_feed_url,
294
+ "?return-empty=true&min-row=#{rows.min}&max-row=#{rows.max}" +
295
+ "&min-col=#{cols.min}&max-col=#{cols.max}")
296
+ doc = @session.request(:get, url)
297
+
298
+ for entry in doc.css("entry")
299
+ row = entry.css("gs|cell")[0]["row"].to_i()
300
+ col = entry.css("gs|cell")[0]["col"].to_i()
301
+ cell_entries[[row, col]] = entry
302
+ end
303
+
304
+ # Updates cell values using batch operation.
305
+ # If the data is large, we split it into multiple operations, otherwise batch may fail.
306
+ @modified.each_slice(25) do |chunk|
307
+
308
+ xml = <<-EOS
309
+ <feed xmlns="http://www.w3.org/2005/Atom"
310
+ xmlns:batch="http://schemas.google.com/gdata/batch"
311
+ xmlns:gs="http://schemas.google.com/spreadsheets/2006">
312
+ <id>#{h(@cells_feed_url)}</id>
313
+ EOS
314
+ for row, col in chunk
315
+ value = @cells[[row, col]]
316
+ entry = cell_entries[[row, col]]
317
+ id = entry.css("id").text
318
+ edit_url = entry.css("link[rel='edit']")[0]["href"]
319
+ xml << <<-EOS
320
+ <entry>
321
+ <batch:id>#{h(row)},#{h(col)}</batch:id>
322
+ <batch:operation type="update"/>
323
+ <id>#{h(id)}</id>
324
+ <link rel="edit" type="application/atom+xml"
325
+ href="#{h(edit_url)}"/>
326
+ <gs:cell row="#{h(row)}" col="#{h(col)}" inputValue="#{h(value)}"/>
327
+ </entry>
328
+ EOS
329
+ end
330
+ xml << <<-"EOS"
331
+ </feed>
332
+ EOS
333
+
334
+ batch_url = concat_url(@cells_feed_url, "/batch")
335
+ result = @session.request(:post, batch_url, :data => xml, :header => {"Content-Type" => "application/atom+xml;charset=utf-8", "If-Match" => "*"})
336
+ for entry in result.css("atom|entry")
337
+ interrupted = entry.css("batch|interrupted")[0]
338
+ if interrupted
339
+ raise(GoogleDriveV1::Error, "Update has failed: %s" %
340
+ interrupted["reason"])
341
+ end
342
+ if !(entry.css("batch|status").first["code"] =~ /^2/)
343
+ raise(GoogleDriveV1::Error, "Updating cell %s has failed: %s" %
344
+ [entry.css("atom|id").text, entry.css("batch|status")[0]["reason"]])
345
+ end
346
+ end
347
+
348
+ end
349
+
350
+ @modified.clear()
351
+ sent = true
352
+
353
+ end
354
+
355
+ return sent
356
+
357
+ end
358
+
359
+ # Calls save() and reload().
360
+ def synchronize()
361
+ save()
362
+ reload()
363
+ end
364
+
365
+ # Deletes this worksheet. Deletion takes effect right away without calling save().
366
+ def delete()
367
+ ws_doc = @session.request(:get, self.worksheet_feed_url)
368
+ edit_url = ws_doc.css("link[rel='edit']")[0]["href"]
369
+ @session.request(:delete, edit_url)
370
+ end
371
+
372
+ # Returns true if you have changes made by []= which haven't been saved.
373
+ def dirty?
374
+ return !@modified.empty?
375
+ end
376
+
377
+ # DEPRECATED: Table and Record feeds are deprecated and they will not be available after
378
+ # March 2012.
379
+ #
380
+ # Creates table for the worksheet and returns GoogleDriveV1::Table.
381
+ # See this document for details:
382
+ # http://code.google.com/intl/en/apis/spreadsheets/docs/3.0/developers_guide_protocol.html#TableFeeds
383
+ def add_table(table_title, summary, columns, options)
384
+
385
+ warn(
386
+ "DEPRECATED: Google Spreadsheet Table and Record feeds are deprecated and they " +
387
+ "will not be available after March 2012.")
388
+ default_options = { :header_row => 1, :num_rows => 0, :start_row => 2}
389
+ options = default_options.merge(options)
390
+
391
+ column_xml = ""
392
+ columns.each() do |index, name|
393
+ column_xml += "<gs:column index='#{h(index)}' name='#{h(name)}'/>\n"
394
+ end
395
+
396
+ xml = <<-"EOS"
397
+ <entry xmlns="http://www.w3.org/2005/Atom"
398
+ xmlns:gs="http://schemas.google.com/spreadsheets/2006">
399
+ <title type='text'>#{h(table_title)}</title>
400
+ <summary type='text'>#{h(summary)}</summary>
401
+ <gs:worksheet name='#{h(self.title)}' />
402
+ <gs:header row='#{options[:header_row]}' />
403
+ <gs:data numRows='#{options[:num_rows]}' startRow='#{options[:start_row]}'>
404
+ #{column_xml}
405
+ </gs:data>
406
+ </entry>
407
+ EOS
408
+
409
+ result = @session.request(:post, self.spreadsheet.tables_feed_url, :data => xml)
410
+ return Table.new(@session, result)
411
+
412
+ end
413
+
414
+ # DEPRECATED: Table and Record feeds are deprecated and they will not be available after
415
+ # March 2012.
416
+ #
417
+ # Returns list of tables for the workwheet.
418
+ def tables
419
+ warn(
420
+ "DEPRECATED: Google Spreadsheet Table and Record feeds are deprecated and they " +
421
+ "will not be available after March 2012.")
422
+ return self.spreadsheet.tables.select(){ |t| t.worksheet_title == self.title }
423
+ end
424
+
425
+ # List feed URL of the worksheet.
426
+ def list_feed_url
427
+ # Gets the worksheets metafeed.
428
+ entry = @session.request(:get, self.worksheet_feed_url)
429
+
430
+ # Gets the URL of list-based feed for the given spreadsheet.
431
+ return entry.css(
432
+ "link[rel='http://schemas.google.com/spreadsheets/2006#listfeed']")[0]["href"]
433
+ end
434
+
435
+ # Provides access to cells using column names, assuming the first row contains column
436
+ # names. Returned object is GoogleDriveV1::List which you can use mostly as
437
+ # Array of Hash.
438
+ #
439
+ # e.g. Assuming the first row is ["x", "y"]:
440
+ # worksheet.list[0]["x"] #=> "1" # i.e. worksheet[2, 1]
441
+ # worksheet.list[0]["y"] #=> "2" # i.e. worksheet[2, 2]
442
+ # worksheet.list[1]["x"] = "3" # i.e. worksheet[3, 1] = "3"
443
+ # worksheet.list[1]["y"] = "4" # i.e. worksheet[3, 2] = "4"
444
+ # worksheet.list.push({"x" => "5", "y" => "6"})
445
+ #
446
+ # Note that update is not sent to the server until you call save().
447
+ def list
448
+ return @list ||= List.new(self)
449
+ end
450
+
451
+ # Returns a [row, col] pair for a cell name string.
452
+ # e.g.
453
+ # worksheet.cell_name_to_row_col("C2") #=> [2, 3]
454
+ def cell_name_to_row_col(cell_name)
455
+ if !cell_name.is_a?(String)
456
+ raise(ArgumentError, "Cell name must be a string: %p" % cell_name)
457
+ end
458
+ if !(cell_name.upcase =~ /^([A-Z]+)(\d+)$/)
459
+ raise(ArgumentError,
460
+ "Cell name must be only letters followed by digits with no spaces in between: %p" %
461
+ cell_name)
462
+ end
463
+ col = 0
464
+ $1.each_byte() do |b|
465
+ # 0x41: "A"
466
+ col = col * 26 + (b - 0x41 + 1)
467
+ end
468
+ row = $2.to_i()
469
+ return [row, col]
470
+ end
471
+
472
+ def inspect
473
+ fields = {:worksheet_feed_url => self.worksheet_feed_url}
474
+ fields[:title] = @title if @title
475
+ return "\#<%p %s>" % [self.class, fields.map(){ |k, v| "%s=%p" % [k, v] }.join(", ")]
476
+ end
477
+
478
+ private
479
+
480
+ def parse_cell_args(args)
481
+ if args.size == 1 && args[0].is_a?(String)
482
+ return cell_name_to_row_col(args[0])
483
+ elsif args.size == 2 && args[0].is_a?(Integer) && args[1].is_a?(Integer)
484
+ if args[0] >= 1 && args[1] >= 1
485
+ return args
486
+ else
487
+ raise(ArgumentError,
488
+ "Row/col must be >= 1 (1-origin), but are %d/%d" % [args[0], args[1]])
489
+ end
490
+ else
491
+ raise(ArgumentError,
492
+ "Arguments must be either one String or two Integer's, but are %p" % [args])
493
+ end
494
+ end
495
+
496
+ end
497
+
498
+ end
@@ -0,0 +1,148 @@
1
+ # Author: Hiroshi Ichikawa <http://gimite.net/>
2
+ # The license of this source is "New BSD Licence"
3
+
4
+ require "rubygems"
5
+ require "google/api_client"
6
+ require "json"
7
+
8
+ require "google_drive_v1/session"
9
+
10
+
11
+ module GoogleDriveV1
12
+
13
+ # Authenticates with given +mail+ and +password+, and returns GoogleDriveV1::Session
14
+ # if succeeds. Raises GoogleDriveV1::AuthenticationError if fails.
15
+ # Google Apps account is supported.
16
+ #
17
+ # +proxy+ is deprecated, and will be removed in the next version.
18
+ def self.login(mail, password, proxy = nil)
19
+ return Session.login(mail, password, proxy)
20
+ end
21
+
22
+ # Authenticates with given OAuth2 token.
23
+ #
24
+ # +access_token+ can be either OAuth2 access_token string or OAuth2::AccessToken.
25
+ # Specifying OAuth::AccessToken is deprecated, and will not work in the next version.
26
+ #
27
+ # +proxy+ is deprecated, and will be removed in the next version.
28
+ #
29
+ # OAuth2 code example:
30
+ #
31
+ # client = OAuth2::Client.new(
32
+ # your_client_id, your_client_secret,
33
+ # :site => "https://accounts.google.com",
34
+ # :token_url => "/o/oauth2/token",
35
+ # :authorize_url => "/o/oauth2/auth")
36
+ # auth_url = client.auth_code.authorize_url(
37
+ # :redirect_uri => "http://example.com/",
38
+ # :scope =>
39
+ # "https://docs.google.com/feeds/ " +
40
+ # "https://docs.googleusercontent.com/ " +
41
+ # "https://spreadsheets.google.com/feeds/")
42
+ # # Redirect the user to auth_url and get authorization code from redirect URL.
43
+ # auth_token = client.auth_code.get_token(
44
+ # authorization_code, :redirect_uri => "http://example.com/")
45
+ # session = GoogleDriveV1.login_with_oauth(auth_token.token)
46
+ #
47
+ # Or, from existing refresh token:
48
+ #
49
+ # auth_token = OAuth2::AccessToken.from_hash(client,
50
+ # {:refresh_token => refresh_token, :expires_at => expires_at})
51
+ # auth_token = auth_token.refresh!
52
+ # session = GoogleDriveV1.login_with_oauth(auth_token.token)
53
+ #
54
+ # If your app is not a Web app, use "urn:ietf:wg:oauth:2.0:oob" as redirect_url. Then
55
+ # authorization code is shown after authorization.
56
+ #
57
+ # See these documents for details:
58
+ #
59
+ # - https://github.com/intridea/oauth2
60
+ # - http://code.google.com/apis/accounts/docs/OAuth2.html
61
+ # - http://oauth.rubyforge.org/
62
+ # - http://code.google.com/apis/accounts/docs/OAuth.html
63
+ def self.login_with_oauth(access_token, proxy = nil)
64
+ return Session.login_with_oauth(access_token, proxy)
65
+ end
66
+
67
+ # Restores session using return value of auth_tokens method of previous session.
68
+ #
69
+ # See GoogleDriveV1.login for description of parameter +proxy+.
70
+ def self.restore_session(auth_tokens, proxy = nil)
71
+ return Session.restore_session(auth_tokens, proxy)
72
+ end
73
+
74
+ # Restores GoogleDriveV1::Session from +path+ and returns it.
75
+ # If +path+ doesn't exist or authentication has failed, prompts the user to authorize the access,
76
+ # stores the session to +path+ and returns it.
77
+ #
78
+ # +path+ defaults to ENV["HOME"] + "/.ruby_google_drive.token".
79
+ #
80
+ # You can specify your own OAuth +client_id+ and +client_secret+. Otherwise the default one is used.
81
+ def self.saved_session(path = nil, proxy = nil, client_id = nil, client_secret = nil)
82
+
83
+ if proxy
84
+ raise(
85
+ ArgumentError,
86
+ "Specifying a proxy object is no longer supported. Set ENV[\"http_proxy\"] instead.")
87
+ end
88
+
89
+ if !client_id && !client_secret
90
+ client_id = "452925651630-egr1f18o96acjjvphpbbd1qlsevkho1d.apps.googleusercontent.com"
91
+ client_secret = "1U3-Krii5x1oLPrwD5zgn-ry"
92
+ elsif !client_id || !client_secret
93
+ raise(ArgumentError, "client_id and client_secret must be both specified or both omitted")
94
+ end
95
+
96
+ path ||= ENV["HOME"] + "/.ruby_google_drive.token"
97
+ if ::File.exist?(path)
98
+ lines = ::File.readlines(path)
99
+ case lines.size
100
+ when 1
101
+ token_data = JSON.parse(lines[0].chomp())
102
+ when 2
103
+ # Old format.
104
+ token_data = nil
105
+ else
106
+ raise(ArgumentError, "Not a token file: %s" % path)
107
+ end
108
+ else
109
+ token_data = nil
110
+ end
111
+
112
+ client = Google::APIClient.new(
113
+ :application_name => "google_drive Ruby library",
114
+ :application_version => "0.3.11"
115
+ )
116
+ auth = client.authorization
117
+ auth.client_id = client_id
118
+ auth.client_secret = client_secret
119
+ auth.scope =
120
+ "https://www.googleapis.com/auth/drive " +
121
+ "https://spreadsheets.google.com/feeds/ " +
122
+ "https://docs.google.com/feeds/ " +
123
+ "https://docs.googleusercontent.com/"
124
+ auth.redirect_uri = "urn:ietf:wg:oauth:2.0:oob"
125
+
126
+ if token_data
127
+
128
+ auth.refresh_token = token_data["refresh_token"]
129
+ auth.fetch_access_token!()
130
+
131
+ else
132
+
133
+ $stderr.print("\n1. Open this page:\n%s\n\n" % auth.authorization_uri)
134
+ $stderr.print("2. Enter the authorization code shown in the page: ")
135
+ auth.code = $stdin.gets().chomp()
136
+ auth.fetch_access_token!()
137
+ token_data = {"refresh_token" => auth.refresh_token}
138
+ open(path, "w", 0600) do |f|
139
+ f.puts(JSON.dump(token_data))
140
+ end
141
+
142
+ end
143
+
144
+ return GoogleDriveV1.login_with_oauth(auth.access_token)
145
+
146
+ end
147
+
148
+ end