divshare 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ == 0.1.0 2008-03-19
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 Eric Watson
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,35 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ config/hoe.rb
7
+ config/requirements.rb
8
+ lib/divshare.rb
9
+ lib/divshare/client.rb
10
+ lib/divshare/divshare_file.rb
11
+ lib/divshare/errors.rb
12
+ lib/divshare/post_args.rb
13
+ lib/divshare/user.rb
14
+ lib/divshare/version.rb
15
+ log/debug.log
16
+ script/destroy
17
+ script/generate
18
+ script/test_run_client
19
+ script/txt2html
20
+ setup.rb
21
+ spec/client_spec.rb
22
+ spec/divshare_file_spec.rb
23
+ spec/fixtures/divshare_mock_valid_audio.html
24
+ spec/fixtures/divshare_mock_valid_video.html
25
+ spec/post_args_spec.rb
26
+ spec/spec_helper.rb
27
+ spec/user_spec.rb
28
+ tasks/deployment.rake
29
+ tasks/environment.rake
30
+ tasks/website.rake
31
+ website/index.html
32
+ website/index.txt
33
+ website/javascripts/rounded_corners_lite.inc.js
34
+ website/stylesheets/screen.css
35
+ website/template.rhtml
@@ -0,0 +1,50 @@
1
+ == Description
2
+
3
+ The divshare gem makes it easier to use the divshare API. To use it, you need
4
+ to create a Divshare account and sign up for an API key.
5
+
6
+ == Usage
7
+
8
+ Here's a brief script that illustrates the basic operations:
9
+
10
+ require 'rubygems'
11
+ require 'divshare'
12
+ client = Divshare::Client.new(api_key, api_secret)
13
+ client.login(email, password)
14
+ all_my_files = client.get_user_files
15
+ all_my_files.each do |f|
16
+ print "#{f.file_name} (#{f.file_size}) "
17
+ puts "was last downloaded #{Time.at(f.last_downloaded_at.to_i)}"
18
+ end
19
+ client.logout
20
+
21
+ Now, going through the same script step-by-step. Use your Divshare API
22
+ key and secret (comes with key) to create a client:
23
+
24
+ client = Divshare::Client.new(api_key, api_secret)
25
+
26
+ Login using the credentials for your Divshare account:
27
+
28
+ client.login(email, password)
29
+
30
+ Get an array of all of your files:
31
+
32
+ all_my_files = client.get_user_files
33
+
34
+ Do something with the files:
35
+
36
+ all_my_files.each do |f|
37
+ print "#{f.file_name} (#{f.file_size}) "
38
+ puts "was last downloaded #{Time.at(f.last_downloaded_at.to_i)}"
39
+ end
40
+
41
+ Logout
42
+
43
+ client.logout
44
+
45
+ == Installation
46
+
47
+ Install using rubygems:
48
+
49
+ sudo gem install divshare
50
+
@@ -0,0 +1,4 @@
1
+ require 'config/requirements'
2
+ require 'config/hoe' # setup Hoe + all gem configuration
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
@@ -0,0 +1,72 @@
1
+ require 'divshare/version'
2
+
3
+ AUTHOR = 'Eric Watson' # can also be an array of Authors
4
+ EMAIL = "wasnotrice@gmail.com"
5
+ DESCRIPTION = "A Ruby interface to the DivShare file hosting service"
6
+ GEM_NAME = 'divshare' # what ppl will type to install your gem
7
+ RUBYFORGE_PROJECT = 'divshare' # The unix name for your project
8
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
9
+ DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
10
+
11
+ @config_file = "~/.rubyforge/user-config.yml"
12
+ @config = nil
13
+ RUBYFORGE_USERNAME = "unknown"
14
+ def rubyforge_username
15
+ unless @config
16
+ begin
17
+ @config = YAML.load(File.read(File.expand_path(@config_file)))
18
+ rescue
19
+ puts <<-EOS
20
+ ERROR: No rubyforge config file found: #{@config_file}
21
+ Run 'rubyforge setup' to prepare your env for access to Rubyforge
22
+ - See http://newgem.rubyforge.org/rubyforge.html for more details
23
+ EOS
24
+ exit
25
+ end
26
+ end
27
+ RUBYFORGE_USERNAME.replace @config["username"]
28
+ end
29
+
30
+
31
+ REV = nil
32
+ # UNCOMMENT IF REQUIRED:
33
+ # REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil
34
+ VERS = Divshare::VERSION::STRING + (REV ? ".#{REV}" : "")
35
+ RDOC_OPTS = ['--quiet', '--title', 'divshare documentation',
36
+ "--opname", "index.html",
37
+ "--line-numbers",
38
+ "--main", "README",
39
+ "--inline-source"]
40
+
41
+ class Hoe
42
+ def extra_deps
43
+ @extra_deps.reject! { |x| Array(x).first == 'hoe' }
44
+ @extra_deps
45
+ end
46
+ end
47
+
48
+ # Generate all the Rake tasks
49
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
50
+ hoe = Hoe.new(GEM_NAME, VERS) do |p|
51
+ p.author = AUTHOR
52
+ p.description = DESCRIPTION
53
+ p.email = EMAIL
54
+ p.summary = DESCRIPTION
55
+ p.url = HOMEPATH
56
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
57
+ p.test_globs = ["test/**/test_*.rb"]
58
+ p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
59
+ p.remote_rdoc_dir = '' # Release to root
60
+
61
+ # == Optional
62
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\\n\\n")
63
+ #p.extra_deps = [] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
64
+
65
+ #p.spec_extras = {} # A hash of extra values to set in the gemspec.
66
+
67
+ end
68
+
69
+ CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
70
+ PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
71
+ hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
72
+ hoe.rsync_args = '-av --delete --ignore-errors'
@@ -0,0 +1,17 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ require 'rubygems'
5
+ %w[rake hoe newgem rubigen].each do |req_gem|
6
+ begin
7
+ require req_gem
8
+ rescue LoadError
9
+ puts "This Rakefile requires the '#{req_gem}' RubyGem."
10
+ puts "Installation: gem install #{req_gem} -y"
11
+ exit
12
+ end
13
+ end
14
+
15
+ $:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
16
+
17
+ require 'divshare'
@@ -0,0 +1,4 @@
1
+ require 'divshare/client'
2
+ require 'divshare/divshare_file'
3
+ require 'divshare/post_args'
4
+ require 'divshare/errors'
@@ -0,0 +1,192 @@
1
+ require 'rubygems'
2
+ require 'cgi'
3
+ require 'net/http'
4
+ require 'hpricot'
5
+ require 'digest/md5'
6
+ require 'divshare/errors'
7
+ require 'divshare/divshare_file'
8
+ require 'divshare/post_args'
9
+ require 'divshare/user'
10
+
11
+ module Divshare
12
+ # This is the main class for interacting with the Divshare API. Use it like this:
13
+ #
14
+ # client = Divshare::Client.new(api_key, api_secret)
15
+ # client.login(email, password)
16
+ # files = client.get_files ['abcdefg-123', 'abcdefg-456']
17
+ # upload_ticket = client.get_upload_ticket
18
+ # client.logout
19
+ #
20
+ class Client
21
+ SUCCESS = '1'
22
+ FAILURE = '0'
23
+
24
+ attr_reader :api_key, :api_secret, :post_url, :api_session_key, :email, :password
25
+
26
+ # Creates a Divshare::Client. The <tt>api_key</tt> and <tt>api_secret</tt>
27
+ # are required, but <tt>email</tt> and <tt>password</tt> are optional. If
28
+ # you omit <tt>email</tt> and <tt>password</tt> here, you must send them
29
+ # with link:login when you call it.
30
+ def initialize(api_key, api_secret, email=nil, password=nil)
31
+ @api_key, @api_secret, @email, @password = api_key, api_secret, email, password
32
+ @api_session_key = nil
33
+ @post_url = "http://www.divshare.com/api/"
34
+ end
35
+
36
+ # file_ids should be an array of file ids
37
+ # def get_files(file_ids)
38
+ # file_ids = [file_ids] unless file_ids.respond_to?(:join)
39
+ # response = send_method(:get_files, 'files' => file_ids.join(','))
40
+ # files_from response
41
+ # end
42
+
43
+ # This method replaces the real get_files until the API is cleared up and
44
+ # working properly. Limitation: it can only retrieve files owned by the
45
+ # logged-in user.
46
+ def get_files(file_ids)
47
+ file_ids = [file_ids] unless file_ids.is_a? Array
48
+ files = get_user_files
49
+ puts file_ids.class
50
+ files.delete_if {|f| file_ids.include?(f.file_id) == false}
51
+ end
52
+
53
+ # A convenience method for finding only one file. Returns a single
54
+ # DivshareFile instead of an array.
55
+ def get_file(file_id)
56
+ raise ArgumentError, "Only one file id allowed for this method" if file_id.is_a?(Array)
57
+ get_files(file_id).first
58
+ end
59
+
60
+ # Returns an array of Divshare::DivshareFile objects belonging to the logged-in user. Use <tt>limit</tt> and
61
+ # <tt>offset</tt> to narrow things down.
62
+ def get_user_files(limit=nil, offset=nil)
63
+ args = {}
64
+ args['limit'] = limit unless limit.nil?
65
+ args['offset'] = offset unless offset.nil?
66
+ response = send_method(:get_user_files, args)
67
+ files_from response
68
+ end
69
+
70
+ # Returns an array of Divshare::DivshareFile objects in the specified folder. Use <tt>limit</tt> and
71
+ # <tt>offset</tt> to narrow things down.
72
+ def get_folder_files(folder_id, limit=nil, offset=nil)
73
+ args = {}
74
+ args['limit'] = limit unless limit.nil?
75
+ args['offset'] = offset unless offset.nil?
76
+ args['folder_id'] = folder_id
77
+ response = send_method(:get_folder_files, args)
78
+ files_from response
79
+ end
80
+
81
+ # Returns information about the logged-in user
82
+ def get_user_info
83
+ response = send_method :get_user_info
84
+ user_from response
85
+ end
86
+
87
+ # Returns an upload ticket string for use in uploading files. See
88
+ # http://www.divshare.com/integrate/api#uploading for more information on
89
+ # how to use the upload ticket once you've got it.
90
+ def get_upload_ticket
91
+ response = send_method :get_upload_ticket
92
+ upload_ticket_from response
93
+ end
94
+
95
+ # Login to the Divshare service. Raises Divshare::APIError if login is
96
+ # unsuccessful.
97
+ def login(email=nil, password=nil)
98
+ logout if @api_session_key
99
+ email ||= @email
100
+ password ||= @password
101
+ response = send_method(:login, {'user_email' => email, 'user_password' => password})
102
+ if response.at(:api_session_key)
103
+ @api_session_key = response.at(:api_session_key).inner_html
104
+ else
105
+ raise Divshare::APIError, "Couldn't log in. Received: \n" + response.to_s
106
+ end
107
+ end
108
+
109
+ # Returns true if logout is successful. Raises Divshare::APIError if logout is
110
+ # unsuccessful.
111
+ def logout
112
+ response = send_method(:logout)
113
+ if response.at(:logged_out) && (%w(true 1).include? response.at(:logged_out).inner_html)
114
+ @api_session_key = nil
115
+ else
116
+ raise Divshare::APIError, "Couldn't log out. Received: \n" + response.to_s
117
+ end
118
+ true
119
+ end
120
+
121
+ # Generates the required MD5 signature as described in
122
+ # http://www.divshare.com/integrate/api#sig
123
+ def sign(method, args)
124
+ Digest::MD5.hexdigest(string_to_sign(args))
125
+ end
126
+
127
+ private
128
+ def files_from(xml)
129
+ xml = xml/:file
130
+ xml = [xml] unless xml.respond_to?(:each)
131
+ files = xml.collect { |f| DivshareFile.new f }
132
+ end
133
+
134
+ def user_from(xml)
135
+ xml = xml.at(:user_info)
136
+ Divshare::User.new(xml)
137
+ end
138
+
139
+ def upload_ticket_from(xml)
140
+ xml = xml.at(:upload_ticket).inner_html
141
+ end
142
+
143
+ # Since login and logout aren't easily re-nameable to use method missing
144
+ def send_method(method_id, *params)
145
+ response = http_post(method_id, *params)
146
+ xml = Hpricot(response).at(:response)
147
+ if xml[:status] == FAILURE
148
+ errors = (xml/:error).collect {|e| e.inner_html}
149
+ raise Divshare::APIError, errors.join("\n")
150
+ end
151
+ xml
152
+ end
153
+
154
+ def post_args(method, args)
155
+ PostArgs.new(self, method, args)
156
+ end
157
+
158
+ def http_post(method, args={})
159
+ url = URI.parse(@post_url)
160
+ tries = 3
161
+ response = ""
162
+ begin
163
+ response = Net::HTTP.post_form(url, post_args(method, args)).body
164
+ rescue
165
+ tries -= 1
166
+ puts "Tries == '#{tries}'"
167
+ if tries > 0
168
+ retry
169
+ else
170
+ raise Divshare::ConnectionError, "Couldn't connect for '#{method}' using #{post_args(method, args)}"
171
+ end
172
+ end
173
+ response
174
+ end
175
+
176
+ # From http://www.divshare.com/integrate/api
177
+ #
178
+ # * Your secret key is 123-secret.
179
+ # * Your session key is 456-session.
180
+ # * You are using the get_user_files method, and you're sending the
181
+ # parameters limit=5 and offset=10.
182
+ #
183
+ # The string used to create your signature will be:
184
+ # 123-secret456-sessionlimit5offset10. Note that the parameters must be in
185
+ # alphabetical order, so limit always comes before offset. Each parameter
186
+ # should be paired with its value as shown.
187
+ def string_to_sign(args)
188
+ args_for_string = args.dup.delete_if {|k,v| %w(api_key method api_sig api_session_key).include?(k) }
189
+ "#{@api_secret}#{@api_session_key}#{args_for_string.to_a.sort.flatten.join}"
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,112 @@
1
+ require 'rubygems'
2
+ require 'hpricot'
3
+
4
+ module Divshare
5
+ # This class represents a file stored at Divshare. Queries from a
6
+ # Divshare::Client to the API return arrays of DivshareFile objects. You
7
+ # can gather any information provided by the API from this object, as well
8
+ # as generate a tag for embedding the file in HTML.
9
+ #
10
+ # The proper embed tag depends on the file type. The Divshare API doesn't
11
+ # distinguish these different filetypes, however, so this library tries to
12
+ # figure out the filetype from the file's extension.
13
+ class DivshareFile
14
+ ATTRIBUTES = %w(file_id file_name file_description file_size downloads last_downloaded_at uploaded_at folder_title folder_id)
15
+ AUDIO = /^\.(mp3)$/i
16
+ VIDEO = /^\.(avi|wmv|mov|mpg|asf)$/i
17
+ DOCUMENT = /^\.(doc|pdf|ppt)$/i
18
+ IMAGE = /^\.(jpg|gif|png)$/i
19
+
20
+ attr_reader *ATTRIBUTES
21
+ attr_reader :medium
22
+
23
+ def initialize(xml=Hpricot(""))
24
+ ATTRIBUTES.each do |attr|
25
+ if xml.at(attr)
26
+ value = xml.at(attr).inner_html
27
+ instance_variable_set("@#{attr}", value)
28
+ end
29
+ end
30
+ @medium = find_medium
31
+ end
32
+
33
+ def audio?
34
+ @medium == "audio"
35
+ end
36
+
37
+ def document?
38
+ @medium == "document"
39
+ end
40
+
41
+ def video?
42
+ @medium == "video"
43
+ end
44
+
45
+ def image?
46
+ @medium == "image"
47
+ end
48
+
49
+ # Image options
50
+ #
51
+ # :size => :fullsize | :midsize | :thumb
52
+ def embed_tag(opts={})
53
+ return nil if @medium.nil?
54
+ self.send("#{@medium}_embed_tag_template", opts).gsub('[FILE ID]', @file_id)
55
+ end
56
+
57
+ def to_s
58
+ s = "#{file_name} <Divshare::DivshareFile>\n"
59
+ ATTRIBUTES.each { |a| s << sprintf(" %s: %s\n", a, self.send(a)) }
60
+ s
61
+ end
62
+
63
+ private
64
+ def find_medium
65
+ ext = @file_name ? File.extname(@file_name) : nil
66
+ medium = case
67
+ when AUDIO.match(ext): "audio"
68
+ when VIDEO.match(ext): "video"
69
+ when DOCUMENT.match(ext): "document"
70
+ when IMAGE.match(ext): "image"
71
+ else nil
72
+ end
73
+ end
74
+
75
+ def audio_embed_tag_template(opts={})
76
+ tag = <<-END_OF_TAG
77
+ <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="335" height="47" id="divaudio2">
78
+ <param name="movie" value="http://www.divshare.com/flash/audio?myId=[FILE ID]" />
79
+ <embed src="http://www.divshare.com/flash/audio?myId=[FILE ID]" width="335" height="47" name="divaudio2" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"></embed>
80
+ </object>
81
+ END_OF_TAG
82
+ end
83
+
84
+ def video_embed_tag_template(opts={})
85
+ tag = <<-END_OF_TAG
86
+ <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,18,0" width="425" height="374" id="divflv">
87
+ <param name="movie" value="http://www.divshare.com/flash/video?myId=[FILE ID]" />
88
+ <param name="allowFullScreen" value="true" />
89
+ <embed src="http://www.divshare.com/flash/video?myId=[FILE ID]" width="425" height="374" name="divflv" allowfullscreen="true" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"></embed>
90
+ </object>
91
+ END_OF_TAG
92
+ end
93
+
94
+ def document_embed_tag_template(opts={})
95
+ tag = <<-END_OF_TAG
96
+ <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="560" height="500" id="divdoc">
97
+ <param name="movie" value="http://www.divshare.com/flash/document/[FILE ID]" />
98
+ <embed src="http://www.divshare.com/flash/document/[FILE ID]" width="560" height="500" name="divdoc" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"></embed>
99
+ </object>
100
+ END_OF_TAG
101
+ end
102
+
103
+ def image_embed_tag_template(opts={:size=>:midsize})
104
+ size = case opts[:size]
105
+ when :midsize, :mid: "midsize/"
106
+ when :thumb, :thumbnail: "thumb/"
107
+ else ""
108
+ end
109
+ tag = "http://www.divshare.com/img/#{size}[FILE ID]"
110
+ end
111
+ end
112
+ end