rscribd 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,96 @@
1
+ module Scribd
2
+
3
+ # A collection on Scribd is a list of {Document Documents} created and
4
+ # maintained by a user.
5
+ #
6
+ # @Collection@ instances and retrieved via the {User#collections} method.
7
+ # @Collections@ cannot be modified using the gem or the API. You can, however,
8
+ # {#add} and {#remove} {Document Documents} from a @Collection@.
9
+
10
+ class Collection < Resource
11
+ # @private
12
+ DOCUMENT_MISSING_ERROR_CODE = 652
13
+ # @private
14
+ DOCUMENT_EXISTS_ERROR_CODE = 653
15
+
16
+ # @return [Scribd::User] The user that created this collection.
17
+ attr_reader :owner
18
+
19
+ # @private
20
+ def initialize(options={})
21
+ super
22
+ if options[:xml] then
23
+ load_attributes(options[:xml])
24
+ @owner = options[:owner]
25
+ @saved = true
26
+ @created = true
27
+ else
28
+ raise "Collections cannot be created, only retrieved."
29
+ end
30
+ end
31
+
32
+ # Adds a {Document} to this collection.
33
+ #
34
+ # @param [Scribd::Document] document The document to add.
35
+ # @param [true, false] ignore_if_exists If @false@, raises an exception if
36
+ # the document is already in the collection.
37
+ # @return [Scribd::Document] The @document@ parameter.
38
+ # @raise [ArgumentError] If an invalid value for @document@ is provided.
39
+ # @raise [Scribd::ResponseError] If @ignore_if_exists@ is set to @false@ and
40
+ # the document already exists in the collection. See the online API
41
+ # documentation for more information.
42
+
43
+ def add(document, ignore_if_exists=true)
44
+ raise ArgumentError, "You can only add Scribd::Documents to collections" unless document.kind_of?(Scribd::Document)
45
+ begin
46
+ API.instance.send_request 'docs.addToCollection', :collection_id => collection_id, :doc_id => document.id, :session_key => owner.session_key
47
+ rescue ResponseError => err
48
+ raise unless ignore_if_exists
49
+ raise if err.code.to_i != DOCUMENT_EXISTS_ERROR_CODE
50
+ end
51
+ return document
52
+ end
53
+
54
+ # @see #add
55
+
56
+ def <<(document)
57
+ add document
58
+ end
59
+
60
+ # Removes a {Document} from this collection.
61
+ #
62
+ # @param [Scribd::Document] document The document to remove.
63
+ # @param [true, false] ignore_if_missing If @false@, raises an exception if
64
+ # the document is not in the collection.
65
+ # @return [Scribd::Document] The @document@ parameter.
66
+ # @raise [ArgumentError] If an invalid value for @document@ is provided.
67
+ # @raise [Scribd::ResponseError] If @ignore_if_missing@ is set to @false@
68
+ # and the document does not exist in the collection. See the online API
69
+ # documentation for more information.
70
+
71
+ def remove(document, ignore_if_missing=true)
72
+ raise ArgumentError, "You can only remove Scribd::Documents from collections" unless document.kind_of?(Scribd::Document)
73
+ begin
74
+ API.instance.send_request 'docs.removeFromCollection', :collection_id => collection_id, :doc_id => document.id, :session_key => owner.session_key
75
+ rescue ResponseError => err
76
+ raise unless ignore_if_missing
77
+ raise if err.code.to_i != DOCUMENT_MISSING_ERROR_CODE
78
+ end
79
+ return document
80
+ end
81
+
82
+ alias_method :delete, :remove
83
+
84
+ # @return [String] The @collection_id@ attribute.
85
+
86
+ def id
87
+ collection_id
88
+ end
89
+
90
+ # @return [String] The @collection_name@ attribute.
91
+
92
+ def name
93
+ collection_name
94
+ end
95
+ end
96
+ end
@@ -1,56 +1,70 @@
1
1
  require 'uri'
2
+ require 'open-uri'
2
3
 
3
4
  module Scribd
4
5
 
5
6
  # A document as shown on the Scribd website. API programs can upload documents
