├── .credentials.yml ├── test ├── fixtures │ ├── ruby.png │ └── rails.png ├── carrierwave_uploader_base_test.rb ├── test_helper.rb ├── dummy_app.rb └── file_upload_test.rb ├── lib └── carrierwave │ ├── dropbox │ └── version.rb │ ├── dropbox.rb │ └── storage │ └── dropbox.rb ├── Gemfile ├── .gitignore ├── .travis.yml ├── Rakefile ├── LICENSE ├── carrierwave-dropbox.gemspec ├── CHANGELOG.md └── README.md /.credentials.yml: -------------------------------------------------------------------------------- 1 | access_token: "" 2 | -------------------------------------------------------------------------------- /test/fixtures/ruby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robin850/carrierwave-dropbox/HEAD/test/fixtures/ruby.png -------------------------------------------------------------------------------- /test/fixtures/rails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robin850/carrierwave-dropbox/HEAD/test/fixtures/rails.png -------------------------------------------------------------------------------- /lib/carrierwave/dropbox/version.rb: -------------------------------------------------------------------------------- 1 | module CarrierWave 2 | module Dropbox 3 | VERSION = "2.0.0" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in carrierwave-dropbox.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | -------------------------------------------------------------------------------- /lib/carrierwave/dropbox.rb: -------------------------------------------------------------------------------- 1 | require 'carrierwave' 2 | require 'carrierwave/storage/dropbox' 3 | 4 | require 'carrierwave/dropbox/version' 5 | 6 | class CarrierWave::Uploader::Base 7 | add_config :dropbox_access_token 8 | 9 | configure do |config| 10 | config.storage_engines[:dropbox] = 'CarrierWave::Storage::Dropbox' 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | 3 | rvm: 4 | - 2.2 5 | - 2.3 6 | - 2.4 7 | - 2.5 8 | - rbx-3.91 9 | 10 | before_install: 11 | - gem install bundler 12 | 13 | matrix: 14 | allow_failures: 15 | - rvm: rbx-3.91 16 | 17 | env: 18 | global: 19 | secure: "KZEG8GjN1P3pHyK8QjOfNF4jZk+GfUonGIJdVplSTJJGE/RywtTCWtPGRmjB3BnLvCrlfLkrhNXwM5ZWN07iDELDJhh4Xlg8mSZ+snsGsezUM7lSHsyHebl2uN2tcRrJQD2Eh55tQDa/BGABRwva6mc6E1gEzgy9UHXVFhT8d30=" 20 | -------------------------------------------------------------------------------- /test/carrierwave_uploader_base_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class CarrierWaveUploaderBaseTest < Minitest::Test 4 | def setup 5 | @object = CarrierWave::Uploader::Base 6 | end 7 | 8 | def test_dropbox_storage 9 | @object.configure do |c| 10 | assert c.storage_engines.key?(:dropbox) 11 | end 12 | end 13 | 14 | def test_respond_to_configuration_methods 15 | assert @object.respond_to?(:dropbox_access_token) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV["RACK_ENV"] = "test" 2 | 3 | require 'rack/test' 4 | 5 | require 'dummy_app' 6 | require 'minitest/autorun' 7 | require 'fileutils' 8 | 9 | class Minitest::Test 10 | include Rack::Test::Methods 11 | 12 | def teardown 13 | FileUtils.rm_rf(File.expand_path("../public", __FILE__)) 14 | end 15 | 16 | def app 17 | DummyApplication 18 | end 19 | 20 | def file_upload(image) 21 | path = File.expand_path("../fixtures/#{image}", __FILE__) 22 | Rack::Test::UploadedFile.new(path, "image/png") 23 | end 24 | 25 | def md5_data(data) 26 | md5 = Digest::MD5.new 27 | md5 << data 28 | md5.to_s 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rake/testtask" 3 | 4 | Rake::TestTask.new("test_unit") do |t| 5 | t.libs << "test" 6 | t.pattern = 'test/**/*_test.rb' 7 | t.verbose = true 8 | t.warning = false 9 | t.description = "Run the unit tests without loading .credentials.yml" 10 | end 11 | 12 | task :load_credentials do 13 | require 'yaml' 14 | 15 | path = File.expand_path("../.credentials.yml", __FILE__) 16 | hash = YAML.load(File.read(path)) 17 | 18 | hash.each do |key, value| 19 | ENV[key.upcase] ||= value 20 | end 21 | end 22 | 23 | desc "Run the unit tests" 24 | task test: [:load_credentials, :test_unit] 25 | 26 | task default: :test 27 | -------------------------------------------------------------------------------- /test/dummy_app.rb: -------------------------------------------------------------------------------- 1 | require 'sinatra' 2 | 3 | require 'active_record' 4 | require 'carrierwave' 5 | require 'carrierwave/dropbox' 6 | require 'carrierwave/orm/activerecord' 7 | 8 | ActiveRecord::Base.establish_connection( 9 | adapter: 'sqlite3', 10 | database: ':memory:' 11 | ) 12 | 13 | ActiveRecord::Schema.define do 14 | create_table :images do |t| 15 | t.string :attachment 16 | end 17 | end 18 | 19 | CarrierWave.configure do |config| 20 | config.dropbox_access_token = ENV["ACCESS_TOKEN"] 21 | end 22 | 23 | class ImageUploader < CarrierWave::Uploader::Base 24 | storage :dropbox 25 | 26 | def store_dir 27 | "test/images/#{model.id}" 28 | end 29 | end 30 | 31 | class Image < ActiveRecord::Base 32 | mount_uploader :attachment, ImageUploader 33 | end 34 | 35 | class DummyApplication < Sinatra::Application 36 | post '/image/upload' do 37 | Image.create!(attachment: params[:attachment]) 38 | end 39 | 40 | put '/image/edit/:id' do |id| 41 | Image.find(id).update(attachment: params[:attachment]) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /test/file_upload_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | require 'net/http' 3 | 4 | class FileUploadTest < Minitest::Test 5 | def test_uploading_a_simple_file 6 | original_count = Image.count 7 | 8 | post "/image/upload", attachment: file_upload('rails.png') 9 | 10 | assert last_response.ok? 11 | assert_equal 1, (Image.count - original_count) 12 | refute Image.last.attachment.url.empty? 13 | end 14 | 15 | 16 | def test_upload_image_editing 17 | post "/image/upload", attachment: file_upload('ruby.png') 18 | image = Image.last 19 | 20 | uploaded_image = Net::HTTP.get(URI image.attachment.url) 21 | assert_match md5_data(File.read(File.expand_path("../fixtures/ruby.png", __FILE__))), md5_data(uploaded_image) 22 | 23 | put "/image/edit/#{image.id}", attachment: file_upload('rails.png') 24 | 25 | new_image = Net::HTTP.get(URI Image.last.attachment.url) 26 | assert last_response.ok? 27 | assert_match md5_data(File.read(File.expand_path("../fixtures/rails.png", __FILE__))), md5_data(new_image) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Robin Dupret 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /carrierwave-dropbox.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'carrierwave/dropbox/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "carrierwave-dropbox" 8 | spec.version = CarrierWave::Dropbox::VERSION 9 | spec.authors = ["Robin Dupret"] 10 | spec.email = ["robin.dupret@gmail.com"] 11 | spec.description = %q{CarrierWave storage for Dropbox} 12 | spec.summary = %q{Dropbox integration for CarrierWave} 13 | spec.homepage = "https://github.com/robin850/carrierwave-dropbox" 14 | spec.license = "MIT" 15 | 16 | spec.files = `git ls-files`.split($/) 17 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 19 | spec.require_paths = ["lib"] 20 | 21 | spec.add_dependency "carrierwave", "~> 1.2" 22 | spec.add_dependency "dropbox-sdk-v2", "~> 0.0.3" 23 | 24 | spec.add_development_dependency "bundler", "~> 1.16" 25 | spec.add_development_dependency "rake", "~> 12.3" 26 | spec.add_development_dependency "rack-test", "~> 0.8" 27 | spec.add_development_dependency "sqlite3", "~> 1.3" 28 | spec.add_development_dependency "sinatra", "~> 2.0" 29 | spec.add_development_dependency "activerecord", "~> 5.1" 30 | end 31 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 2.0.0 4 | 5 | * Only test against Ruby 2.2+ and Rails 5.1+. This gem might work 6 | with previous versions of Ruby and Rails but it's not guaranteed. 7 | 8 | * Use file IDs instead of file name to retrieve files. 9 | 10 | This allows people to move files around and still get these files 11 | without changing the URL. 12 | 13 | *mful* 14 | 15 | * Use version 2 of the Dropbox API (*Steve Bell*) 16 | 17 | ## 1.2.1 (February 24, 2014) 18 | 19 | * Use the uploader's store_path instead of the root 20 | 21 | ## 1.2.0 (January 12, 2014) 22 | 23 | * Make the `dropbox_client` method public 24 | 25 | You can now easily access to the `dropbox_client` method to get 26 | information about the current connection or check if the latter is 27 | active or not. 28 | 29 | * Use the root path instead of the `Public` directory by default 30 | 31 | ## 1.1.1 (October 5, 2013) 32 | 33 | * Fix deletion when using app_folder accounts 34 | 35 | ## 1.1.0 (September 22, 2013) 36 | 37 | * Make the gem working with "app_folder" applications. 38 | 39 | Correct the returned URL for applications which are not "dropbox" ones 40 | since they are not only accessible publicly. 41 | 42 | Resolve #1 43 | 44 | ## 1.0.2 (August 3, 2013) 45 | 46 | * Add a rescue block for `DropboxError` since CarrierWave is trying to 47 | delete all specified versions of a file even if they do not exist. 48 | 49 | ## 1.0.1 (August 2, 2013) 50 | 51 | * Ensure resource edition works 52 | 53 | ## 1.0.0 (August 2, 2013) 54 | 55 | * First release 56 | -------------------------------------------------------------------------------- /lib/carrierwave/storage/dropbox.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'dropbox' 3 | 4 | module CarrierWave 5 | module Storage 6 | class Dropbox < Abstract 7 | 8 | # Stubs we must implement to create and save 9 | # files (here on Dropbox) 10 | 11 | # Store a single file 12 | def store!(file) 13 | location = "/#{uploader.store_path}" 14 | res = dropbox_client.upload(location, file.to_file) 15 | uploader.model.update_column uploader.mounted_as, res.id 16 | end 17 | 18 | # Retrieve a single file 19 | def retrieve!(file_id) 20 | # allow for use of either path or ID as the file identifier 21 | id = file_id.match(/^id:/) ? file_id : "/#{uploader.store_path file_id}" 22 | CarrierWave::Storage::Dropbox::File.new(uploader, config, id, dropbox_client) 23 | end 24 | 25 | def dropbox_client 26 | @dropbox_client ||= begin 27 | ::Dropbox::Client.new(config[:access_token]) 28 | end 29 | end 30 | 31 | private 32 | 33 | def config 34 | @config ||= {} 35 | 36 | @config[:access_token] ||= uploader.dropbox_access_token 37 | 38 | @config 39 | end 40 | 41 | class File 42 | include CarrierWave::Utilities::Uri 43 | attr_reader :file_id 44 | 45 | def initialize(uploader, config, file_id, client) 46 | @uploader, @config, @file_id, @client = uploader, config, file_id, client 47 | end 48 | 49 | def file_data 50 | @file_data ||= @client.get_temporary_link(@file_id) 51 | end 52 | 53 | def filename 54 | file_data[0].name 55 | end 56 | 57 | def url 58 | file_data[1] 59 | end 60 | 61 | def delete 62 | begin 63 | @client.delete @file_id 64 | rescue ::Dropbox::ApiError 65 | end 66 | end 67 | end 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Carrierwave uploads on Dropbox 2 | 3 | This gem allows you to easily upload your medias on Dropbox using the awesome 4 | [CarrierWave](https://github.com/carrierwaveuploader/carrierwave) gem. 5 | 6 | ## Installation 7 | 8 | First, you have to create a [Dropbox app](https://www.dropbox.com/developers/apps). 9 | You can either create a "full dropbox" or "app folder" application. Please see 10 | [this wiki](https://github.com/janko-m/paperclip-dropbox/wiki/Access-types) for 11 | further information and gotchas. 12 | 13 | Then, add this line to your application's Gemfile: 14 | 15 | ~~~ruby 16 | gem 'carrierwave-dropbox' 17 | ~~~ 18 | 19 | Then run `bundle` to install the gem. 20 | 21 | To make a Dropbox app and generate a token, go [here](https://www.dropbox.com/developers/apps). 22 | Then, specify the token in your configuration (in an initializer for instance) 23 | like this: 24 | 25 | ~~~ruby 26 | CarrierWave.configure do |config| 27 | config.dropbox_access_token = Rails.application.dropbox_access_token 28 | end 29 | ~~~ 30 | 31 | **Note**: It's advisable not to directly store the credentials in your files 32 | especially if you are using a SCM (e.g. Git). You should rather rely on Rails' 33 | `secrets` feature. 34 | 35 | Then you can either specify in your uploader files the storage or define it 36 | globally through `CarrierWave.configure`: 37 | 38 | ~~~ruby 39 | class ImageUploader < CarrierWave::Uploader::Base 40 | storage :dropbox 41 | end 42 | ~~~ 43 | 44 | ## Notable differences from other storage engines 45 | 46 | Unlike typical CarrierWave storage engines, we do not assume an uploaded file 47 | will always be at the same path, as Dropbox UI users may move files around. As 48 | such, this gem relies on the file id. There are two significant implications to 49 | this approach: 50 | 51 | 1. The `#store_path` and `#store_dir` methods are not guaranteed to be accurate 52 | after the initial file upload. We do not overwrite these methods as the end user 53 | will often overwrite these methods to specify where the file should initially 54 | be stored. 55 | 2. The default `#filename` method is not accurate, as we are storing the Dropbox 56 | id, rather than the name of the file. It's recommended that end users overwrite 57 | the `#filename` method to delegate to the `CarrierWave::Storage::Dropbox::File` 58 | interface. For example: 59 | 60 | ~~~ruby 61 | class MyUploader < CarrierWave::Uploader::Base 62 | storage :dropbox 63 | 64 | def filename 65 | if original_filename 66 | # Perform any file name manipulation on initial upload 67 | elsif file 68 | file.filename 69 | end 70 | end 71 | end 72 | ~~~ 73 | 74 | ## Special thanks 75 | 76 | This project is highly based on these two gems: 77 | 78 | * [paperclip-dropbox](https://github.com/janko-m/paperclip-dropbox) 79 | * [carrierwave-aws](https://github.com/sorentwo/carrierwave-aws) 80 | 81 | Thanks to their respective authors and contributors! 82 | 83 | ## Contributing 84 | 85 | 1. Fork it 86 | 2. Create your feature branch (`git checkout -b my-new-feature`) 87 | 3. Commit your changes (`git commit -am 'Add some feature'`) 88 | 4. Push to the branch (`git push origin my-new-feature`) 89 | 5. Create new Pull Request 90 | 91 | ## License 92 | 93 | Please see the `LICENSE` file for further information. 94 | --------------------------------------------------------------------------------