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,251 @@
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 "google_drive/util"
8
+ require "google_drive/acl"
9
+
10
+
11
+ module GoogleDrive
12
+
13
+ # A file in Google Drive, including Google Docs document/spreadsheet/presentation.
14
+ #
15
+ # Use GoogleDrive::Session#files or GoogleDrive::Session#file_by_title to
16
+ # get this object.
17
+ class File
18
+
19
+ include(Util)
20
+
21
+ def initialize(session, entry_or_url) #:nodoc:
22
+ @session = session
23
+ if !entry_or_url
24
+ # TODO Delete this after editing spreadsheet.rb.
25
+ @document_feed_entry = nil
26
+ @document_feed_url = entry_or_url
27
+ elsif entry_or_url.is_a?(String)
28
+ @document_feed_entry = nil
29
+ @document_feed_url = entry_or_url
30
+ else
31
+ @document_feed_entry = entry_or_url
32
+ # This is usually equal to the URL in <link rel="self">. But the URL in
33
+ # <link rel="self"> in collection feed is e.g.
34
+ # https://docs.google.com/feeds/default/private/full/folder%3Aroot/contents/folder%3Axxx
35
+ # and deletion of the URL doesn't delete the file itself.
36
+ # So we construct the URL here using resource ID instead.
37
+ @document_feed_url = "%s/%s?v=3" % [DOCS_BASE_URL, CGI.escape(self.resource_id)]
38
+ end
39
+ @acl = nil
40
+ end
41
+
42
+ # URL of feed used in document list feed API.
43
+ attr_reader(:document_feed_url)
44
+
45
+ # <entry> element of document list feed as Nokogiri::XML::Element.
46
+ #
47
+ # Set <tt>params[:reload]</tt> to true to force reloading the feed.
48
+ def document_feed_entry(params = {})
49
+ if !@document_feed_entry || params[:reload]
50
+ @document_feed_entry =
51
+ @session.request(:get, self.document_feed_url, :auth => :writely).css("entry")[0]
52
+ end
53
+ return @document_feed_entry
54
+ end
55
+
56
+ # Resource ID.
57
+ def resource_id
58
+ return self.document_feed_entry.css("gd|resourceId").text
59
+ end
60
+
61
+ # The type of resourse. e.g. "document", "spreadsheet", "folder"
62
+ def resource_type
63
+ return self.resource_id.split(/:/)[0]
64
+ end
65
+
66
+ # Title of the file.
67
+ #
68
+ # Set <tt>params[:reload]</tt> to true to force reloading the title.
69
+ def title(params = {})
70
+ return document_feed_entry(params).css("title").text
71
+ end
72
+
73
+ # URL to view/edit the file in a Web browser.
74
+ #
75
+ # e.g. "https://docs.google.com/file/d/xxxx/edit"
76
+ def human_url
77
+ return self.document_feed_entry.css("link[rel='alternate']")[0]["href"]
78
+ end
79
+
80
+ # ACL feed URL of the file.
81
+ def acl_feed_url
82
+ orig_acl_feed_url = self.document_feed_entry.css(
83
+ "gd|feedLink[rel='http://schemas.google.com/acl/2007#accessControlList']")[0]["href"]
84
+ case orig_acl_feed_url
85
+ when %r{^https?://docs.google.com/feeds/default/private/full/.*/acl(\?.*)?$}
86
+ return orig_acl_feed_url
87
+ when %r{^https?://docs.google.com/feeds/acl/private/full/([^\?]*)(\?.*)?$}
88
+ # URL of old API version. Converts to v3 URL.
89
+ return "#{DOCS_BASE_URL}/#{$1}/acl"
90
+ else
91
+ raise(GoogleDrive::Error,
92
+ "ACL feed URL is in unknown format: #{orig_acl_feed_url}")
93
+ end
94
+ end
95
+
96
+ # Content types you can specify in methods download_to_file, download_to_string,
97
+ # download_to_io .
98
+ def available_content_types
99
+ return self.document_feed_entry.css("content").map(){ |c| c["type"] }
100
+ end
101
+
102
+ # Downloads the file to a local file.
103
+ #
104
+ # e.g.
105
+ # file.download_to_file("/path/to/hoge.txt")
106
+ # file.download_to_file("/path/to/hoge", :content_type => "text/plain")
107
+ def download_to_file(path, params = {})
108
+ params = params.dup()
109
+ if !params[:content_type]
110
+ params[:content_type] = EXT_TO_CONTENT_TYPE[::File.extname(path).downcase]
111
+ params[:content_type_is_hint] = true
112
+ end
113
+ open(path, "wb") do |f|
114
+ download_to_io(f, params)
115
+ end
116
+ end
117
+
118
+ # Downloads the file and returns as a String.
119
+ #
120
+ # e.g.
121
+ # file.download_to_string() #=> "Hello world."
122
+ # file.download_to_string(:content_type => "text/plain") #=> "Hello world."
123
+ def download_to_string(params = {})
124
+ sio = StringIO.new()
125
+ download_to_io(sio, params)
126
+ return sio.string
127
+ end
128
+
129
+ # Downloads the file and writes it to +io+.
130
+ def download_to_io(io, params = {})
131
+ all_contents = self.document_feed_entry.css("content")
132
+ if params[:content_type] && (!params[:content_type_is_hint] || all_contents.size > 1)
133
+ contents = all_contents.select(){ |c| c["type"] == params[:content_type] }
134
+ else
135
+ contents = all_contents
136
+ end
137
+ if contents.size == 1
138
+ url = contents[0]["src"]
139
+ else
140
+ if contents.empty?
141
+ raise(GoogleDrive::Error,
142
+ ("Downloading with content type %p not supported for this file. " +
143
+ "Specify one of these to content_type: %p") %
144
+ [params[:content_type], self.available_content_types])
145
+ else
146
+ raise(GoogleDrive::Error,
147
+ ("Multiple content types are available for this file. " +
148
+ "Specify one of these to content_type: %p") %
149
+ [self.available_content_types])
150
+ end
151
+ end
152
+ # TODO Use streaming if possible.
153
+ body = @session.request(:get, url, :response_type => :raw, :auth => :writely)
154
+ io.write(body)
155
+ end
156
+
157
+ # Updates the file with the content of the local file.
158
+ #
159
+ # e.g.
160
+ # file.update_from_file("/path/to/hoge.txt")
161
+ def update_from_file(path, params = {})
162
+ params = {:file_name => ::File.basename(path)}.merge(params)
163
+ open(path, "rb") do |f|
164
+ update_from_io(f, params)
165
+ end
166
+ end
167
+
168
+ # Updates the file with +content+.
169
+ #
170
+ # e.g.
171
+ # file.update_from_string("Good bye, world.")
172
+ def update_from_string(content, params = {})
173
+ update_from_io(StringIO.new(content), params)
174
+ end
175
+
176
+ # Reads content from +io+ and updates the file with the content.
177
+ def update_from_io(io, params = {})
178
+ params = {:header => {"If-Match" => "*"}}.merge(params)
179
+ initial_url = self.document_feed_entry.css(
180
+ "link[rel='http://schemas.google.com/g/2005#resumable-edit-media']")[0]["href"]
181
+ @document_feed_entry = @session.upload_raw(
182
+ :put, initial_url, io, self.title, params)
183
+ end
184
+
185
+ # If +permanent+ is +false+, moves the file to the trash.
186
+ # If +permanent+ is +true+, deletes the file permanently.
187
+ def delete(permanent = false)
188
+ url = to_v3_url(self.document_feed_url)
189
+ url = concat_url(url, "?delete=true") if permanent
190
+ @session.request(:delete, url,
191
+ :auth => :writely, :header => {"If-Match" => "*"})
192
+ end
193
+
194
+ # Renames title of the file.
195
+ def rename(title)
196
+ edit_url = self.document_feed_entry.css("link[rel='edit']").first["href"]
197
+ xml = <<-"EOS"
198
+ <atom:entry
199
+ xmlns:atom="http://www.w3.org/2005/Atom"
200
+ xmlns:docs="http://schemas.google.com/docs/2007">
201
+ <atom:title>#{h(title)}</atom:title>
202
+ </atom:entry>
203
+ EOS
204
+ header = {"Content-Type" => "application/atom+xml", "If-Match" => "*"}
205
+ @session.request(:put, edit_url, :data => xml, :auth => :writely, :header => header)
206
+ end
207
+
208
+ alias title= rename
209
+
210
+ # Returns GoogleDrive::Acl object for the file.
211
+ #
212
+ # With the object, you can see and modify people who can access the file.
213
+ # Modifications take effect immediately.
214
+ #
215
+ # Set <tt>params[:reload]</tt> to true to force reloading the data.
216
+ #
217
+ # e.g.
218
+ # # Dumps people who have access:
219
+ # for entry in file.acl
220
+ # p [entry.scope_type, entry.scope, entry.role]
221
+ # # => e.g. ["user", "example1@gmail.com", "owner"]
222
+ # end
223
+ #
224
+ # # Shares the file with new people:
225
+ # # NOTE: This sends email to the new people.
226
+ # file.acl.push(
227
+ # {:scope_type => "user", :scope => "example2@gmail.com", :role => "reader"})
228
+ # file.acl.push(
229
+ # {:scope_type => "user", :scope => "example3@gmail.com", :role => "writer"})
230
+ #
231
+ # # Changes the role of a person:
232
+ # file.acl[1].role = "writer"
233
+ #
234
+ # # Deletes an ACL entry:
235
+ # file.acl.delete(file.acl[1])
236
+ def acl(params = {})
237
+ if !@acl || params[:reload]
238
+ @acl = Acl.new(@session, self.acl_feed_url)
239
+ end
240
+ return @acl
241
+ end
242
+
243
+ def inspect
244
+ fields = {:document_feed_url => self.document_feed_url}
245
+ fields[:title] = self.title if @document_feed_entry
246
+ return "\#<%p %s>" % [self.class, fields.map(){ |k, v| "%s=%p" % [k, v] }.join(", ")]
247
+ end
248
+
249
+ end
250
+
251
+ end
@@ -0,0 +1,119 @@
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/list_row"
7
+
8
+
9
+ module GoogleDrive
10
+
11
+ # Provides access to cells using column names.
12
+ # Use GoogleDrive::Worksheet#list to get GoogleDrive::List object.
13
+ #--
14
+ # This is implemented as wrapper of GoogleDrive::Worksheet i.e. using cells
15
+ # feed, not list feed. In this way, we can easily provide consistent API as
16
+ # GoogleDrive::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 (GoogleDrive::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 GoogleDrive::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 GoogleDrive::Worksheet#save().
46
+ def []=(index, hash)
47
+ self[index].replace(hash)
48
+ end
49
+
50
+ # Iterates over Hash-like object (GoogleDrive::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 GoogleDrive::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 GoogleDrive::ListRow for the new row.
81
+ #
82
+ # Note that update is not sent to the server until
83
+ # you call GoogleDrive::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(GoogleDrive::Error, "Column doesn't exist: %p" % key) if !col
114
+ return col
115
+ end
116
+
117
+ end
118
+
119
+ end
@@ -0,0 +1,88 @@
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/util"
7
+ require "google_drive/error"
8
+
9
+
10
+ module GoogleDrive
11
+
12
+ # Hash-like object returned by GoogleDrive::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
@@ -0,0 +1,26 @@
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 GoogleDrive
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
@@ -0,0 +1,47 @@
1
+ # Author: Hiroshi Ichikawa <http://gimite.net/>
2
+ # The license of this source is "New BSD Licence"
3
+
4
+ require "rubygems"
5
+ require "oauth2"
6
+
7
+
8
+ module GoogleDrive
9
+
10
+ class OAuth2Fetcher #:nodoc:
11
+
12
+ class Response
13
+
14
+ def initialize(raw_res)
15
+ @raw_res = raw_res
16
+ end
17
+
18
+ def code
19
+ return @raw_res.status.to_s()
20
+ end
21
+
22
+ def body
23
+ return @raw_res.body
24
+ end
25
+
26
+ def [](name)
27
+ return @raw_res.headers[name]
28
+ end
29
+
30
+ end
31
+
32
+ def initialize(oauth2_token)
33
+ @oauth2_token = oauth2_token
34
+ end
35
+
36
+ def request_raw(method, url, data, extra_header, auth)
37
+ if method == :delete || method == :get
38
+ raw_res = @oauth2_token.request(method, url, {:headers => extra_header})
39
+ else
40
+ raw_res = @oauth2_token.request(method, url, {:headers => extra_header, :body => data})
41
+ end
42
+ return Response.new(raw_res)
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,31 @@
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
+
7
+
8
+ module GoogleDrive
9
+
10
+ # DEPRECATED: Table and Record feeds are deprecated and they will not be available after
11
+ # March 2012.
12
+ #
13
+ # Use GoogleDrive::Table#records to get GoogleDrive::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