6
- # from files or URL's, tag them, and change their settings. An API program can
7
+ # from files or URLs, tag them, and change their settings. An API program can
7
8
  # access any document, but it can only modify documents owned by the logged-in
8
9
  # user.
9
10
  #
10
- # To upload a new document to Scribd, you must create a new Document instance,
11
- # set the +file+ attribute to the file's path, and then save the document:
11
+ # To upload a new document to Scribd, you must create a new {Document}
12
+ # instance, set the @file@ attribute to the file's path, and then save the
13
+ # document:
12
14
  #
13
- # doc = Scribd::Document.new
14
- # doc.file = '/path/or/URL/of/file.txt'
15
- # doc.save
15
+ # <pre><code>
16
+ # doc = Scribd::Document.new
17
+ # doc.file = '/path/or/URL/of/file.txt'
18
+ # doc.save
19
+ # </code></pre>
16
20
  #
17
21
  # You can do this more simply with one line of code:
18
22
  #
19
- # doc = Scribd::Document.create :file => '/path/or/URL/of/file.txt'
23
+ # <pre><code>doc = Scribd::Document.create :file => '/path/or/URL/of/file.txt'</code></pre>
20
24
  #
21
25
  # If you are uploading a file that does not have an extension (like ".txt"),
22
- # you need to specify the +type+ attribute as well:
26
+ # you need to specify the @type@ attribute as well:
23
27
  #
24
- # doc = Scribd::Document.upload :file => 'CHANGELOG', :type => 'txt'
28
+ # <pre><code>doc = Scribd::Document.upload :file => 'CHANGELOG', :type => 'txt'</code></pre>
25
29
  #
26
30
  # Aside from these two attributes, you can set other attributes that affect
27
31
  # how the file is displayed on Scribd. See the API documentation online for a
28
- # list of attributes, at
29
- # http://www.scribd.com/developers/api?method_name=docs.search (consult the
30
- # "Result explanation" section).
32
+ # list of attributes.
31
33
  #
32
34
  # These attributes can be accessed or changed directly
33
- # (<tt>doc.title = 'Title'</tt>). You must save a document after changing its
35
+ # (@doc.title = 'Title'@). You must save a document after changing its
34
36
  # attributes in order for those changes to take effect. Not all attributes can
35
37
  # be modified; see the API documentation online for details.
36
38
  #
37
- # A document can be associated with a Scribd::User via the +owner+ attribute.
38
- # This is not always the case, however. Documents retrieved from the find
39
- # method will not be associated with their owners.
39
+ # A document can be associated with a Scribd::User via the @owner@ attribute.
40
+ # This is not always the case, however. {Document Documents} retrieved from
41
+ # the {.find} method will not be associated with their owners.
40
42
  #
41
- # The +owner+ attribute is read/write, however, changes made to it only apply
43
+ # The @owner@ attribute is read/write, however, changes made to it only apply
42
44
  # _before_ the document is saved. Once it is saved, the owner is set in stone
43
45
  # and cannot be modified:
44
46
  #
45
- # doc = Scribd::Document.new :file => 'test.txt'
46
- # doc.user = Scribd::User.signup(:username => 'newuser', :password => 'newpass', :email => 'your@email.com')
47
- # doc.save #=> Uploads the document as "newuser", regardless of who the Scribd API user is
48
- # doc.user = Scribd::API.instance.user #=> raises NotImplementedError
47
+ # <pre><code>
48
+ # doc = Scribd::Document.new :file => 'test.txt'
49
+ # doc.user = Scribd::User.signup(:username => 'newuser', :password => 'newpass', :email => 'your@email.com')
50
+ # doc.save #=> Uploads the document as "newuser", regardless of who the Scribd API user is
51
+ # doc.user = Scribd::API.instance.user #=> raises NotImplementedError
52
+ #</code></pre>
53
+ #
54
+ # h2. Special attributes
55
+ #
56
+ # Normally any attributes other than @file@ and @type@ are sent to and dealt
57
+ # by the API; however, there are a few attributes you can set on an instance
58
+ # that have special meaning:
59
+ #
60
+ # | @thumbnail@ | Set this to the path to, a @File@ object for, or the URL string for an image file you want to act as the document's thumbnail. Note that for URLs, the thumbnail will be downloaded to memory before being transmitted to the Scribd API server. |
49
61
 
