google_drive 0.3.11 → 1.0.0.pre1

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.
Files changed (34) hide show
  1. checksums.yaml +7 -7
  2. data/README.rdoc +27 -10
  3. data/lib/google_drive/acl.rb +40 -58
  4. data/lib/google_drive/acl_entry.rb +76 -56
  5. data/lib/google_drive/api_client_fetcher.rb +49 -0
  6. data/lib/google_drive/collection.rb +69 -71
  7. data/lib/google_drive/file.rb +171 -128
  8. data/lib/google_drive/session.rb +234 -268
  9. data/lib/google_drive/spreadsheet.rb +19 -163
  10. data/lib/google_drive/util.rb +126 -17
  11. data/lib/google_drive/worksheet.rb +108 -80
  12. data/lib/google_drive.rb +63 -57
  13. data/lib/google_drive_v1/acl.rb +115 -0
  14. data/lib/google_drive_v1/acl_entry.rb +100 -0
  15. data/lib/google_drive_v1/api_client_fetcher.rb +47 -0
  16. data/lib/google_drive_v1/authentication_error.rb +14 -0
  17. data/lib/{google_drive → google_drive_v1}/basic_fetcher.rb +1 -1
  18. data/lib/{google_drive → google_drive_v1}/client_login_fetcher.rb +2 -2
  19. data/lib/google_drive_v1/collection.rb +167 -0
  20. data/lib/google_drive_v1/error.rb +12 -0
  21. data/lib/google_drive_v1/file.rb +258 -0
  22. data/lib/google_drive_v1/list.rb +119 -0
  23. data/lib/google_drive_v1/list_row.rb +88 -0
  24. data/lib/{google_drive → google_drive_v1}/oauth1_fetcher.rb +1 -1
  25. data/lib/{google_drive → google_drive_v1}/oauth2_fetcher.rb +2 -2
  26. data/lib/google_drive_v1/record.rb +31 -0
  27. data/lib/google_drive_v1/session.rb +522 -0
  28. data/lib/google_drive_v1/spreadsheet.rb +248 -0
  29. data/lib/google_drive_v1/table.rb +60 -0
  30. data/lib/google_drive_v1/util.rb +73 -0
  31. data/lib/google_drive_v1/worksheet.rb +498 -0
  32. data/lib/google_drive_v1.rb +148 -0
  33. metadata +112 -77
  34. data/doc_src/google_drive/acl_entry.rb +0 -33
