parallel588_google_drive 0.3.3
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.
- data/README.rdoc +91 -0
- data/doc_src/google_drive/acl.rb +20 -0
- data/doc_src/google_drive/acl_entry.rb +33 -0
- data/lib/google_drive/acl.rb +115 -0
- data/lib/google_drive/acl_entry.rb +89 -0
- data/lib/google_drive/authentication_error.rb +14 -0
- data/lib/google_drive/client_login_fetcher.rb +56 -0
- data/lib/google_drive/collection.rb +139 -0
- data/lib/google_drive/error.rb +12 -0
- data/lib/google_drive/file.rb +251 -0
- data/lib/google_drive/list.rb +119 -0
- data/lib/google_drive/list_row.rb +88 -0
- data/lib/google_drive/oauth1_fetcher.rb +26 -0
- data/lib/google_drive/oauth2_fetcher.rb +47 -0
- data/lib/google_drive/record.rb +31 -0
- data/lib/google_drive/session.rb +485 -0
- data/lib/google_drive/spreadsheet.rb +227 -0
- data/lib/google_drive/table.rb +60 -0
- data/lib/google_drive/util.rb +68 -0
- data/lib/google_drive/worksheet.rb +475 -0
- data/lib/google_drive.rb +126 -0
- metadata +164 -0
@@ -0,0 +1,227 @@
|
|
1
|
+
# Author: Hiroshi Ichikawa <http://gimite.net/>
|
2
|
+
# The license of this source is "New BSD Licence"
|
3
|
+
|
4
|
+
require "google_drive/util"
|
5
|
+
require "google_drive/error"
|
6
|
+
require "google_drive/worksheet"
|
7
|
+
require "google_drive/table"
|
8
|
+
require "google_drive/acl"
|
9
|
+
require "google_drive/file"
|
10
|
+
|
11
|
+
|
12
|
+
module GoogleDrive
|
13
|
+
|
14
|
+
# A spreadsheet.
|
15
|
+
#
|
16
|
+
# Use methods in GoogleDrive::Session to get GoogleDrive::Spreadsheet object.
|
17
|
+
class Spreadsheet < GoogleDrive::File
|
18
|
+
|
19
|
+
include(Util)
|
20
|
+
|
21
|
+
SUPPORTED_EXPORT_FORMAT = Set.new(["xls", "csv", "pdf", "ods", "tsv", "html"])
|
22
|
+
|
23
|
+
def initialize(session, worksheets_feed_url, title = nil) #:nodoc:
|
24
|
+
super(session, nil)
|
25
|
+
@worksheets_feed_url = worksheets_feed_url
|
26
|
+
@title = title
|
27
|
+
end
|
28
|
+
|
29
|
+
# URL of worksheet-based feed of the spreadsheet.
|
30
|
+
attr_reader(:worksheets_feed_url)
|
31
|
+
|
32
|
+
# Title of the spreadsheet.
|
33
|
+
#
|
34
|
+
# Set <tt>params[:reload]</tt> to true to force reloading the title.
|
35
|
+
def title(params = {})
|
36
|
+
if !@title || params[:reload]
|
37
|
+
@title = spreadsheet_feed_entry(params).css("title").text
|
38
|
+
end
|
39
|
+
return @title
|
40
|
+
end
|
41
|
+
|
42
|
+
# Key of the spreadsheet.
|
43
|
+
def key
|
44
|
+
if !(@worksheets_feed_url =~
|
45
|
+
%r{^https?://spreadsheets.google.com/feeds/worksheets/(.*)/private/.*$})
|
46
|
+
raise(GoogleDrive::Error,
|
47
|
+
"Worksheets feed URL is in unknown format: #{@worksheets_feed_url}")
|
48
|
+
end
|
49
|
+
return $1
|
50
|
+
end
|
51
|
+
|
52
|
+
# Spreadsheet feed URL of the spreadsheet.
|
53
|
+
def spreadsheet_feed_url
|
54
|
+
return "https://spreadsheets.google.com/feeds/spreadsheets/private/full/#{self.key}"
|
55
|
+
end
|
56
|
+
|
57
|
+
# URL which you can open the spreadsheet in a Web browser with.
|
58
|
+
#
|
59
|
+
# e.g. "http://spreadsheets.google.com/ccc?key=pz7XtlQC-PYx-jrVMJErTcg"
|
60
|
+
def human_url
|
61
|
+
# Uses Document feed because Spreadsheet feed returns wrong URL for Apps account.
|
62
|
+
return self.document_feed_entry.css("link[rel='alternate']")[0]["href"]
|
63
|
+
end
|
64
|
+
|
65
|
+
# DEPRECATED: Table and Record feeds are deprecated and they will not be available after
|
66
|
+
# March 2012.
|
67
|
+
#
|
68
|
+
# Tables feed URL of the spreadsheet.
|
69
|
+
def tables_feed_url
|
70
|
+
warn(
|
71
|
+
"DEPRECATED: Google Spreadsheet Table and Record feeds are deprecated and they " +
|
72
|
+
"will not be available after March 2012.")
|
73
|
+
return "https://spreadsheets.google.com/feeds/#{self.key}/tables"
|
74
|
+
end
|
75
|
+
|
76
|
+
# URL of feed used in document list feed API.
|
77
|
+
def document_feed_url
|
78
|
+
return "https://docs.google.com/feeds/documents/private/full/spreadsheet%3A#{self.key}"
|
79
|
+
end
|
80
|
+
|
81
|
+
# <entry> element of spreadsheet feed as Nokogiri::XML::Element.
|
82
|
+
#
|
83
|
+
# Set <tt>params[:reload]</tt> to true to force reloading the feed.
|
84
|
+
def spreadsheet_feed_entry(params = {})
|
85
|
+
if !@spreadsheet_feed_entry || params[:reload]
|
86
|
+
@spreadsheet_feed_entry =
|
87
|
+
@session.request(:get, self.spreadsheet_feed_url).css("entry")[0]
|
88
|
+
end
|
89
|
+
return @spreadsheet_feed_entry
|
90
|
+
end
|
91
|
+
|
92
|
+
# <entry> element of document list feed as Nokogiri::XML::Element.
|
93
|
+
#
|
94
|
+
# Set <tt>params[:reload]</tt> to true to force reloading the feed.
|
95
|
+
def document_feed_entry(params = {})
|
96
|
+
if !@document_feed_entry || params[:reload]
|
97
|
+
@document_feed_entry =
|
98
|
+
@session.request(:get, self.document_feed_url, :auth => :writely).css("entry")[0]
|
99
|
+
end
|
100
|
+
return @document_feed_entry
|
101
|
+
end
|
102
|
+
|
103
|
+
# Creates copy of this spreadsheet with the given title.
|
104
|
+
def duplicate(new_title = nil)
|
105
|
+
new_title ||= (self.title ? "Copy of " + self.title : "Untitled")
|
106
|
+
header = {"GData-Version" => "3.0", "Content-Type" => "application/atom+xml"}
|
107
|
+
xml = <<-"EOS"
|
108
|
+
<entry xmlns='http://www.w3.org/2005/Atom'>
|
109
|
+
<id>#{h(self.document_feed_url)}</id>
|
110
|
+
<title>#{h(new_title)}</title>
|
111
|
+
</entry>
|
112
|
+
EOS
|
113
|
+
doc = @session.request(
|
114
|
+
:post, DOCS_BASE_URL, :data => xml, :header => header, :auth => :writely)
|
115
|
+
ss_url = doc.css(
|
116
|
+
"link[rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed']")[0]["href"]
|
117
|
+
return Spreadsheet.new(@session, ss_url, new_title)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Exports the spreadsheet in +format+ and returns it as String.
|
121
|
+
#
|
122
|
+
# +format+ can be either "xls", "csv", "pdf", "ods", "tsv" or "html".
|
123
|
+
# In format such as "csv", only the worksheet specified with +worksheet_index+ is
|
124
|
+
# exported.
|
125
|
+
def export_as_string(format, worksheet_index = nil)
|
126
|
+
gid_param = worksheet_index ? "&gid=#{worksheet_index}" : ""
|
127
|
+
url =
|
128
|
+
"https://spreadsheets.google.com/feeds/download/spreadsheets/Export" +
|
129
|
+
"?key=#{key}&exportFormat=#{format}#{gid_param}"
|
130
|
+
return @session.request(:get, url, :response_type => :raw)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Exports the spreadsheet in +format+ as a local file.
|
134
|
+
#
|
135
|
+
# +format+ can be either "xls", "csv", "pdf", "ods", "tsv" or "html".
|
136
|
+
# If +format+ is nil, it is guessed from the file name.
|
137
|
+
# In format such as "csv", only the worksheet specified with +worksheet_index+ is exported.
|
138
|
+
#
|
139
|
+
# e.g.
|
140
|
+
# spreadsheet.export_as_file("hoge.ods")
|
141
|
+
# spreadsheet.export_as_file("hoge.csv", nil, 0)
|
142
|
+
def export_as_file(local_path, format = nil, worksheet_index = nil)
|
143
|
+
if !format
|
144
|
+
format = ::File.extname(local_path).gsub(/^\./, "")
|
145
|
+
if !SUPPORTED_EXPORT_FORMAT.include?(format)
|
146
|
+
raise(ArgumentError,
|
147
|
+
("Cannot guess format from the file name: %s\n" +
|
148
|
+
"Specify format argument explicitly.") %
|
149
|
+
local_path)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
open(local_path, "wb") do |f|
|
153
|
+
f.write(export_as_string(format, worksheet_index))
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def download_to_io(io, params = {})
|
158
|
+
# General downloading API doesn't work for spreadsheets because it requires a different
|
159
|
+
# authorization token, and it has a bug that it downloads PDF when text/html is
|
160
|
+
# requested.
|
161
|
+
raise(NotImplementedError,
|
162
|
+
"Use export_as_file or export_as_string instead for GoogleDrive::Spreadsheet.")
|
163
|
+
end
|
164
|
+
|
165
|
+
# Returns worksheets of the spreadsheet as array of GoogleDrive::Worksheet.
|
166
|
+
def worksheets
|
167
|
+
doc = @session.request(:get, @worksheets_feed_url)
|
168
|
+
if doc.root.name != "feed"
|
169
|
+
raise(GoogleDrive::Error,
|
170
|
+
"%s doesn't look like a worksheets feed URL because its root is not <feed>." %
|
171
|
+
@worksheets_feed_url)
|
172
|
+
end
|
173
|
+
result = []
|
174
|
+
doc.css("entry").each() do |entry|
|
175
|
+
title = entry.css("title").text
|
176
|
+
url = entry.css(
|
177
|
+
"link[rel='http://schemas.google.com/spreadsheets/2006#cellsfeed']")[0]["href"]
|
178
|
+
result.push(Worksheet.new(@session, self, url, title))
|
179
|
+
end
|
180
|
+
return result.freeze()
|
181
|
+
end
|
182
|
+
|
183
|
+
# Returns a GoogleDrive::Worksheet with the given title in the spreadsheet.
|
184
|
+
#
|
185
|
+
# Returns nil if not found. Returns the first one when multiple worksheets with the
|
186
|
+
# title are found.
|
187
|
+
def worksheet_by_title(title)
|
188
|
+
return self.worksheets.find(){ |ws| ws.title == title }
|
189
|
+
end
|
190
|
+
|
191
|
+
# Adds a new worksheet to the spreadsheet. Returns added GoogleDrive::Worksheet.
|
192
|
+
def add_worksheet(title, max_rows = 100, max_cols = 20)
|
193
|
+
xml = <<-"EOS"
|
194
|
+
<entry xmlns='http://www.w3.org/2005/Atom'
|
195
|
+
xmlns:gs='http://schemas.google.com/spreadsheets/2006'>
|
196
|
+
<title>#{h(title)}</title>
|
197
|
+
<gs:rowCount>#{h(max_rows)}</gs:rowCount>
|
198
|
+
<gs:colCount>#{h(max_cols)}</gs:colCount>
|
199
|
+
</entry>
|
200
|
+
EOS
|
201
|
+
doc = @session.request(:post, @worksheets_feed_url, :data => xml)
|
202
|
+
url = doc.css(
|
203
|
+
"link[rel='http://schemas.google.com/spreadsheets/2006#cellsfeed']")[0]["href"]
|
204
|
+
return Worksheet.new(@session, self, url, title)
|
205
|
+
end
|
206
|
+
|
207
|
+
# DEPRECATED: Table and Record feeds are deprecated and they will not be available after
|
208
|
+
# March 2012.
|
209
|
+
#
|
210
|
+
# Returns list of tables in the spreadsheet.
|
211
|
+
def tables
|
212
|
+
warn(
|
213
|
+
"DEPRECATED: Google Spreadsheet Table and Record feeds are deprecated and they " +
|
214
|
+
"will not be available after March 2012.")
|
215
|
+
doc = @session.request(:get, self.tables_feed_url)
|
216
|
+
return doc.css("entry").map(){ |e| Table.new(@session, e) }.freeze()
|
217
|
+
end
|
218
|
+
|
219
|
+
def inspect
|
220
|
+
fields = {:worksheets_feed_url => self.worksheets_feed_url}
|
221
|
+
fields[:title] = @title if @title
|
222
|
+
return "\#<%p %s>" % [self.class, fields.map(){ |k, v| "%s=%p" % [k, v] }.join(", ")]
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# Author: Hiroshi Ichikawa <http://gimite.net/>
|
2
|
+
# The license of this source is "New BSD Licence"
|
3
|
+
|
4
|
+
require "google_drive/util"
|
5
|
+
require "google_drive/error"
|
6
|
+
require "google_drive/record"
|
7
|
+
|
8
|
+
|
9
|
+
module GoogleDrive
|
10
|
+
|
11
|
+
# DEPRECATED: Table and Record feeds are deprecated and they will not be available after
|
12
|
+
# March 2012.
|
13
|
+
#
|
14
|
+
# Use GoogleDrive::Worksheet#add_table to create table.
|
15
|
+
# Use GoogleDrive::Worksheet#tables to get GoogleDrive::Table objects.
|
16
|
+
class Table
|
17
|
+
|
18
|
+
include(Util)
|
19
|
+
|
20
|
+
def initialize(session, entry) #:nodoc:
|
21
|
+
@columns = {}
|
22
|
+
@worksheet_title = entry.css("gs|worksheet")[0]["name"]
|
23
|
+
@records_url = entry.css("content")[0]["src"]
|
24
|
+
@edit_url = entry.css("link[rel='edit']")[0]["href"]
|
25
|
+
@session = session
|
26
|
+
end
|
27
|
+
|
28
|
+
# Title of the worksheet the table belongs to.
|
29
|
+
attr_reader(:worksheet_title)
|
30
|
+
|
31
|
+
# Adds a record.
|
32
|
+
def add_record(values)
|
33
|
+
fields = ""
|
34
|
+
values.each() do |name, value|
|
35
|
+
fields += "<gs:field name='#{h(name)}'>#{h(value)}</gs:field>"
|
36
|
+
end
|
37
|
+
xml =<<-EOS
|
38
|
+
<entry
|
39
|
+
xmlns="http://www.w3.org/2005/Atom"
|
40
|
+
xmlns:gs="http://schemas.google.com/spreadsheets/2006">
|
41
|
+
#{fields}
|
42
|
+
</entry>
|
43
|
+
EOS
|
44
|
+
@session.request(:post, @records_url, :data => xml)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns records in the table.
|
48
|
+
def records
|
49
|
+
doc = @session.request(:get, @records_url)
|
50
|
+
return doc.css("entry").map(){ |e| Record.new(@session, e) }
|
51
|
+
end
|
52
|
+
|
53
|
+
# Deletes this table. Deletion takes effect right away without calling save().
|
54
|
+
def delete
|
55
|
+
@session.request(:delete, @edit_url, :header => {"If-Match" => "*"})
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# Author: Hiroshi Ichikawa <http://gimite.net/>
|
2
|
+
# The license of this source is "New BSD Licence"
|
3
|
+
|
4
|
+
require "cgi"
|
5
|
+
|
6
|
+
|
7
|
+
module GoogleDrive
|
8
|
+
|
9
|
+
module Util #:nodoc:
|
10
|
+
|
11
|
+
# The beginning of Doc List API URL that is used in all requests (version 3).
|
12
|
+
DOCS_BASE_URL = "https://docs.google.com/feeds/default/private/full"
|
13
|
+
|
14
|
+
EXT_TO_CONTENT_TYPE = {
|
15
|
+
".csv" =>"text/csv",
|
16
|
+
".tsv" =>"text/tab-separated-values",
|
17
|
+
".tab" =>"text/tab-separated-values",
|
18
|
+
".doc" =>"application/msword",
|
19
|
+
".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
20
|
+
".ods" =>"application/x-vnd.oasis.opendocument.spreadsheet",
|
21
|
+
".odt" =>"application/vnd.oasis.opendocument.text",
|
22
|
+
".rtf" =>"application/rtf",
|
23
|
+
".sxw" =>"application/vnd.sun.xml.writer",
|
24
|
+
".txt" =>"text/plain",
|
25
|
+
".xls" =>"application/vnd.ms-excel",
|
26
|
+
".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
27
|
+
".pdf" =>"application/pdf",
|
28
|
+
".png" =>"image/png",
|
29
|
+
".ppt" =>"application/vnd.ms-powerpoint",
|
30
|
+
".pps" =>"application/vnd.ms-powerpoint",
|
31
|
+
".htm" =>"text/html",
|
32
|
+
".html" =>"text/html",
|
33
|
+
".zip" =>"application/zip",
|
34
|
+
".swf" =>"application/x-shockwave-flash",
|
35
|
+
}
|
36
|
+
|
37
|
+
module_function
|
38
|
+
|
39
|
+
def encode_query(params)
|
40
|
+
return params.map(){ |k, v| CGI.escape(k.to_s()) + "=" + CGI.escape(v.to_s()) }.join("&")
|
41
|
+
end
|
42
|
+
|
43
|
+
def concat_url(url, piece)
|
44
|
+
(url_base, url_query) = url.split(/\?/, 2)
|
45
|
+
(piece_base, piece_query) = piece.split(/\?/, 2)
|
46
|
+
result_query = [url_query, piece_query].select(){ |s| s && !s.empty? }.join("&")
|
47
|
+
return (url_base || "") +
|
48
|
+
(piece_base || "") +
|
49
|
+
(result_query.empty? ? "" : "?#{result_query}")
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns a URL with added version parameter ("?v=3") if needed.
|
53
|
+
def to_v3_url(url)
|
54
|
+
if url =~ %r{docs.google.com/feeds/default/private/} && !(url =~ /[?&]v=3/)
|
55
|
+
return concat_url(url, "?v=3")
|
56
|
+
else
|
57
|
+
return url
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def h(str)
|
62
|
+
# Should also escape "\n" to keep it in cell contents.
|
63
|
+
return CGI.escapeHTML(str.to_s()).gsub(/\n/, '
')
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|