rscribd 0.0.2 → 0.0.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.
- data/History.txt +3 -0
- data/Manifest.txt +14 -0
- data/README.txt +83 -0
- data/Rakefile +18 -0
- data/lib/api.rb +219 -0
- data/lib/doc.rb +233 -0
- data/lib/exceptions.rb +33 -0
- data/lib/multipart.rb +39 -0
- data/lib/resource.rb +158 -0
- data/lib/rscribd.rb +26 -0
- data/lib/user.rb +142 -0
- data/sample/01_upload.rb +47 -0
- data/sample/02_user.rb +44 -0
- data/sample/test.txt +1 -1
- metadata +73 -50
- data/lib/scribd/base.rb +0 -381
- data/lib/scribd/docs.rb +0 -246
- data/lib/scribd/search.rb +0 -85
- data/lib/scribd.rb +0 -3
- data/sample/01-docs_upload-text.rb +0 -30
- data/sample/02-docs_upload-binary.rb +0 -32
- data/sample/03-docs_uploadFromUrl.rb +0 -30
- data/sample/04-docs_changeSettings.rb +0 -53
- data/sample/05-docs_getConversionStatus.rb +0 -39
- data/sample/test.gif +0 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
= rscribd
|
2
|
+
|
3
|
+
* 1.0.3 (Jan 3, 2007)
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
This gem provides a simple and powerful library for the Scribd API, allowing you
|
8
|
+
to write Ruby applications or Ruby on Rails websites that upload, convert,
|
9
|
+
display, search, and control documents in many formats.
|
10
|
+
|
11
|
+
== FEATURES/PROBLEMS:
|
12
|
+
|
13
|
+
* Upload your documents to Scribd's servers and access them using the gem
|
14
|
+
* Upload local files or from remote web sites
|
15
|
+
* Search, tag, and organize documents
|
16
|
+
* Associate documents with your users' accounts
|
17
|
+
|
18
|
+
== SYNOPSIS:
|
19
|
+
|
20
|
+
This API allows you to use Scribd's Flash viewer on your website. You'll be able
|
21
|
+
to take advantage of Scribd's scalable document conversion system to convert
|
22
|
+
your documents into platform-independent formats. You can leverage Scribd's
|
23
|
+
storage system to store your documents in accessible manner. Scribd's ad system
|
24
|
+
will help you monetize your documents easily.
|
25
|
+
|
26
|
+
First, you'll need to get a Scribd API account. Visit
|
27
|
+
http://www.scribd.com/platform to apply for a platform account.
|
28
|
+
|
29
|
+
On the Platform site you will be given an API key and secret. The API object
|
30
|
+
will need these to authenticate you:
|
31
|
+
|
32
|
+
require 'rscribd'
|
33
|
+
Scribd::API.instance.key = 'your API key'
|
34
|
+
Scribd::API.instance.secret = 'your API secret'
|
35
|
+
|
36
|
+
Next, log into the Scribd website:
|
37
|
+
|
38
|
+
Scribd::User.login 'username', 'password'
|
39
|
+
|
40
|
+
You are now ready to use Scribd to manage your documents. For instance, to
|
41
|
+
upload a document:
|
42
|
+
|
43
|
+
doc = Scribd::Document.upload(:file => 'your-file.pdf')
|
44
|
+
|
45
|
+
For more information, please see the documentation for the Scribd::API,
|
46
|
+
Scribd::User, and Scribd::Document classes. (You can also check out the docs for
|
47
|
+
the other classes for a more in-depth look at this gem's features).
|
48
|
+
|
49
|
+
== REQUIREMENTS:
|
50
|
+
|
51
|
+
* A Scribd API account
|
52
|
+
* mime-types gem
|
53
|
+
|
54
|
+
== INSTALL:
|
55
|
+
|
56
|
+
* The client library is a RubyGem called *rscribd*. To install, type:
|
57
|
+
|
58
|
+
sudo gem install rscribd
|
59
|
+
|
60
|
+
== LICENSE:
|
61
|
+
|
62
|
+
(The MIT License)
|
63
|
+
|
64
|
+
Copyright (c) 2007-2008 The Scribd Team
|
65
|
+
|
66
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
67
|
+
a copy of this software and associated documentation files (the
|
68
|
+
'Software'), to deal in the Software without restriction, including
|
69
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
70
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
71
|
+
permit persons to whom the Software is furnished to do so, subject to
|
72
|
+
the following conditions:
|
73
|
+
|
74
|
+
The above copyright notice and this permission notice shall be
|
75
|
+
included in all copies or substantial portions of the Software.
|
76
|
+
|
77
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
78
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
79
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
80
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
81
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
82
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
83
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
|
6
|
+
Hoe.new('rscribd', '0.0.3') do |p|
|
7
|
+
p.rubyforge_name = 'rscribd'
|
8
|
+
p.author = 'Jared Friedman'
|
9
|
+
p.email = 'api@scribd.com'
|
10
|
+
p.summary = 'Ruby client library for the Scribd API'
|
11
|
+
p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
|
12
|
+
p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
|
13
|
+
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
14
|
+
p.extra_deps << [ 'mime-types', '>0.0.0' ]
|
15
|
+
p.remote_rdoc_dir = ''
|
16
|
+
end
|
17
|
+
|
18
|
+
# vim: syntax=Ruby
|
data/lib/api.rb
ADDED
@@ -0,0 +1,219 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'md5'
|
3
|
+
require 'rexml/document'
|
4
|
+
|
5
|
+
module Scribd
|
6
|
+
|
7
|
+
# This class acts as the top-level interface between Scribd and your
|
8
|
+
# application. Before you can begin using the Scribd API, you must specify for
|
9
|
+
# this object your API key and API secret. They are available on your
|
10
|
+
# Platform home page.
|
11
|
+
#
|
12
|
+
# This class is a singleton. Its only instance is accessed using the
|
13
|
+
# +instance+ class method.
|
14
|
+
#
|
15
|
+
# To begin, first specify your API key and secret:
|
16
|
+
#
|
17
|
+
# Scribd::API.instance.key = 'your API key here'
|
18
|
+
# Scribd::API.instance.secret = 'your API secret here'
|
19
|
+
#
|
20
|
+
# (If you set the +SCRIBD_API_KEY+ and +SCRIBD_API_SECRET+ Ruby environment
|
21
|
+
# variables before loading the gem, these values will be set automatically for
|
22
|
+
# you.)
|
23
|
+
#
|
24
|
+
# Next, you should log in to Scribd, or create a new account through the gem.
|
25
|
+
#
|
26
|
+
# user = Scribd::User.login 'login', 'password'
|
27
|
+
#
|
28
|
+
# You are now free to use the Scribd::User or Scribd::Document classes to work
|
29
|
+
# with Scribd documents or your user account.
|
30
|
+
#
|
31
|
+
# If you need the Scribd::User instance for the currently logged in user at a
|
32
|
+
# later point in time, you can retrieve it using the +user+ attribute:
|
33
|
+
#
|
34
|
+
# user = Scribd::API.instance.user
|
35
|
+
#
|
36
|
+
# In addition, you can save and restore sessions by simply storing this user
|
37
|
+
# instance and assigning it to the API at a later time. For example, to
|
38
|
+
# restore the session retrieved in the previous example:
|
39
|
+
#
|
40
|
+
# Scribd::API.instance.user = user
|
41
|
+
#
|
42
|
+
# In addition to working with Scribd users, you can also work with your own
|
43
|
+
# website's user accounts. To do this, set the Scribd API user to a string
|
44
|
+
# containing a unique identifier for that user (perhaps a login name or a user
|
45
|
+
# ID):
|
46
|
+
#
|
47
|
+
# Scribd::API.instance.user = my_user_object.mangled_user_id
|
48
|
+
#
|
49
|
+
# A "phantom" Scribd user will be set up with that ID, so you any documents
|
50
|
+
# you upload will be associated with that account.
|
51
|
+
#
|
52
|
+
# For more hints on what you can do with the Scribd API, please see the
|
53
|
+
# Scribd::Document class.
|
54
|
+
|
55
|
+
class API
|
56
|
+
include Singleton
|
57
|
+
|
58
|
+
HOST = 'api.scribd.com' #:nodoc:
|
59
|
+
PORT = 80 #:nodoc:
|
60
|
+
REQUEST_PATH = '/api' #:nodoc:
|
61
|
+
TRIES = 3 #:nodoc:
|
62
|
+
|
63
|
+
attr_accessor :key # The API key you were given when you created a Platform account.
|
64
|
+
attr_accessor :secret # The API secret used to validate your key (also provided with your account).
|
65
|
+
attr_accessor :user # The currently logged in user.
|
66
|
+
attr_accessor :asynchronous # If true, requests are processed asynchronously. If false, requests are blocking.
|
67
|
+
attr_accessor :debug # If true, extended debugging information is printed
|
68
|
+
|
69
|
+
def initialize #:nodoc:
|
70
|
+
@asychronous = false
|
71
|
+
@key = ENV['SCRIBD_API_KEY']
|
72
|
+
@secret = ENV['SCRIBD_API_SECRET']
|
73
|
+
end
|
74
|
+
|
75
|
+
def send_request(method, fields={}) #:nodoc:
|
76
|
+
raise NotReadyError unless @key and @secret
|
77
|
+
# See if method is given
|
78
|
+
raise ArgumentError, "Method should be given" if method.nil? || method.empty?
|
79
|
+
|
80
|
+
debug("** Remote method call: #{method}; fields: #{fields.inspect}")
|
81
|
+
|
82
|
+
# replace pesky hashes to prevent accidents
|
83
|
+
fields = fields.stringify_keys
|
84
|
+
|
85
|
+
# Complete fields with the method name
|
86
|
+
fields['method'] = method
|
87
|
+
fields['api_key'] = @key
|
88
|
+
|
89
|
+
if fields['session_key'].nil? and fields['my_user_id'].nil? then
|
90
|
+
if @user.kind_of? Scribd::User then
|
91
|
+
fields['session_key'] = @user.session_key
|
92
|
+
elsif @user.kind_of? String then
|
93
|
+
fields['my_user_id'] = @user
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
nil_fields = fields.keys.select { |key| fields[key] == nil }
|
98
|
+
nil_fields.each { |key| fields.delete key }
|
99
|
+
|
100
|
+
# Don't include file in parameters to calculate signature
|
101
|
+
sign_fields = {}
|
102
|
+
fields.map {|k,v| sign_fields[k] = v if k != 'file' }
|
103
|
+
#fields.delete_if {|k,v| k=='file'}
|
104
|
+
|
105
|
+
fields['api_sig'] = sign(sign_fields)
|
106
|
+
debug("** POST parameters: #{fields.inspect}")
|
107
|
+
|
108
|
+
# Create the connection
|
109
|
+
http = Net::HTTP.new(HOST, PORT)
|
110
|
+
# TODO configure timeouts through the properties
|
111
|
+
|
112
|
+
# API methods can be SLOW. Make sure this is set to something big to prevent spurious timeouts
|
113
|
+
http.read_timeout = 15*60
|
114
|
+
|
115
|
+
request = Net::HTTP::Post.new(REQUEST_PATH)
|
116
|
+
request.multipart_params = fields
|
117
|
+
|
118
|
+
tries = TRIES
|
119
|
+
begin
|
120
|
+
tries -= 1
|
121
|
+
res = http.request(request)
|
122
|
+
rescue Exception
|
123
|
+
$stderr.puts "Request encountered error, will retry #{tries} more."
|
124
|
+
if tries > 0
|
125
|
+
# Retrying several times with sleeps is recommended.
|
126
|
+
# This will prevent temporary downtimes at Scribd from breaking API applications
|
127
|
+
sleep(20)
|
128
|
+
retry
|
129
|
+
end
|
130
|
+
raise $!
|
131
|
+
end
|
132
|
+
|
133
|
+
debug "** Response:"
|
134
|
+
debug(res.body)
|
135
|
+
debug "** End response"
|
136
|
+
|
137
|
+
# Convert response into XML
|
138
|
+
xml = REXML::Document.new(res.body)
|
139
|
+
raise MalformedResponseError, "The response received from the remote host could not be interpreted" unless xml.elements['/rsp']
|
140
|
+
|
141
|
+
# See if there was an error and raise an exception
|
142
|
+
if xml.elements['/rsp'].attributes['stat'] == 'fail'
|
143
|
+
# Load default code and error
|
144
|
+
code, message = -1, "Unidentified error:\n#{res.body}"
|
145
|
+
|
146
|
+
# Get actual error code and message
|
147
|
+
err = xml.elements['/rsp/error']
|
148
|
+
code, message = err.attributes['code'], err.attributes['message'] if err
|
149
|
+
|
150
|
+
# Add more data
|
151
|
+
message = "Method: #{method} Response: code=#{code} message=#{message}"
|
152
|
+
|
153
|
+
raise ResponseError.new(code), message
|
154
|
+
end
|
155
|
+
|
156
|
+
return xml
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
# FIXME: Since we don't need XMLRPC, the exception could be different
|
162
|
+
# TODO: It would probably be better if we wrapped the fault
|
163
|
+
# in something more meaningful. At the very least, a broad
|
164
|
+
# division of errors, such as retryable and fatal.
|
165
|
+
def error(el) #:nodoc:
|
166
|
+
att = el.attributes
|
167
|
+
fe = XMLRPC::FaultException.new(att['code'].to_i, att['msg'])
|
168
|
+
$stderr.puts "ERR: #{fe.faultString} (#{fe.faultCode})"
|
169
|
+
raise fe
|
170
|
+
end
|
171
|
+
|
172
|
+
# Checks if a string parameter is given and not empty.
|
173
|
+
#
|
174
|
+
# Parameters:
|
175
|
+
# name - parameter name for an error message.
|
176
|
+
# value - value.
|
177
|
+
#
|
178
|
+
# Raises:
|
179
|
+
# ArgumentError if the value is nil, or empty.
|
180
|
+
#
|
181
|
+
def check_not_empty(name, value) #:nodoc:
|
182
|
+
check_given(name, value)
|
183
|
+
raise ArgumentError, "#{name} must not be empty" if value.to_s.empty?
|
184
|
+
end
|
185
|
+
|
186
|
+
# Checks if the value is given.
|
187
|
+
#
|
188
|
+
# Parameters:
|
189
|
+
# name - parameter name for an error message.
|
190
|
+
# value - value.
|
191
|
+
#
|
192
|
+
# Raises:
|
193
|
+
# ArgumentError if the value is nil.
|
194
|
+
#
|
195
|
+
def check_given(name, value) #:nodoc:
|
196
|
+
raise ArgumentError, "#{name} must be given" if value.nil?
|
197
|
+
end
|
198
|
+
|
199
|
+
# Sign the arguments hash with our very own signature.
|
200
|
+
#
|
201
|
+
# Parameters:
|
202
|
+
# args - method arguments to be sent to the server API
|
203
|
+
#
|
204
|
+
# Returns:
|
205
|
+
# signature
|
206
|
+
#
|
207
|
+
def sign(args)
|
208
|
+
return MD5.md5(@secret + args.sort.flatten.join).to_s
|
209
|
+
end
|
210
|
+
|
211
|
+
# Outputs whatever is given into the $stderr if debugging is enabled.
|
212
|
+
#
|
213
|
+
# Parameters:
|
214
|
+
# args - content to output
|
215
|
+
def debug(*args)
|
216
|
+
$stderr.puts(sprintf(*args)) if @debug
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
data/lib/doc.rb
ADDED
@@ -0,0 +1,233 @@
|
|
1
|
+
require "uri"
|
2
|
+
|
3
|
+
module Scribd
|
4
|
+
|
5
|
+
# 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
|
+
# access any document, but it can only modify documents owned by the logged-in
|
8
|
+
# user.
|
9
|
+
#
|
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:
|
12
|
+
#
|
13
|
+
# doc = Scribd::Document.new
|
14
|
+
# doc.file = '/path/or/URL/of/file.txt'
|
15
|
+
# doc.save
|
16
|
+
#
|
17
|
+
# You can do this more simply with one line of code:
|
18
|
+
#
|
19
|
+
# doc = Scribd::Document.create :file => '/path/or/URL/of/file.txt'
|
20
|
+
#
|
21
|
+
# If you are uploading a file that does not have an extension (like ".txt"),
|
22
|
+
# you need to specify the +type+ attribute as well:
|
23
|
+
#
|
24
|
+
# doc = Scribd::Document.upload :file => 'CHANGELOG', :type => 'txt'
|
25
|
+
#
|
26
|
+
# Aside from these two attributes, you can set other attributes that affect
|
27
|
+
# how the file is displayed on Scribd. See the API documentation online for a
|
28
|
+
# list of attributes, at http://www.scribd.com/platform/documentation?method_name=docs.search&subtab=api
|
29
|
+
# (consult the "Result explanation" section).
|
30
|
+
#
|
31
|
+
# These attributes can be accessed or changed directly
|
32
|
+
# (<tt>doc.title = 'Title'</tt>). You must save a document after changing its
|
33
|
+
# attributes in order for those changes to take effect. Not all attributes can
|
34
|
+
# be modified; see the API documentation online for details.
|
35
|
+
#
|
36
|
+
# A document can be associated with a Scribd::User via the +owner+ attribute.
|
37
|
+
# This is not always the case, however. Documents retrieved from the find
|
38
|
+
# method will not be associated with their owners.
|
39
|
+
#
|
40
|
+
# The +owner+ attribute is read/write, however, changes made to it only apply
|
41
|
+
# _before_ the document is saved. Once it is saved, the owner is set in stone
|
42
|
+
# and cannot be modified:
|
43
|
+
#
|
44
|
+
# doc = Scribd::Document.new :file => 'test.txt'
|
45
|
+
# doc.user = Scribd::User.signup(:username => 'newuser', :password => 'newpass', :email => 'your@email.com')
|
46
|
+
# doc.save #=> Uploads the document as "newuser", regardless of who the Scribd API user is
|
47
|
+
# doc.user = Scribd::API.instance.user #=> raises NotImplementedError
|
48
|
+
|
49
|
+
class Document < Resource
|
50
|
+
|
51
|
+
# Creates a new, unsaved document with the given attributes. The document
|
52
|
+
# must be saved before it will appear on the website.
|
53
|
+
|
54
|
+
def initialize(options={})
|
55
|
+
super
|
56
|
+
if options[:xml] then
|
57
|
+
load_attributes(options[:xml])
|
58
|
+
@attributes[:owner] = options[:owner]
|
59
|
+
@saved = true
|
60
|
+
@created = true
|
61
|
+
else
|
62
|
+
@attributes = options
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# For document objects that have not been saved for the first time, uploads
|
67
|
+
# the document, sets its attributes, and saves it. Otherwise, updates any
|
68
|
+
# changed attributes and saves it. Returns true if the save completed
|
69
|
+
# successfully. Throws an exception if save fails.
|
70
|
+
#
|
71
|
+
# For first-time saves, you must have specified a +file+ attribute. This can
|
72
|
+
# either be a local path to a file, or an HTTP, HTTPS, or FTP URL. In either
|
73
|
+
# case, the file at that location will be uploaded to create the document.
|
74
|
+
#
|
75
|
+
# If you create a document, specify the +file+ attribute again, and then
|
76
|
+
# save it, Scribd replaces the existing document with the file given, while
|
77
|
+
# keeping all other properties (title, description, etc.) the same, in a
|
78
|
+
# process called _revisioning_.
|
79
|
+
#
|
80
|
+
# This method can throw a +Timeout+ exception if the connection is slow or
|
81
|
+
# inaccessible. A ResponseError will be thrown if a remote problem occurs.
|
82
|
+
# A PrivilegeError will be thrown if you try to upload a new revision for a
|
83
|
+
# document with no associated user (i.e., one retrieved from the find
|
84
|
+
# method).
|
85
|
+
#
|
86
|
+
# You must specify the +type+ attribute alongside the +file+ attribute if
|
87
|
+
# the file's type cannot be determined from its name.
|
88
|
+
|
89
|
+
def save
|
90
|
+
if not created? and @attributes[:file].nil? then
|
91
|
+
raise "'file' attribute must be specified for new documents"
|
92
|
+
return false
|
93
|
+
end
|
94
|
+
|
95
|
+
if created? and @attributes[:file] and (@attributes[:owner].nil? or @attributes[:owner].session_key.nil?) then
|
96
|
+
raise PrivilegeError, "The current API user is not the owner of this document"
|
97
|
+
end
|
98
|
+
|
99
|
+
# Make a request form
|
100
|
+
fields = Hash.new
|
101
|
+
if @attributes[:file] then
|
102
|
+
ext = @attributes[:file].split('.').last unless @attributes[:file].index('.').nil?
|
103
|
+
fields[:doc_type] = @attributes[:type]
|
104
|
+
fields[:doc_type] ||= ext
|
105
|
+
fields[:rev_id] = @attributes[:doc_id]
|
106
|
+
end
|
107
|
+
fields[:session_key] = @attributes[:owner].session_key if @attributes[:owner]
|
108
|
+
response = nil
|
109
|
+
|
110
|
+
if @attributes[:file] then
|
111
|
+
uri = nil
|
112
|
+
begin
|
113
|
+
uri = URI.parse @attributes[:file]
|
114
|
+
rescue URI::InvalidURIError
|
115
|
+
uri = nil # Some valid file paths are not valid URI's (but some are)
|
116
|
+
end
|
117
|
+
if uri.kind_of? URI::HTTP or uri.kind_of? URI::HTTPS or uri.kind_of? URI::FTP then
|
118
|
+
fields[:url] = @attributes[:file]
|
119
|
+
response = API.instance.send_request 'docs.uploadFromUrl', fields
|
120
|
+
elsif uri.kind_of? URI::Generic or uri.nil? then
|
121
|
+
File.open(@attributes[:file]) do |file|
|
122
|
+
fields[:file] = file
|
123
|
+
response = API.instance.send_request 'docs.upload', fields
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
fields = @attributes.dup # fields is what we send to the server
|
129
|
+
|
130
|
+
if response then
|
131
|
+
# Extract our response
|
132
|
+
xml = response.get_elements('/rsp')[0]
|
133
|
+
load_attributes(xml)
|
134
|
+
@created = true
|
135
|
+
end
|
136
|
+
|
137
|
+
fields.delete :file
|
138
|
+
fields.delete :type
|
139
|
+
|
140
|
+
changed_attributes = fields.dup # changed_attributes is what we will stick into @attributes once we update remotely
|
141
|
+
|
142
|
+
fields[:session_key] = fields[:owner].session_key if fields[:owner]
|
143
|
+
changed_attributes[:owner] ||= API.instance.user
|
144
|
+
fields[:doc_ids] = self.id
|
145
|
+
|
146
|
+
API.instance.send_request('docs.changeSettings', fields)
|
147
|
+
|
148
|
+
@attributes.update(changed_attributes)
|
149
|
+
|
150
|
+
@saved = true
|
151
|
+
return true
|
152
|
+
end
|
153
|
+
|
154
|
+
# Quickly updates an array of documents with the given attributes. The
|
155
|
+
# documents can have different owners, but all of them must be modifiable.
|
156
|
+
|
157
|
+
def self.update_all(docs, options)
|
158
|
+
raise ArgumentError, "docs must be an array" unless docs.kind_of? Array
|
159
|
+
raise ArgumentError, "docs must consist of Scribd::Document objects" unless docs.all? { |doc| doc.kind_of? Document }
|
160
|
+
raise ArgumentError, "You can't modify one or more documents" if docs.any? { |doc| doc.owner.nil? }
|
161
|
+
raise ArgumentError, "options must be a hash" unless options.kind_of? Hash
|
162
|
+
|
163
|
+
docs_by_user = docs.inject(Hash.new { |hash, key| hash[key] = Array.new }) { |hash, doc| hash[doc.owner] << doc; hash }
|
164
|
+
docs_by_user.each { |user, doc_list| API.instance.send_request 'docs.changeSettings', options.merge(:doc_ids => doc_list.collect { |doc| doc.id }.join(','), :session_key => user.session_key) }
|
165
|
+
end
|
166
|
+
|
167
|
+
# Searches for documents matching a query. This method is called with a hash
|
168
|
+
# of options to documents by their content. You must at a minimum supply a
|
169
|
+
# +query+ option, with a string that will become the full-text search query.
|
170
|
+
# For a list of other supported options, please see the online API
|
171
|
+
# documentation at http://www.scribd.com/platform/documentation?method_name=docs.search&subtab=api
|
172
|
+
#
|
173
|
+
# Documents returned from this method will have their +owner+ attributes set
|
174
|
+
# to nil.
|
175
|
+
|
176
|
+
def self.find(options)
|
177
|
+
raise ArgumentError, "The find method takes a hash of options" unless options.kind_of? Hash
|
178
|
+
raise ArgumentError, "You must specify the query option" unless options[:query]
|
179
|
+
|
180
|
+
options[:scope] ||= 'all'
|
181
|
+
|
182
|
+
response = API.instance.send_request('docs.search', options)
|
183
|
+
documents = []
|
184
|
+
response.elements['/rsp/result_set'].elements.each do |doc|
|
185
|
+
documents << Document.new(:xml => doc)
|
186
|
+
end
|
187
|
+
return documents
|
188
|
+
end
|
189
|
+
|
190
|
+
# Synonym for create.
|
191
|
+
|
192
|
+
def self.upload(options={})
|
193
|
+
create options
|
194
|
+
end
|
195
|
+
|
196
|
+
# Returns the conversion status of this document. When a document is
|
197
|
+
# uploaded it must be converted before it can be used. The conversion is
|
198
|
+
# non-blocking; you can query this method to determine whether the document
|
199
|
+
# is ready to be displayed.
|
200
|
+
#
|
201
|
+
# The conversion status is returned as a string. For a full list of
|
202
|
+
# conversion statuses, see the online API documentation at
|
203
|
+
# http://www.scribd.com/platform/documentation?method_name=docs.getConversionStatus&subtab=api
|
204
|
+
#
|
205
|
+
# Unlike other properties of a document, this is retrieved from the server
|
206
|
+
# every time it's queried.
|
207
|
+
|
208
|
+
def conversion_status
|
209
|
+
response = API.instance.send_request('docs.getConversionStatus', :doc_id => self.id)
|
210
|
+
response.elements['/rsp/conversion_status'].text
|
211
|
+
end
|
212
|
+
|
213
|
+
# Deletes a document. Returns true if successful.
|
214
|
+
|
215
|
+
def destroy
|
216
|
+
response = API.instance.send_request('docs.delete', :doc_id => self.id)
|
217
|
+
return response.elements['/rsp'].attributes['stat'] == 'ok'
|
218
|
+
end
|
219
|
+
|
220
|
+
# Returns the +doc_id+ attribute.
|
221
|
+
|
222
|
+
def id
|
223
|
+
self.doc_id
|
224
|
+
end
|
225
|
+
|
226
|
+
# Ensures that the +owner+ attribute cannot be set once the document is
|
227
|
+
# saved.
|
228
|
+
|
229
|
+
def owner=(newuser) #:nodoc:
|
230
|
+
saved? ? raise(NotImplementedError, "Cannot change a document's owner once the document has been saved") : super
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
data/lib/exceptions.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
module Scribd
|
2
|
+
|
3
|
+
# Raised when API calls are made before a key and secret are specified.
|
4
|
+
|
5
|
+
class NotReadyError < StandardError; end
|
6
|
+
|
7
|
+
# Raised when the XML returned by Scribd is malformed.
|
8
|
+
|
9
|
+
class MalformedResponseError < StandardError; end
|
10
|
+
|
11
|
+
# Raised when trying to perform an action that isn't allowed for the current
|
12
|
+
# active user. Note that this exception is thrown only if the error originates
|
13
|
+
# locally. If the request must go out to the Scribd server before the
|
14
|
+
# privilege error occurs, a ResponseError will be thrown. Unless a method's
|
15
|
+
# documentation indicates otherwise, assume that the error will originate
|
16
|
+
# remotely and a ResponseError will be thrown.
|
17
|
+
|
18
|
+
class PrivilegeError < StandardError; end
|
19
|
+
|
20
|
+
# Raised when a remote error occurs. Remote errors are referenced by numerical
|
21
|
+
# code. The online API documentation has a list of possible error codes and
|
22
|
+
# their descriptions for each API method.
|
23
|
+
|
24
|
+
class ResponseError < RuntimeError
|
25
|
+
attr_reader :code # The error code.
|
26
|
+
|
27
|
+
# Initializes the error with a given code.
|
28
|
+
|
29
|
+
def initialize(code)
|
30
|
+
@code = code
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/multipart.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
require "rubygems"
|
3
|
+
require "mime/types" # Requires gem install mime-types
|
4
|
+
require "base64"
|
5
|
+
require 'cgi'
|
6
|
+
|
7
|
+
# This is based on a great snippet from Pivot Labs, used with permission:
|
8
|
+
# http://pivots.pivotallabs.com/users/damon/blog/articles/227-standup-04-27-07-testing-file-uploads
|
9
|
+
module Net #:nodoc:
|
10
|
+
class HTTP::Post
|
11
|
+
def multipart_params=(param_hash={})
|
12
|
+
boundary_token = [Array.new(8) {rand(256)}].join
|
13
|
+
self.content_type = "multipart/form-data; boundary=#{boundary_token}"
|
14
|
+
boundary_marker = "--#{boundary_token}\r\n"
|
15
|
+
self.body = param_hash.map { |param_name, param_value|
|
16
|
+
boundary_marker + case param_value
|
17
|
+
when File
|
18
|
+
file_to_multipart(param_name, param_value)
|
19
|
+
else
|
20
|
+
text_to_multipart(param_name, param_value.to_s)
|
21
|
+
end
|
22
|
+
}.join('') + "--#{boundary_token}--\r\n"
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
def file_to_multipart(key,file)
|
27
|
+
filename = File.basename(file.path)
|
28
|
+
mime_types = MIME::Types.of(filename)
|
29
|
+
mime_type = mime_types.empty? ? "application/octet-stream" : mime_types.first.content_type
|
30
|
+
part = %Q|Content-Disposition: form-data; name="#{key}"; filename="#{filename}"\r\n|
|
31
|
+
part += "Content-Transfer-Encoding: binary\r\n"
|
32
|
+
part += "Content-Type: #{mime_type}\r\n\r\n#{file.read}\r\n"
|
33
|
+
end
|
34
|
+
|
35
|
+
def text_to_multipart(key,value)
|
36
|
+
"Content-Disposition: form-data; name=\"#{key}\"\r\n\r\n#{value}\r\n"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|