50
62
  class Document < Resource
51
63
 
52
64
  # Creates a new, unsaved document with the given attributes. The document
53
65
  # must be saved before it will appear on the website.
66
+ #
67
+ # @param [Hash] options The document's attributes.
54
68
 
55
69
  def initialize(options={})
56
70
  super
@@ -70,23 +84,24 @@ module Scribd
70
84
  # changed attributes and saves it. Returns true if the save completed
71
85
  # successfully. Throws an exception if save fails.
72
86
  #
73
- # For first-time saves, you must have specified a +file+ attribute. This can
87
+ # For first-time saves, you must have specified a @file@ attribute. This can
74
88
  # either be a local path to a file, or an HTTP, HTTPS, or FTP URL. In either
75
89
  # case, the file at that location will be uploaded to create the document.
76
90
  #
77
- # If you create a document, specify the +file+ attribute again, and then
91
+ # If you create a document, specify the @file@ attribute again, and then
78
92
  # save it, Scribd replaces the existing document with the file given, while
79
93
  # keeping all other properties (title, description, etc.) the same, in a
80
94
  # process called _revisioning_.
81
95
  #
82
- # This method can throw a +Timeout+ exception if the connection is slow or
83
- # inaccessible. A Scribd::ResponseError will be thrown if a remote problem
84
- # occurs. A Scribd::PrivilegeError will be thrown if you try to upload a new
85
- # revision for a document with no associated user (i.e., one retrieved from
86
- # the find method).
87
- #
88
- # You must specify the +type+ attribute alongside the +file+ attribute if
96
+ # You must specify the @type@ attribute alongside the @file@ attribute if
89
97
  # the file's type cannot be determined from its name.
98
+ #
99
+ # @raise [Timeout] If the connection is slow or inaccessible.
100
+ # @raise [Scribd::ResponseError] If a remote problem occurs.
101
+ # @raise [Scribd::PrivilegeError] If you try to upload a new revision for a
102
+ # document with no associated user (i.e., one retrieved from the {.find}
103
+ # method).
104
+ # @return [true, false] Whether or not the upload was successful.
90
105
 
91
106
  def save
92
107
  if not created? and @attributes[:file].nil? then
@@ -100,6 +115,7 @@ module Scribd
100
115
  # Make a request form
101
116
  response = nil
102
117
  fields = @attributes.dup
118
+ fields.delete :thumbnail
103
119
  fields[:session_key] = fields.delete(:owner).session_key if fields[:owner]
104
120
  if file = @attributes[:file] then
105
121
  fields.delete :file
@@ -136,6 +152,24 @@ module Scribd
136
152
  load_attributes(xml)
137
153
  @created = true
138
154
  end
155
+
156
+ if thumb = fields.delete(:thumbnail) then
157
+ begin
158
+ uri = URI.parse(thumb)
159
+ rescue URI::InvalidURIError
160
+ uri = nil
161
+ end
162
+
163
+ file = nil
164
+ if uri.kind_of?(URI::HTTP) or uri.kind_of?(URI::HTTPS) or uri.kind_of?(URI::FTP) then
165
+ file = open(uri)
166
+ elsif uri.kind_of?(URI::Generic) or uri.nil? then
167
+ file = thumb.kind_of?(File) ? thumb : File.open(thumb, 'rb')
168
+ end
169
+
170
+ API.instance.send_request('docs.uploadThumb', :file => file, :doc_id => self.id)
171
+ file.close
172
+ end
139
173
 
140
174
  fields.delete :access if fields[:file] # when uploading a doc, don't send access twice
141
175
  fields.delete :file
@@ -160,6 +194,13 @@ module Scribd
160
194
 
161
195
  # Quickly updates an array of documents with the given attributes. The
162
196
  # documents can have different owners, but all of them must be modifiable.
