parallel588_google_drive 0.3.3

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