rscribd 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|