Browse files

Initial commit

remove README

swap summary with description in gemspec

Initial commit
  • Loading branch information...
1 parent c5702e1 commit 543d32c98dbb97170226b709902e286243255681 @odiszapc committed Nov 18, 2012
View
1 .gitignore
@@ -15,3 +15,4 @@ spec/reports
test/tmp
test/version_tmp
tmp
+.idea/*
View
2 Gemfile
@@ -1,4 +1,4 @@
source 'https://rubygems.org'
# Specify your gem's dependencies in rapidshare-ext.gemspec
-gemspec
+gemspec
View
17 History.md
@@ -0,0 +1,17 @@
+## ver.0.0.1 2012-11-17
+
+It has began. We have the features as follows:
+
+* [added] Creating folders
+* [added] Removing folders
+* [added] Moving folders
+* [added] Uploading files
+* [added] Removing files
+* [added] Renaming files
+* [added] Moving files
+* [added] Viewing folders hierarchy
+* [added] Dealing with orphan folders
+* [added] Erasing all account data
+* [added] Get file info
+* [added] Folder/file identification by path
+* [added] Integration tests (Forkers, be careful, account is fully erased after each test !!!)
View
1 README
@@ -1 +0,0 @@
-This file was created by JetBrains RubyMine (Enoki) RM-122.782 for binding GitHub repository
View
143 README.md
@@ -1,10 +1,12 @@
# Rapidshare::Ext
-TODO: Write a gem description
+Makes your interactions with Rapidshare API more pleasant by providing new handy features: creating/moving/deleting files/folders in a user friendly way, upload files, etc.
+
+This gem extends the existing one - https://github.com/defkode/rapidshare, so it has all features implemented in the source library. In addition, it much simplifies operations with data in your Rapidshare account.
## Installation
-Add this line to your application's Gemfile:
+Add this line to your Gemfile:
gem 'rapidshare-ext'
@@ -18,12 +20,145 @@ Or install it yourself as:
## Usage
-TODO: Write usage instructions here
+First, create an instance:
+```ruby
+api = Rapidshare::API.new(:login => 'my_login', :password => 'my_password')
+api = Rapidshare::API.new(:cookie => 'cookie_here') # More preferable way
+```
+
+### Folders
+As you note you can have a hierarchy of folders in your account.
+
+Creating folders:
+```ruby
+folder_id = api.add_folder "a/b/c" # => <FOLDER ID>
+```
+
+Deleting folders:
+```ruby
+api.remove_folder("/a/b/c")
+```
+
+Moving folders:
+```ruby
+api.move_folder("/a/b/c", :to => "/a")
+```
+This moves folder "c" from directory "/a/b/" and places it under the directory "/a"
+
+Get hierarchy of all folders in account:
+```ruby
+api.folders_hierarchy
+# => {
+# <folder ID> => {
+# :parent => <parent folder ID>,
+# :name => <folder name>,
+# :path => <folder absolute path>
+# },
+# ...
+# }
+```
+
+Note, that after the folder hierarhy is generated first time the data is cached permanently to improve performance.
+
+So, if you want to invalidate the cache just call the above method with trailing "!":
+```ruby
+api.folders_hierarchy!
+```
+
+If folder tree is inconsistent (orphans are found) the Exception will be thrown. To automatically normalize the tree, call the method with :consistent flag:
+```ruby
+api.folders_hierarchy :consistent => true
+```
+Be careful with a tree consistency, orphan folders may contain a critical data.
+
+A more secure way to deal with consistency is to fix orphans first and then generate folders tree:
+```ruby
+api.add_folder "/garbage"
+api.move_orphans :to => "/garbage" # Collect all orphans and place them under the /garbage folder
+tree = api.folders_hierarchy
+```
+
+### Orphans
+Ok, the Rapidshare has its common problem: orphan folders. What is this? For example we have the following directory tree:
+```
+ROOT
+`-a <- RS API allows us to delete JUST THIS folder, so hierarchy relation between folders will be lost and the folders "c" and "b" will become orphans
+ `-b
+ `-c
+```
+Orphans is invisible in your File Manager on the Rapidshare web site, so you may want to hide data in that way (stupid idea)
+We can fix it by detecting all orphan fodlers and moving them to a specific fodler:
+```ruby
+move_orphans :to => "/"
+```
+
+Or we can just kill'em all:
+```ruby
+remove_orphans!
+```
+
+Get folder ID or path:
+```ruby
+id = api.folder_id("/foo/bar") # <ID>
+api.folder_path(id) # "/foo/bar"
+```
+
+### Files
+
+File uploading is simple now:
+```ruby
+api.upload("/home/odiszapc/my_damn_cat.mov", :to => "/gallery/video", :as => "cat1.mov")
+# => {
+# :id => 1,
+# :size => 12345, # File size in bytes
+# :checksum => <MD5>,
+# :url => <DOWNLOAD_URL>, # https://rapidshare/.......
+# :already_exists? => true/false # Does the file already exists within a specific folder, real uploading will not being performed in this case
+#}
+```
+After uploading has been completed the file will be stored in a Rapidshare as "/gallery/video/cat1.mov"
+To get download url after uploading:
+```ruby
+result = api.upload("/home/odiszapc/my_damn_cat.mov", :to => "/gallery/video", :as => "cat1.mov")
+result[:url]
+```
+
+By default, file is uploaded to root folder:
+```ruby
+api.upload("/home/odiszapc/my_damn_humster.mov")
+```
+
+Deleting files:
+```ruby
+api.remove_file("/putin/is/a/good/reason/to/live/abroad/ticket_to_Nikaragua.jpg")
+```
+
+Renaming files:
+```ruby
+api.rename_file("/foo/bar.rar", "baz.rar")
+```
+
+Moving files:
+```ruby
+api.move_file("/foo/bar/baz.rar", :to => "/foo") # new file path: "/foo/baz.rar"
+api.move_file("/foo/bar/baz.rar") # move to a root folder
+```
+
+Get file ID:
+```ruby
+api.file_id("/foo/bar/baz.rar") # => <ID>
+```
+
+### Account
+You can null your account by deleting all data involved. Be carefull with it, all data will be lost:
+```ruby
+api.erase_all_data!
+```
## Contributing
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Added some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
-5. Create new Pull Request
+5. Create new Pull Request
View
22 Rakefile
@@ -1,2 +1,24 @@
#!/usr/bin/env rake
require "bundler/gem_tasks"
+require 'rake/testtask'
+include Rake::DSL
+
+desc "Run tests"
+task :test => ['test:unit', 'test:integration']
+
+task :default => :test
+
+namespace :test do
+ Rake::TestTask.new :unit do |t|
+ t.libs << 'test'
+ t.pattern = 'test/unit/*_test.rb'
+ end
+
+ Rake::TestTask.new :integration do |t|
+ t.libs << 'test'
+ t.pattern = 'test/integration/*_test.rb'
+ end
+end
+
+
+
View
61 lib/rapidshare-base/api.rb
@@ -0,0 +1,61 @@
+module Rapidshare
+ class API
+ # TODO this class is getting long. keep general request-related and helper
+ # method here and move specific method calls like :getaccountdetails to other
+ # class (service)?
+ #
+ # TODO enable users to define their own parsers and pass them as code blocks?
+ # not really that practical, but it would be a cool piece of code :)
+
+ # Calls specific RapidShare API service and returns result.
+ #
+ # Throws exception if error is received from RapidShare API.
+ #
+ # Params:
+ # * *service_name* - name of the RapidShare service, for example +checkfiles+
+ # * *params* - hash of service parameters and options (listed below)
+ # * *parser* - option, determines how the response body will be parsed:
+ # * *none* - default value, returns response body as it is
+ # * *csv* - comma-separated values, for example: _getrapidtranslogs_.
+ # Returns array or arrays, one array per each line.
+ # * *hash* - lines with key and value separated by "=", for example:
+ # _getaccountdetails_. Returns hash.
+ # * *server* - option, determines which server will be used to send request
+ #
+ def self.request(service_name, params = {})
+ params.symbolize_keys!
+
+ parser = (params.delete(:parser) || :none).to_sym
+ unless [:none, :csv, :hash].include?(parser)
+ raise Rapidshare::API::Error.new("Invalid parser for request method: #{parser}")
+ end
+
+ server = params.delete(:server)
+ server_url = server ? "https://#{server}/cgi-bin/rsapi.cgi?sub=%s&%s" : URL
+
+ http_method = (params.delete(:method) || :get).to_sym
+ raise Exception, "invalid HTTP method #{http_method}" unless self.respond_to? http_method
+
+ case http_method
+ when :get
+ response = self.send(http_method, server_url % [service_name, params.to_query])
+ else
+ params[:sub] = service_name
+ response = self.send(http_method, server_url.gsub(/\?sub=%s&%s$/,''), params)
+ end
+
+ if response.start_with?(ERROR_PREFIX)
+ case error = response.sub(ERROR_PREFIX, "").split('.').first
+ when "Login failed"
+ raise Rapidshare::API::Error::LoginFailed
+ when "Invalid routine called"
+ raise Rapidshare::API::Error::InvalidRoutineCalled.new(service_name)
+ else
+ raise Rapidshare::API::Error.new(error)
+ end
+ end
+
+ self.parse_response(parser, response)
+ end
+ end
+end
View
11 lib/rapidshare-ext.rb
@@ -1,7 +1,10 @@
+require "rest-client"
+require "rapidshare"
+require "rapidshare-base/utils"
+require "rapidshare-base/api"
+require "rapidshare-ext/api"
require "rapidshare-ext/version"
-module Rapidshare
- module Ext
- # Your code goes here...
- end
+class Rapidshare::API
+ include Rapidshare::Ext::API
end
View
499 lib/rapidshare-ext/api.rb
@@ -0,0 +1,499 @@
+module Rapidshare
+ module Ext
+ module API
+
+ FILE_COLUMNS = "downloads,lastdownload,filename,size,serverid,type,x,y,realfolder,killdeadline,uploadtime,comment,md5hex,licids,sentby"
+
+ # @param [String] path Folder name with absolute path to be created
+ # @param [Hash] params
+ # @return [Integer]
+ #
+ # Creates a folder in a Rapidshare virtual filesystem
+ #
+ # api.add_folder("/a/b/c") #=> <Random folder ID from Rapidshare>, 1234 for example
+ def add_folder(path, params = {})
+ @tree = folders_hierarchy
+ i = 1
+ parent = 0
+ folder_id = nil
+ while i <= path.split('/').count do
+ base_path = path.split('/')[0,i].join('/')
+ folder_id = self.folder_id base_path
+ if folder_id
+ parent = folder_id
+ i += 1
+ else
+ # Create folder
+ folder_name = path.split('/')[i-1]
+ add_folder_params = {
+ :name => folder_name,
+ :parent => parent
+ }.merge params
+
+ # The following code deals with #{} because of rest client #to_i returns HTTP code
+ folder_id = "#{addrealfolder(add_folder_params)}".to_i
+ raise "error while creating folder" if parent < 0
+ @tree[folder_id] = {
+ :parent => parent,
+ :name => folder_name,
+ :path => (@tree[parent] || {})[:path].to_s + ('/' if @tree[parent]).to_s + folder_name
+ }
+ parent = folder_id
+ path == base_path + '/' + folder_name
+ i += 1
+ next
+ end
+ end
+ folder_id
+ end
+
+ # Removes a specified folder
+ #
+ # @param [String] path
+ # @param [Hash] params
+ # @return [Array]
+ #
+ # api.remove_folder("/a/b/c")
+ def remove_folder(path, params = {})
+ folder_id = self.folder_id path_trim(path)
+ raise Exception, "Folder #{path} could not be found" if folder_id.nil?
+
+ # TODO
+ tree = folders_hierarchy :from => path
+ tree.each_pair do |child_folder_id, data|
+ delrealfolder_params = {
+ :realfolder => child_folder_id
+ }.merge params
+
+ delrealfolder delrealfolder_params
+ @tree.delete folder_id
+ end
+
+ params = {
+ :realfolder => folder_id
+ }.merge params
+
+ delrealfolder params
+
+ @tree.delete folder_id
+ end
+
+ # Moves folder into a specified one
+ #
+ # @param [String] source_path
+ # @param [Hash] params
+ # :to => <destination folder path>, default: "/"
+ #
+ # api.move_folder("/a/b/c", :to => "/a")
+ def move_folder(source_path, params = {})
+ dest_path = path_trim(params.delete(:to) || '/')
+ source_folder_id = folder_id(source_path)
+ dest_folder_id = folder_id(dest_path)
+
+ params = {
+ :realfolder => source_folder_id,
+ :newparent => dest_folder_id
+ }.merge params
+
+ moverealfolder params
+
+ @tree = folders_hierarchy
+ @tree[source_folder_id][:parent] = dest_folder_id
+ @tree[source_folder_id][:path] = "#{folder_path(dest_folder_id)}/#{@tree[source_folder_id][:name]}"
+ true
+ end
+
+ # Upload file to a specified folder
+ #
+ # @param [String] file_path
+ # @param [Hash] params
+ # <tt>:to</tt>::
+ # Folder to place uploaded file to, default: "/"
+ # <tt>:as</tt>::
+ # The name file will have in storage after it has been uploaded
+ #
+ # api.upload("/home/odiszapc/my_damn_cat.mov", :to => "/gallery/video", :as => "cat1.mov")
+ def upload(file_path, params = {})
+ raise Exception unless File.exist? file_path
+ dest_path = path_trim(params.delete(:to) || '/')
+ folder_id = self.add_folder dest_path
+ file_name = params.delete(:as) || File.basename(file_path)
+
+ # Check file already exists within a folder
+ listfiles_params = {
+ :realfolder => folder_id,
+ :filename => "#{file_name}",
+ :fields => "md5hex,size",
+ :parser => :csv
+ }
+ listfiles_response = self.listfiles listfiles_params
+
+ # In case of file is not existing upload it
+ if "NONE" == listfiles_response[0][0]
+ upload_server = "rs#{self.nextuploadserver}.rapidshare.com"
+
+ upload_params = {
+ :server => upload_server,
+ :folder => folder_id,
+ :filename => file_name,
+ :filecontent => file_path,
+ :method => :post,
+ :parser => :csv
+ }.merge params
+
+ resp = request(:upload, upload_params)
+ raise Exception, "File uploading failed: #{resp.inspect}" unless "COMPLETE" == resp[0][0]
+
+ id = resp[1][0].to_i
+ md5_hash = resp[1][3]
+ size = resp[1][2].to_i
+ already_exists = false
+ else
+ id = listfiles_response[0][0].to_i
+ md5_hash = listfiles_response[0][1]
+ size = listfiles_response[0][2].to_i
+ already_exists = true
+ end
+
+ raise Exception, "Invalid File ID: #{resp.inspect}" unless id
+ raise Exception, "Invalid MD5 hash: #{resp.inspect}" unless md5_hash
+ raise Exception, "Invalid File Size: #{resp.inspect}" unless size
+
+ {
+ :id => id,
+ :size => size,
+ :checksum => md5_hash.downcase,
+ :url => "https://rapidshare.com/files/#{id}/#{URI::encode file_name}",
+ :already_exists? => already_exists
+ }
+ end
+
+ # Delete file
+ #
+ # @param [String] path
+ # @param [Hash] params
+ #
+ # api.remove_file("/putin/is/a/good/reason/to/live/abroad/ticket_to_Nikaragua.jpg")
+ def remove_file(path, params = {})
+ params = {
+ :files => file_id(path).to_s
+ }.merge params
+
+ deletefiles params
+ end
+
+ # Rename file
+ #
+ # @param [String] remote_path
+ # @param [String] name
+ # @param [Hash] params
+ #
+ # api.rename_file("/foo/bar.rar", "baz.rar")
+ def rename_file(remote_path, name, params = {})
+ file_id = file_id remote_path
+
+ params = {
+ :fileid => file_id,
+ :newname => name
+ }.merge params
+
+ renamefile params
+ # TODO: duplicates check
+ end
+
+ # Moves file to a specified folder
+ #
+ # @param [String] remote_path
+ # @param [Hash] params
+ # <tt>:to</tt>::
+ # Destination folder path, default: "/"
+ #
+ # api.move_file("/foo/bar/baz.rar", :to => "/foo")
+ # api.move_file("/foo/bar/baz.rar") # move to a root folder
+ def move_file(remote_path, params = {})
+ file_id = file_id remote_path
+ dest_path = path_trim(params.delete(:to) || '/')
+
+ params = {
+ :files => file_id,
+ :realfolder => folder_id(dest_path)
+ }.merge params
+
+ movefilestorealfolder params
+ end
+
+ # See #folders_hierarchy method
+ def folders_hierarchy!(params = {})
+ params[:force] = true
+ folders_hierarchy params
+ end
+
+ alias :reload! :folders_hierarchy!
+
+ # Build folders hierarchy in the following format:
+ # {
+ # <folder ID> => {
+ # :parent => <parent folder ID>,
+ # :name => <folder name>,
+ # :path => <folder absolute path>
+ # },
+ # ...
+ # }
+ #
+ # @param [Hash] params
+ # <tt>:force</tt>::
+ # Invalidate cached tree, default: false
+ # After each call of this method the generated tree will be saved as cache
+ # to avoid unnecessary queries to be performed fpr a future calls
+ # <tt>:consistent</tt>::
+ # Delete all found orphans, default: false
+ def folders_hierarchy(params = {})
+ force_load = params.delete :force
+ from_folder_path = path_trim(params.delete(:from) || '/')
+ remove_orphans = params.delete(:consistent)
+
+ if @tree && !force_load
+ if from_folder_path.empty?
+ return @tree
+ else
+ return slice_tree @tree, :from => from_folder_path
+ end
+ end
+
+ return @tree if @tree && !force_load # TODO: about slices here (:from parameter)
+
+ from_folder_id = folder_id from_folder_path
+ raise Exception, "Folder #{from_folder_path} could not be found" if from_folder_id.nil?
+
+ response = listrealfolders
+
+ if 'NONE' == response
+ @tree = {}
+ else
+ intermediate = response.split(' ').map do |str|
+ params = str.split ','
+ [params[0].to_i, {:parent => params[1].to_i, :name => params[2]}]
+ end
+
+ @tree = Hash[intermediate]
+ end
+
+ # Kill orphans
+ remove_orphans! if remove_orphans
+
+ # Validate folder tree consistency
+ @tree.each_pair do |folder_id, data|
+ parent_id = data[:parent]
+ if !parent_id.zero? && @tree[parent_id].nil?
+
+ error = "There is no parent folder with id ##{data[:parent]} for the folder \"#{data[:name]}\" [#{folder_id}]"
+ raise error
+ end
+ end
+
+ @tree.each_pair do |folder_id, data|
+ @tree[folder_id][:path] = folder_path folder_id
+ end
+
+ @tree = slice_tree @tree, :from => from_folder_path unless from_folder_path.empty?
+ @tree
+ end
+
+ # Build tree relative to a specified folder
+ # If the source tree is:
+ # tree = {
+ # 1 => {:parent => 0, :name => "a", :path => "a"},
+ # 2 => {:parent => 1, :name => "b", :path => "a/b"},
+ # 3 => {:parent => 2, :name => "c", :path => "a/b/c"},
+ # ...
+ # }
+ # slice_tree tree, :from => "/a"
+ # Result will be as follows:
+ # {
+ # 2 => {:parent => 1, :name => "b", :path => "b"},
+ # 3 => {:parent => 2, :name => "c", :path => "b/c"},
+ # ...
+ # }
+ def slice_tree(tree, params = {})
+ from_folder_path = path_trim(params.delete(:from) || '/')
+
+ result_tree = tree.dup
+
+ unless from_folder_path == ''
+
+ result_tree.keep_if do |folder_id, data|
+ data[:path].start_with? "#{from_folder_path}/"
+ end
+
+ result_tree.each_pair do |folder_id, data|
+ path = result_tree[folder_id][:path]
+ result_tree[folder_id][:path] = path.gsub /#{from_folder_path.gsub /\//, '\/'}\//, ''
+ end
+ end
+
+ result_tree
+ end
+
+ # Fix inconsistent folder tree (Yes, getting a broken folder hierarchy is possible with a stupid Rapidshare API)
+ # by deleting orphan folders (folders with no parent folder), this folders are invisible in Rapidshare File Manager
+ # So, this method deletes orphan folders
+ def remove_orphans!
+ @tree = folders_hierarchy
+ @tree.each_pair do |folder_id, data|
+ @tree.delete_if do |folder_id, data|
+ if orphan? folder_id
+ delrealfolder :realfolder => folder_id
+ true
+ end
+ end
+ end
+ end
+
+ # Places all existing orphan folders under the specific folder
+ # Orphan folder is a folder with non existing parent (yes, it's possible)
+ #
+ # Example:
+ # move_orphans :to => "/"
+ def move_orphans(params = {})
+ new_folder = folder_id params[:to].to_s
+ orphans = detect_gaps.join(',')
+ if orphans.any?
+ moverealfolder :realfolder => orphans.join(','), :newparent => new_folder
+ end
+ end
+
+ # Returns gap list between folders
+ # See #gap? for example
+ def detect_gaps
+ @tree = folders_hierarchy
+ @tree.keep_if do |folder_id, data|
+ gap? folder_id # This is wrong
+ end.keys
+ end
+
+ # The name speaks for itself
+ # WARNING!!! All data will be lost!!!
+ # Use it carefully
+ def erase_all_data!
+ @tree = folders_hierarchy
+ @tree.keys.each do |folder_id|
+ delrealfolder :realfolder => folder_id
+ end
+ folders_hierarchy!
+ end
+
+ # Check if folder with given id placed on the bottom of folder hierarchy
+ def root_folder?(folder_id)
+ @tree = folders_hierarchy
+ @tree[folder_id][:parent].zero?
+ end
+
+ # Check if the given folder has no parent
+ def gap?(folder_id)
+ @tree = folders_hierarchy
+ parent_id = @tree[folder_id][:parent]
+ @tree[parent_id].nil?
+ end
+
+ # Check if folder has any gaps in it hierarchy
+ # For example we have the following hierarchy:
+ #
+ # ROOT
+ # `-a <- if we remove just this folder then the folder "c" and "b" will become orphans
+ # `-b
+ # `-c
+ def orphan?(folder_id)
+ @tree = folders_hierarchy
+ parent_id = @tree[folder_id][:parent]
+ return false if root_folder? folder_id
+ return true if gap? folder_id
+ orphan?(parent_id)
+ end
+
+ # Translate folder ID to a human readable path
+ #
+ # api.folder_path(123) # -> "foo/bar/baz"
+ def folder_path(folder_id)
+ @tree = folders_hierarchy
+ parent_id = @tree[folder_id][:parent]
+ (folder_path(parent_id) if parent_id.nonzero?).to_s + ('/' if parent_id.nonzero?).to_s + @tree[folder_id][:name]
+ end
+
+ # Get folder ID by path
+ #
+ # api.folder_id("foo/bar/baz") # -> 123
+ def folder_id(folder_path)
+ folder_path = path_trim(folder_path)
+ return 0 if folder_path.empty?
+
+ @tree = folders_hierarchy
+ index = @tree.find_index do |folder_id, data|
+ data[:path] == folder_path
+ end
+ @tree.keys[index] unless index.nil?
+ end
+
+ # Get file info in the following format:
+ #
+ # {
+ # :downloads,
+ # :lastdownload,
+ # :filename,
+ # :size,
+ # :serverid,
+ # :type,
+ # :x,
+ # :y,
+ # :realfolder,
+ # :killdeadline,
+ # :uploadtime,
+ # :comment,
+ # :md5hex,
+ # :licids,
+ # :sentby
+ # }
+ # See http://images.rapidshare.com/apidoc.txt for more details
+ def file_info(file_path, params = {})
+ folder_path = File.dirname file_path
+ file_name = File.basename file_path
+
+ folder_id = folder_id folder_path
+
+ listfiles_params = {
+ :realfolder => folder_id,
+ :filename => "#{file_name}",
+ :fields => FILE_COLUMNS,
+ :parser => :csv
+ }.merge params
+
+ resp = listfiles(listfiles_params)[0]
+ return nil if "NONE" == resp[0]
+
+ response = {}
+
+ fields = listfiles_params[:fields].split(',')
+ fields.unshift "id"
+ fields.each_with_index do |value, index|
+ response[value.to_sym] = resp[index]
+ end
+
+ response
+ end
+
+ # Get file ID by absolute path
+ #
+ # api.file_id("foo/bar/baz/file.rar") # -> 456
+ def file_id(file_path, params = {})
+ params[:fields] = ""
+ file_info = file_info file_path, params
+ (file_info || {})[:id].to_i
+ end
+
+ protected
+
+ def path_trim(path)
+ path.gsub(/\A\/+/, '').gsub(/\/+\Z/, '')
+ end
+ end
+ end
+end
View
0 lib/rapidshare/utils.rb
No changes.
View
20 rapidshare-ext.gemspec
@@ -2,16 +2,26 @@
require File.expand_path('../lib/rapidshare-ext/version', __FILE__)
Gem::Specification.new do |gem|
+ gem.name = "rapidshare-ext"
+ gem.version = Rapidshare::Ext::VERSION
+ gem.date = '2012-11-18'
gem.authors = ["odiszapc"]
gem.email = ["odiszapc@gmail.com"]
- gem.description = %q{TODO: Write a gem description}
- gem.summary = %q{TODO: Write a gem summary}
- gem.homepage = ""
+ gem.description = %q{Simplifies interactions with Rapidshare API with a set of handy features}
+ gem.summary = %q{Makes your interactions with Rapidshare API more pleasant by providing new handy features: creating/moving/deleting files/folders in a user friendly way, upload files, etc}
+ gem.homepage = "http://github.com/odiszapc/rapidshare-ext"
gem.files = `git ls-files`.split($\)
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
- gem.name = "rapidshare-ext"
gem.require_paths = ["lib"]
- gem.version = Rapidshare::Ext::VERSION
+
+
+ gem.add_dependency('rapidshare', '~> 0.5.3')
+ gem.add_dependency('rest-client', '~> 1.6.7')
+
+ gem.add_development_dependency('test-unit')
+ gem.add_development_dependency('shoulda')
+ gem.add_development_dependency('simplecov')
+ gem.add_development_dependency('fakeweb')
end
View
1 test/fixtures/files/upload1.txt
@@ -0,0 +1 @@
+Test file for upload
View
173 test/integration/rapidshare-ext_test.rb
@@ -0,0 +1,173 @@
+# encoding: utf-8
+require 'digest/md5'
+require File.expand_path(File.dirname(__FILE__) + "/../test_helper")
+
+class RapidshareExtTest < Test::Unit::TestCase
+
+ def setup
+ FakeWeb.allow_net_connect = true
+ @rs = Rapidshare::API.new :cookie => ENV['RAPIDSHARE_COOKIE']
+ @rs.erase_all_data!
+ end
+
+ context "Api" do
+ should "Upload file" do
+ assertion = ->(resp, size_local, digest_local, remote_filename) do
+ assert_instance_of Hash, resp
+ assert_kind_of Integer, resp[:id]
+ assert_kind_of Integer, resp[:size]
+ assert_equal size_local, resp[:size]
+ assert_instance_of String, resp[:checksum]
+ assert_match /[a-z0-9]{32}/, resp[:checksum]
+ assert_equal digest_local, resp[:checksum]
+ assert_instance_of String, resp[:url]
+ assert_equal "https://rapidshare.com/files/#{resp[:id]}/#{URI::encode(remote_filename)}", resp[:url]
+ end
+
+ file_info_assertion = ->(info, file_id, digest_local, size_local, remote_filename, remote_dir) do
+ assert_equal info[:filename], remote_filename
+ assert_equal info[:id].to_i, file_id
+ assert_equal info[:md5hex].downcase, digest_local
+ assert_equal info[:realfolder].to_i, @rs.folder_id(remote_dir)
+ assert_equal info[:size].to_i, size_local
+ end
+
+ local_path = File.expand_path(File.dirname(__FILE__) + "/../fixtures/files/upload1.txt")
+ remote_filename = "upload_file_1.txt"
+ remote_dir = "a/b/c"
+ remote_path = "#{remote_dir}/#{remote_filename}"
+ digest_local = Digest::MD5.hexdigest(File.read(local_path))
+ size_local = File.size local_path
+
+ # Initial upload
+ response = @rs.upload local_path, :as => remote_filename, :to => remote_dir
+ assertion.call response, size_local, digest_local, remote_filename
+ assert_false response[:already_exists?]
+
+ # Check file ID
+ file_id = @rs.file_id remote_path
+ assert_kind_of Integer, file_id
+ assert_equal file_id, response[:id]
+
+ # Check file info
+ info = @rs.file_info remote_path
+ file_info_assertion.call info, file_id, digest_local, size_local, remote_filename, remote_dir
+
+ # Upload the same file again
+ response = @rs.upload local_path, :as => remote_filename, :to => remote_dir
+ assertion.call response, size_local, digest_local, remote_filename
+ assert_true response[:already_exists?]
+
+ # Rename file
+ remote_filename_2 ="foo.txt"
+ remote_path_2 = "#{remote_dir}/#{remote_filename_2}"
+ @rs.rename_file remote_path, remote_filename_2
+ info = @rs.file_info remote_path_2
+ file_info_assertion.call info, @rs.file_id(remote_path_2), digest_local, size_local, remote_filename_2, remote_dir
+
+ # Move file
+ remote_dir_3 = "a/b"
+ remote_path_3 = "#{remote_dir_3}/#{remote_filename_2}"
+ @rs.move_file remote_path_2, :to => remote_dir_3
+
+ info = @rs.file_info remote_path_3
+ file_info_assertion.call info, @rs.file_id(remote_path_3), digest_local, size_local, remote_filename_2, remote_dir_3
+
+ # Delete file
+ @rs.remove_file remote_path_3
+
+ info = @rs.file_info remote_path_3
+ assert_nil info
+ end
+
+ should "Create folder" do
+ folder_id = @rs.add_folder "a/b/c"
+ assert_kind_of Integer, folder_id
+ assert_not_equal 0, folder_id
+ tree = @rs.folders_hierarchy
+
+ assert_equal 3, tree.count
+ assert_equal "a/b/c", tree[folder_id][:path]
+ assert_equal "a/b", tree[tree[folder_id][:parent]][:path]
+ assert_equal "a", tree[tree[tree[folder_id][:parent]][:parent]][:path]
+ end
+
+ should "Move folder" do
+ folder_id = @rs.add_folder "a/b/c"
+ assert_kind_of Integer, folder_id
+ assert_not_equal 0, folder_id
+ tree = @rs.folders_hierarchy
+
+ assert_equal 3, tree.count
+ assert_equal "a/b/c", tree[folder_id][:path]
+ assert_equal "a/b", tree[tree[folder_id][:parent]][:path]
+ assert_equal "a", tree[tree[tree[folder_id][:parent]][:parent]][:path]
+
+ @rs.move_folder "a/b/c", :to => 'a'
+
+ tree = @rs.reload!
+
+ assert_equal 3, tree.count
+ assert_equal "a/c", tree[folder_id][:path]
+ assert_equal @rs.folder_id("a"), tree[folder_id][:parent]
+ end
+
+ should "Build folder tree" do
+ # Create folder
+ folder_id = @rs.add_folder "a/b/c"
+ assert_kind_of Integer, folder_id
+ assert_not_equal 0, folder_id
+ tree = @rs.folders_hierarchy
+
+ # Validate tree
+ assert_equal 3, tree.count
+ assert_equal "a/b/c", tree[folder_id][:path]
+ assert_equal "a/b", tree[tree[folder_id][:parent]][:path]
+ assert_equal "a", tree[tree[tree[folder_id][:parent]][:parent]][:path]
+
+ # Validate subtree
+ sub_tree = @rs.folders_hierarchy :from => 'a/b'
+ assert_equal 1, sub_tree.count
+ assert_equal "c", sub_tree[folder_id][:path]
+ end
+
+ should "Remove folder" do
+ folder_id = @rs.add_folder "a/b/c"
+ assert_kind_of Integer, folder_id
+ assert_not_equal 0, folder_id
+ tree = @rs.folders_hierarchy
+ assert_equal 3, tree.count
+
+ @rs.remove_folder "a/b/c"
+
+ tree = @rs.folders_hierarchy!
+ assert_equal 2, tree.count
+
+
+ folder_id = @rs.add_folder "a/b/c"
+ assert_kind_of Integer, folder_id
+ assert_not_equal 0, folder_id
+ tree = @rs.folders_hierarchy!
+ assert_equal 3, tree.count
+
+ @rs.remove_folder "a"
+ tree = @rs.folders_hierarchy!
+ assert_equal 0, tree.count
+ end
+
+ should "Erase account" do
+ folder_id = @rs.add_folder "a/b/c"
+ assert_kind_of Integer, folder_id
+
+ folder_ids = @rs.folders_hierarchy.keys
+ assert_true folder_ids.count > 0
+
+ # Delete all data from account
+ @rs.erase_all_data!
+
+ folder_ids = @rs.folders_hierarchy.keys
+ assert_equal 0, folder_ids.count
+ end
+ end
+end
+
View
40 test/test_helper.rb
@@ -0,0 +1,40 @@
+require 'test/unit'
+require 'shoulda'
+require 'fakeweb'
+
+require 'rapidshare-ext'
+
+class Test::Unit::TestCase
+
+ # don't allow internet connections for testing (we should use fixtures, except
+ # integration testing)
+ FakeWeb.allow_net_connect = false
+
+ def read_fixture(filename, extension = 'txt')
+ # add extension to file unless it already has it
+ filename += ".#{extension}" unless (filename =~ /\.\w+$/)
+
+ File.read File.expand_path(File.dirname(__FILE__) + "/fixtures/#{filename}")
+ end
+
+ # general setup, can be overriden or extended in specific tests
+ #
+ def setup
+ @cookie = 'F0EEB41B38363A41F0D125102637DB7236468731F8DB760DC57934B4714C8D13'
+
+ # mock http requests for login into Rapidshare
+ #
+ FakeWeb.register_uri(:get,
+ 'https://api.rapidshare.com/cgi-bin/rsapi.cgi?sub=getaccountdetails&login=valid_login&password=valid_password&withcookie=1&cookie=',
+ :body => read_fixture('getaccountdetails_valid.txt')
+ )
+
+ FakeWeb.register_uri(:get,
+ "https://api.rapidshare.com/cgi-bin/rsapi.cgi?sub=getaccountdetails&cookie=#{@cookie}",
+ :body => read_fixture('getaccountdetails_valid.txt')
+ )
+
+ @rs = Rapidshare::API.new(:login => 'valid_login', :password => 'valid_password')
+ end
+
+end
View
28 test/unit/rapidshare-ext_test.rb
@@ -1,8 +1,28 @@
-require 'test/unit'
+require 'test_helper'
class RapidshareExtTest < Test::Unit::TestCase
- def test_first
- assert true
- rs = Rapidshare::Api.new
+ context "Interface" do
+ should "Respond to certain methods" do
+ assert_respond_to @rs, :add_folder
+ assert_respond_to @rs, :remove_folder
+ assert_respond_to @rs, :move_folder
+ assert_respond_to @rs, :upload
+ assert_respond_to @rs, :remove_file
+ assert_respond_to @rs, :rename_file
+ assert_respond_to @rs, :folders_hierarchy
+ assert_respond_to @rs, :folders_hierarchy!
+ assert_respond_to @rs, :slice_tree
+ assert_respond_to @rs, :remove_orphans!
+ assert_respond_to @rs, :move_orphans
+ assert_respond_to @rs, :detect_gaps
+ assert_respond_to @rs, :erase_all_data!
+ assert_respond_to @rs, :root_folder?
+ assert_respond_to @rs, :gap?
+ assert_respond_to @rs, :orphan?
+ assert_respond_to @rs, :folder_path
+ assert_respond_to @rs, :folder_id
+ assert_respond_to @rs, :file_info
+ assert_respond_to @rs, :file_id
+ end
end
end

0 comments on commit 543d32c

Please sign in to comment.