google_drive 0.3.11 → 1.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
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