@@ -0,0 +1,100 @@
1
+ # Author: Guy Boertje <https://github.com/guyboertje>
2
+ # Author: David R. Albrecht <https://github.com/eldavido>
3
+ # Author: Hiroshi Ichikawa <http://gimite.net/>
4
+ # Author: Phuogn Nguyen <https://github.com/phuongnd08>
5
+ # The license of this source is "New BSD Licence"
6
+
7
+ module GoogleDriveV1
8
+
9
+ # An entry of an ACL (access control list) of a spreadsheet.
10
+ #
11
+ # Use GoogleDriveV1::Acl#[] to get GoogleDriveV1::AclEntry object.
12
+ #
13
+ # This code is based on https://github.com/guyboertje/gdata-spreadsheet-ruby .
14
+ class AclEntry
15
+
16
+ include(Util)
17
+
18
+ PARAM_NAMES = [:acl, :scope_type, :scope, :with_key, :role, :title, :edit_url, :etag] #:nodoc:
19
+
20
+ # +params+ is a Hash object with keys +:scope_type+, +:scope+ and +:role+.
21
+ # See scope_type and role for the document of the fields.
22
+ def initialize(params)
23
+ @params = {:role => "reader"}
24
+ for name, value in params
25
+ if !name.is_a?(Symbol)
26
+ raise(ArgumentError, "Key must be Symbol, but is %p" % name)
27
+ elsif !PARAM_NAMES.include?(name)
28
+ raise(ArgumentError, "Invalid key: %p" % name)
29
+ end
30
+ @params[name] = value
31
+ end
32
+ end
33
+
34
+ attr_accessor(:params) #:nodoc:
35
+
36
+ PARAM_NAMES.each() do |name|
37
+ define_method(name) do
38
+ return @params[name]
39
+ end
40
+ end
41
+
42
+ def edit_url
43
+ warn(
44
+ "WARNING: GoogleDriveV1::AclEntry\#edit_url is deprecated and will be removed in the next version.")
45
+ return self.edit_url_internal
46
+ end
47
+
48
+ def edit_url_internal #:nodoc:
49
+ return @params[:edit_url]
50
+ end
51
+
52
+ # Changes the role of the scope.
53
+ #
54
+ # e.g.
55
+ # spreadsheet.acl[1].role = "writer"
56
+ def role=(role)
57
+ @params[:role] = role
58
+ @params[:acl].update_role(self)
59
+ end
60
+
61
+ def inspect
62
+ return "\#<%p scope_type=%p, scope=%p, with_key=%p, role=%p>" %
63
+ [self.class, @params[:scope_type], @params[:scope], @params[:with_key], @params[:role]]
64
+ end
65
+
66
+ def to_xml() #:nodoc:
67
+
68
+ etag_attr = self.etag ? "gd:etag='#{h(self.etag)}'" : ""
69
+ value_attr = self.scope ? "value='#{h(self.scope)}'" : ""
70
+ if self.with_key
71
+ role_tag = <<-EOS
72
+ <gAcl:withKey key='[ACL KEY]'>
73
+ <gAcl:role value='#{h(self.role)}'/>
74
+ </gAcl:withKey>
75
+ EOS
76
+ else
77
+ role_tag = <<-EOS
78
+ <gAcl:role value='#{h(self.role)}'/>
79
+ EOS
80
+ end
81
+
82
+ return <<-EOS
83
+ <entry
84
+ xmlns='http://www.w3.org/2005/Atom'
85
+ xmlns:gAcl='http://schemas.google.com/acl/2007'
86
+ xmlns:gd='http://schemas.google.com/g/2005'
87
+ #{etag_attr}>
88
+ <category scheme='http://schemas.google.com/g/2005#kind'
89
+ term='http://schemas.google.com/acl/2007#accessRule'/>
90
+ #{role_tag}
91
+ <gAcl:scope type='#{h(self.scope_type)}' #{value_attr}/>
92
+ </entry>
93
+ EOS
94
+
95
+ end
96
+
97
+ end
98
+
99
+ end
100
+
@@ -0,0 +1,47 @@
1
+ # Author: Hiroshi Ichikawa <http://gimite.net/>
2
+ # The license of this source is "New BSD Licence"
3
+
4
+ require "net/https"
5
+ require "uri"
6
+ Net::HTTP.version_1_2
7
+
8
+
9
+ module GoogleDriveV1
10
+
11
+ class ApiClientFetcher
12
+
13
+ class Response
14
+
15
+ def initialize(client_response)
16
+ @client_response = client_response
17
+ end
18
+
19
+ def code
20
+ return @client_response.status.to_s()
21
+ end
22
+
23
+ def body
24
+ return @client_response.body
25
+ end
26
+
27
+ end
28
+
29
+ def initialize(client)
30
+ @client = client
31
+ end
32
+
33
+ attr_reader(:client)
34
+
35
+ def request_raw(method, url, data, extra_header, auth)
36
+ p [method, url, data, extra_header, auth]
37
+ client_response = @client.execute(
38
+ :http_method => method,
39
+ :uri => url,
40
+ :body => data,
41
+ :headers => extra_header)
42
+ return Response.new(client_response)
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,14 @@
1
+ # Author: Hiroshi Ichikawa <http://gimite.net/>
2
+ # The license of this source is "New BSD Licence"
3
+
4
+ require "google_drive_v1/error"
5
+
6
+
7
+ module GoogleDriveV1
8
+
9
+ # Raised when GoogleDriveV1.login has failed.
10
+ class AuthenticationError < GoogleDriveV1::Error
11
+
12
+ end
13
+
14
+ end
@@ -6,7 +6,7 @@ require "uri"
6
6
  Net::HTTP.version_1_2
7
7
 
8
8
 
9
- module GoogleDrive
9
+ module GoogleDriveV1
10
10
 
11
11
  class BasicFetcher #:nodoc:
12
12
 