197
+ #
198
+ # @param [Array<Scribd::Document>] docs An array of documents to update.
199
+ # @param [Hash] options The attributes to assign to all of those documents.
200
+ # @raise [ArgumentError] If an invalid value for @docs@ is given.
201
+ # @raise [ArgumentError] If an invalid value for @options@ is given.
202
+ # @raise [ArgumentError] If one or more documents cannot be modified because
203
+ # it has no owner (e.g., it was retrieved from a call to {.find}).
163
204
 
164
205
  def self.update_all(docs, options)
165
206
  raise ArgumentError, "docs must be an array" unless docs.kind_of? Array
@@ -170,48 +211,49 @@ module Scribd
170
211
  docs_by_user = docs.inject(Hash.new { |hash, key| hash[key] = Array.new }) { |hash, doc| hash[doc.owner] << doc; hash }
171
212
  docs_by_user.each { |user, doc_list| API.instance.send_request 'docs.changeSettings', options.merge(:doc_ids => doc_list.collect(&:id).join(','), :session_key => user.session_key) }
172
213
  end
173
-
174
- # === Finding by query
175
- #
176
- # This method is called with a scope and a hash of options to documents by
177
- # their content. You must at a minimum supply a +query+ option, with a
178
- # string that will become the full-text search query. For a list of other
179
- # supported options, please see the online API documentation at
180
- # http://www.scribd.com/developers/api?method_name=docs.search
181
- #
182
- # The scope can be any value given for the +scope+ parameter in the above
183
- # website, or <tt>:first</tt> to return the first result only (not an array
184
- # of results).
185
- #
186
- # The +num_results+ option has been aliased as +limit+, and the +num_start+
187
- # option has been aliased as +offset+.
214
+
215
+ # @overload find(options={})
216
+ # This method is called with a hash of options to documents by their
217
+ # content. You must at a minimum supply a @query@ option, with a string
218
+ # that will become the full-text search query. For a list of other
219
+ # supported options, please see the online API documentation.
188
220
  #
189
- # Documents returned from this method will have their +owner+ attributes set
190
- # to nil.
221
+ # Documents retrieved by this method have no {User} stored in their
222
+ # @owner@ attribute; in other words, they cannot be modified.
191
223
  #
192
- # Scribd::Document.find(:all, :query => 'cats and dogs', :limit => 10)
224
+ # @param [Hash] options Options for the search.
225
+ # @option options [String] :query The search query (required).
226
+ # @option options [Fixnum] :limit An alias for the @num_results@ option.
227
+ # @option options [Fixnum] :offset An alias for the @num_start@ option.
228
+ # @return [Array<Scribd::Document>] An array of documents found.
229
+ # @example
230
+ # Scribd::Document.find(:all, :query => 'cats and dogs', :limit => 10)
193
231
  #
194
- # === Finding by ID
232
+ # @overload find(id, options={})
233
+ # Passing in simply a numerical ID loads the document with that ID. You
234
+ # can pass additional options as defined in the API documentation.
195
235
  #
196
- # Passing in simply a numerical ID loads the document with that ID. You can
197
- # pass additional options as defined at
198
- # httphttp://www.scribd.com/developers/api?method_name=docs.getSettings
236
+ # For now only documents that belong to the current user can be accessed
237
+ # in this manner.
199
238
  #
200
- # Scribd::Document.find(108196)
239
+ # @param [Fixnum] id The Scribd ID of the document to locate.
240
+ # @param [Hash] options Options to pass to the API find method.
241
+ # @return [Scribd::Document] The document found.
242
+ # @return [nil] If nothing was found.
243
+ # @example
244
+ # Scribd::Document.find(108196)
201
245
  #
202
- # For now only documents that belong to the current user can be accessed in
203
- # this manner.
246
+ # @raise [ArgumentError] If neither of the two correct argument forms is
247
+ # provided.
204
248
 
205
- def self.find(scope, options={})
206
- doc_id = scope.kind_of?(Integer) ? scope : nil
207
- raise ArgumentError, "You must specify a query or document ID" unless options[:query] or doc_id
249
+ def self.find(options={})
250
+ doc_id = options.kind_of?(Integer) ? options : nil
251
+ raise ArgumentError, "You must specify a query or document ID" unless doc_id or (options.kind_of?(Hash) and options[:query])
208
252
 
