gdocs4ruby 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,3 @@
1
+ #=CHANGELOG
2
+ #==version 0.1.0
3
+ #* Initial Version
data/README ADDED
@@ -0,0 +1,76 @@
1
+ #=GDocs4Ruby
2
+ #
3
+ #==Introduction
4
+ #GDocs4Ruby is a full featured wrapper for version 2.0 of the Google Documents API (aka DocList). GDocs4Ruby provides the ability
5
+ #to create, update and delete google documents, metadata and content. The gem also includes support for folders, modifying
6
+ #permissions for documents via ACL feeds, and much more.
7
+ #
8
+ #GDocs4Ruby uses the GData4Ruby Gem for interacting with the Google API. Check out http://cookingandcoding.com/gdata4ruby for
9
+ #other Google API Libraries based using GData4Ruby.
10
+ #
11
+ #==Author and Contact Information
12
+ #GDocs4Ruby was created and is maintained by {Mike Reich}[mailto:mike@seabourneconsulting.com]
13
+ #and is licenses under the GPL v2. Feel free to use and update, but be sure to contribute your
14
+ #code back to the project and attribute as required by the license.
15
+ #===Website
16
+ #http://cookingandcoding.com/gdocs4ruby/
17
+ #
18
+ #==Description
19
+ #GDocs4Ruby includes the following classes: Service, Folder, BaseObject, Spreadsheet, Document, Presentation.
20
+ #
21
+ #The Service object provides functionality for authenticating with the Google Documents API, grabbing a list of
22
+ #documents and a list of folders associated with the account.
23
+ #
24
+ #Interacting with objects is done by using the associated subclass of BaseObject, i.e. Document. Every object
25
+ #class supports the same inherited methods for creating, updating and deleting, in addition to changing
26
+ #ACL permissions, and adding and removing from various folders.
27
+ #
28
+ #==Examples
29
+ #Below are some common usage examples. For more examples, check the documentation. Also, check out the example code
30
+ #for integrating with Rails at http://cookingandcoding.com/gdocs4ruby/example/
31
+ #===Service
32
+ #1. Authenticate
33
+ # service = Service.new
34
+ # service.authenticate("user@gmail.com", "password")
35
+ #
36
+ #2. Get Document List
37
+ # documents = service.files
38
+ #
39
+ #3. Get Folder List
40
+ # folders = serivce.folders
41
+ #
42
+ #===Documents
43
+ #1. Create a new Document
44
+ # doc = Document.new(@service)
45
+ # doc.title = 'Test Document'
46
+ # doc.content = '<h1>Test Content HTML</h1>'
47
+ # doc.content_type = 'html'
48
+ # doc.save
49
+ #
50
+ #2. Deleting a Document
51
+ # doc = Document.find(@service, {:id => @doc_id})
52
+ # doc.delete
53
+ #
54
+ #3. Finding an existing Document by id
55
+ # doc = Document.find(@service, {:id => @doc_id})
56
+ #
57
+ #4. Full Text Query
58
+ # doc = Document.find(@service, 'content text')
59
+ #
60
+ # or
61
+ #
62
+ # doc = Document.find(@service, {:query => 'content text'})
63
+ #
64
+ #5. Finding an Existing Document by Title
65
+ # doc = Document.find(@service, nil, {'title' => 'Test Document'})
66
+ #
67
+ #6. Updating a Document with Content from a Local File
68
+ # doc = Document.find(@service, {:id => @doc_id})
69
+ # doc.title = 'New Title'
70
+ # doc.local_file = '/path/to/some/file'
71
+ # doc.save
72
+ #
73
+ #7. Retrieving an Export
74
+ # doc = Document.find(@service, {:id => @doc_id})
75
+ # doc.download_to_file('pdf', '/path/to/save/location.pdf')
76
+
@@ -0,0 +1,408 @@
1
+ # Author:: Mike Reich (mike@seabourneconsulting.com)
2
+ # Copyright:: Copyright (C) 2010 Mike Reich
3
+ # License:: GPL v2
4
+ #--
5
+ # Licensed under the General Public License (GPL), Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ #
15
+ # Feel free to use and update, but be sure to contribute your
16
+ # code back to the project and attribute as required by the license.
17
+ #++
18
+ require 'gdata4ruby/gdata_object'
19
+ require 'gdata4ruby/acl/access_rule'
20
+
21
+ module GDocs4Ruby
22
+
23
+ #The base object class that includes all major methods for interacting with Google Documents API.
24
+ #=Usage
25
+ #All usages assume you've already authenticated with the service, and have a service object called
26
+ #@service.
27
+ #*Note*: You probably don't want to instantiate a BaseObject directly, but rather use any of the subclasses
28
+ #Document, Spreadsheet, and Presentation
29
+ #1. Create a new Document
30
+ # doc = BaseObject.new(@service)
31
+ # doc.title = 'Test Document'
32
+ # doc.content = '<h1>Test Content HTML</h1>'
33
+ # doc.content_type = 'html'
34
+ # doc.save
35
+ #
36
+ #2. Deleting a Document
37
+ # doc = BaseObject.find(@service, {:id => @doc_id})
38
+ # doc.delete
39
+ #
40
+ #3. Finding an existing Document by id
41
+ # doc = BaseObject.find(@service, {:id => @doc_id})
42
+ #
43
+ #4. Full Text Query
44
+ # doc = BaseObject.find(@service, 'content text')
45
+ #
46
+ # or
47
+ #
48
+ # doc = BaseObject.find(@service, {:query => 'content text'})
49
+ #
50
+ #5. Finding an Existing Document by Title
51
+ # doc = BaseObject.find(@service, nil, 'any', {'title' => 'Test Document'})
52
+ #
53
+ #6. Updating a Document with Content from a Local File
54
+ # doc = BaseObject.find(@service, {:id => @doc_id})
55
+ # doc.title = 'New Title'
56
+ # doc.local_file = '/path/to/some/file'
57
+ # doc.save
58
+
59
+ class BaseObject < GData4Ruby::GDataObject
60
+ ENTRY_XML = '<atom:entry xmlns:atom="http://www.w3.org/2005/Atom">
61
+ <atom:title></atom:title>
62
+ </atom:entry>'
63
+ BOUNDARY = 'GDOCS4RUBY_BOUNDARY'
64
+ UPLOAD_TYPES = {'' => 'text/txt',
65
+ :csv => 'text/csv',
66
+ :tsv => 'text/tab-separated-values',
67
+ :tab => 'text/tab-separated-values',
68
+ :html => 'text/html',
69
+ :htm => 'text/html',
70
+ :doc => 'application/msword',
71
+ :docx => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
72
+ :ods => 'application/x-vnd.oasis.opendocument.spreadsheet',
73
+ :odt => 'application/vnd.oasis.opendocument.text',
74
+ :rtf => 'application/rtf',
75
+ :sxw => 'application/vnd.sun.xml.writer',
76
+ :txt => 'text/plain',
77
+ :xls => 'application/vnd.ms-excel',
78
+ :xlsx => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
79
+ :pdf => 'application/pdf',
80
+ :ppt => 'application/vnd.ms-powerpoint',
81
+ :pps => 'application/vnd.ms-powerpoint',
82
+ :pptx => 'application/vnd.ms-powerpoint'}
83
+
84
+ FEEDS = {:document => "https://docs.google.com/feeds/documents/private/full/",
85
+ :folder => "http://docs.google.com/feeds/folders/private/full/",
86
+ :spreadsheet => "https://docs.google.com/feeds/documents/private/full/",
87
+ :presentation => "https://docs.google.com/feeds/documents/private/full/",
88
+ :any => "https://docs.google.com/feeds/documents/private/full/"}
89
+
90
+ QUERY_FEEDS = {:document => "https://docs.google.com/feeds/documents/private/full/-/document",
91
+ :folder => "http://docs.google.com/feeds/folders/private/full/?showfolders=true",
92
+ :spreadsheet => "https://docs.google.com/feeds/documents/private/full/-/spreadsheet",
93
+ :presentation => "https://docs.google.com/feeds/documents/private/full/-/presentation",
94
+ :any => "https://docs.google.com/feeds/documents/private/full/"}
95
+
96
+ TYPES = ["document", "folder", "spreadsheet", "presentation", "any"]
97
+
98
+ #The raw date the document was published
99
+ attr_reader :published
100
+
101
+ #The raw date the document was last updated
102
+ attr_reader :updated
103
+
104
+ #The author/owner name
105
+ attr_reader :author_name
106
+
107
+ #The author/owner email
108
+ attr_reader :author_email
109
+
110
+ #An array of folders this object belongs to
111
+ attr_reader :folders
112
+
113
+ #Quota bytes used by the document
114
+ attr_reader :bytes_used
115
+
116
+ #The type of the object, one of TYPES
117
+ attr_reader :type
118
+
119
+ #The uri for the html editor of the object (the web editor for documents)
120
+ attr_reader :html_uri
121
+
122
+ #The content uri for exporting the object content
123
+ attr_reader :content_uri
124
+
125
+ #Flag indicating whether the document has the 'viewed' category link.
126
+ attr_reader :viewed
127
+
128
+ #A local file to upload content from
129
+ attr_accessor :local_file
130
+
131
+ #Creates a new BaseObject instance. Requires a valid GData4Ruby::Service object.
132
+ def initialize(service, attributes = {})
133
+ super(service, attributes)
134
+ @xml = ENTRY_XML
135
+ @folders = []
136
+ @content_uri = nil
137
+ @edit_content_uri = nil
138
+ @viewed = false
139
+ @content = @content_type = nil
140
+ end
141
+
142
+ public
143
+ #Loads a string containing an XML <entry> from a Google API feed.
144
+ def load(string)
145
+ super(string)
146
+ @folders = []
147
+ xml = REXML::Document.new(string)
148
+ xml.root.elements.each(){}.map do |ele|
149
+ @etag = xml.root.attributes['etag'] if xml.root.attributes['etag']
150
+ case ele.name
151
+ when 'published'
152
+ @published = ele.text
153
+ when 'updated'
154
+ @updated = ele.text
155
+ when 'content'
156
+ @content_uri = ele.attributes['src']
157
+ when 'link'
158
+ case ele.attributes['rel']
159
+ when 'edit-media'
160
+ @edit_content_uri = ele.attributes['href']
161
+ when 'alternate'
162
+ @html_uri = ele.attributes['href']
163
+ end
164
+ when 'author'
165
+ ele.elements.each('name'){}.map {|e| @author_name = e.text}
166
+ ele.elements.each('email'){}.map {|e| @author_email = e.text}
167
+ when 'quotaBytesUsed'
168
+ @bytes_used = ele.text
169
+ end
170
+ end
171
+ @categories.each do |cat|
172
+ @folders << cat[:label] if cat[:scheme] and cat[:scheme].include? "folders"
173
+ @viewed = true if cat[:label] and cat[:label] == 'viewed'
174
+ @type = cat[:label] if cat[:scheme] and cat[:scheme] == 'http://schemas.google.com/g/2005#kind'
175
+ end
176
+ return xml.root
177
+ end
178
+
179
+ #Sets content to save to the document. Content must be formatted as one of the types in UPLOAD_TYPES.
180
+ def content=(value)
181
+ @content = value
182
+ end
183
+
184
+ #Sets the content_type stored in content. content_type must be one of the keys in UPLOAD_TYPES.
185
+ def content_type=(value)
186
+ @content_type = value
187
+ end
188
+
189
+ #Saves or creates the object depending on whether it exists or not.
190
+ def save
191
+ if @exists
192
+ if (not @local_file.nil? and @local_file.is_a? String) or @content
193
+ @include_etag = false
194
+ if @local_file
195
+ ret = service.send_request(GData4Ruby::Request.new(:put, @edit_content_uri, create_multipart_message([{:type => 'application/atom+xml', :content => to_xml()}, {:type => UPLOAD_TYPES[File.extname(@local_file).gsub(".", "").to_sym], :content => get_file(@local_file).read}]), {'Content-Type' => "multipart/related; boundary=#{BOUNDARY}", 'Content-Length' => File.size(@local_file).to_s, 'Slug' => File.basename(@local_file), 'If-Match' => "*"}))
196
+ elsif @content
197
+ ret = service.send_request(GData4Ruby::Request.new(:put, @edit_content_uri, create_multipart_message([{:type => 'application/atom+xml', :content => to_xml()}, {:type => UPLOAD_TYPES[@content_type.to_sym], :content => @content}]), {'Content-Type' => "multipart/related; boundary=#{BOUNDARY}", 'Content-Length' => @content.size.to_s, 'Slug' => @title, 'If-Match' => "*"}))
198
+ end
199
+ else
200
+ ret = service.send_request(GData4Ruby::Request.new(:put, @edit_uri, to_xml()))
201
+ end
202
+ if not load(ret.read_body)
203
+ raise SaveFailed
204
+ end
205
+ return true
206
+ else
207
+ return create
208
+ end
209
+ end
210
+
211
+ #Retrieves an array of GData4Ruby::AccessRules representing the access rules for the object, as contained in the Google ACL.
212
+ def access_rules
213
+ rules = []
214
+ ret = service.send_request(GData4Ruby::Request.new(:get, @acl_uri))
215
+ xml = REXML::Document.new(ret.read_body).root
216
+ xml.elements.each("entry") do |e|
217
+ ele = GData4Ruby::Utils::add_namespaces(e)
218
+ rule = GData4Ruby::ACL::AccessRule.new(service, self)
219
+ puts ele.to_s if service.debug
220
+ rule.load(ele.to_s)
221
+ rules << rule
222
+ end
223
+ rules
224
+ end
225
+
226
+ #Adds a new access rule for the object. Parameters are:
227
+ #*user*:: the user (email address) to add permissions for
228
+ #*role*:: can be one of 'viewer' or 'writer' depending on whether the user should be able to edit or read only.
229
+ def add_access_rule(user, role)
230
+ a = GData4Ruby::ACL::AccessRule.new(service, self)
231
+ a.user = user
232
+ a.role = role
233
+ a.save
234
+ end
235
+
236
+ # Waiting for V3 to graduate
237
+ # def set_publicly_writable(value)
238
+ # if value
239
+ # a = GData4Ruby::ACL::AccessRule.new(service, self)
240
+ # a.role = 'writer'
241
+ # a.save
242
+ # else
243
+ # remove_access_rule('default', 'writer')
244
+ # end
245
+ # end
246
+
247
+ # Waiting for V3 to graduate
248
+ # def set_publicly_readable(value)
249
+ # if value
250
+ # a = GData4Ruby::ACL::AccessRule.new(service, self)
251
+ # a.role = 'reader'
252
+ # a.save
253
+ # else
254
+ # remove_access_rule('default', 'reader')
255
+ # end
256
+ # end
257
+
258
+ #Updates an existing access rule for the object. Parameters are:
259
+ #*user*:: the user (email address) to update permissions
260
+ #*role*:: can be one of 'viewer' or 'writer' depending on whether the user should be able to edit or read only.
261
+ def update_access_rule(user, role)
262
+ a = GData4Ruby::ACL::AccessRule.find(service, self, {:user => user})
263
+ if a
264
+ a.role = role
265
+ if a.save
266
+ return true
267
+ end
268
+ end
269
+ return false
270
+ end
271
+
272
+ #Removes an access rule for the specified user. Parameter should be a valid user Id (email address).
273
+ def remove_access_rule(user)
274
+ a = GData4Ruby::ACL::AccessRule.find(service, self, {:user => user})
275
+ if a
276
+ if a.delete
277
+ return true
278
+ end
279
+ end
280
+ return false
281
+ end
282
+
283
+ #Creates a new object instance on the Google server if the object doesn't already exist.
284
+ def create
285
+ ret = if (not @local_file.nil? and @local_file.is_a? String) or @content
286
+ if @local_file
287
+ service.send_request(GData4Ruby::Request.new(:post, DOCUMENT_LIST_FEED, create_multipart_message([{:type => 'application/atom+xml', :content => to_xml()}, {:type => UPLOAD_TYPES[File.extname(@local_file).gsub(".", "").to_sym], :content => get_file(@local_file).read}]), {'Content-Type' => "multipart/related; boundary=#{BOUNDARY}", 'Content-Length' => File.size(@local_file).to_s, 'Slug' => File.basename(@local_file)}))
288
+ elsif @content
289
+ service.send_request(GData4Ruby::Request.new(:post, DOCUMENT_LIST_FEED, create_multipart_message([{:type => 'application/atom+xml', :content => to_xml()}, {:type => UPLOAD_TYPES[@content_type.to_sym], :content => @content}]), {'Content-Type' => "multipart/related; boundary=#{BOUNDARY}", 'Content-Length' => @content.size.to_s, 'Slug' => @title}))
290
+ end
291
+ else
292
+ service.send_request(GData4Ruby::Request.new(:post, DOCUMENT_LIST_FEED, to_xml()))
293
+ end
294
+ if not load(ret.read_body)
295
+ raise SaveFailed
296
+ end
297
+ return ret
298
+ end
299
+
300
+ #Returns a simple iframe containing the html_uri link for the document.
301
+ #
302
+ #*Note:* you must either be logged in to Google
303
+ #as the owner of the document for this to work, or have appropriate read/write permissions set up on the document.
304
+ def to_iframe(options = {})
305
+ width = options[:width] || '800'
306
+ height = options[:height] || '500'
307
+ return "<iframe height='#{height}' width='#{width}' src='#{@html_uri}'></iframe>"
308
+ end
309
+
310
+ #Saves arbitrary content to the object. Parameters must be:
311
+ #*content*:: the content to upload. Content should be formatted as one of the UPLOAD_TYPES.
312
+ #*type*:: an optional paramter specifying the type, if not HTML. This value must be a mime-type enumerated in UPLOAD_TYPES.
313
+ def put_content(content, type = 'text/html')
314
+ ret = service.send_request(GData4Ruby::Request.new(:put, @edit_content_uri, content, {'Content-Type' => type,
315
+ 'Content-Length' => content.length.to_s,
316
+ 'If-Match' => "*"}))
317
+ load(ret.body)
318
+ end
319
+
320
+ #Finds a BaseObject based on a text query or by an id. Parameters are:
321
+ #*service*:: A valid Service object to search.
322
+ #*query*:: either a string containing a text query to search by, or a hash containing an +id+ key with an associated id to find, or a +query+ key containint a text query to search for.
323
+ #*type*:: used to limit results to a specific document type, as list in TYPES.
324
+ #*args*:: a hash containing optional additional query paramters to use. See http://code.google.com/apis/gdata/docs/2.0/reference.html#Queries for a full list of possible values. Example:
325
+ # {'max-results' => '100'}
326
+ #If an ID is specified, a single instance of the document is returned if found, otherwise false.
327
+ #If a query term is specified, and array of matching results is returned, or an empty array if nothing
328
+ #was found.
329
+ def self.find(service, query, type = 'any', args = {})
330
+ raise ArgumentError, 'query must be a hash or string' if not query.is_a? Hash and not query.is_a? String
331
+ raise ArgumentError, "type must be one of #{TYPES.join(" ")}" if not TYPES.include? type
332
+ if query.is_a? Hash and query[:id]
333
+ id = query[:id]
334
+ puts "id passed, finding event by id" if service.debug
335
+ puts "id = "+id if service.debug
336
+ d = service.send_request(GData4Ruby::Request.new(:get, FEEDS[type.to_sym]+id, {"If-Not-Match" => "*"}))
337
+ puts d.inspect if service.debug
338
+ if d
339
+ return get_instance(service, d)
340
+ end
341
+ else
342
+ results = []
343
+ term = query.is_a?(Hash) ? CGI::escape(query[:query]) : CGI::escape(query)
344
+ args["q"] = term if term and term != ''
345
+ ret = service.send_request(GData4Ruby::Request.new(:get, QUERY_FEEDS[type.to_sym], nil, nil, args))
346
+ xml = REXML::Document.new(ret.body).root
347
+ xml.elements.each("entry") do |e|
348
+ results << get_instance(service, e)
349
+ end
350
+ return results
351
+ end
352
+ return false
353
+ end
354
+
355
+ #Adds the object to the specified folder. The parameter must be a valid Folder object.
356
+ def add_to_folder(folder)
357
+ raise ArgumentError, 'folder must be a GDocs4Ruby::Folder' if not folder.is_a? Folder
358
+ @service.send_request(GData4Ruby::Request.new(:post, folder.content_uri, to_xml))
359
+ end
360
+
361
+ #Removes the object from the specified folder. The parameter must be a valid Folder object.
362
+ def remove_from_folder(folder)
363
+ raise ArgumentError, 'folder must be a GDocs4Ruby::Folder' if not folder.is_a? Folder
364
+ @service.send_request(GData4Ruby::Request.new(:delete, folder.content_uri+"/"+CGI::escape(id), nil, {"If-Match" => "*"}))
365
+ end
366
+
367
+ private
368
+ def self.get_instance(service, d)
369
+ if d.is_a? Net::HTTPOK
370
+ xml = REXML::Document.new(d.read_body).root
371
+ if xml.name == 'feed'
372
+ xml = xml.elements.each("entry"){}[0]
373
+ end
374
+ else
375
+ xml = d
376
+ end
377
+ ele = GData4Ruby::Utils::add_namespaces(xml)
378
+ obj = BaseObject.new(service)
379
+ obj.load(ele.to_s)
380
+ case obj.type
381
+ when 'document'
382
+ doc = Document.new(service)
383
+ when 'spreadsheet'
384
+ doc = Spreadsheet.new(service)
385
+ when 'folder'
386
+ doc = Folder.new(service)
387
+ when 'presentation'
388
+ doc = Presentation.new(service)
389
+ end
390
+ doc.load(ele.to_s)
391
+ doc
392
+ end
393
+
394
+ def get_file(filename)
395
+ file = File.open(filename, "rb")
396
+ raise FileNotFoundError if not file
397
+ return file
398
+ end
399
+
400
+ def create_multipart_message(parts)
401
+ ret = ''
402
+ parts.each do |p|
403
+ ret += "--#{BOUNDARY}\nContent-Type: #{p[:type]}\n\n#{p[:content]}\n\n"
404
+ end
405
+ ret += "--#{BOUNDARY}--\n"
406
+ end
407
+ end
408
+ end
@@ -0,0 +1,97 @@
1
+ # Author:: Mike Reich (mike@seabourneconsulting.com)
2
+ # Copyright:: Copyright (C) 2010 Mike Reich
3
+ # License:: GPL v2
4
+ #--
5
+ # Licensed under the General Public License (GPL), Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ #
15
+ # Feel free to use and update, but be sure to contribute your
16
+ # code back to the project and attribute as required by the license.
17
+ #++
18
+ module GDocs4Ruby
19
+ #The Document class represents a Google Word Document. Also check out BaseObject, the superclass of Document.
20
+ #=Usage
21
+ #All usages assume you've already authenticated with the service, and have a service object called
22
+ #@service.
23
+ #1. Create a new Document
24
+ # doc = Document.new(@service)
25
+ # doc.title = 'Test Document'
26
+ # doc.content = '<h1>Test Content HTML</h1>'
27
+ # doc.content_type = 'html'
28
+ # doc.save
29
+ #
30
+ #2. Deleting a Document
31
+ # doc = Document.find(@service, {:id => @doc_id})
32
+ # doc.delete
33
+ #
34
+ #3. Finding an existing Document by id
35
+ # doc = Document.find(@service, {:id => @doc_id})
36
+ #
37
+ #4. Full Text Query
38
+ # doc = Document.find(@service, 'content text')
39
+ #
40
+ # or
41
+ #
42
+ # doc = Document.find(@service, {:query => 'content text'})
43
+ #
44
+ #5. Finding an Existing Document by Title
45
+ # doc = Document.find(@service, nil, {'title' => 'Test Document'})
46
+ #
47
+ #6. Updating a Document with Content from a Local File
48
+ # doc = Document.find(@service, {:id => @doc_id})
49
+ # doc.title = 'New Title'
50
+ # doc.local_file = '/path/to/some/file'
51
+ # doc.save
52
+ #
53
+ #7. Retrieving an Export
54
+ # doc = Document.find(@service, {:id => @doc_id})
55
+ # doc.download_to_file('pdf', '/path/to/save/location.pdf')
56
+
57
+ class Document < BaseObject
58
+ DOCUMENT_XML = '<?xml version="1.0" encoding="UTF-8"?>
59
+ <atom:entry xmlns:atom="http://www.w3.org/2005/Atom">
60
+ <atom:category scheme="http://schemas.google.com/g/2005#kind"
61
+ term="http://schemas.google.com/docs/2007#document" label="document"/>
62
+ <atom:title>example document</atom:title>
63
+ </atom:entry>'
64
+ DOWNLOAD_TYPES = ['doc', 'html', 'odt', 'pdf', 'png', 'rtf', 'txt', 'zip']
65
+ EXPORT_URI = 'https://docs.google.com/feeds/download/documents/Export'
66
+
67
+ #Creates a new Document instance. Requires a valid Service object.
68
+ def initialize(service, attributes = {})
69
+ super(service, attributes)
70
+ @xml = DOCUMENT_XML
71
+ end
72
+
73
+ #Retrieves an export of the Document. The parameter +type+ must be one of the DOWNLOAD_TYPES.
74
+ def get_content(type)
75
+ if !@exists
76
+ raise DocumentDoesntExist
77
+ end
78
+ if not DOWNLOAD_TYPES.include? type
79
+ raise ArgumentError
80
+ end
81
+ ret = service.send_request(GData4Ruby::Request.new(:get, EXPORT_URI, nil, nil, {"docId" => @id,"exportFormat" => type}))
82
+ ret.body
83
+ end
84
+
85
+ #Helper function limit queries to Documents. See BaseObject#find for syntax. Type is not required and assumed to be 'document'.
86
+ def self.find(service, query, args = {})
87
+ super(service, query, 'document', args)
88
+ end
89
+
90
+ #Downloads the export retrieved through get_content to a specified local file. Parameters are:
91
+ #*type*:: must be a valid type enumerated in DOWNLOAD_TYPES
92
+ #*location*:: a valid file location for the local system
93
+ def download_to_file(type, location)
94
+ File.open(location, 'wb+') {|f| f.write(get_content(type)) }
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,126 @@
1
+ # Author:: Mike Reich (mike@seabourneconsulting.com)
2
+ # Copyright:: Copyright (C) 2010 Mike Reich
3
+ # License:: GPL v2
4
+ #--
5
+ # Licensed under the General Public License (GPL), Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ #
15
+ # Feel free to use and update, but be sure to contribute your
16
+ # code back to the project and attribute as required by the license.
17
+ #++
18
+ module GDocs4Ruby
19
+ #The Document class represents a Google Documents Folder.
20
+ #=Usage
21
+ #Assumes a valid and authenticated @service object.
22
+ #1. Retrieving a list of folders
23
+ # @service.folders
24
+ #
25
+ #2. Getting a list of files in a folder
26
+ # @folder = @service.folders.first
27
+ # @folder.files
28
+ #
29
+ #3. Getting a list of sub folders in a folder
30
+ # @folder = @service.folders.first
31
+ # @folder.sub_folders
32
+ #
33
+ class Folder < BaseObject
34
+ FOLDER_XML = '<?xml version="1.0" encoding="UTF-8"?>
35
+ <atom:entry xmlns:atom="http://www.w3.org/2005/Atom">
36
+ <atom:category scheme="http://schemas.google.com/g/2005#kind"
37
+ term="http://schemas.google.com/docs/2007#folder" label="folder"/>
38
+ <atom:title></atom:title>
39
+ </atom:entry>'
40
+
41
+ #Creates a new Folder instance. Requires a valid GData4Ruby#Service object.
42
+ def initialize(service, attributes = {})
43
+ super(service, attributes)
44
+ @xml = FOLDER_XML
45
+ end
46
+
47
+ public
48
+ #Loads the Calendar with returned data from Google Calendar feed. Returns true if successful.
49
+ def load(string)
50
+ super(string)
51
+ xml = REXML::Document.new(string)
52
+ xml.root.elements.each(){}.map do |ele|
53
+ # case ele.name
54
+ #
55
+ # end
56
+ end
57
+
58
+ @folder_feed = @id
59
+ return true
60
+ end
61
+
62
+ #Returns a list of sub folders that this folder contains.
63
+ def sub_folders
64
+ ret = service.send_request(GData4Ruby::Request.new(:get, @content_uri+"/-/folder?showfolders=true"))
65
+ folders = []
66
+ REXML::Document.new(ret.body).root.elements.each("entry"){}.map do |entry|
67
+ entry = GData4Ruby::Utils::add_namespaces(entry)
68
+ folder = Folder.new(service)
69
+ puts entry.to_s if service.debug
70
+ folder.load("<?xml version='1.0' encoding='UTF-8'?>#{entry.to_s}")
71
+ folders << folder
72
+ end
73
+ return folders
74
+ end
75
+
76
+ #Returns a list of files in the folder
77
+ def files
78
+ return nil if @content_uri == nil
79
+ contents = []
80
+ ret = @service.send_request(GData4Ruby::Request.new(:get, @content_uri))
81
+ xml = REXML::Document.new(ret.body)
82
+ xml.root.elements.each('entry'){}.map do |ele|
83
+ ele = GData4Ruby::Utils::add_namespaces(ele)
84
+ obj = BaseObject.new(@service)
85
+ obj.load(ele.to_s)
86
+ case obj.type
87
+ when 'document'
88
+ doc = Document.new(@service)
89
+ when 'spreadsheet'
90
+ doc = Spreadsheet.new(@service)
91
+ when 'presentation'
92
+ doc = Presentation.new(service)
93
+ end
94
+ doc.load(ele.to_s)
95
+ contents << doc
96
+ end
97
+ return contents
98
+ end
99
+
100
+ #Helper function limit queries to Folders. See BaseObject#find for syntax. Type is not required and assumed to be 'document'.
101
+ def self.find(service, query, args = {})
102
+ raise ArgumentError if not query.is_a? Hash and not query.is_a? String
103
+ ret = query.is_a?(String) ? [] : nil
104
+ service.folders.each do |f|
105
+ if (query.is_a? Hash and ((query[:id] and f.id == query[:id]) or (query[:query] and f.title.include? query[:query])))
106
+ return f
107
+ end
108
+ if (query.is_a? String and f.title.include? query)
109
+ ret << f
110
+ end
111
+ end
112
+ return ret
113
+ end
114
+
115
+ #A reference to the parent folder. If there is no parent folder, nil is returned.
116
+ def parent
117
+ return nil if @parent_uri == nil
118
+ ret = @service.send_request(GData4Ruby::Request.new(:get, @parent_uri))
119
+ folder = nil
120
+ puts ret.body if @service.debug
121
+ folder = Folder.new(@service)
122
+ folder.load(ret.body)
123
+ return folder
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,83 @@
1
+ # Author:: Mike Reich (mike@seabourneconsulting.com)
2
+ # Copyright:: Copyright (C) 2010 Mike Reich
3
+ # License:: GPL v2
4
+ #--
5
+ # Licensed under the General Public License (GPL), Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ #
15
+ # Feel free to use and update, but be sure to contribute your
16
+ # code back to the project and attribute as required by the license.
17
+ #++
18
+ module GDocs4Ruby
19
+ #The Presentation class represents a Google Presentation. Also check out Document and BaseObject, the superclass of Presentation.
20
+ #=Usage
21
+ #All usages assume you've already authenticated with the service, and have a service object called
22
+ #@service.
23
+ #1. Create a new Presentation
24
+ # p = Presentation.new(@service)
25
+ # p.title = 'Test Presentation'
26
+ # p.local_file = '/path/to/some/spreadsheet.ppt'
27
+ # p.save
28
+ #
29
+ #2. Deleting a Presentation
30
+ # p = Presentation.find(@service, {:id => @doc_id})
31
+ # p.delete
32
+ #
33
+ #3. Finding an existing Presentation by id
34
+ # p = Presentation.find(@service, {:id => @doc_id})
35
+ #
36
+ #4. Full Text Query
37
+ # p = Presentation.find(@service, 'content text')
38
+ #
39
+ # or
40
+ #
41
+ # p = Presentation.find(@service, {:query => 'content text'})
42
+ #
43
+ #5. Finding an Existing Spreadsheet by Title
44
+ # p = Presentation.find(@service, nil, {'title' => 'Test Presentation'})
45
+ #
46
+ #6. Retrieving an Export
47
+ # p = Presentation.find(@service, {:id => @doc_id})
48
+ # p.download_to_file('pdf', '/path/to/save/location.pdf')
49
+
50
+ class Presentation < Document
51
+ DOCUMENT_XML = '<?xml version="1.0" encoding="UTF-8"?>
52
+ <atom:entry xmlns:atom="http://www.w3.org/2005/Atom">
53
+ <atom:category scheme="http://schemas.google.com/g/2005#kind"
54
+ term="http://schemas.google.com/docs/2007#spreadsheet" label="presentation"/>
55
+ <atom:title></atom:title>
56
+ </atom:entry>'
57
+ DOWNLOAD_TYPES = ['pdf', 'png', 'ppt', 'swf', 'txt']
58
+ EXPORT_URI = 'http://docs.google.com/feeds/download/presentations/Export'
59
+
60
+ #Helper function limit queries to Presentations. See BaseObject::find for syntax. Type is not required and assumed to be 'presentation'.
61
+ def self.find(service, query, args = {})
62
+ BaseObject::find(service, query, 'presentation', args)
63
+ end
64
+
65
+ #Creates a new Presentation instance. Requires a valid Service object.
66
+ def initialize(service, attributes = {})
67
+ super(service, attributes)
68
+ @xml = DOCUMENT_XML
69
+ end
70
+
71
+ #Retrieves an export of the Presentation. The parameter +type+ must be one of the DOWNLOAD_TYPES
72
+ def get_content(type)
73
+ if !@exists
74
+ raise DocumentDoesntExist
75
+ end
76
+ if not DOWNLOAD_TYPES.include? type
77
+ raise ArgumentError
78
+ end
79
+ ret = service.send_request(GData4Ruby::Request.new(:get, EXPORT_URI, nil, nil, {"docID" => @id,"exportFormat" => type}))
80
+ ret.body
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,105 @@
1
+ # Author:: Mike Reich (mike@seabourneconsulting.com)
2
+ # Copyright:: Copyright (C) 2010 Mike Reich
3
+ # License:: GPL v2
4
+ #--
5
+ # Licensed under the General Public License (GPL), Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ #
15
+ # Feel free to use and update, but be sure to contribute your
16
+ # code back to the project and attribute as required by the license.
17
+ #++
18
+ require 'gdocs4ruby/base'
19
+ require 'gdocs4ruby/base_object'
20
+ require 'gdocs4ruby/folder'
21
+ require 'gdocs4ruby/document'
22
+ require 'gdocs4ruby/spreadsheet'
23
+ require 'gdocs4ruby/presentation'
24
+
25
+ module GDocs4Ruby
26
+
27
+ DOCUMENT_LIST_FEED = "https://docs.google.com/feeds/documents/private/full"
28
+ FOLDER_LIST_FEED = "http://docs.google.com/feeds/documents/private/full/-/folder?showfolders=true"
29
+
30
+ #The service class is the main handler for all direct interactions with the
31
+ #Google Documents API. A service represents a single user account. Each user
32
+ #account can have multiple documents and folders.
33
+ #=Usage
34
+ #
35
+ #1. Authenticate
36
+ # service = Service.new
37
+ # service.authenticate("user@gmail.com", "password")
38
+ #
39
+ #2. Get Document List
40
+ # documents = service.files
41
+ #
42
+ #3. Get Folder List
43
+ # folders = serivce.folders
44
+ #
45
+ class Service < GData4Ruby::Service
46
+ #Accepts an optional attributes hash for initialization values
47
+ def initialize(attributes = {})
48
+ super(attributes)
49
+ end
50
+
51
+ # The authenticate method passes the username and password to google servers.
52
+ # If authentication succeeds, returns true, otherwise raises the AuthenticationFailed error.
53
+ def authenticate(username, password, service='writely')
54
+ super(username, password, service)
55
+ end
56
+
57
+ #Helper function to reauthenticate to a new Google service without having to re-set credentials.
58
+ def reauthenticate(service='writely')
59
+ authenticate(@account, @password, service)
60
+ end
61
+
62
+ #Returns an array of Folder objects for each folder associated with
63
+ #the authenticated account.
64
+ def folders
65
+ if not @auth_token
66
+ raise NotAuthenticated
67
+ end
68
+ ret = send_request(GData4Ruby::Request.new(:get, FOLDER_LIST_FEED))
69
+ folders = []
70
+ REXML::Document.new(ret.body).root.elements.each("entry"){}.map do |entry|
71
+ entry = GData4Ruby::Utils::add_namespaces(entry)
72
+ folder = Folder.new(self)
73
+ puts entry.to_s if debug
74
+ folder.load("<?xml version='1.0' encoding='UTF-8'?>#{entry.to_s}")
75
+ folders << folder
76
+ end
77
+ return folders
78
+ end
79
+
80
+ #Returns an array of objects for each document in the account. Note that this
81
+ #method will return all documents for the account, including documents contained in
82
+ #subfolders.
83
+ def files
84
+ contents = []
85
+ ret = send_request(GData4Ruby::Request.new(:get, DOCUMENT_LIST_FEED))
86
+ xml = REXML::Document.new(ret.body)
87
+ xml.root.elements.each('entry'){}.map do |ele|
88
+ ele = GData4Ruby::Utils::add_namespaces(ele)
89
+ obj = BaseObject.new(self)
90
+ obj.load(ele.to_s)
91
+ case obj.type
92
+ when 'document'
93
+ doc = Document.new(self)
94
+ when 'spreadsheet'
95
+ doc = Spreadsheet.new(self)
96
+ when 'presentation'
97
+ doc = Presentation.new(self)
98
+ end
99
+ doc.load(ele.to_s)
100
+ contents << doc
101
+ end
102
+ return contents
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,85 @@
1
+ # Author:: Mike Reich (mike@seabourneconsulting.com)
2
+ # Copyright:: Copyright (C) 2010 Mike Reich
3
+ # License:: GPL v2
4
+ #--
5
+ # Licensed under the General Public License (GPL), Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ #
15
+ # Feel free to use and update, but be sure to contribute your
16
+ # code back to the project and attribute as required by the license.
17
+ #++
18
+ module GDocs4Ruby
19
+ #The Spreadsheet class represents a Google Spreadsheet. Also check out Document and BaseObject, the superclass of Spreadsheet.
20
+ #=Usage
21
+ #All usages assume you've already authenticated with the service, and have a service object called
22
+ #@service.
23
+ #1. Create a new Spreadsheet
24
+ # s = Spreadsheet.new(@service)
25
+ # s.title = 'Test Spreadsheet'
26
+ # s.local_file = '/path/to/some/spreadsheet.xls'
27
+ # s.save
28
+ #
29
+ #2. Deleting a Spreadsheet
30
+ # s = Spreadsheet.find(@service, {:id => @doc_id})
31
+ # s.delete
32
+ #
33
+ #3. Finding an existing Spreadsheet by id
34
+ # s = Spreadsheet.find(@service, {:id => @doc_id})
35
+ #
36
+ #4. Full Text Query
37
+ # s = Spreadsheet.find(@service, 'content text')
38
+ #
39
+ # or
40
+ #
41
+ # s = Spreadsheet.find(@service, {:query => 'content text'})
42
+ #
43
+ #5. Finding an Existing Spreadsheet by Title
44
+ # s = Spreadsheet.find(@service, nil, {'title' => 'Test Spreadsheet'})
45
+ #
46
+ #6. Retrieving an Export
47
+ # s = Spreadsheet.find(@service, {:id => @doc_id})
48
+ # s.download_to_file('pdf', '/path/to/save/location.pdf')
49
+
50
+ class Spreadsheet < Document
51
+ DOCUMENT_XML = '<?xml version="1.0" encoding="UTF-8"?>
52
+ <atom:entry xmlns:atom="http://www.w3.org/2005/Atom">
53
+ <atom:category scheme="http://schemas.google.com/g/2005#kind"
54
+ term="http://schemas.google.com/docs/2007#spreadsheet" label="spreadsheet"/>
55
+ <atom:title></atom:title>
56
+ </atom:entry>'
57
+ DOWNLOAD_TYPES = ['xls', 'csv', 'pdf', 'ods', 'tsv', 'html']
58
+ EXPORT_URI = 'http://spreadsheets.google.com/feeds/download/spreadsheets/Export'
59
+
60
+ #Helper function limit queries to Spreadsheets. See BaseObject::find for syntax. Type is not required and assumed to be 'spreadsheet'.
61
+ def self.find(service, query, args = {})
62
+ BaseObject::find(service, query, 'spreadsheet', args)
63
+ end
64
+
65
+ #Creates a new Spreadsheet instance. Requires a valid Service object.
66
+ def initialize(service, attributes = {})
67
+ super(service, attributes)
68
+ @xml = DOCUMENT_XML
69
+ end
70
+
71
+ #Retrieves an export of the Spreadsheet. The parameter +type+ must be one of the DOWNLOAD_TYPES.
72
+ def get_content(type)
73
+ if !@exists
74
+ raise DocumentDoesntExist
75
+ end
76
+ if not DOWNLOAD_TYPES.include? type
77
+ raise ArgumentError
78
+ end
79
+ service.reauthenticate('wise')
80
+ ret = service.send_request(GData4Ruby::Request.new(:get, EXPORT_URI, nil, nil, {"key" => @id.gsub(/\w.*:/, ""),"exportFormat" => type}))
81
+ service.reauthenticate()
82
+ ret.body
83
+ end
84
+ end
85
+ end
data/lib/gdocs4ruby.rb ADDED
@@ -0,0 +1 @@
1
+ require "gdocs4ruby/service"
data/test/unit.rb ADDED
@@ -0,0 +1,3 @@
1
+ if __FILE__ == $0
2
+ # TODO Generated stub
3
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gdocs4ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Mike Reich
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-03-12 00:00:00 +11:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: gdata4ruby
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.1.0
24
+ version:
25
+ description: GDocs4Ruby is a full featured wrapper for version 2.0 of the Google Documents API (aka DocList). GDocs4Ruby provides the ability to create, update and delete google documents, metadata and content. The gem also includes support for folders, modifying permissions for documents via ACL feeds, and much more.
26
+ email: mike@seabourneconsulting.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - README
35
+ - CHANGELOG
36
+ - lib/gdocs4ruby.rb
37
+ - lib/gdocs4ruby/service.rb
38
+ - lib/gdocs4ruby/folder.rb
39
+ - lib/gdocs4ruby/document.rb
40
+ - lib/gdocs4ruby/base_object.rb
41
+ - lib/gdocs4ruby/spreadsheet.rb
42
+ - lib/gdocs4ruby/presentation.rb
43
+ has_rdoc: true
44
+ homepage: http://gdocs4ruby.rubyforge.org/
45
+ licenses: []
46
+
47
+ post_install_message:
48
+ rdoc_options: []
49
+
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ requirements: []
65
+
66
+ rubyforge_project: gdocs4ruby
67
+ rubygems_version: 1.3.5
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: A full featured wrapper for interacting with the Google Docs API
71
+ test_files:
72
+ - test/unit.rb