diff --git a/.coveralls.yml b/.coveralls.yml index b9b942b..380531e 100644 --- a/.coveralls.yml +++ b/.coveralls.yml @@ -1 +1 @@ -repo_token: vED8szM0CKiJzSUuQmmbuuWStRxi3wMSV \ No newline at end of file +repo_token: 9Fl9auxZBvbw6mQ2MDCN6HAs0qeBSANwg diff --git a/.travis.yml b/.travis.yml index 11a115a..23a0abe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,3 @@ language: ruby rvm: - - 1.9.3 + - 2.2.2 diff --git a/Gemfile b/Gemfile index 7ff0da7..b3138bf 100644 --- a/Gemfile +++ b/Gemfile @@ -5,4 +5,4 @@ gemspec gem 'rake' gem 'rspec' gem 'webmock', :require => 'webmock/rspec' -gem 'coveralls', require: false \ No newline at end of file +gem 'coveralls', require: false diff --git a/README.md b/README.md index 5279d1c..2d723d1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # CarrierWave for WebDAV [![Build Status](https://travis-ci.org/qinix/carrierwave-webdav.png?branch=master)](https://travis-ci.org/qinix/carrierwave-webdav) +[![Coverage Status](https://coveralls.io/repos/github/qinix/carrierwave-webdav/badge.svg?branch=master)](https://coveralls.io/github/qinix/carrierwave-webdav?branch=master) This gem adds support for WebDAV to [CarrierWave](https://github.com/carrierwaveuploader/carrierwave/) @@ -57,4 +58,3 @@ them yourself. In Rails for example, you could use the `send_data` method. 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request - diff --git a/carrierwave-webdav.gemspec b/carrierwave-webdav.gemspec index 267e6e0..6784d0d 100644 --- a/carrierwave-webdav.gemspec +++ b/carrierwave-webdav.gemspec @@ -19,5 +19,4 @@ Gem::Specification.new do |gem| gem.add_dependency 'carrierwave' gem.add_dependency 'httparty' - end diff --git a/lib/carrierwave/storage/webdav.rb b/lib/carrierwave/storage/webdav.rb index fe0bf8d..81b4c34 100644 --- a/lib/carrierwave/storage/webdav.rb +++ b/lib/carrierwave/storage/webdav.rb @@ -1,8 +1,57 @@ require 'carrierwave/httparty_monkey' +require 'carrierwave/webdav/file' module CarrierWave module Storage class WebDAV < Abstract + # Store file in WebDAV cache directory + # + # === Parameters + # + # [ file (CarrierWave::SanitizedFile) ] the file to store + # + # === Returns + # + # [ CarrierWave::WebDAV::File ] a sanitized file + # + def cache!(file) + cached = build_webdav_file(uploader.cache_path) + cached.write(file.read) + cached + end + + # Retrieve file with given cache identifier from WebDAV + # + # === Parameters + # + # [ identifier (String) ] cache identifier + # + # === Returns + # + # [ CarrierWave::WebDAV::File ] a sanitized file + # + def retrieve_from_cache!(identifier) + build_webdav_file(uploader.cache_path(identifier)) + end + + # Delete cache directory from WebDAV + # + # === Parameters + # + # [ path (String) ] cache path + # + # === Returns + # + # [ HTTParty::Response ] httparty response object + # + # === Raises + # + # [ CarrierWave::IntegrityError ] + # + def delete_dir!(path) + cached = build_webdav_file(path) + cached.delete_dir + end # Store the file in WebDAV # @@ -12,10 +61,10 @@ class WebDAV < Abstract # # === Returns # - # [ CarrierWave::SanitizedFile ] a sanitized file + # [ CarrierWave::WebDAV::File ] a sanitized file # def store!(file) - stored = CarrierWave::Storage::WebDAV::File.new(uploader, uploader.store_path) + stored = build_webdav_file(uploader.store_path) stored.write(file.read) stored end @@ -28,117 +77,17 @@ def store!(file) # # === Returns # - # [ CarrierWave::Store::WebDAV::File ] a sanitized file + # [ CarrierWave::WebDAV::File ] a sanitized file # def retrieve!(identifier) - CarrierWave::Storage::WebDAV::File.new(uploader, uploader.store_path(identifier)) + build_webdav_file(uploader.store_path(identifier)) end - class File - attr_reader :path - attr_reader :uploader - attr_reader :options - attr_reader :server # Like 'https://www.WebDAV.com/dav' - - def initialize(uploader, path) - @path = path - @path.sub! /^\//, '' - @uploader = uploader - @server = uploader.webdav_server - @server.sub! /\/$/, '' - @write_server = uploader.webdav_write_server - @write_server.sub!(/\/$/, '') if @write_server - @username = uploader.webdav_username - @password = uploader.webdav_password || '' - @options = {} - @options = { basic_auth: { username: @username, password: @password } } if @username - end - - def read - res = HTTParty.get(url, options) - if res.code != 200 - raise CarrierWave::IntegrityError.new("Can't download a file: #{res.inspect}") - end - res.body - end - - def headers - res = HTTParty.head(url, options) - if res.code != 200 - raise CarrierWave::IntegrityError.new("Can't headers for a file: #{res.inspect}") - end - res.headers - end - - def write(file) - mkcol - - res = HTTParty.put(write_url, options.merge({ body: file })) - if res.code != 201 and res.code != 204 - raise CarrierWave::IntegrityError.new("Can't put a new file: #{res.inspect}") - end - res - end - - def length - read.bytesize - end - - def content_type - headers.content_type - end - - def delete - res = HTTParty.delete(write_url, options) - if res.code != 200 and res.code != 204 and res.code != 404 - raise CarrierWave::IntegrityError.new("Can't delete a file: #{res.inspect}") - end - res - end - - def url - "#{server}/#{path}" - end - - alias :content_length :length - alias :file_length :length - alias :size :length - private - def write_url - @write_server ? "#{@write_server}/#{path}" : url - end - - def mkcol - return if dirs_to_create.empty? - - - use_server = @write_server ? @write_server : server - dirs_to_create.each do |dir| - - res = HTTParty.mkcol("#{use_server}#{dir}", options) - unless [200, 201, 207, 409].include? res.code - raise CarrierWave::IntegrityError.new("Can't create a new collection: #{res.inspect}") - end - end # Make collections recursively - end - - def dirs_to_create - dirs = [] - path.split('/')[0...-1].each do |dir| - dirs << "#{dirs[-1]}/#{dir}" - end - find_dirs_that_dont_exist(dirs) - end - - def find_dirs_that_dont_exist dirs - dirs.reject{|dir| - res = HTTParty.propfind("#{server}/#{dir}", options) - [200,201,207].include? res.code - } - end - end # File + def build_webdav_file(path) + CarrierWave::WebDAV::File.new(uploader, path) + end end # WebDAV end # Storage end # CarrierWave diff --git a/lib/carrierwave/webdav/file.rb b/lib/carrierwave/webdav/file.rb new file mode 100644 index 0000000..0fbe1da --- /dev/null +++ b/lib/carrierwave/webdav/file.rb @@ -0,0 +1,122 @@ +module CarrierWave + module WebDAV + class File + attr_reader :path + attr_reader :uploader + attr_reader :options + attr_reader :server # Like 'https://www.WebDAV.com/dav' + + def initialize(uploader, path) + @path = path + @path.sub! /^\//, '' + @uploader = uploader + @server = uploader.webdav_server + @server.sub! /\/$/, '' + @write_server = uploader.webdav_write_server + @write_server.sub!(/\/$/, '') if @write_server + @username = uploader.webdav_username + @password = uploader.webdav_password || '' + @options = {} + @options = { basic_auth: { username: @username, password: @password } } if @username + end + + def read + res = HTTParty.get(read_url, options) + if res.code != 200 + raise CarrierWave::IntegrityError.new("Can't download a file: #{res.inspect}") + end + res.body + end + + def headers + res = HTTParty.head(read_url, options) + if res.code != 200 + raise CarrierWave::IntegrityError.new("Can't headers for a file: #{res.inspect}") + end + res.headers + end + + def write(file) + mkcol + + res = HTTParty.put(write_url, options.merge({ body: file })) + if res.code != 201 and res.code != 204 + raise CarrierWave::IntegrityError.new("Can't put a new file: #{res.inspect}") + end + res + end + + def length + read.bytesize + end + + def content_type + headers.content_type + end + + def delete + res = HTTParty.delete(write_url, options) + if res.code != 200 and res.code != 204 and res.code != 404 + raise CarrierWave::IntegrityError.new("Can't delete a file: #{res.inspect}") + end + res + end + + def delete_dir + @path += '/' unless path.end_with?('/') + delete + end + + def url + if host = uploader.asset_host + host.respond_to?(:call) ? host.call(self) : [host, path].join('/') + else + read_url + end + end + + alias :content_length :length + alias :file_length :length + alias :size :length + + private + + def read_url + "#{server}/#{path}" + end + + def write_url + @write_server ? "#{@write_server}/#{path}" : read_url + end + + def mkcol + return if dirs_to_create.empty? + + use_server = @write_server ? @write_server : server + + dirs_to_create.each do |dir| + res = HTTParty.mkcol("#{use_server}#{dir}", options) + unless [200, 201, 207, 409].include? res.code + raise CarrierWave::IntegrityError.new("Can't create a new collection: #{res.inspect}") + end + end # Make collections recursively + end + + def dirs_to_create + dirs = [] + path.split('/')[0...-1].each do |dir| + dirs << "#{dirs[-1]}/#{dir}" + end # Make path like a/b/c/t.txt to array ['/a', '/a/b', '/a/b/c'] + + find_dirs_that_dont_exist dirs + end + + def find_dirs_that_dont_exist(dirs) + dirs.reject do |dir| + res = HTTParty.propfind("#{server}/#{dir}", options) + [200,201,207].include? res.code + end + end + end # File + end # WebDAV +end # CarrierWave diff --git a/lib/carrierwave/webdav/version.rb b/lib/carrierwave/webdav/version.rb index 7f0057b..a084bd2 100644 --- a/lib/carrierwave/webdav/version.rb +++ b/lib/carrierwave/webdav/version.rb @@ -1,5 +1,5 @@ module CarrierWave module WebDAV - VERSION = "0.4.1" + VERSION = "0.5.0" end end diff --git a/spec/fixtures/tmp_test.txt b/spec/fixtures/tmp_test.txt new file mode 100644 index 0000000..27c7b93 --- /dev/null +++ b/spec/fixtures/tmp_test.txt @@ -0,0 +1 @@ +Hello, this is tmp test data. diff --git a/spec/lib/webdav_file_spec.rb b/spec/lib/webdav_file_spec.rb new file mode 100644 index 0000000..a8a1cd4 --- /dev/null +++ b/spec/lib/webdav_file_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +describe CarrierWave::WebDAV::File do + describe '#url' do + let(:webdav_server) { 'https://your.webdavserver.com/dav/' } + + before do + CarrierWave.configure do |config| + config.storage = :webdav + config.cache_storage = :webdav + config.webdav_server = webdav_server + config.asset_host = host + + config.webdav_autocreates_dirs = true + end + + @uploader = CarrierWave::Uploader::Base.new + @file = File.open(file_path('test.txt')) + + stub_request(:put, %r{#{Regexp.escape(webdav_server)}}).to_return(status: 201) + end + + context 'when asset_host is set' do + let(:host) { 'http://asset.host' } + + it 'path contains asset_host' do + @uploader.cache!(@file) + expect(@uploader.file.url).to eq [host, @uploader.file.path].join('/') + end + end + + context 'when asset_host is not set' do + let(:host) { nil } + + it 'path does not contain asset_host' do + @uploader.cache!(@file) + expect(@uploader.file.url).to eq [webdav_server, @uploader.file.path].join('/') + end + end + end +end diff --git a/spec/lib/webdav_spec.rb b/spec/lib/webdav_spec.rb deleted file mode 100644 index 924736b..0000000 --- a/spec/lib/webdav_spec.rb +++ /dev/null @@ -1,94 +0,0 @@ -require 'spec_helper' -require 'carrierwave/webdav' -require 'uri' - -describe CarrierWave::Storage::WebDAV do - - before do - CarrierWave.configure do |config| - config.storage = :webdav - config.webdav_server = 'https://your.webdavserver.com/dav/' - config.webdav_username = 'your_webdav_username' - config.webdav_password = 'your_webdav_password' - end - - @uploader = CarrierWave::Uploader::Base.new - @uploader.stub store_path: 'uploads/test.txt' - - @storage = CarrierWave::Storage::WebDAV.new(@uploader) - @file = CarrierWave::SanitizedFile.new(file_path('test.txt')) - - @uri = URI(@uploader.webdav_server) - @uri.user = @uploader.webdav_username - @uri.password = @uploader.webdav_password - @uri.merge! @uploader.store_path - end - - it 'should store from WebDAV' do - stub_mkcol @uri - stub_put @uri - webdav_file = @storage.store!(@file) - stub_get @uri - expect(@file.read).to eq(webdav_file.read) - end - - it 'should retrieve a file from WebDAV' do - stub_mkcol @uri - stub_put @uri - webdav_file = @storage.store!(@file) - stub_get @uri - retrieved_file = @storage.retrieve!(webdav_file) - expect(@file.read).to eq(retrieved_file.read) - expect(File.basename(@file.path)).to eq(File.basename(retrieved_file.path)) - end - - it 'should delete a file from WebDAV' do - stub_mkcol @uri - stub_put @uri - webdav_file = @storage.store!(@file) - stub_delete @uri - expect(Net::HTTPOK).to eq(webdav_file.delete.response.class) - end - - it 'should size equal' do - stub_mkcol @uri - stub_put @uri - webdav_file = @storage.store!(@file) - stub_get @uri - expect(@file.size).to eq(webdav_file.size) - end - - it 'assigns file content type to attribute' do - stub_mkcol @uri - stub_put @uri - webdav_file = @storage.store!(@file) - stub_head @uri - expect(@file.content_type).to eq(webdav_file.content_type) - end - - it 'should url equal' do - stub_mkcol @uri - stub_put @uri - webdav_file = @storage.store!(@file) - expect("#{@uploader.webdav_server}/#{@uploader.store_path}").to eq(webdav_file.url) - end - - it 'should save through secure server' do - CarrierWave.configure do |config| - config.webdav_write_server = 'https://secure.your.webdavserver.com/dav/' - end - - secure_uri = URI(@uploader.webdav_write_server) - secure_uri.user = @uploader.webdav_username - secure_uri.password = @uploader.webdav_password - secure_uri.merge! @uploader.store_path - - stub_mkcol secure_uri - stub_put secure_uri - webdav_file = @storage.store!(@file) - - stub_get @uri - expect(@file.read).to eq(webdav_file.read) - end - -end diff --git a/spec/lib/webdav_storage_spec.rb b/spec/lib/webdav_storage_spec.rb new file mode 100644 index 0000000..49cbc73 --- /dev/null +++ b/spec/lib/webdav_storage_spec.rb @@ -0,0 +1,136 @@ +require 'spec_helper' +require 'uri' + +describe CarrierWave::Storage::WebDAV do + + before do + CarrierWave.configure do |config| + config.storage = :webdav + config.webdav_server = 'https://your.webdavserver.com/dav/' + config.webdav_username = 'your_webdav_username' + config.webdav_password = 'your_webdav_password' + end + + @uploader = CarrierWave::Uploader::Base.new + + allow(@uploader).to receive(:store_path) { 'uploads/test.txt' } + allow(@uploader).to receive(:cache_path) { 'uploads/tmp_test.txt' } + + @storage = CarrierWave::Storage::WebDAV.new(@uploader) + @file = CarrierWave::SanitizedFile.new(file_path('test.txt')) + + # NOTE: specs fail with this options + # + # @uri.user = @uploader.webdav_username + # @uri.password = @uploader.webdav_password + + @uri = URI(File.join(@uploader.webdav_server, @uploader.store_path)) + @cache_uri = URI(File.join(@uploader.webdav_server, @uploader.cache_path)) + end + + describe '#cache!' do + it 'should cache from WebDAV' do + stub_mkcol @cache_uri + stub_put @cache_uri + webdav_file = @storage.cache!(@file) + stub_get @cache_uri + expect(@file.read).to eq(webdav_file.read) + end + end + + describe '#retrieve_from_cache!' do + it 'should retreive a cache file from WebDAV' do + stub_get @cache_uri + webdav_file = @storage.retrieve_from_cache!('tmp_test.txt') + expect(@file.read).to eq(webdav_file.read) + end + end + + describe '#delete_dir!' do + it 'should delete cache directory' do + stub_delete File.join(@uploader.webdav_server, 'uploads/') + result = @storage.delete_dir!('uploads') + expect(Net::HTTPOK).to eq(result.response.class) + end + end + + it 'should store from WebDAV' do + stub_mkcol @uri + stub_put @uri + webdav_file = @storage.store!(@file) + stub_get @uri + expect(@file.read).to eq(webdav_file.read) + end + + it 'should retrieve a file from WebDAV' do + stub_mkcol @uri + stub_put @uri + webdav_file = @storage.store!(@file) + stub_get @uri + retrieved_file = @storage.retrieve!(webdav_file) + expect(@file.read).to eq(retrieved_file.read) + expect(File.basename(@file.path)).to eq(File.basename(retrieved_file.path)) + end + + it 'should delete a file from WebDAV' do + stub_mkcol @uri + stub_put @uri + webdav_file = @storage.store!(@file) + stub_delete @uri + expect(Net::HTTPOK).to eq(webdav_file.delete.response.class) + end + + it 'should size equal' do + stub_mkcol @uri + stub_put @uri + webdav_file = @storage.store!(@file) + stub_get @uri + expect(@file.size).to eq(webdav_file.size) + end + + it 'assigns file content type to attribute' do + stub_mkcol @uri + stub_put @uri + webdav_file = @storage.store!(@file) + stub_head @uri + expect(@file.content_type).to eq(webdav_file.content_type) + end + + it 'should url equal' do + stub_mkcol @uri + stub_put @uri + webdav_file = @storage.store!(@file) + expect("#{@uploader.webdav_server}/#{@uploader.store_path}").to eq(webdav_file.url) + end + + context 'when use write server' do + before do + CarrierWave.configure do |config| + config.webdav_write_server = 'https://secure.your.webdavserver.com/dav/' + end + end + + after do + CarrierWave.configure do |config| + config.webdav_write_server = nil + end + end + + it 'should save through secure server' do + secure_uri = URI(File.join(@uploader.webdav_write_server, @uploader.store_path)) + + # NOTE: specs fail with this options + # + # secure_uri.user = @uploader.webdav_username + # secure_uri.password = @uploader.webdav_password + + stub_mkcol secure_uri + stub_put secure_uri + webdav_file = @storage.store!(@file) + + stub_get @uri + expect(@file.read).to eq(webdav_file.read) + end + end + +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 0a2f8d2..e7bb23e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -4,6 +4,7 @@ Dir['spec/supports/**/*.rb'].each { |f| require File.expand_path(f) } require 'carrierwave' +require 'carrierwave/webdav' if ENV['TRAVIS'] require 'coveralls' @@ -14,4 +15,3 @@ WebMock.disable_net_connect! config.include Helpers end -