@@ -1,10 +1,10 @@
1
1
  # Author: Hiroshi Ichikawa <http://gimite.net/>
2
2
  # The license of this source is "New BSD Licence"
3
3
 
4
- require "google_drive/basic_fetcher"
4
+ require "google_drive_v1/basic_fetcher"
5
5
 
6
6
 
7
- module GoogleDrive
7
+ module GoogleDriveV1
8
8
 
9
9
  class ClientLoginFetcher < BasicFetcher #:nodoc:
10
10
 
@@ -0,0 +1,167 @@
1
+ # Author: Hiroshi Ichikawa <http://gimite.net/>
2
+ # The license of this source is "New BSD Licence"
3
+
4
+ require "google_drive_v1/util"
5
+ require "google_drive_v1/error"
6
+ require "google_drive_v1/spreadsheet"
7
+
8
+
9
+ module GoogleDriveV1
10
+
11
+ # Use GoogleDriveV1::Session#root_collection, GoogleDriveV1::Collection#subcollections,
12
+ # or GoogleDriveV1::Session#collection_by_url to get GoogleDriveV1::Collection object.
13
+ class Collection < GoogleDriveV1::File
14
+
15
+ include(Util)
16
+
17
+ ROOT_URL = "#{DOCS_BASE_URL}/folder%3Aroot" #:nodoc:
18
+
19
+ alias collection_feed_url document_feed_url
20
+
21
+ def contents_url
22
+ if self.root?
23
+ # The root collection doesn't have document feed.
24
+ return concat_url(ROOT_URL, "/contents")
25
+ else
26
+ return self.document_feed_entry_internal.css(
27
+ "content[type='application/atom+xml;type=feed']")[0]["src"]
28
+ end
29
+ end
30
+
31
+ # Title of the collection.
32
+ #
33
+ # Set <tt>params[:reload]</tt> to true to force reloading the title.
34
+ def title(params = {})
35
+ if self.root?
36
+ # The root collection doesn't have document feed.
37
+ return nil
38
+ else
39
+ return super
40
+ end
41
+ end
42
+
43
+ def resource_id
44
+ return self.root? ? nil : super
45
+ end
46
+
47
+ # Adds the given GoogleDriveV1::File to the collection.
48
+ def add(file)
49
+ header = {"GData-Version" => "3.0", "Content-Type" => "application/atom+xml;charset=utf-8"}
50
+ xml = <<-"EOS"
51
+ <entry xmlns="http://www.w3.org/2005/Atom">
52
+ <id>#{h(file.document_feed_url)}</id>
53
+ </entry>
54
+ EOS
55
+ @session.request(
56
+ :post, self.contents_url, :data => xml, :header => header, :auth => :writely)
57
+ return nil
58
+ end
59
+
60
+ # Creates a sub-collection with given title. Returns GoogleDriveV1::Collection object.
61
+ def create_subcollection(title)
62
+ header = {"GData-Version" => "3.0", "Content-Type" => "application/atom+xml;charset=utf-8"}
63
+ xml = <<-EOS
64
+ <entry xmlns="http://www.w3.org/2005/Atom">
65
+ <category scheme="http://schemas.google.com/g/2005#kind"
66
+ term="http://schemas.google.com/docs/2007#folder"/>
67
+ <title>#{h(title)}</title>
68
+ </entry>
69
+ EOS
70
+ doc = @session.request(
71
+ :post, contents_url, :data => xml, :header => header, :auth => :writely)
72
+ return @session.entry_element_to_file(doc)
73
+ end
74
+
75
+ # Removes the given GoogleDriveV1::File from the collection.
76
+ def remove(file)
77
+ url = to_v3_url("#{contents_url}/#{file.resource_id}")
78
+ @session.request(:delete, url, :auth => :writely, :header => {"If-Match" => "*"})
79
+ end
80
+
81
+ # Returns true if this is a root collection
82
+ def root?
83
+ self.document_feed_url == ROOT_URL
84
+ end
85
+
86
+ # Returns all the files (including spreadsheets, documents, subcollections) in the collection.
87
+ #
88
+ # You can specify query parameters described at
89
+ # https://developers.google.com/google-apps/documents-list/#getting_a_list_of_documents_and_files
90
+ #
91
+ # e.g.
92
+ #
93
+ # # Gets all the files in collection, including subcollections.
94
+ # collection.files
95
+ #
96
+ # # Gets only files with title "hoge".
97
+ # collection.files("title" => "hoge", "title-exact" => "true")
98
+ def files(params = {})
99
+ return files_with_type(nil, params)
100
+ end
101
+
102
+ alias contents files
103
+
104
+ # Returns all the spreadsheets in the collection.
105
+ def spreadsheets(params = {})
106
+ return files_with_type("spreadsheet", params)
107
+ end
108
+
109
+ # Returns all the Google Docs documents in the collection.
110
+ def documents(params = {})
111
+ return files_with_type("document", params)
112
+ end
113
+
114
+ # Returns all its subcollections.
115
+ def subcollections(params = {})
116
+ return files_with_type("folder", params)
117
+ end
118
+
119
+ # Returns a file (can be a spreadsheet, document, subcollection or other files) in the
120
+ # collection which exactly matches +title+ as GoogleDriveV1::File.
121
+ # Returns nil if not found. If multiple collections with the +title+ are found, returns
122
+ # one of them.
123
+ #
124
+ # If given an Array, does a recursive subcollection traversal.
125
+ def file_by_title(title)
126
+ return file_by_title_with_type(title, nil)
127
+ end
128
+
129
+ # Returns its subcollection whose title exactly matches +title+ as GoogleDriveV1::Collection.
130
+ # Returns nil if not found. If multiple collections with the +title+ are found, returns
131
+ # one of them.
132
+ #
133
+ # If given an Array, does a recursive subcollection traversal.
134
+ def subcollection_by_title(title)
135
+ return file_by_title_with_type(title, "folder")
136
+ end
137
+
138
+ protected
139
+
140
+ def file_by_title_with_type(title, type)
141
+ if title.is_a?(Array)
142
+ rel_path = title
143
+ if rel_path.empty?
144
+ return self
145
+ else
146
+ parent = subcollection_by_title(rel_path[0...-1])
147
+ return parent && parent.file_by_title_with_type(rel_path[-1], type)
148
+ end
149
+ else
150
+ return files_with_type(type, "title" => title, "title-exact" => "true")[0]
151
+ end
152
+ end
153
+
154
+ private
155
+
156
+ def files_with_type(type, params = {})
157
+ contents_url = self.contents_url
158
+ contents_url = concat_url(contents_url, "/-/#{type}") if type
159
+ contents_url = concat_url(contents_url, "?" + encode_query(params))
160
+ header = {"GData-Version" => "3.0", "Content-Type" => "application/atom+xml;charset=utf-8"}
161
+ doc = @session.request(:get, contents_url, :header => header, :auth => :writely)
162
+ return doc.css("feed > entry").map(){ |e| @session.entry_element_to_file(e) }
163
+ end
164
+
165
+ end
166
+
167
+ end
@@ -0,0 +1,12 @@
1
+ # Author: Hiroshi Ichikawa <http://gimite.net/>
2
+ # The license of this source is "New BSD Licence"
3
+
4
+
5
+ module GoogleDriveV1
6
+
7
+ # Raised when spreadsheets.google.com has returned error.
8
+ class Error < RuntimeError
9
+
10
+ end
11
+
12
+ end
@@ -0,0 +1,258 @@
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_v1/util"
8
+ require "google_drive_v1/acl"
9
+
10
+
11
+ module GoogleDriveV1
12
+
13
+ # A file in Google Drive, including Google Docs document/spreadsheet/presentation.
14
+ #
15
+ # Use GoogleDriveV1::Session#files or GoogleDriveV1::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
+ warn(
50
+ "WARNING: GoogleDriveV1::file\#document_feed_entry is deprecated and will be removed " +
51
+ "in the next version.")
52
+ return self.document_feed_entry_internal(params)
53
+ end
54
+
55
+ def document_feed_entry_internal(params = {}) #:nodoc:
56
+ if !@document_feed_entry || params[:reload]
57
+ @document_feed_entry =
58
+ @session.request(:get, self.document_feed_url, :auth => :writely).css("entry")[0]
59
+ end
60
+ return @document_feed_entry
61
+ end
62
+
63
+ # Resource ID.
64
+ def resource_id
65
+ return self.document_feed_entry_internal.css("gd|resourceId").text
66
+ end
67
+
68
+ # The type of resourse. e.g. "document", "spreadsheet", "folder"
69
+ def resource_type
70
+ return self.resource_id.split(/:/)[0]
71
+ end
72
+
73
+ # Title of the file.
74
+ #
75
+ # Set <tt>params[:reload]</tt> to true to force reloading the title.
76
+ def title(params = {})
77
+ return document_feed_entry_internal(params).css("title").text
78
+ end
79
+
80
+ # URL to view/edit the file in a Web browser.
81
+ #
82
+ # e.g. "https://docs.google.com/file/d/xxxx/edit"
83
+ def human_url
84
+ return self.document_feed_entry_internal.css("link[rel='alternate']")[0]["href"]
85
+ end
86
+
87
+ # ACL feed URL of the file.
88
+ def acl_feed_url
89
+ orig_acl_feed_url = self.document_feed_entry_internal.css(
90
+ "gd|feedLink[rel='http://schemas.google.com/acl/2007#accessControlList']")[0]["href"]
91
+ case orig_acl_feed_url
92
+ when %r{^https?://docs.google.com/feeds/default/private/full/.*/acl(\?.*)?$}
93
+ return orig_acl_feed_url
94
+ when %r{^https?://docs.google.com/feeds/acl/private/full/([^\?]*)(\?.*)?$}
95
+ # URL of old API version. Converts to v3 URL.
96
+ return "#{DOCS_BASE_URL}/#{$1}/acl"
97
+ else
98
+ raise(GoogleDriveV1::Error,
99
+ "ACL feed URL is in unknown format: #{orig_acl_feed_url}")
100
+ end
101
+ end
102
+
103
+ # Content types you can specify in methods download_to_file, download_to_string,
104
+ # download_to_io .
105
+ def available_content_types
106
+ return self.document_feed_entry_internal.css("content").map(){ |c| c["type"] }
107
+ end
108
+
109
+ # Downloads the file to a local file.
110
+ #
111
+ # e.g.
112
+ # file.download_to_file("/path/to/hoge.txt")
113
+ # file.download_to_file("/path/to/hoge", :content_type => "text/plain")
114
+ def download_to_file(path, params = {})
115
+ params = params.dup()
116
+ if !params[:content_type]
117
+ params[:content_type] = EXT_TO_CONTENT_TYPE[::File.extname(path).downcase]
118
+ params[:content_type_is_hint] = true
119
+ end
120
+ open(path, "wb") do |f|
121
+ download_to_io(f, params)
122
+ end
123
+ end
124
+
125
+ # Downloads the file and returns as a String.
126
+ #
127
+ # e.g.
128
+ # file.download_to_string() #=> "Hello world."
129
+ # file.download_to_string(:content_type => "text/plain") #=> "Hello world."
130
+ def download_to_string(params = {})
131
+ sio = StringIO.new()
132
+ download_to_io(sio, params)
133
+ return sio.string
134
+ end
135
+
136
+ # Downloads the file and writes it to +io+.
137
+ def download_to_io(io, params = {})
138
+ all_contents = self.document_feed_entry_internal.css("content")
139
+ if params[:content_type] && (!params[:content_type_is_hint] || all_contents.size > 1)
140
+ contents = all_contents.select(){ |c| c["type"] == params[:content_type] }
141
+ else
142
+ contents = all_contents
143
+ end
144
+ if contents.size == 1
145
+ url = contents[0]["src"]
146
+ else
147
+ if contents.empty?
148
+ raise(GoogleDriveV1::Error,
149
+ ("Downloading with content type %p not supported for this file. " +
150
+ "Specify one of these to content_type: %p") %
151
+ [params[:content_type], self.available_content_types])
152
+ else
153
+ raise(GoogleDriveV1::Error,
154
+ ("Multiple content types are available for this file. " +
155
+ "Specify one of these to content_type: %p") %
156
+ [self.available_content_types])
157
+ end
158
+ end
159
+ # TODO Use streaming if possible.
160
+ body = @session.request(:get, url, :response_type => :raw, :auth => :writely)
161
+ io.write(body)
162
+ end
163
+
164
+ # Updates the file with the content of the local file.
165
+ #
166
+ # e.g.
167
+ # file.update_from_file("/path/to/hoge.txt")
168
+ def update_from_file(path, params = {})
169
+ params = {:file_name => ::File.basename(path)}.merge(params)
170
+ open(path, "rb") do |f|
171
+ update_from_io(f, params)
172
+ end
173
+ end
174
+
175
+ # Updates the file with +content+.
176
+ #
177
+ # e.g.
178
+ # file.update_from_string("Good bye, world.")
179
+ def update_from_string(content, params = {})
180
+ update_from_io(StringIO.new(content), params)
181
+ end
182
+
183
+ # Reads content from +io+ and updates the file with the content.
184
+ def update_from_io(io, params = {})
185
+ params = {:header => {"If-Match" => "*"}}.merge(params)
186
+ initial_url = self.document_feed_entry_internal.css(
187
+ "link[rel='http://schemas.google.com/g/2005#resumable-edit-media']")[0]["href"]
188
+ @document_feed_entry = @session.upload_raw(
189
+ :put, initial_url, io, self.title, params)
190
+ end
191
+
192
+ # If +permanent+ is +false+, moves the file to the trash.
193
+ # If +permanent+ is +true+, deletes the file permanently.
194
+ def delete(permanent = false)
195
+ url = to_v3_url(self.document_feed_url)
196
+ url = concat_url(url, "?delete=true") if permanent
197
+ @session.request(:delete, url,
198
+ :auth => :writely, :header => {"If-Match" => "*"})
199
+ end
200
+
201
+ # Renames title of the file.
202
+ def rename(title)
203
+ edit_url = self.document_feed_entry_internal.css("link[rel='edit']").first["href"]
204
+ xml = <<-"EOS"
205
+ <atom:entry
206
+ xmlns:atom="http://www.w3.org/2005/Atom"
207
+ xmlns:docs="http://schemas.google.com/docs/2007">
208
+ <atom:title>#{h(title)}</atom:title>
209
+ </atom:entry>
210
+ EOS
211
+ header = {"Content-Type" => "application/atom+xml;charset=utf-8", "If-Match" => "*"}
212
+ @session.request(:put, edit_url, :data => xml, :auth => :writely, :header => header)
213
+ end
214
+
215
+ alias title= rename
216
+
217
+ # Returns GoogleDriveV1::Acl object for the file.
218
+ #
219
+ # With the object, you can see and modify people who can access the file.
220
+ # Modifications take effect immediately.
221
+ #
222
+ # Set <tt>params[:reload]</tt> to true to force reloading the data.
223
+ #
224
+ # e.g.
225
+ # # Dumps people who have access:
226
+ # for entry in file.acl
227
+ # p [entry.scope_type, entry.scope, entry.role]
228
+ # # => e.g. ["user", "example1@gmail.com", "owner"]
229
+ # end
230
+ #
231
+ # # Shares the file with new people:
232
+ # # NOTE: This sends email to the new people.
233
+ # file.acl.push(
234
+ # {:scope_type => "user", :scope => "example2@gmail.com", :role => "reader"})
235
+ # file.acl.push(
236
+ # {:scope_type => "user", :scope => "example3@gmail.com", :role => "writer"})
237
+ #
238
+ # # Changes the role of a person:
239
+ # file.acl[1].role = "writer"
240
+ #
241
+ # # Deletes an ACL entry:
242
+ # file.acl.delete(file.acl[1])
243
+ def acl(params = {})
244
+ if !@acl || params[:reload]
245
+ @acl = Acl.new(@session, self.acl_feed_url)
246
+ end
247
+ return @acl
248
+ end
249
+
250
+ def inspect
251
+ fields = {:document_feed_url => self.document_feed_url}
252
+ fields[:title] = self.title if @document_feed_entry
253
+ return "\#<%p %s>" % [self.class, fields.map(){ |k, v| "%s=%p" % [k, v] }.join(", ")]
254
+ end
255
+
256
+ end
257
+
258
+ end