209
253
  if doc_id then
210
- options[:doc_id] = doc_id
211
- response = API.instance.send_request('docs.getSettings', options)
254
+ response = API.instance.send_request('docs.getSettings', :doc_id => doc_id)
212
255
  return Document.new(:xml => response.elements['/rsp'])
213
256
  else
214
- options[:scope] = scope == :first ? 'all' : scope.to_s
215
257
  options[:num_results] = options[:limit]
216
258
  options[:num_start] = options[:offset]
217
259
  response = API.instance.send_request('docs.search', options)
@@ -219,55 +261,54 @@ module Scribd
219
261
  response.elements['/rsp/result_set'].elements.each do |doc|
220
262
  documents << Document.new(:xml => doc)
221
263
  end
222
- return scope == :first ? documents.first : documents
264
+ return documents
223
265
  end
224
266
  end
225
267
 
226
- # === Featured docs
268
+ # Returns featured documents found in a given with given options.
227
269
  #
228
- # This method is called with a scope and a hash of options. For a list of
229
- # supported options, please see the online API documentation at
230
- # http://www.scribd.com/developers/api?method_name=docs.featured
270
+ # This method is called with a hash of options. For a list of supported
271
+ # options, please see the online API documentation.
231
272
  #
232
- # The scope can be either <tt>:first</tt> to return the first result only (not an array
233
- # of results) or <tt>:all</tt> to return an array. Include a +scope+ option
234
- # to control the parameter described in the API documentation.
273
+ # Documents returned from this method will have their @owner@ attributes set
274
+ # to @nil@ (i.e., they are read-only).
235
275
  #
236
- # Scribd::Document.featured(:all, :scope => 'hot', :limit => 10)
237
- #
238
- # Documents returned from this method will have their +owner+ attributes set
239
- # to nil.
276
+ # @param [Hash] options Options to pass to the API find method.
277
+ # @return [Array<Scribd::Document>] An array of documents found.
278
+ # @example
279
+ # Scribd::Document.featured(:scope => 'hot', :limit => 10)
240
280
 
241
- def self.featured(scope, options = {})
281
+ def self.featured(options = {})
242
282
  response = API.instance.send_request('docs.featured', options)
243
283
  documents = []
244
284
  response.elements['/rsp/result_set'].elements.each do |doc|
245
285
  documents << Document.new(:xml => doc)
246
286
  end
247
- scope == :first ? documents.first : documents
287
+ return documents
248
288
  end
249
289
 
250
- # === Browse docs
251
- #
252
- # This method is called with a scope and a hash of options. For a list of
253
- # supported options, please see the online API documentation at
254
- # http://www.scribd.com/developers/api?method_name=docs.browse
290
+ # Returns documents found by the Scribd browser with given options. The
291
+ # browser provides documents suitable for a browse page.
255
292
  #
256
- # The scope can be either <tt>:first</tt> to return the first result only (not an array
257
- # of results) or <tt>:all</tt> to return an array.
293
+ # This method is called with a hash of options. For a list of supported
294
+ # options, please see the online API documentation.
258
295
  #
259
- # Scribd::Document.browse(:all, :sort => 'views', :category_id => 1, :limit => 10)
296
+ # Documents returned from this method will have their @owner@ attributes set
297
+ # to @nil@ (i.e., they are read-only).
260
298
  #
261
- # Documents returned from this method will have their +owner+ attributes set
262
- # to nil.
299
+ # @param [Hash] options Options to pass to the API find method.
300
+ # @return [Array<Scribd::Document>] An array of documents found.
301
+ # @example
302
+ # Scribd::Document.browse(:sort => 'views', :limit => 10)
303
+ # @see Scribd::Category#browse
263
304
 
264
- def self.browse(scope, options = {})
305
+ def self.browse(options = {})
265
306
  response = API.instance.send_request('docs.browse', options)
266
307
  documents = []
267
308
  response.elements['/rsp/result_set'].elements.each do |doc|
268
309
  documents << Document.new(:xml => doc)
269
310
  end
270
- scope == :first ? documents.first : documents
311
+ return documents
271
312
  end
272
313
 
273
314
  class << self
