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.
@@ -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/, '&#x0a;')
64
+ end
65
+
66
+ end
67
+
68
+ end