google_drive 1.0.4 → 1.0.5
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.
- checksums.yaml +5 -13
- data/README.rdoc +15 -18
- data/lib/google_drive.rb +66 -59
- data/lib/google_drive/config.rb +37 -0
- data/lib/google_drive/error.rb +0 -4
- data/lib/google_drive/session.rb +18 -17
- metadata +28 -47
- data/lib/google_drive_v0.rb +0 -148
- data/lib/google_drive_v0/acl.rb +0 -115
- data/lib/google_drive_v0/acl_entry.rb +0 -100
- data/lib/google_drive_v0/api_client_fetcher.rb +0 -47
- data/lib/google_drive_v0/authentication_error.rb +0 -14
- data/lib/google_drive_v0/basic_fetcher.rb +0 -50
- data/lib/google_drive_v0/client_login_fetcher.rb +0 -31
- data/lib/google_drive_v0/collection.rb +0 -167
- data/lib/google_drive_v0/error.rb +0 -12
- data/lib/google_drive_v0/file.rb +0 -258
- data/lib/google_drive_v0/list.rb +0 -119
- data/lib/google_drive_v0/list_row.rb +0 -88
- data/lib/google_drive_v0/oauth1_fetcher.rb +0 -26
- data/lib/google_drive_v0/oauth2_fetcher.rb +0 -24
- data/lib/google_drive_v0/record.rb +0 -31
- data/lib/google_drive_v0/session.rb +0 -522
- data/lib/google_drive_v0/spreadsheet.rb +0 -248
- data/lib/google_drive_v0/table.rb +0 -60
- data/lib/google_drive_v0/util.rb +0 -73
- data/lib/google_drive_v0/worksheet.rb +0 -498
data/lib/google_drive_v0/list.rb
DELETED
@@ -1,119 +0,0 @@
|
|
1
|
-
# Author: Hiroshi Ichikawa <http://gimite.net/>
|
2
|
-
# The license of this source is "New BSD Licence"
|
3
|
-
|
4
|
-
require "google_drive_v0/util"
|
5
|
-
require "google_drive_v0/error"
|
6
|
-
require "google_drive_v0/list_row"
|
7
|
-
|
8
|
-
|
9
|
-
module GoogleDriveV0
|
10
|
-
|
11
|
-
# Provides access to cells using column names.
|
12
|
-
# Use GoogleDriveV0::Worksheet#list to get GoogleDriveV0::List object.
|
13
|
-
#--
|
14
|
-
# This is implemented as wrapper of GoogleDriveV0::Worksheet i.e. using cells
|
15
|
-
# feed, not list feed. In this way, we can easily provide consistent API as
|
16
|
-
# GoogleDriveV0::Worksheet using save()/reload().
|
17
|
-
class List
|
18
|
-
|
19
|
-
include(Enumerable)
|
20
|
-
|
21
|
-
def initialize(worksheet) #:nodoc:
|
22
|
-
@worksheet = worksheet
|
23
|
-
end
|
24
|
-
|
25
|
-
# Number of non-empty rows in the worksheet excluding the first row.
|
26
|
-
def size
|
27
|
-
return @worksheet.num_rows - 1
|
28
|
-
end
|
29
|
-
|
30
|
-
# Returns Hash-like object (GoogleDriveV0::ListRow) for the row with the
|
31
|
-
# index. Keys of the object are colum names (the first row).
|
32
|
-
# The second row has index 0.
|
33
|
-
#
|
34
|
-
# Note that updates to the returned object are not sent to the server until
|
35
|
-
# you call GoogleDriveV0::Worksheet#save().
|
36
|
-
def [](index)
|
37
|
-
return ListRow.new(self, index)
|
38
|
-
end
|
39
|
-
|
40
|
-
# Updates the row with the index with the given Hash object.
|
41
|
-
# Keys of +hash+ are colum names (the first row).
|
42
|
-
# The second row has index 0.
|
43
|
-
#
|
44
|
-
# Note that update is not sent to the server until
|
45
|
-
# you call GoogleDriveV0::Worksheet#save().
|
46
|
-
def []=(index, hash)
|
47
|
-
self[index].replace(hash)
|
48
|
-
end
|
49
|
-
|
50
|
-
# Iterates over Hash-like object (GoogleDriveV0::ListRow) for each row
|
51
|
-
# (except for the first row).
|
52
|
-
# Keys of the object are colum names (the first row).
|
53
|
-
def each(&block)
|
54
|
-
for i in 0...self.size
|
55
|
-
yield(self[i])
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
# Column names i.e. the contents of the first row.
|
60
|
-
# Duplicates are removed.
|
61
|
-
def keys
|
62
|
-
return (1..@worksheet.num_cols).map(){ |i| @worksheet[1, i] }.uniq()
|
63
|
-
end
|
64
|
-
|
65
|
-
# Updates column names i.e. the contents of the first row.
|
66
|
-
#
|
67
|
-
# Note that update is not sent to the server until
|
68
|
-
# you call GoogleDriveV0::Worksheet#save().
|
69
|
-
def keys=(ary)
|
70
|
-
for i in 1..ary.size
|
71
|
-
@worksheet[1, i] = ary[i - 1]
|
72
|
-
end
|
73
|
-
for i in (ary.size + 1)..@worksheet.num_cols
|
74
|
-
@worksheet[1, i] = ""
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
# Adds a new row to the bottom.
|
79
|
-
# Keys of +hash+ are colum names (the first row).
|
80
|
-
# Returns GoogleDriveV0::ListRow for the new row.
|
81
|
-
#
|
82
|
-
# Note that update is not sent to the server until
|
83
|
-
# you call GoogleDriveV0::Worksheet#save().
|
84
|
-
def push(hash)
|
85
|
-
row = self[self.size]
|
86
|
-
row.update(hash)
|
87
|
-
return row
|
88
|
-
end
|
89
|
-
|
90
|
-
# Returns all rows (except for the first row) as Array of Hash.
|
91
|
-
# Keys of Hash objects are colum names (the first row).
|
92
|
-
def to_hash_array()
|
93
|
-
return self.map(){ |r| r.to_hash() }
|
94
|
-
end
|
95
|
-
|
96
|
-
def get(index, key) #:nodoc:
|
97
|
-
return @worksheet[index + 2, key_to_col(key)]
|
98
|
-
end
|
99
|
-
|
100
|
-
def numeric_value(index, key) #:nodoc:
|
101
|
-
return @worksheet.numeric_value(index + 2, key_to_col(key))
|
102
|
-
end
|
103
|
-
|
104
|
-
def set(index, key, value) #:nodoc:
|
105
|
-
@worksheet[index + 2, key_to_col(key)] = value
|
106
|
-
end
|
107
|
-
|
108
|
-
private
|
109
|
-
|
110
|
-
def key_to_col(key)
|
111
|
-
key = key.to_s()
|
112
|
-
col = (1..@worksheet.num_cols).find(){ |c| @worksheet[1, c] == key }
|
113
|
-
raise(GoogleDriveV0::Error, "Column doesn't exist: %p" % key) if !col
|
114
|
-
return col
|
115
|
-
end
|
116
|
-
|
117
|
-
end
|
118
|
-
|
119
|
-
end
|
@@ -1,88 +0,0 @@
|
|
1
|
-
# Author: Hiroshi Ichikawa <http://gimite.net/>
|
2
|
-
# The license of this source is "New BSD Licence"
|
3
|
-
|
4
|
-
require "forwardable"
|
5
|
-
|
6
|
-
require "google_drive_v0/util"
|
7
|
-
require "google_drive_v0/error"
|
8
|
-
|
9
|
-
|
10
|
-
module GoogleDriveV0
|
11
|
-
|
12
|
-
# Hash-like object returned by GoogleDriveV0::List#[].
|
13
|
-
class ListRow
|
14
|
-
|
15
|
-
include(Enumerable)
|
16
|
-
extend(Forwardable)
|
17
|
-
|
18
|
-
def_delegators(:to_hash,
|
19
|
-
:keys, :values, :each_key, :each_value, :each, :each_pair, :hash,
|
20
|
-
:assoc, :fetch, :flatten, :key, :invert, :size, :length, :rassoc,
|
21
|
-
:merge, :reject, :select, :sort, :to_a, :values_at)
|
22
|
-
|
23
|
-
def initialize(list, index) #:nodoc:
|
24
|
-
@list = list
|
25
|
-
@index = index
|
26
|
-
end
|
27
|
-
|
28
|
-
def [](key)
|
29
|
-
return @list.get(@index, key)
|
30
|
-
end
|
31
|
-
|
32
|
-
def numeric_value(key)
|
33
|
-
return @list.numeric_value(@index, key)
|
34
|
-
end
|
35
|
-
|
36
|
-
def []=(key, value)
|
37
|
-
@list.set(@index, key, value)
|
38
|
-
end
|
39
|
-
|
40
|
-
def has_key?(key)
|
41
|
-
return @list.keys.include?(key)
|
42
|
-
end
|
43
|
-
|
44
|
-
alias include? has_key?
|
45
|
-
alias key? has_key?
|
46
|
-
alias member? has_key?
|
47
|
-
|
48
|
-
def update(hash)
|
49
|
-
for k, v in hash
|
50
|
-
self[k] = v
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
alias merge! update
|
55
|
-
|
56
|
-
def replace(hash)
|
57
|
-
clear()
|
58
|
-
update(hash)
|
59
|
-
end
|
60
|
-
|
61
|
-
def clear()
|
62
|
-
for key in @list.keys
|
63
|
-
self[key] = ""
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def to_hash()
|
68
|
-
result = {}
|
69
|
-
for key in @list.keys
|
70
|
-
result[key] = self[key]
|
71
|
-
end
|
72
|
-
return result
|
73
|
-
end
|
74
|
-
|
75
|
-
def ==(other)
|
76
|
-
return self.class == other.class && self.to_hash() == other.to_hash()
|
77
|
-
end
|
78
|
-
|
79
|
-
alias === ==
|
80
|
-
alias eql? ==
|
81
|
-
|
82
|
-
def inspect
|
83
|
-
return "\#<%p %p>" % [self.class, to_hash()]
|
84
|
-
end
|
85
|
-
|
86
|
-
end
|
87
|
-
|
88
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
# Author: Hiroshi Ichikawa <http://gimite.net/>
|
2
|
-
# The license of this source is "New BSD Licence"
|
3
|
-
|
4
|
-
require "rubygems"
|
5
|
-
require "oauth"
|
6
|
-
|
7
|
-
|
8
|
-
module GoogleDriveV0
|
9
|
-
|
10
|
-
class OAuth1Fetcher #:nodoc:
|
11
|
-
|
12
|
-
def initialize(oauth1_token)
|
13
|
-
@oauth1_token = oauth1_token
|
14
|
-
end
|
15
|
-
|
16
|
-
def request_raw(method, url, data, extra_header, auth)
|
17
|
-
if method == :delete || method == :get
|
18
|
-
return @oauth1_token.__send__(method, url, extra_header)
|
19
|
-
else
|
20
|
-
return @oauth1_token.__send__(method, url, data, extra_header)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
# Author: Hiroshi Ichikawa <http://gimite.net/>
|
2
|
-
# The license of this source is "New BSD Licence"
|
3
|
-
|
4
|
-
require "google_drive_v0/basic_fetcher"
|
5
|
-
|
6
|
-
|
7
|
-
module GoogleDriveV0
|
8
|
-
|
9
|
-
class OAuth2Fetcher < BasicFetcher #:nodoc:
|
10
|
-
|
11
|
-
def initialize(auth_token, proxy)
|
12
|
-
super(proxy)
|
13
|
-
@auth_token = auth_token
|
14
|
-
end
|
15
|
-
|
16
|
-
private
|
17
|
-
|
18
|
-
def auth_header(auth)
|
19
|
-
return {"Authorization" => "Bearer %s" % @auth_token}
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
# Author: Hiroshi Ichikawa <http://gimite.net/>
|
2
|
-
# The license of this source is "New BSD Licence"
|
3
|
-
|
4
|
-
require "google_drive_v0/util"
|
5
|
-
require "google_drive_v0/error"
|
6
|
-
|
7
|
-
|
8
|
-
module GoogleDriveV0
|
9
|
-
|
10
|
-
# DEPRECATED: Table and Record feeds are deprecated and they will not be available after
|
11
|
-
# March 2012.
|
12
|
-
#
|
13
|
-
# Use GoogleDriveV0::Table#records to get GoogleDriveV0::Record objects.
|
14
|
-
class Record < Hash
|
15
|
-
include(Util)
|
16
|
-
|
17
|
-
def initialize(session, entry) #:nodoc:
|
18
|
-
@session = session
|
19
|
-
entry.css("gs|field").each() do |field|
|
20
|
-
self[field["name"]] = field.inner_text
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def inspect #:nodoc:
|
25
|
-
content = self.map(){ |k, v| "%p => %p" % [k, v] }.join(", ")
|
26
|
-
return "\#<%p:{%s}>" % [self.class, content]
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
end
|
@@ -1,522 +0,0 @@
|
|
1
|
-
# Author: Hiroshi Ichikawa <http://gimite.net/>
|
2
|
-
# The license of this source is "New BSD Licence"
|
3
|
-
|
4
|
-
require "cgi"
|
5
|
-
require "stringio"
|
6
|
-
|
7
|
-
require "rubygems"
|
8
|
-
require "nokogiri"
|
9
|
-
require "oauth"
|
10
|
-
require "oauth2"
|
11
|
-
|
12
|
-
require "google_drive_v0/util"
|
13
|
-
require "google_drive_v0/client_login_fetcher"
|
14
|
-
require "google_drive_v0/oauth1_fetcher"
|
15
|
-
require "google_drive_v0/oauth2_fetcher"
|
16
|
-
require "google_drive_v0/error"
|
17
|
-
require "google_drive_v0/authentication_error"
|
18
|
-
require "google_drive_v0/spreadsheet"
|
19
|
-
require "google_drive_v0/worksheet"
|
20
|
-
require "google_drive_v0/collection"
|
21
|
-
require "google_drive_v0/file"
|
22
|
-
|
23
|
-
|
24
|
-
module GoogleDriveV0
|
25
|
-
|
26
|
-
# Use GoogleDriveV0.login or GoogleDriveV0.saved_session to get
|
27
|
-
# GoogleDriveV0::Session object.
|
28
|
-
class Session
|
29
|
-
|
30
|
-
include(Util)
|
31
|
-
extend(Util)
|
32
|
-
|
33
|
-
UPLOAD_CHUNK_SIZE = 512 * 1024
|
34
|
-
|
35
|
-
# DEPRECATED: Will be removed in the next version.
|
36
|
-
#
|
37
|
-
# The same as GoogleDriveV0.login.
|
38
|
-
def self.login(mail, password, proxy = nil)
|
39
|
-
warn(
|
40
|
-
"WARNING: GoogleDriveV0.login is deprecated and will be removed in the next version. " +
|
41
|
-
"Use GoogleDriveV0.login_with_oauth instead.")
|
42
|
-
session = Session.new(nil, ClientLoginFetcher.new({}, proxy))
|
43
|
-
session.login(mail, password)
|
44
|
-
return session
|
45
|
-
end
|
46
|
-
|
47
|
-
# The same as GoogleDriveV0.login_with_oauth.
|
48
|
-
def self.login_with_oauth(access_token, proxy = nil)
|
49
|
-
if proxy
|
50
|
-
warn(
|
51
|
-
"WARNING: Specifying a proxy object is deprecated and will not work in the next version. " +
|
52
|
-
"Set ENV[\"http_proxy\"] instead.")
|
53
|
-
end
|
54
|
-
case access_token
|
55
|
-
when OAuth::AccessToken
|
56
|
-
warn(
|
57
|
-
"WARNING: Authorization with OAuth1 is deprecated and will not work in the next version. " +
|
58
|
-
"Use OAuth2 instead.")
|
59
|
-
raise(GoogleDriveV0::Error, "proxy is not supported with OAuth1.") if proxy
|
60
|
-
fetcher = OAuth1Fetcher.new(access_token)
|
61
|
-
when OAuth2::AccessToken
|
62
|
-
fetcher = OAuth2Fetcher.new(access_token.token, proxy)
|
63
|
-
when String
|
64
|
-
fetcher = OAuth2Fetcher.new(access_token, proxy)
|
65
|
-
else
|
66
|
-
raise(GoogleDriveV0::Error,
|
67
|
-
"access_token is neither String, OAuth2::Token nor OAuth::Token: %p" % access_token)
|
68
|
-
end
|
69
|
-
return Session.new(nil, fetcher)
|
70
|
-
end
|
71
|
-
|
72
|
-
# The same as GoogleDriveV0.restore_session.
|
73
|
-
def self.restore_session(auth_tokens, proxy = nil)
|
74
|
-
warn(
|
75
|
-
"WARNING: GoogleDriveV0.restore_session is deprecated and will be removed in the next version. " +
|
76
|
-
"Use GoogleDriveV0.login_with_oauth instead.")
|
77
|
-
return Session.new(auth_tokens, nil, proxy)
|
78
|
-
end
|
79
|
-
|
80
|
-
# Creates a dummy GoogleDriveV0::Session object for testing.
|
81
|
-
def self.new_dummy()
|
82
|
-
return Session.new(nil, Object.new())
|
83
|
-
end
|
84
|
-
|
85
|
-
# DEPRECATED: Use GoogleDriveV0.restore_session instead.
|
86
|
-
def initialize(auth_tokens = nil, fetcher = nil, proxy = nil)
|
87
|
-
if fetcher
|
88
|
-
@fetcher = fetcher
|
89
|
-
else
|
90
|
-
@fetcher = ClientLoginFetcher.new(auth_tokens || {}, proxy)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
# Authenticates with given +mail+ and +password+, and updates current session object
|
95
|
-
# if succeeds. Raises GoogleDriveV0::AuthenticationError if fails.
|
96
|
-
# Google Apps account is supported.
|
97
|
-
def login(mail, password)
|
98
|
-
if !@fetcher.is_a?(ClientLoginFetcher)
|
99
|
-
raise(GoogleDriveV0::Error,
|
100
|
-
"Cannot call login for session created by login_with_oauth.")
|
101
|
-
end
|
102
|
-
begin
|
103
|
-
@fetcher.auth_tokens = {
|
104
|
-
:wise => authenticate(mail, password, :wise),
|
105
|
-
:writely => authenticate(mail, password, :writely),
|
106
|
-
}
|
107
|
-
rescue GoogleDriveV0::Error => ex
|
108
|
-
return true if @on_auth_fail && @on_auth_fail.call()
|
109
|
-
raise(AuthenticationError, "Authentication failed for #{mail}: #{ex.message}")
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
# Authentication tokens.
|
114
|
-
def auth_tokens
|
115
|
-
warn(
|
116
|
-
"WARNING: GoogleDriveV0::Session\#auth_tokens is deprecated and will be removed in the next version.")
|
117
|
-
if !@fetcher.is_a?(ClientLoginFetcher)
|
118
|
-
raise(GoogleDriveV0::Error,
|
119
|
-
"Cannot call auth_tokens for session created by " +
|
120
|
-
"login_with_oauth.")
|
121
|
-
end
|
122
|
-
return @fetcher.auth_tokens
|
123
|
-
end
|
124
|
-
|
125
|
-
# Authentication token.
|
126
|
-
def auth_token(auth = :wise)
|
127
|
-
warn(
|
128
|
-
"WARNING: GoogleDriveV0::Session\#auth_token is deprecated and will be removed in the next version.")
|
129
|
-
return self.auth_tokens[auth]
|
130
|
-
end
|
131
|
-
|
132
|
-
# Proc or Method called when authentication has failed.
|
133
|
-
# When this function returns +true+, it tries again.
|
134
|
-
def on_auth_fail
|
135
|
-
warn(
|
136
|
-
"WARNING: GoogleDriveV0::Session\#on_auth_fail is deprecated and will be removed in the next version.")
|
137
|
-
return @on_auth_fail
|
138
|
-
end
|
139
|
-
|
140
|
-
def on_auth_fail=(func)
|
141
|
-
warn(
|
142
|
-
"WARNING: GoogleDriveV0::Session\#on_auth_fail is deprecated and will be removed in the next version.")
|
143
|
-
@on_auth_fail = func
|
144
|
-
end
|
145
|
-
|
146
|
-
# Returns list of files for the user as array of GoogleDriveV0::File or its subclass.
|
147
|
-
# You can specify query parameters described at
|
148
|
-
# https://developers.google.com/google-apps/documents-list/#getting_a_list_of_documents_and_files
|
149
|
-
#
|
150
|
-
# files doesn't return collections unless "showfolders" => true is specified.
|
151
|
-
#
|
152
|
-
# e.g.
|
153
|
-
# session.files
|
154
|
-
# session.files("title" => "hoge", "title-exact" => "true")
|
155
|
-
def files(params = {})
|
156
|
-
url = concat_url(
|
157
|
-
"#{DOCS_BASE_URL}?v=3", "?" + encode_query(params))
|
158
|
-
doc = request(:get, url, :auth => :writely)
|
159
|
-
return doc.css("feed > entry").map(){ |e| entry_element_to_file(e) }
|
160
|
-
end
|
161
|
-
|
162
|
-
# Returns GoogleDriveV0::File or its subclass whose title exactly matches +title+.
|
163
|
-
# Returns nil if not found. If multiple files with the +title+ are found, returns
|
164
|
-
# one of them.
|
165
|
-
#
|
166
|
-
# If given an Array, traverses collections by title. e.g.
|
167
|
-
# session.file_by_title(["myfolder", "mysubfolder/even/w/slash", "myfile"])
|
168
|
-
def file_by_title(title)
|
169
|
-
if title.is_a?(Array)
|
170
|
-
return self.root_collection.file_by_title(title)
|
171
|
-
else
|
172
|
-
return files("title" => title, "title-exact" => "true")[0]
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
# Returns list of spreadsheets for the user as array of GoogleDriveV0::Spreadsheet.
|
177
|
-
# You can specify query parameters e.g. "title", "title-exact".
|
178
|
-
#
|
179
|
-
# e.g.
|
180
|
-
# session.spreadsheets
|
181
|
-
# session.spreadsheets("title" => "hoge")
|
182
|
-
# session.spreadsheets("title" => "hoge", "title-exact" => "true")
|
183
|
-
def spreadsheets(params = {})
|
184
|
-
url = concat_url(
|
185
|
-
"#{DOCS_BASE_URL}/-/spreadsheet?v=3", "?" + encode_query(params))
|
186
|
-
doc = request(:get, url, :auth => :writely)
|
187
|
-
# The API may return non-spreadsheets too when title-exact is specified.
|
188
|
-
# Probably a bug. For workaround, only returns Spreadsheet instances.
|
189
|
-
return doc.css("feed > entry").
|
190
|
-
map(){ |e| entry_element_to_file(e) }.
|
191
|
-
select(){ |f| f.is_a?(Spreadsheet) }
|
192
|
-
end
|
193
|
-
|
194
|
-
# Returns GoogleDriveV0::Spreadsheet with given +key+.
|
195
|
-
#
|
196
|
-
# e.g.
|
197
|
-
# # http://spreadsheets.google.com/ccc?key=pz7XtlQC-PYx-jrVMJErTcg&hl=ja
|
198
|
-
# session.spreadsheet_by_key("pz7XtlQC-PYx-jrVMJErTcg")
|
199
|
-
def spreadsheet_by_key(key)
|
200
|
-
url = "https://spreadsheets.google.com/feeds/worksheets/#{key}/private/full"
|
201
|
-
return Spreadsheet.new(self, url)
|
202
|
-
end
|
203
|
-
|
204
|
-
# Returns GoogleDriveV0::Spreadsheet with given +url+. You must specify either of:
|
205
|
-
# - URL of the page you open to access the spreadsheet in your browser
|
206
|
-
# - URL of worksheet-based feed of the spreadseet
|
207
|
-
#
|
208
|
-
# e.g.
|
209
|
-
# session.spreadsheet_by_url(
|
210
|
-
# "https://docs.google.com/spreadsheet/ccc?key=pz7XtlQC-PYx-jrVMJErTcg")
|
211
|
-
# session.spreadsheet_by_url(
|
212
|
-
# "https://spreadsheets.google.com/feeds/" +
|
213
|
-
# "worksheets/pz7XtlQC-PYx-jrVMJErTcg/private/full")
|
214
|
-
def spreadsheet_by_url(url)
|
215
|
-
# Tries to parse it as URL of human-readable spreadsheet.
|
216
|
-
uri = URI.parse(url)
|
217
|
-
if ["spreadsheets.google.com", "docs.google.com"].include?(uri.host)
|
218
|
-
case uri.path
|
219
|
-
when /\/d\/([^\/]+)/
|
220
|
-
return spreadsheet_by_key($1)
|
221
|
-
when /\/ccc$/
|
222
|
-
if (uri.query || "").split(/&/).find(){ |s| s=~ /^key=(.*)$/ }
|
223
|
-
return spreadsheet_by_key($1)
|
224
|
-
end
|
225
|
-
end
|
226
|
-
end
|
227
|
-
# Assumes the URL is worksheets feed URL.
|
228
|
-
return Spreadsheet.new(self, url)
|
229
|
-
end
|
230
|
-
|
231
|
-
# Returns GoogleDriveV0::Spreadsheet with given +title+.
|
232
|
-
# Returns nil if not found. If multiple spreadsheets with the +title+ are found, returns
|
233
|
-
# one of them.
|
234
|
-
def spreadsheet_by_title(title)
|
235
|
-
return spreadsheets({"title" => title, "title-exact" => "true"})[0]
|
236
|
-
end
|
237
|
-
|
238
|
-
# Returns GoogleDriveV0::Worksheet with given +url+.
|
239
|
-
# You must specify URL of cell-based feed of the worksheet.
|
240
|
-
#
|
241
|
-
# e.g.
|
242
|
-
# session.worksheet_by_url(
|
243
|
-
# "http://spreadsheets.google.com/feeds/" +
|
244
|
-
# "cells/pz7XtlQC-PYxNmbBVgyiNWg/od6/private/full")
|
245
|
-
def worksheet_by_url(url)
|
246
|
-
return Worksheet.new(self, nil, url)
|
247
|
-
end
|
248
|
-
|
249
|
-
# Returns the root collection.
|
250
|
-
def root_collection
|
251
|
-
return Collection.new(self, Collection::ROOT_URL)
|
252
|
-
end
|
253
|
-
|
254
|
-
# Returns the top-level collections (direct children of the root collection).
|
255
|
-
def collections
|
256
|
-
return self.root_collection.subcollections
|
257
|
-
end
|
258
|
-
|
259
|
-
# Returns a top-level collection whose title exactly matches +title+ as
|
260
|
-
# GoogleDriveV0::Collection.
|
261
|
-
# Returns nil if not found. If multiple collections with the +title+ are found, returns
|
262
|
-
# one of them.
|
263
|
-
def collection_by_title(title)
|
264
|
-
return self.root_collection.subcollection_by_title(title)
|
265
|
-
end
|
266
|
-
|
267
|
-
# Returns GoogleDriveV0::Collection with given +url+.
|
268
|
-
# You must specify either of:
|
269
|
-
# - URL of the page you get when you go to https://docs.google.com/ with your browser and
|
270
|
-
# open a collection
|
271
|
-
# - URL of collection (folder) feed
|
272
|
-
#
|
273
|
-
# e.g.
|
274
|
-
# session.collection_by_url(
|
275
|
-
# "https://drive.google.com/#folders/" +
|
276
|
-
# "0B9GfDpQ2pBVUODNmOGE0NjIzMWU3ZC00NmUyLTk5NzEtYaFkZjY1MjAyxjMc")
|
277
|
-
# session.collection_by_url(
|
278
|
-
# "http://docs.google.com/feeds/default/private/full/folder%3A" +
|
279
|
-
# "0B9GfDpQ2pBVUODNmOGE0NjIzMWU3ZC00NmUyLTk5NzEtYaFkZjY1MjAyxjMc")
|
280
|
-
def collection_by_url(url)
|
281
|
-
uri = URI.parse(url)
|
282
|
-
if ["docs.google.com", "drive.google.com"].include?(uri.host) &&
|
283
|
-
uri.fragment =~ /^folders\/(.+)$/
|
284
|
-
# Looks like a URL of human-readable collection page. Converts to collection feed URL.
|
285
|
-
url = "#{DOCS_BASE_URL}/folder%3A#{$1}"
|
286
|
-
end
|
287
|
-
return Collection.new(self, to_v3_url(url))
|
288
|
-
end
|
289
|
-
|
290
|
-
# Creates new spreadsheet and returns the new GoogleDriveV0::Spreadsheet.
|
291
|
-
#
|
292
|
-
# e.g.
|
293
|
-
# session.create_spreadsheet("My new sheet")
|
294
|
-
def create_spreadsheet(
|
295
|
-
title = "Untitled",
|
296
|
-
feed_url = "https://docs.google.com/feeds/documents/private/full")
|
297
|
-
|
298
|
-
xml = <<-"EOS"
|
299
|
-
<atom:entry
|
300
|
-
xmlns:atom="http://www.w3.org/2005/Atom"
|
301
|
-
xmlns:docs="http://schemas.google.com/docs/2007">
|
302
|
-
<atom:category
|
303
|
-
scheme="http://schemas.google.com/g/2005#kind"
|
304
|
-
term="http://schemas.google.com/docs/2007#spreadsheet"
|
305
|
-
label="spreadsheet"/>
|
306
|
-
<atom:title>#{h(title)}</atom:title>
|
307
|
-
</atom:entry>
|
308
|
-
EOS
|
309
|
-
|
310
|
-
doc = request(:post, feed_url, :data => xml, :auth => :writely)
|
311
|
-
ss_url = doc.css(
|
312
|
-
"link[rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed']")[0]["href"]
|
313
|
-
return Spreadsheet.new(self, ss_url, title)
|
314
|
-
|
315
|
-
end
|
316
|
-
|
317
|
-
# Uploads a file with the given +title+ and +content+.
|
318
|
-
# Returns a GoogleSpreadsheet::File object.
|
319
|
-
#
|
320
|
-
# e.g.
|
321
|
-
# # Uploads and converts to a Google Docs document:
|
322
|
-
# session.upload_from_string(
|
323
|
-
# "Hello world.", "Hello", :content_type => "text/plain")
|
324
|
-
#
|
325
|
-
# # Uploads without conversion:
|
326
|
-
# session.upload_from_string(
|
327
|
-
# "Hello world.", "Hello", :content_type => "text/plain", :convert => false)
|
328
|
-
#
|
329
|
-
# # Uploads and converts to a Google Spreadsheet:
|
330
|
-
# session.upload_from_string("hoge\tfoo\n", "Hoge", :content_type => "text/tab-separated-values")
|
331
|
-
# session.upload_from_string("hoge,foo\n", "Hoge", :content_type => "text/tsv")
|
332
|
-
def upload_from_string(content, title = "Untitled", params = {})
|
333
|
-
return upload_from_io(StringIO.new(content), title, params)
|
334
|
-
end
|
335
|
-
|
336
|
-
# Uploads a local file.
|
337
|
-
# Returns a GoogleSpreadsheet::File object.
|
338
|
-
#
|
339
|
-
# e.g.
|
340
|
-
# # Uploads a text file and converts to a Google Docs document:
|
341
|
-
# session.upload_from_file("/path/to/hoge.txt")
|
342
|
-
#
|
343
|
-
# # Uploads without conversion:
|
344
|
-
# session.upload_from_file("/path/to/hoge.txt", "Hoge", :convert => false)
|
345
|
-
#
|
346
|
-
# # Uploads with explicit content type:
|
347
|
-
# session.upload_from_file("/path/to/hoge", "Hoge", :content_type => "text/plain")
|
348
|
-
#
|
349
|
-
# # Uploads a text file and converts to a Google Spreadsheet:
|
350
|
-
# session.upload_from_file("/path/to/hoge.tsv", "Hoge")
|
351
|
-
# session.upload_from_file("/path/to/hoge.csv", "Hoge")
|
352
|
-
# session.upload_from_file("/path/to/hoge", "Hoge", :content_type => "text/tab-separated-values")
|
353
|
-
# session.upload_from_file("/path/to/hoge", "Hoge", :content_type => "text/csv")
|
354
|
-
def upload_from_file(path, title = nil, params = {})
|
355
|
-
file_name = ::File.basename(path)
|
356
|
-
params = {:file_name => file_name}.merge(params)
|
357
|
-
open(path, "rb") do |f|
|
358
|
-
return upload_from_io(f, title || file_name, params)
|
359
|
-
end
|
360
|
-
end
|
361
|
-
|
362
|
-
# Uploads a file. Reads content from +io+.
|
363
|
-
# Returns a GoogleSpreadsheet::File object.
|
364
|
-
def upload_from_io(io, title = "Untitled", params = {})
|
365
|
-
doc = request(:get, "#{DOCS_BASE_URL}?v=3",
|
366
|
-
:auth => :writely)
|
367
|
-
initial_url = doc.css(
|
368
|
-
"link[rel='http://schemas.google.com/g/2005#resumable-create-media']")[0]["href"]
|
369
|
-
entry = upload_raw(:post, initial_url, io, title, params)
|
370
|
-
return entry_element_to_file(entry)
|
371
|
-
end
|
372
|
-
|
373
|
-
def upload_raw(method, url, io, title = "Untitled", params = {}) #:nodoc:
|
374
|
-
|
375
|
-
params = {:convert => true}.merge(params)
|
376
|
-
pos = io.pos
|
377
|
-
io.seek(0, IO::SEEK_END)
|
378
|
-
total_bytes = io.pos - pos
|
379
|
-
io.pos = pos
|
380
|
-
content_type = params[:content_type]
|
381
|
-
if !content_type && params[:file_name]
|
382
|
-
content_type = EXT_TO_CONTENT_TYPE[::File.extname(params[:file_name]).downcase]
|
383
|
-
end
|
384
|
-
if !content_type
|
385
|
-
content_type = "application/octet-stream"
|
386
|
-
end
|
387
|
-
|
388
|
-
initial_xml = <<-"EOS"
|
389
|
-
<entry xmlns="http://www.w3.org/2005/Atom"
|
390
|
-
xmlns:docs="http://schemas.google.com/docs/2007">
|
391
|
-
<title>#{h(title)}</title>
|
392
|
-
</entry>
|
393
|
-
EOS
|
394
|
-
|
395
|
-
default_initial_header = {
|
396
|
-
"Content-Type" => "application/atom+xml;charset=utf-8",
|
397
|
-
"X-Upload-Content-Type" => content_type,
|
398
|
-
"X-Upload-Content-Length" => total_bytes.to_s(),
|
399
|
-
}
|
400
|
-
initial_full_url = concat_url(url, params[:convert] ? "?convert=true" : "?convert=false")
|
401
|
-
initial_response = request(method, initial_full_url,
|
402
|
-
:header => default_initial_header.merge(params[:header] || {}),
|
403
|
-
:data => initial_xml,
|
404
|
-
:auth => :writely,
|
405
|
-
:response_type => :response)
|
406
|
-
upload_url = initial_response["location"]
|
407
|
-
|
408
|
-
if total_bytes > 0
|
409
|
-
sent_bytes = 0
|
410
|
-
while data = io.read(UPLOAD_CHUNK_SIZE)
|
411
|
-
content_range = "bytes %d-%d/%d" % [
|
412
|
-
sent_bytes,
|
413
|
-
sent_bytes + data.bytesize - 1,
|
414
|
-
total_bytes,
|
415
|
-
]
|
416
|
-
upload_header = {
|
417
|
-
"Content-Type" => content_type,
|
418
|
-
"Content-Range" => content_range,
|
419
|
-
}
|
420
|
-
doc = request(
|
421
|
-
:put, upload_url, :header => upload_header, :data => data, :auth => :writely)
|
422
|
-
sent_bytes += data.bytesize
|
423
|
-
end
|
424
|
-
else
|
425
|
-
upload_header = {
|
426
|
-
"Content-Type" => content_type,
|
427
|
-
}
|
428
|
-
doc = request(
|
429
|
-
:put, upload_url, :header => upload_header, :data => "", :auth => :writely)
|
430
|
-
end
|
431
|
-
|
432
|
-
return doc.root
|
433
|
-
|
434
|
-
end
|
435
|
-
|
436
|
-
def entry_element_to_file(entry) #:nodoc:
|
437
|
-
type, resource_id = entry.css("gd|resourceId").text.split(/:/)
|
438
|
-
title = entry.css("title").text
|
439
|
-
case type
|
440
|
-
when "folder"
|
441
|
-
return Collection.new(self, entry)
|
442
|
-
when "spreadsheet"
|
443
|
-
worksheets_feed_link = entry.css(
|
444
|
-
"link[rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed']")[0]
|
445
|
-
return Spreadsheet.new(self, worksheets_feed_link["href"], title)
|
446
|
-
else
|
447
|
-
return GoogleDriveV0::File.new(self, entry)
|
448
|
-
end
|
449
|
-
end
|
450
|
-
|
451
|
-
def request(method, url, params = {}) #:nodoc:
|
452
|
-
|
453
|
-
# Always uses HTTPS.
|
454
|
-
url = url.gsub(%r{^http://}, "https://")
|
455
|
-
data = params[:data]
|
456
|
-
auth = params[:auth] || :wise
|
457
|
-
if params[:header]
|
458
|
-
extra_header = params[:header]
|
459
|
-
elsif data
|
460
|
-
extra_header = {"Content-Type" => "application/atom+xml;charset=utf-8"}
|
461
|
-
else
|
462
|
-
extra_header = {}
|
463
|
-
end
|
464
|
-
response_type = params[:response_type] || :xml
|
465
|
-
|
466
|
-
while true
|
467
|
-
response = @fetcher.request_raw(method, url, data, extra_header, auth)
|
468
|
-
if response.code == "401" && @on_auth_fail && @on_auth_fail.call()
|
469
|
-
next
|
470
|
-
end
|
471
|
-
if !(response.code =~ /^[23]/)
|
472
|
-
raise(
|
473
|
-
response.code == "401" ? AuthenticationError : GoogleDriveV0::Error,
|
474
|
-
"Response code #{response.code} for #{method} #{url}: " +
|
475
|
-
CGI.unescapeHTML(response.body))
|
476
|
-
end
|
477
|
-
return convert_response(response, response_type)
|
478
|
-
end
|
479
|
-
|
480
|
-
end
|
481
|
-
|
482
|
-
def inspect
|
483
|
-
return "#<%p:0x%x>" % [self.class, self.object_id]
|
484
|
-
end
|
485
|
-
|
486
|
-
private
|
487
|
-
|
488
|
-
def convert_response(response, response_type)
|
489
|
-
case response_type
|
490
|
-
when :xml
|
491
|
-
return Nokogiri.XML(response.body)
|
492
|
-
when :raw
|
493
|
-
return response.body
|
494
|
-
when :response
|
495
|
-
return response
|
496
|
-
else
|
497
|
-
raise(GoogleDriveV0::Error,
|
498
|
-
"Unknown params[:response_type]: %s" % response_type)
|
499
|
-
end
|
500
|
-
end
|
501
|
-
|
502
|
-
def authenticate(mail, password, auth)
|
503
|
-
params = {
|
504
|
-
"accountType" => "HOSTED_OR_GOOGLE",
|
505
|
-
"Email" => mail,
|
506
|
-
"Passwd" => password,
|
507
|
-
"service" => auth.to_s(),
|
508
|
-
"source" => "Gimite-RubyGoogleDrive-1.00",
|
509
|
-
}
|
510
|
-
header = {"Content-Type" => "application/x-www-form-urlencoded"}
|
511
|
-
response = request(:post,
|
512
|
-
"https://www.google.com/accounts/ClientLogin",
|
513
|
-
:data => encode_query(params),
|
514
|
-
:auth => :none,
|
515
|
-
:header => header,
|
516
|
-
:response_type => :raw)
|
517
|
-
return response.slice(/^Auth=(.*)$/, 1)
|
518
|
-
end
|
519
|
-
|
520
|
-
end
|
521
|
-
|
522
|
-
end
|