@@ -279,20 +320,25 @@ module Scribd
279
320
  # non-blocking; you can query this method to determine whether the document
280
321
  # is ready to be displayed.
281
322
  #
282
- # The conversion status is returned as a string. For a full list of
283
- # conversion statuses, see the online API documentation at
284
- # http://www.scribd.com/developers/api?method_name=docs.getConversionStatus
323
+ # For a full list of conversion statuses, see the online API documentation.
285
324
  #
286
325
  # Unlike other properties of a document, this is retrieved from the server
287
326
  # every time it's queried.
327
+ #
328
+ # @return [String] The document's conversion status.
288
329
 
289
330
  def conversion_status
290
331
  response = API.instance.send_request('docs.getConversionStatus', :doc_id => self.id)
291
332
  response.elements['/rsp/conversion_status'].text
292
333
  end
293
334
 
294
- # Returns the document read count. This is only retrieved from the server the first time it's queried.
295
- # To force re-retrieval on subsequent calls include :force => true in the options parameter.
335
+ # Returns the document read count. This is only retrieved from the API
336
+ # server the first time it's queried unless @force@ is set to @true@.
337
+ #
338
+ # @param [Hash] options A hash of options.
339
+ # @option options [true, false] :force If true, clears the local cache for
340
+ # this value and re-retrieves it from the API server.
341
+ # @return [String] The number of reads this document has received.
296
342
 
297
343
  def reads(options = {})
298
344
  if @reads.nil? || options[:force]
@@ -302,32 +348,64 @@ module Scribd
302
348
  @reads
303
349
  end
304
350
 
305
- # Deletes a document. Returns true if successful.
351
+ # Deletes a document.
352
+ #
353
+ # @return [true, false] Whether or not the document was successfully
354
+ # deleted.
306
355
 
307
356
  def destroy
308
357
  response = API.instance.send_request('docs.delete', :doc_id => self.id)
309
358
  return response.elements['/rsp'].attributes['stat'] == 'ok'
310
359
  end
311
360
 
312
- # Returns the +doc_id+ attribute.
361
+ # Grants a user access to this document.
362
+ #
363
+ # @param [String] user_identifier The user identifier as used in your embed
364
+ # code.
365
+ # @see Scribd::Security.grant_access
313
366
 
314
- def id
315
- self.doc_id
367
+ def grant_access(user_identifier)
368
+ Scribd::Security.grant_access user_identifier, self
369
+ end
370
+
371
+ # Revokes access to this document from a user.
372
+ #
373
+ # @param [String] user_identifier The user identifier as used in your embed
374
+ # code.
375
+ # @see Scribd::Security.revoke_access
376
+
377
+ def revoke_access(user_identifier)
378
+ Scribd::Security.revoke_access user_identifier, self
379
+ end
380
+
381
+ # @return [Array<String>] A list of user identifiers that have access to
382
+ # this document.
383
+ # @see Scribd::Security.document_access_list
384
+
385
+ def access_list
386
+ Scribd::Security.document_access_list(self)
316
387
  end
317
388
 
318
- # Ensures that the +owner+ attribute cannot be set once the document is
319
- # saved.
389
+ # @return The @document_id@ attribute.
320
390
 
321
- def owner=(newuser) #:nodoc:
391
+ def id
392
+ self.doc_id
393
+ end
394
+
395
+ # @private
396
+ def owner=(newuser)
397
+ # don't allow them to set the owner if the document is saved
322
398
  saved? ? raise(NotImplementedError, "Cannot change a document's owner once the document has been saved") : super
323
399
  end
324
400
 
325
401
  # Retrieves a document's download URL. You can provide a format for the
326
- # download. Valid formats are listed at
327
- # http://www.scribd.com/developers/api?method_name=docs.getDownloadUrl
402
+ # download. Valid formats are listed in the online API documentation.
328
403
  #
329
404
  # If you do not provide a format, the link will be for the document's
330
405
  # original format.
406
+ #
407
+ # @param [String] format The download format.
408
+ # @return [String] The download URL.
331
409
 
332
410
  def download_url(format='original')
333
411
  @download_urls[format] ||= begin