divshare 0.1.0

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.
@@ -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