scribd-rscribd 1.0.2
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 +56 -0
- data/Manifest.txt +14 -0
- data/README.txt +84 -0
- data/Rakefile +29 -0
- data/lib/rscribd.rb +38 -0
- data/lib/scribdapi.rb +218 -0
- data/lib/scribddoc.rb +282 -0
- data/lib/scribderrors.rb +34 -0
- data/lib/scribdmultiparthack.rb +39 -0
- data/lib/scribdresource.rb +164 -0
- data/lib/scribduser.rb +143 -0
- data/sample/01_upload.rb +47 -0
- data/sample/02_user.rb +44 -0
- data/sample/test.txt +1 -0
- metadata +88 -0
data/History.txt
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
=== 1.0.2 / 2009-7-9
|
2
|
+
|
3
|
+
* Added support for getting information about the API user when no other user
|
4
|
+
has been logged in.
|
5
|
+
* Files to upload can now be specified as both file paths and File streams.
|
6
|
+
|
7
|
+
=== 1.0.1 / 2009-3-4
|
8
|
+
|
9
|
+
* Fixed a bug that would cause some API parameters to not be sent when uploading
|
10
|
+
a document.
|
11
|
+
|
12
|
+
=== 1.0.0 / 2008-11-26
|
13
|
+
|
14
|
+
* First release version.
|
15
|
+
* Comprehensive RSpec of all code.
|
16
|
+
* read_attributes method now works correctly.
|
17
|
+
* Uploading a document as a not-yet-created user no longer uploads it into the
|
18
|
+
default API account.
|
19
|
+
* Finding documents owned by a not-yet-created user now returns nil.
|
20
|
+
* Symbol#to_proc definedd for RScribd use in non-Rails projects.
|
21
|
+
* Minor code style improvements.
|
22
|
+
|
23
|
+
=== 0.1.2 / 2008-11-26
|
24
|
+
|
25
|
+
* Happy Thanksgiving!
|
26
|
+
* Potential security issue resolved by having the document's privacy setting set
|
27
|
+
in the same request as the upload.
|
28
|
+
* Bug fixed where some files with uppercase extensions would not convert.
|
29
|
+
|
30
|
+
=== 0.1.1 / 2008-10-6
|
31
|
+
|
32
|
+
* Fixed bug that occurs when parsing empty elements.
|
33
|
+
|
34
|
+
=== 0.1.0 / 2008-10-1
|
35
|
+
|
36
|
+
* Changed Scribd::Document.find method to take an ActiveRecord-style scope
|
37
|
+
variable (which can be :all, :first, etc.) as its first parameter. Options are
|
38
|
+
now specified as the second parameter.
|
39
|
+
* Scribd::Document.find can also be used with a single integer to find a
|
40
|
+
document by ID.
|
41
|
+
* Scribd::Document.find now takes offset and limit parameters (for scopes that
|
42
|
+
return arrays of documents).
|
43
|
+
* Added a Scribd::Document#download_url method.
|
44
|
+
* Should no longer raise an exception when logging in.
|
45
|
+
|
46
|
+
=== 0.0.5 / 2008-09-25
|
47
|
+
|
48
|
+
* Updated RScribd to work with server-side API changes.
|
49
|
+
|
50
|
+
=== 0.0.4 / 2008-02-20
|
51
|
+
|
52
|
+
* Fixed a bug that caused errors when Rails models shared the same name as Scribd objects.
|
53
|
+
|
54
|
+
=== 0.0.3 / 2008-02-18
|
55
|
+
|
56
|
+
* Initial RubyForge project
|
data/Manifest.txt
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
lib/scribdapi.rb
|
6
|
+
lib/scribddoc.rb
|
7
|
+
lib/scribderrors.rb
|
8
|
+
lib/scribdmultiparthack.rb
|
9
|
+
lib/scribdresource.rb
|
10
|
+
lib/rscribd.rb
|
11
|
+
lib/scribduser.rb
|
12
|
+
sample/01_upload.rb
|
13
|
+
sample/02_user.rb
|
14
|
+
sample/test.txt
|
data/README.txt
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
= rscribd
|
2
|
+
|
3
|
+
* 1.0.2 (July 9, 2009)
|
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. For more information on
|
10
|
+
the Scribd platform, visit http://www.scribd.com/publisher
|
11
|
+
|
12
|
+
== FEATURES:
|
13
|
+
|
14
|
+
* Upload your documents to Scribd's servers and access them using the gem
|
15
|
+
* Upload local files or from remote web sites
|
16
|
+
* Search, tag, and organize documents
|
17
|
+
* Associate documents with your users' accounts
|
18
|
+
|
19
|
+
== SYNOPSIS:
|
20
|
+
|
21
|
+
This API allows you to use Scribd's Flash viewer on your website. You'll be able
|
22
|
+
to take advantage of Scribd's scalable document conversion system to convert
|
23
|
+
your documents into platform-independent formats. You can leverage Scribd's
|
24
|
+
storage system to store your documents in accessible manner. Scribd's ad system
|
25
|
+
will help you monetize your documents easily.
|
26
|
+
|
27
|
+
First, you'll need to get a Scribd API account. Visit
|
28
|
+
http://www.scribd.com/publisher/api to apply for a platform account.
|
29
|
+
|
30
|
+
On the Platform site you will be given an API key and secret. The API object
|
31
|
+
will need these to authenticate you:
|
32
|
+
|
33
|
+
require 'rscribd'
|
34
|
+
Scribd::API.instance.key = 'your API key'
|
35
|
+
Scribd::API.instance.secret = 'your API secret'
|
36
|
+
|
37
|
+
Next, log into the Scribd website:
|
38
|
+
|
39
|
+
Scribd::User.login 'username', 'password'
|
40
|
+
|
41
|
+
You are now ready to use Scribd to manage your documents. For instance, to
|
42
|
+
upload a document:
|
43
|
+
|
44
|
+
doc = Scribd::Document.upload(:file => 'your-file.pdf')
|
45
|
+
|
46
|
+
For more information, please see the documentation for the Scribd::API,
|
47
|
+
Scribd::User, and Scribd::Document classes. (You can also check out the docs for
|
48
|
+
the other classes for a more in-depth look at this gem's features).
|
49
|
+
|
50
|
+
== REQUIREMENTS:
|
51
|
+
|
52
|
+
* A Scribd API account
|
53
|
+
* mime-types gem
|
54
|
+
|
55
|
+
== INSTALL:
|
56
|
+
|
57
|
+
* The client library is a RubyGem called *rscribd*. To install, type:
|
58
|
+
|
59
|
+
sudo gem install rscribd
|
60
|
+
|
61
|
+
== LICENSE:
|
62
|
+
|
63
|
+
(The MIT License)
|
64
|
+
|
65
|
+
Copyright (c) 2007-2008 The Scribd Team
|
66
|
+
|
67
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
68
|
+
a copy of this software and associated documentation files (the
|
69
|
+
'Software'), to deal in the Software without restriction, including
|
70
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
71
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
72
|
+
permit persons to whom the Software is furnished to do so, subject to
|
73
|
+
the following conditions:
|
74
|
+
|
75
|
+
The above copyright notice and this permission notice shall be
|
76
|
+
included in all copies or substantial portions of the Software.
|
77
|
+
|
78
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
79
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
80
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
81
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
82
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
83
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
84
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hoe'
|
3
|
+
require 'spec/rake/spectask'
|
4
|
+
|
5
|
+
Hoe.spec('rscribd') do |p|
|
6
|
+
p.version = '1.0.2'
|
7
|
+
p.rubyforge_name = 'rscribd'
|
8
|
+
p.author = 'Jared Friedman, Tim Morgan'
|
9
|
+
p.email = 'api@scribd.com'
|
10
|
+
p.summary = 'Ruby client library for the Scribd API'
|
11
|
+
p.description = p.paragraphs_of('README.txt', 3).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
|
+
# desc "Verify gem specs"
|
19
|
+
# Spec::Rake::SpecTask.new do |t|
|
20
|
+
# t.spec_files = FileList['spec/*.rb']
|
21
|
+
# t.spec_opts = [ '-cfs' ]
|
22
|
+
# end
|
23
|
+
|
24
|
+
namespace :github do
|
25
|
+
desc "Prepare for GitHub gem packaging"
|
26
|
+
task :prepare do
|
27
|
+
`rake debug_gem > rscribd.gemspec`
|
28
|
+
end
|
29
|
+
end
|
data/lib/rscribd.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module Scribd # :nodoc:
|
4
|
+
end
|
5
|
+
|
6
|
+
class Symbol
|
7
|
+
def to_proc
|
8
|
+
Proc.new { |*args| args.shift.__send__(self, *args) }
|
9
|
+
end unless method_defined?(:to_proc)
|
10
|
+
end
|
11
|
+
|
12
|
+
class Hash #:nodoc:
|
13
|
+
# Taken from Rails, with appreciation to DHH
|
14
|
+
def stringify_keys
|
15
|
+
inject({}) do |options, (key, value)|
|
16
|
+
options[key.to_s] = value
|
17
|
+
options
|
18
|
+
end
|
19
|
+
end unless method_defined?(:stringify_keys)
|
20
|
+
end
|
21
|
+
|
22
|
+
class Array #:nodoc:
|
23
|
+
def to_hsh
|
24
|
+
h = Hash.new
|
25
|
+
each { |k, v| h[k] = v }
|
26
|
+
h
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# The reason these files have such terrible names is so they don't conflict with
|
31
|
+
# files in Rails's app/models directory; Rails seems to prefer loading those
|
32
|
+
# when require is called.
|
33
|
+
require 'scribdmultiparthack'
|
34
|
+
require 'scribderrors'
|
35
|
+
require 'scribdapi'
|
36
|
+
require 'scribdresource'
|
37
|
+
require 'scribddoc'
|
38
|
+
require 'scribduser'
|
data/lib/scribdapi.rb
ADDED
@@ -0,0 +1,218 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'digest/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
|
+
@user = User.new
|
74
|
+
end
|
75
|
+
|
76
|
+
def send_request(method, fields={}) #:nodoc:
|
77
|
+
raise NotReadyError unless @key and @secret
|
78
|
+
# See if method is given
|
79
|
+
raise ArgumentError, "Method should be given" if method.nil? || method.empty?
|
80
|
+
|
81
|
+
debug("** Remote method call: #{method}; fields: #{fields.inspect}")
|
82
|
+
|
83
|
+
# replace pesky hashes to prevent accidents
|
84
|
+
fields = fields.stringify_keys
|
85
|
+
|
86
|
+
# Complete fields with the method name
|
87
|
+
fields['method'] = method
|
88
|
+
fields['api_key'] = @key
|
89
|
+
|
90
|
+
if fields['session_key'].nil? and fields['my_user_id'].nil? then
|
91
|
+
if @user.kind_of? Scribd::User then
|
92
|
+
fields['session_key'] = @user.session_key
|
93
|
+
elsif @user.kind_of? String then
|
94
|
+
fields['my_user_id'] = @user
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
fields.reject! { |k, v| v.nil? }
|
99
|
+
|
100
|
+
# Don't include file in parameters to calculate signature
|
101
|
+
sign_fields = fields.dup
|
102
|
+
sign_fields.delete 'file'
|
103
|
+
|
104
|
+
fields['api_sig'] = sign(sign_fields)
|
105
|
+
debug("** POST parameters: #{fields.inspect}")
|
106
|
+
|
107
|
+
# Create the connection
|
108
|
+
http = Net::HTTP.new(HOST, PORT)
|
109
|
+
# TODO configure timeouts through the properties
|
110
|
+
|
111
|
+
# API methods can be SLOW. Make sure this is set to something big to prevent spurious timeouts
|
112
|
+
http.read_timeout = 15*60
|
113
|
+
|
114
|
+
request = Net::HTTP::Post.new(REQUEST_PATH)
|
115
|
+
request.multipart_params = fields
|
116
|
+
|
117
|
+
tries = TRIES
|
118
|
+
begin
|
119
|
+
tries -= 1
|
120
|
+
res = http.request(request)
|
121
|
+
rescue Exception
|
122
|
+
$stderr.puts "Request encountered error, will retry #{tries} more."
|
123
|
+
if tries > 0
|
124
|
+
# Retrying several times with sleeps is recommended.
|
125
|
+
# This will prevent temporary downtimes at Scribd from breaking API applications
|
126
|
+
sleep(20)
|
127
|
+
retry
|
128
|
+
end
|
129
|
+
raise $!
|
130
|
+
end
|
131
|
+
|
132
|
+
debug "** Response:"
|
133
|
+
debug(res.body)
|
134
|
+
debug "** End response"
|
135
|
+
|
136
|
+
# Convert response into XML
|
137
|
+
xml = REXML::Document.new(res.body)
|
138
|
+
raise MalformedResponseError, "The response received from the remote host could not be interpreted" unless xml.elements['/rsp']
|
139
|
+
|
140
|
+
# See if there was an error and raise an exception
|
141
|
+
if xml.elements['/rsp'].attributes['stat'] == 'fail'
|
142
|
+
# Load default code and error
|
143
|
+
code, message = -1, "Unidentified error:\n#{res.body}"
|
144
|
+
|
145
|
+
# Get actual error code and message
|
146
|
+
err = xml.elements['/rsp/error']
|
147
|
+
code, message = err.attributes['code'], err.attributes['message'] if err
|
148
|
+
|
149
|
+
# Add more data
|
150
|
+
message = "Method: #{method} Response: code=#{code} message=#{message}"
|
151
|
+
|
152
|
+
raise ResponseError.new(code), message
|
153
|
+
end
|
154
|
+
|
155
|
+
return xml
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
# FIXME: Since we don't need XMLRPC, the exception could be different
|
161
|
+
# TODO: It would probably be better if we wrapped the fault
|
162
|
+
# in something more meaningful. At the very least, a broad
|
163
|
+
# division of errors, such as retryable and fatal.
|
164
|
+
def error(el) #:nodoc:
|
165
|
+
att = el.attributes
|
166
|
+
fe = XMLRPC::FaultException.new(att['code'].to_i, att['msg'])
|
167
|
+
$stderr.puts "ERR: #{fe.faultString} (#{fe.faultCode})"
|
168
|
+
raise fe
|
169
|
+
end
|
170
|
+
|
171
|
+
# Checks if a string parameter is given and not empty.
|
172
|
+
#
|
173
|
+
# Parameters:
|
174
|
+
# name - parameter name for an error message.
|
175
|
+
# value - value.
|
176
|
+
#
|
177
|
+
# Raises:
|
178
|
+
# ArgumentError if the value is nil, or empty.
|
179
|
+
#
|
180
|
+
def check_not_empty(name, value) #:nodoc:
|
181
|
+
check_given(name, value)
|
182
|
+
raise ArgumentError, "#{name} must not be empty" if value.to_s.empty?
|
183
|
+
end
|
184
|
+
|
185
|
+
# Checks if the value is given.
|
186
|
+
#
|
187
|
+
# Parameters:
|
188
|
+
# name - parameter name for an error message.
|
189
|
+
# value - value.
|
190
|
+
#
|
191
|
+
# Raises:
|
192
|
+
# ArgumentError if the value is nil.
|
193
|
+
#
|
194
|
+
def check_given(name, value) #:nodoc:
|
195
|
+
raise ArgumentError, "#{name} must be given" if value.nil?
|
196
|
+
end
|
197
|
+
|
198
|
+
# Sign the arguments hash with our very own signature.
|
199
|
+
#
|
200
|
+
# Parameters:
|
201
|
+
# args - method arguments to be sent to the server API
|
202
|
+
#
|
203
|
+
# Returns:
|
204
|
+
# signature
|
205
|
+
#
|
206
|
+
def sign(args)
|
207
|
+
return Digest::MD5.hexdigest(@secret + args.sort.flatten.join).to_s
|
208
|
+
end
|
209
|
+
|
210
|
+
# Outputs whatever is given into the $stderr if debugging is enabled.
|
211
|
+
#
|
212
|
+
# Parameters:
|
213
|
+
# args - content to output
|
214
|
+
def debug(str)
|
215
|
+
$stderr.puts(str) if @debug
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
data/lib/scribddoc.rb
ADDED
@@ -0,0 +1,282 @@
|
|
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
|
29
|
+
# http://www.scribd.com/publisher/api?method_name=docs.search (consult the
|
30
|
+
# "Result explanation" section).
|
31
|
+
#
|
32
|
+
# These attributes can be accessed or changed directly
|
33
|
+
# (<tt>doc.title = 'Title'</tt>). You must save a document after changing its
|
34
|
+
# attributes in order for those changes to take effect. Not all attributes can
|
35
|
+
# be modified; see the API documentation online for details.
|
36
|
+
#
|
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.
|
40
|
+
#
|
41
|
+
# The +owner+ attribute is read/write, however, changes made to it only apply
|
42
|
+
# _before_ the document is saved. Once it is saved, the owner is set in stone
|
43
|
+
# and cannot be modified:
|
44
|
+
#
|
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
|
49
|
+
|
50
|
+
class Document < Resource
|
51
|
+
|
52
|
+
# Creates a new, unsaved document with the given attributes. The document
|
53
|
+
# must be saved before it will appear on the website.
|
54
|
+
|
55
|
+
def initialize(options={})
|
56
|
+
super
|
57
|
+
@download_urls = Hash.new
|
58
|
+
if options[:xml] then
|
59
|
+
load_attributes(options[:xml])
|
60
|
+
@attributes[:owner] = options[:owner]
|
61
|
+
@saved = true
|
62
|
+
@created = true
|
63
|
+
else
|
64
|
+
@attributes = options
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# For document objects that have not been saved for the first time, uploads
|
69
|
+
# the document, sets its attributes, and saves it. Otherwise, updates any
|
70
|
+
# changed attributes and saves it. Returns true if the save completed
|
71
|
+
# successfully. Throws an exception if save fails.
|
72
|
+
#
|
73
|
+
# For first-time saves, you must have specified a +file+ attribute. This can
|
74
|
+
# either be a local path to a file, or an HTTP, HTTPS, or FTP URL. In either
|
75
|
+
# case, the file at that location will be uploaded to create the document.
|
76
|
+
#
|
77
|
+
# If you create a document, specify the +file+ attribute again, and then
|
78
|
+
# save it, Scribd replaces the existing document with the file given, while
|
79
|
+
# keeping all other properties (title, description, etc.) the same, in a
|
80
|
+
# process called _revisioning_.
|
81
|
+
#
|
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
|
89
|
+
# the file's type cannot be determined from its name.
|
90
|
+
|
91
|
+
def save
|
92
|
+
if not created? and @attributes[:file].nil? then
|
93
|
+
raise "'file' attribute must be specified for new documents"
|
94
|
+
return false
|
95
|
+
end
|
96
|
+
|
97
|
+
if created? and @attributes[:file] and (@attributes[:owner].nil? or @attributes[:owner].session_key.nil?) then
|
98
|
+
raise PrivilegeError, "The current API user is not the owner of this document"
|
99
|
+
end
|
100
|
+
|
101
|
+
# Make a request form
|
102
|
+
fields = @attributes.dup
|
103
|
+
if file = @attributes[:file] then
|
104
|
+
fields.delete :file
|
105
|
+
is_file_object = file.is_a?(File)
|
106
|
+
file_path = is_file_object ? file.path : file
|
107
|
+
ext = File.extname(file_path).gsub(/^\./, '')
|
108
|
+
ext = nil if ext == ''
|
109
|
+
fields[:doc_type] = fields.delete(:type)
|
110
|
+
fields[:doc_type] ||= ext
|
111
|
+
fields[:doc_type].downcase! if fields[:doc_type]
|
112
|
+
fields[:rev_id] = fields.delete(:doc_id)
|
113
|
+
end
|
114
|
+
fields[:session_key] = fields.delete(:owner).session_key if fields[:owner]
|
115
|
+
response = nil
|
116
|
+
|
117
|
+
if @attributes[:file] then
|
118
|
+
uri = nil
|
119
|
+
begin
|
120
|
+
uri = URI.parse @attributes[:file]
|
121
|
+
rescue URI::InvalidURIError
|
122
|
+
uri = nil # Some valid file paths are not valid URI's (but some are)
|
123
|
+
end
|
124
|
+
if uri.kind_of? URI::HTTP or uri.kind_of? URI::HTTPS or uri.kind_of? URI::FTP then
|
125
|
+
fields[:url] = @attributes[:file]
|
126
|
+
response = API.instance.send_request 'docs.uploadFromUrl', fields
|
127
|
+
elsif uri.kind_of? URI::Generic or uri.nil? then
|
128
|
+
file_obj = is_file_object ? file : File.open(file, 'r')
|
129
|
+
fields[:file] = file_obj
|
130
|
+
response = API.instance.send_request 'docs.upload', fields
|
131
|
+
file_obj.close unless is_file_object
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
fields = @attributes.dup # fields is what we send to the server
|
136
|
+
|
137
|
+
if response then
|
138
|
+
# Extract our response
|
139
|
+
xml = response.get_elements('/rsp')[0]
|
140
|
+
load_attributes(xml)
|
141
|
+
@created = true
|
142
|
+
end
|
143
|
+
|
144
|
+
fields.delete :file
|
145
|
+
fields.delete :type
|
146
|
+
fields.delete :access
|
147
|
+
|
148
|
+
changed_attributes = fields.dup # changed_attributes is what we will stick into @attributes once we update remotely
|
149
|
+
|
150
|
+
fields[:session_key] = fields[:owner].session_key if fields[:owner]
|
151
|
+
changed_attributes[:owner] ||= API.instance.user
|
152
|
+
fields[:doc_ids] = self.id
|
153
|
+
|
154
|
+
API.instance.send_request('docs.changeSettings', fields)
|
155
|
+
|
156
|
+
@attributes.update(changed_attributes)
|
157
|
+
|
158
|
+
@saved = true
|
159
|
+
return true
|
160
|
+
end
|
161
|
+
|
162
|
+
# Quickly updates an array of documents with the given attributes. The
|
163
|
+
# documents can have different owners, but all of them must be modifiable.
|
164
|
+
|
165
|
+
def self.update_all(docs, options)
|
166
|
+
raise ArgumentError, "docs must be an array" unless docs.kind_of? Array
|
167
|
+
raise ArgumentError, "docs must consist of Scribd::Document objects" unless docs.all? { |doc| doc.kind_of? Document }
|
168
|
+
raise ArgumentError, "You can't modify one or more documents" if docs.any? { |doc| doc.owner.nil? }
|
169
|
+
raise ArgumentError, "options must be a hash" unless options.kind_of? Hash
|
170
|
+
|
171
|
+
docs_by_user = docs.inject(Hash.new { |hash, key| hash[key] = Array.new }) { |hash, doc| hash[doc.owner] << doc; hash }
|
172
|
+
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) }
|
173
|
+
end
|
174
|
+
|
175
|
+
# === Finding by query
|
176
|
+
#
|
177
|
+
# This method is called with a scope and a hash of options to documents by
|
178
|
+
# their content. You must at a minimum supply a +query+ option, with a
|
179
|
+
# string that will become the full-text search query. For a list of other
|
180
|
+
# supported options, please see the online API documentation at
|
181
|
+
# http://www.scribd.com/publisher/api?method_name=docs.search
|
182
|
+
#
|
183
|
+
# The scope can be any value given for the +scope+ parameter in the above
|
184
|
+
# website, or <tt>:first</tt> to return the first result only (not an array
|
185
|
+
# of results).
|
186
|
+
#
|
187
|
+
# The +num_results+ option has been aliased as +limit+, and the +num_start+
|
188
|
+
# option has been aliased as +offset+.
|
189
|
+
#
|
190
|
+
# Documents returned from this method will have their +owner+ attributes set
|
191
|
+
# to nil.
|
192
|
+
#
|
193
|
+
# Scribd::Document.find(:all, :query => 'cats and dogs', :limit => 10)
|
194
|
+
#
|
195
|
+
# === Finding by ID
|
196
|
+
#
|
197
|
+
# Passing in simply a numerical ID loads the document with that ID. You can
|
198
|
+
# pass additional options as defined at
|
199
|
+
# httphttp://www.scribd.com/publisher/api?method_name=docs.getSettings
|
200
|
+
#
|
201
|
+
# Scribd::Document.find(108196)
|
202
|
+
#
|
203
|
+
# For now only documents that belong to the current user can be accessed in
|
204
|
+
# this manner.
|
205
|
+
|
206
|
+
def self.find(scope, options={})
|
207
|
+
doc_id = scope if scope.kind_of?(Integer)
|
208
|
+
raise ArgumentError, "You must specify a query or document ID" unless options[:query] or doc_id
|
209
|
+
|
210
|
+
if doc_id then
|
211
|
+
options[:doc_id] = doc_id
|
212
|
+
response = API.instance.send_request('docs.getSettings', options)
|
213
|
+
return Document.new(:xml => response.elements['/rsp'])
|
214
|
+
else
|
215
|
+
options[:scope] = scope == :first ? 'all' : scope.to_s
|
216
|
+
options[:num_results] = options[:limit]
|
217
|
+
options[:num_start] = options[:offset]
|
218
|
+
response = API.instance.send_request('docs.search', options)
|
219
|
+
documents = []
|
220
|
+
response.elements['/rsp/result_set'].elements.each do |doc|
|
221
|
+
documents << Document.new(:xml => doc)
|
222
|
+
end
|
223
|
+
return scope == :first ? documents.first : documents
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
class << self
|
228
|
+
alias_method :upload, :create
|
229
|
+
end
|
230
|
+
|
231
|
+
# Returns the conversion status of this document. When a document is
|
232
|
+
# uploaded it must be converted before it can be used. The conversion is
|
233
|
+
# non-blocking; you can query this method to determine whether the document
|
234
|
+
# is ready to be displayed.
|
235
|
+
#
|
236
|
+
# The conversion status is returned as a string. For a full list of
|
237
|
+
# conversion statuses, see the online API documentation at
|
238
|
+
# http://www.scribd.com/publisher/api?method_name=docs.getConversionStatus
|
239
|
+
#
|
240
|
+
# Unlike other properties of a document, this is retrieved from the server
|
241
|
+
# every time it's queried.
|
242
|
+
|
243
|
+
def conversion_status
|
244
|
+
response = API.instance.send_request('docs.getConversionStatus', :doc_id => self.id)
|
245
|
+
response.elements['/rsp/conversion_status'].text
|
246
|
+
end
|
247
|
+
|
248
|
+
# Deletes a document. Returns true if successful.
|
249
|
+
|
250
|
+
def destroy
|
251
|
+
response = API.instance.send_request('docs.delete', :doc_id => self.id)
|
252
|
+
return response.elements['/rsp'].attributes['stat'] == 'ok'
|
253
|
+
end
|
254
|
+
|
255
|
+
# Returns the +doc_id+ attribute.
|
256
|
+
|
257
|
+
def id
|
258
|
+
self.doc_id
|
259
|
+
end
|
260
|
+
|
261
|
+
# Ensures that the +owner+ attribute cannot be set once the document is
|
262
|
+
# saved.
|
263
|
+
|
264
|
+
def owner=(newuser) #:nodoc:
|
265
|
+
saved? ? raise(NotImplementedError, "Cannot change a document's owner once the document has been saved") : super
|
266
|
+
end
|
267
|
+
|
268
|
+
# Retrieves a document's download URL. You can provide a format for the
|
269
|
+
# download. Valid formats are listed at
|
270
|
+
# http://www.scribd.com/publisher/api?method_name=docs.getDownloadUrl
|
271
|
+
#
|
272
|
+
# If you do not provide a format, the link will be for the document's
|
273
|
+
# original format.
|
274
|
+
|
275
|
+
def download_url(format='original')
|
276
|
+
@download_urls[format] ||= begin
|
277
|
+
response = API.instance.send_request('docs.getDownloadUrl', :doc_id => self.id, :doc_type => format)
|
278
|
+
response.elements['/rsp/download_link'].cdatas.first.to_s
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
data/lib/scribderrors.rb
ADDED
@@ -0,0 +1,34 @@
|
|
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 Scribd::ResponseError will be thrown. Unless a
|
15
|
+
# method's documentation indicates otherwise, assume that the error will
|
16
|
+
# originate remotely and a Scribd::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
|
+
# The error code.
|
26
|
+
attr_reader :code
|
27
|
+
|
28
|
+
# Initializes the error with a given code.
|
29
|
+
|
30
|
+
def initialize(code)
|
31
|
+
@code = code
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# This is based on a great snippet from Pivot Labs, used with permission:
|
2
|
+
# http://pivots.pivotallabs.com/users/damon/blog/articles/227-standup-04-27-07-testing-file-uploads
|
3
|
+
|
4
|
+
require 'net/https'
|
5
|
+
require 'rubygems'
|
6
|
+
require 'mime/types' # Requires gem install mime-types
|
7
|
+
require 'cgi'
|
8
|
+
|
9
|
+
module Net #:nodoc:all
|
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
|
@@ -0,0 +1,164 @@
|
|
1
|
+
module Scribd
|
2
|
+
|
3
|
+
# Describes a remote object that the Scribd API lets you interact with. All
|
4
|
+
# such objects are modeled after the <tt>ActiveRecord</tt> ORM approach.
|
5
|
+
#
|
6
|
+
# The Resource superclass is never directly used; you will interact with
|
7
|
+
# actual Scribd entities like Document and User, which inherit functionality
|
8
|
+
# from this superclass.
|
9
|
+
#
|
10
|
+
# Objects have one or more attributes (also called fields) that can be
|
11
|
+
# accessed directly through synonymous methods. For instance, if your resource
|
12
|
+
# has an attribute +title+, you can get and set the title like so:
|
13
|
+
#
|
14
|
+
# obj.title #=> "Title"
|
15
|
+
# obj.title = "New Title"
|
16
|
+
#
|
17
|
+
# The specific attributes that a Document or a User or any other resource has
|
18
|
+
# are not stored locally. They are downloaded remotely whenever a resource is
|
19
|
+
# loaded from the remote server. Thus, you can modify any attribute you want,
|
20
|
+
# though it may or may not have any effect:
|
21
|
+
#
|
22
|
+
# doc = Scribd::Document.find(:text => 'foo').first
|
23
|
+
# doc.self_destruct_in = 5.seconds #=> Does not produce error
|
24
|
+
# doc.save #=> Has no effect, since that attribute doesn't exist. Your document does not explode.
|
25
|
+
#
|
26
|
+
# As shown above, when you make changes to an attribute, these changes are not
|
27
|
+
# immediately reflected remotely. They are only stored locally until such time
|
28
|
+
# as save is called. When you call save, the remote object is updated to
|
29
|
+
# reflect the changes you made in its API instance.
|
30
|
+
|
31
|
+
class Resource
|
32
|
+
|
33
|
+
# Initializes instance variables.
|
34
|
+
|
35
|
+
def initialize(options={})
|
36
|
+
@saved = false
|
37
|
+
@created = false
|
38
|
+
@attributes = Hash.new
|
39
|
+
end
|
40
|
+
|
41
|
+
# Creates a new instance with the given attributes, saves it immediately,
|
42
|
+
# and returns it. You should call its created? method if you need to verify
|
43
|
+
# that the object was saved successfully.
|
44
|
+
|
45
|
+
def self.create(options={})
|
46
|
+
obj = new(options)
|
47
|
+
obj.save
|
48
|
+
obj
|
49
|
+
end
|
50
|
+
|
51
|
+
# Throws NotImplementedError by default.
|
52
|
+
|
53
|
+
def save
|
54
|
+
raise NotImplementedError, "Cannot save #{self.class.to_s} objects"
|
55
|
+
end
|
56
|
+
|
57
|
+
# Throws NotImplementedError by default.
|
58
|
+
|
59
|
+
def self.find(options)
|
60
|
+
raise NotImplementedError, "Cannot find #{self.class.to_s} objects"
|
61
|
+
end
|
62
|
+
|
63
|
+
# Throws NotImplementedError by default.
|
64
|
+
|
65
|
+
def destroy
|
66
|
+
raise NotImplementedError, "Cannot destroy #{self.class.to_s} objects"
|
67
|
+
end
|
68
|
+
|
69
|
+
# Returns true if this document's attributes have been updated remotely, and
|
70
|
+
# thus their local values reflect the remote values.
|
71
|
+
|
72
|
+
def saved?
|
73
|
+
@saved
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns true if this document has been created remotely, and corresponds
|
77
|
+
# to a document on the Scribd website.
|
78
|
+
|
79
|
+
def created?
|
80
|
+
@created
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns the value of an attribute, referenced by string or symbol, or nil
|
84
|
+
# if the attribute cannot be read.
|
85
|
+
|
86
|
+
def read_attribute(attribute)
|
87
|
+
raise ArgumentError, "Attribute must respond to to_sym" unless attribute.respond_to? :to_sym
|
88
|
+
@attributes[attribute.to_sym]
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns a map of attributes to their values, given an array of attributes,
|
92
|
+
# referenced by string or symbol. Attributes that cannot be read are
|
93
|
+
# ignored.
|
94
|
+
|
95
|
+
def read_attributes(attributes)
|
96
|
+
raise ArgumentError, "Attributes must be listed in an Enumeration" unless attributes.kind_of?(Enumerable)
|
97
|
+
raise ArgumentError, "All attributes must respond to to_sym" unless attributes.all? { |a| a.respond_to? :to_sym }
|
98
|
+
keys = attributes.map(&:to_sym)
|
99
|
+
values = @attributes.values_at(*keys)
|
100
|
+
keys.zip(values).to_hsh
|
101
|
+
end
|
102
|
+
|
103
|
+
# Assigns values to attributes. Takes a hash that specifies the
|
104
|
+
# attribute-value pairs to update. Does not perform a save. Non-writeable
|
105
|
+
# attributes are ignored.
|
106
|
+
|
107
|
+
def write_attributes(values)
|
108
|
+
raise ArgumentError, "Values must be specified through a hash of attributes" unless values.kind_of? Hash
|
109
|
+
raise ArgumentError, "All attributes must respond to to_sym" unless values.keys.all? { |a| a.respond_to? :to_sym }
|
110
|
+
@attributes.update values.map { |k,v| [ k.to_sym, v ] }.to_hsh
|
111
|
+
end
|
112
|
+
|
113
|
+
# Gets or sets attributes for the resource. Any named attribute can be
|
114
|
+
# retrieved for changed through a method call, even if it doesn't exist.
|
115
|
+
# Such attributes will be ignored and purged when the document is saved:
|
116
|
+
#
|
117
|
+
# doc = Scribd::Document.new
|
118
|
+
# doc.foobar #=> Returns nil
|
119
|
+
# doc.foobar = 12
|
120
|
+
# doc.foobar #=> Returns 12
|
121
|
+
# doc.save
|
122
|
+
# doc.foobar #=> Returns nil
|
123
|
+
#
|
124
|
+
# Because of this, no Scribd resource will ever raise NoMethodError.
|
125
|
+
|
126
|
+
def method_missing(meth, *args)
|
127
|
+
if meth.to_s =~ /(\w+)=/ then
|
128
|
+
raise ArgumentError, "Only one parameter can be passed to attribute=" unless args.size == 1
|
129
|
+
@attributes[$1.to_sym] = args[0]
|
130
|
+
else
|
131
|
+
@attributes[meth]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Pretty-print for debugging output of Scribd resources.
|
136
|
+
|
137
|
+
def inspect #:nodoc:
|
138
|
+
"#<#{self.class.to_s} #{@attributes.select { |k, v| not v.nil? }.collect { |k,v| k.to_s + '=' + v.to_s }.join(', ')}>"
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
def load_attributes(xml)
|
144
|
+
@attributes.clear
|
145
|
+
xml.each_element do |element|
|
146
|
+
text = if element.text.nil? or element.text.chomp.strip.empty? then
|
147
|
+
(element.cdatas and not element.cdatas.empty?) ? element.cdatas.first.value : nil
|
148
|
+
else
|
149
|
+
element.text
|
150
|
+
end
|
151
|
+
|
152
|
+
@attributes[element.name.to_sym] = if element.attributes['type'] == 'integer' then
|
153
|
+
text.to_i
|
154
|
+
elsif element.attributes['type'] == 'float' then
|
155
|
+
text.to_f
|
156
|
+
elsif element.attributes['type'] == 'symbol' then
|
157
|
+
text.to_sym
|
158
|
+
else
|
159
|
+
text
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
data/lib/scribduser.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
module Scribd
|
2
|
+
|
3
|
+
# A user of the Scribd website. API programs can use this class to log in as a
|
4
|
+
# Scribd user, create new user accounts, and get information about the current
|
5
|
+
# user.
|
6
|
+
#
|
7
|
+
# An API program begins by logging into Scribd:
|
8
|
+
#
|
9
|
+
# user = Scribd::User.login 'login', 'pass'
|
10
|
+
#
|
11
|
+
# You can now access information about this user through direct method calls:
|
12
|
+
#
|
13
|
+
# user.name #=> 'Real Name'
|
14
|
+
#
|
15
|
+
# If, at any time, you would like to retrieve the Scribd::User instance for
|
16
|
+
# the currently logged-in user, simply call:
|
17
|
+
#
|
18
|
+
# user = Scribd::API.instance.user
|
19
|
+
#
|
20
|
+
# For information on a user's attributes, please consult the online API
|
21
|
+
# documentation at http://www.scribd.com/publisher/api?method_name=user.login
|
22
|
+
#
|
23
|
+
# You can create a new account with the signup (a.k.a. create) method:
|
24
|
+
#
|
25
|
+
# user = Scribd::User.signup :username => 'testuser', :password => 'testpassword', :email => your@email.com
|
26
|
+
|
27
|
+
class User < Resource
|
28
|
+
|
29
|
+
# Creates a new, unsaved user with the given attributes. You can eventually
|
30
|
+
# use this record to create a new Scribd account.
|
31
|
+
|
32
|
+
def initialize(options={})
|
33
|
+
super
|
34
|
+
if options[:xml] then
|
35
|
+
load_attributes(options[:xml])
|
36
|
+
@saved = true
|
37
|
+
@created = true
|
38
|
+
else
|
39
|
+
@attributes = options
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# For new, unsaved records, creates a new Scribd user with the provided
|
44
|
+
# attributes, then logs in as that user. Currently modification of existing
|
45
|
+
# Scribd users is not supported. Throws a ResponseError if a remote error
|
46
|
+
# occurs.
|
47
|
+
|
48
|
+
def save
|
49
|
+
if not created? then
|
50
|
+
response = API.instance.send_request('user.signup', @attributes)
|
51
|
+
xml = response.get_elements('/rsp')[0]
|
52
|
+
load_attributes(xml)
|
53
|
+
API.instance.user = self
|
54
|
+
else
|
55
|
+
raise NotImplementedError, "Cannot update a user once that user's been saved"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns a list of all documents owned by this user. This list is _not_
|
60
|
+
# backed by the server, so if you add or remove items from it, it will not
|
61
|
+
# make those changes server-side. This also has some tricky consequences
|
62
|
+
# when modifying a list of documents while iterating over it:
|
63
|
+
#
|
64
|
+
# docs = user.documents
|
65
|
+
# docs.each(&:destroy)
|
66
|
+
# docs #=> Still populated, because it hasn't been updated
|
67
|
+
# docs = user.documents #=> Now it's empty
|
68
|
+
#
|
69
|
+
# Scribd::Document instances returned through this method have more
|
70
|
+
# attributes than those returned by the Scribd::Document.find method. The
|
71
|
+
# additional attributes are documented online at
|
72
|
+
# http://www.scribd.com/publisher/api?method_name=docs.getSettings
|
73
|
+
|
74
|
+
def documents
|
75
|
+
response = API.instance.send_request('docs.getList', { :session_key => @attributes[:session_key] })
|
76
|
+
documents = Array.new
|
77
|
+
response.elements['/rsp/resultset'].elements.each do |doc|
|
78
|
+
documents << Document.new(:xml => doc, :owner => self)
|
79
|
+
end
|
80
|
+
return documents
|
81
|
+
end
|
82
|
+
|
83
|
+
# Finds documents owned by this user matching a given query. The parameters
|
84
|
+
# provided to this method are identical to those provided to
|
85
|
+
# Scribd::Document.find.
|
86
|
+
|
87
|
+
def find_documents(options)
|
88
|
+
return nil unless @attributes[:session_key]
|
89
|
+
Document.find options.merge(:scope => 'user', :session_key => @attributes[:session_key])
|
90
|
+
end
|
91
|
+
|
92
|
+
# Loads a Scribd::Document by ID. You can only load such documents if they
|
93
|
+
# belong to this user.
|
94
|
+
|
95
|
+
def find_document(document_id)
|
96
|
+
return nil unless @attributes[:session_key]
|
97
|
+
response = API.instance.send_request('docs.getSettings', { :doc_id => document_id, :session_key => @attributes[:session_key] })
|
98
|
+
Document.new :xml => response.elements['/rsp'], :owner => self
|
99
|
+
end
|
100
|
+
|
101
|
+
# Uploads a document to a user's document list. This method takes the
|
102
|
+
# following options:
|
103
|
+
#
|
104
|
+
# +file+:: The location of a file on disk or the URL to a file on the Web
|
105
|
+
# +type+:: The file's type (e.g., "txt" or "ppt"). Optional if the file has
|
106
|
+
# an extension (like "file.txt").
|
107
|
+
#
|
108
|
+
# There are other options you can specify. For more information, see the
|
109
|
+
# Scribd::Document.save method.
|
110
|
+
|
111
|
+
def upload(options)
|
112
|
+
raise NotReadyError, "User hasn't been created yet" unless created?
|
113
|
+
Document.create options.merge(:owner => self)
|
114
|
+
end
|
115
|
+
|
116
|
+
class << self
|
117
|
+
alias_method :signup, :create
|
118
|
+
end
|
119
|
+
|
120
|
+
# Logs into Scribd using the given username and password. This user will be
|
121
|
+
# used for all subsequent Scribd API calls. You must log in before you can
|
122
|
+
# use protected API functions. Returns the Scribd::User instance for the
|
123
|
+
# logged in user.
|
124
|
+
|
125
|
+
def self.login(username, password)
|
126
|
+
response = API.instance.send_request('user.login', { :username => username, :password => password })
|
127
|
+
xml = response.get_elements('/rsp')[0]
|
128
|
+
user = User.new(:xml => xml)
|
129
|
+
API.instance.user = user
|
130
|
+
return user
|
131
|
+
end
|
132
|
+
|
133
|
+
# Returns the +user_id+ attribute.
|
134
|
+
|
135
|
+
def id
|
136
|
+
self.user_id
|
137
|
+
end
|
138
|
+
|
139
|
+
def to_s #:nodoc:
|
140
|
+
@attributes[:username]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
data/sample/01_upload.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# Example 1 - Uploading a test text file and removing it afterwards.
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rscribd'
|
5
|
+
|
6
|
+
# Use your API key / secret here
|
7
|
+
api_key = ''
|
8
|
+
api_secret = ''
|
9
|
+
|
10
|
+
# Create a scribd object
|
11
|
+
Scribd::API.instance.key = api_key
|
12
|
+
Scribd::API.instance.secret = api_secret
|
13
|
+
#Scribd::API.instance.debug = true
|
14
|
+
|
15
|
+
begin
|
16
|
+
Scribd::User.login 'LOGIN', 'PASSWORD'
|
17
|
+
# Upload the document from a file
|
18
|
+
print "Uploading a document ... "
|
19
|
+
|
20
|
+
doc = Scribd::Document.upload(:file => 'test.txt')
|
21
|
+
puts "Done doc_id=#{doc.id}, doc_access_key=#{doc.access_key}"
|
22
|
+
|
23
|
+
# Poll API until conversion is complete
|
24
|
+
while (doc.conversion_status == 'PROCESSING')
|
25
|
+
puts "Document conversion is processing"
|
26
|
+
sleep(2) # Very important to sleep to prevent a runaway loop that will get your app blocked
|
27
|
+
end
|
28
|
+
puts "Document conversion is complete"
|
29
|
+
|
30
|
+
# Edit various options of the document
|
31
|
+
# Note you can also edit options before your doc is done converting
|
32
|
+
doc.title = 'This is a test doc!'
|
33
|
+
doc.description = "I'm testing out the Scribd API!"
|
34
|
+
doc.access = 'private'
|
35
|
+
doc.language = 'en'
|
36
|
+
doc.license = 'c'
|
37
|
+
doc.tags = 'test,api'
|
38
|
+
doc.show_ads = true
|
39
|
+
doc.save
|
40
|
+
|
41
|
+
# Delete the uploaded document
|
42
|
+
print "Deleting a document ... "
|
43
|
+
doc.destroy
|
44
|
+
puts "Done doc_id=#{doc.id}"
|
45
|
+
rescue Scribd::ResponseError => e
|
46
|
+
puts "failed code=#{e.code} error='#{e.message}'"
|
47
|
+
end
|
data/sample/02_user.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# Example 2 - Signing in as a user, and accessing a user's files.
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rscribd'
|
5
|
+
|
6
|
+
# Use your API key / secret here
|
7
|
+
api_key = ''
|
8
|
+
api_secret = ''
|
9
|
+
|
10
|
+
# Edit these to a real Scribd username/password pair
|
11
|
+
username = ''
|
12
|
+
password = ''
|
13
|
+
|
14
|
+
# Create a scribd object
|
15
|
+
Scribd::API.instance.key = api_key
|
16
|
+
Scribd::API.instance.secret = api_secret
|
17
|
+
#Scribd::API.instance.debug = true
|
18
|
+
|
19
|
+
begin
|
20
|
+
|
21
|
+
# Login your Scribd API object as a particular user
|
22
|
+
# NOTE: Edit this to the username and password of a real Scribd user
|
23
|
+
user = Scribd::User.login username, password
|
24
|
+
|
25
|
+
docs = user.documents
|
26
|
+
|
27
|
+
puts "User #{user.username} has #{docs.size} docs"
|
28
|
+
if docs.size > 0
|
29
|
+
puts "User's docs:"
|
30
|
+
for doc in docs
|
31
|
+
puts "#{doc.title}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
results = Scribd::Document.find(:all, :query => 'checklist') # Search over the user's docs for the string 'checklist'
|
36
|
+
puts "Search through docs turned up #{results.size} results:"
|
37
|
+
for doc in results
|
38
|
+
puts "#{doc.title}"
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
rescue Scribd::ResponseError => e
|
43
|
+
puts "failed code=#{e.code} error='#{e.message}'"
|
44
|
+
end
|
data/sample/test.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
This is a simple test text file to try out the neat Scribd API.
|
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: scribd-rscribd
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jared Friedman, Tim Morgan
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-07-09 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: mime-types
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: hoe
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.8.2
|
34
|
+
version:
|
35
|
+
description: This gem provides a simple and powerful library for the Scribd API, allowing you to write Ruby applications or Ruby on Rails websites that upload, convert, display, search, and control documents in many formats. For more information on the Scribd platform, visit http://www.scribd.com/publisher
|
36
|
+
email: api@scribd.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- History.txt
|
43
|
+
- Manifest.txt
|
44
|
+
- README.txt
|
45
|
+
files:
|
46
|
+
- History.txt
|
47
|
+
- Manifest.txt
|
48
|
+
- README.txt
|
49
|
+
- Rakefile
|
50
|
+
- lib/scribdapi.rb
|
51
|
+
- lib/scribddoc.rb
|
52
|
+
- lib/scribderrors.rb
|
53
|
+
- lib/scribdmultiparthack.rb
|
54
|
+
- lib/scribdresource.rb
|
55
|
+
- lib/rscribd.rb
|
56
|
+
- lib/scribduser.rb
|
57
|
+
- sample/01_upload.rb
|
58
|
+
- sample/02_user.rb
|
59
|
+
- sample/test.txt
|
60
|
+
has_rdoc: true
|
61
|
+
homepage: http://github.com/scribd/rscribd
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options:
|
64
|
+
- --main
|
65
|
+
- README.txt
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: "0"
|
73
|
+
version:
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: "0"
|
79
|
+
version:
|
80
|
+
requirements: []
|
81
|
+
|
82
|
+
rubyforge_project: rscribd
|
83
|
+
rubygems_version: 1.2.0
|
84
|
+
signing_key:
|
85
|
+
specification_version: 2
|
86
|
+
summary: Ruby client library for the Scribd API
|
87
|
+
test_files: []
|
88
|
+
|