paperclip-dropbox 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Dropbox
2
2
 
3
- This gem extends [Paperclip](https://github.com/thoughtbot/paperclip) with Dropbox storage.
3
+ This gem extends [Paperclip](https://github.com/thoughtbot/paperclip) with
4
+ Dropbox storage.
4
5
 
5
6
  ## Installation
6
7
 
@@ -10,9 +11,59 @@ Put it in your `Gemfile`:
10
11
  gem "paperclip-dropbox"
11
12
  ```
12
13
 
13
- Ano run `bundle install`.
14
+ And run `bundle install`.
14
15
 
15
- ## Usage
16
+ ## Dropbox Setup
17
+
18
+ You must [create a Dropbox app](https://www.dropbox.com/developers/apps) and
19
+ authorize it to access the Dropbox account you want to use for storage. You have
20
+ a choice of two access types: **App folder** or **Full Dropbox**.
21
+
22
+ ### "Full Dropbox" access
23
+
24
+ Files will be stored in the [Public folder](https://www.dropbox.com/help/16/en).
25
+ Download URLs are predictable, valid forever, and don't require an API call to
26
+ retrieve, but this may not be a good thing if you don't want your files to be
27
+ easily accessed. When using one account to store data for multiple sites (e.g.
28
+ staging and production instances), it's up to you to make sure they don't step
29
+ on each other's toes.
30
+
31
+ Note that accounts created after October 4, 2012 don't have the Public folder
32
+ enabled by default: [Go here](https://www.dropbox.com/enable_public_folder) to
33
+ enable it. If you get a message that the folder is deleted, just create a folder
34
+ in the root named "Public", and it should gain the special icon.
35
+
36
+ ### "App folder" access
37
+
38
+ Files will be stored in a subfolder under Apps (configurable in the app
39
+ settings). Download URLs are generated on demand by calling the Dropbox API, and
40
+ are only valid for 4 hours. This means your files are slightly "less public",
41
+ and you can isolate data from multiple sites by creating multiple apps.
42
+
43
+ **In app folder mode, every call to `#url` on an attachment will result in an
44
+ HTTP request to Dropbox.** Whether or not this is acceptable will depend on what
45
+ you're storing and how you're exposing it to users.
46
+
47
+ ### Authorizing your app
48
+
49
+ After creating your app, it will have an "App key" and "App secret". Provide
50
+ these and the access type (`dropbox` or `app_folder`) to the authorization Rake task:
51
+
52
+ ```
53
+ $ rake dropbox:authorize APP_KEY=your_app_key APP_SECRET=your_app_secret ACCESS_TYPE=your_access_type
54
+ ```
55
+
56
+ It will output an authorization URL that you must visit to grant the app access.
57
+ It will then output your access token, access token secret, and user ID.
58
+
59
+ For non-Rails projects, you must require this task in your `Rakefile`:
60
+
61
+ ```ruby
62
+ # Rakefile
63
+ load "paperclip/dropbox/tasks.rake"
64
+ ```
65
+
66
+ ## Configuration
16
67
 
17
68
  Example:
18
69
 
@@ -25,20 +76,13 @@ class User < ActiveRecord::Base
25
76
  end
26
77
  ```
27
78
 
28
- Valid options for `#has_attached_file` are:
29
-
30
- - `:dropbox_credentials` – A Hash, a File, or a path to the file where your
31
- Dropbox configuration is located
32
-
33
- - `:dropbox_options` – A Hash that accepts some Dropbox-specific options (they
34
- are explained more below)
35
-
36
- ## Configuration
37
-
38
79
  ### The `:dropbox_credentials` option
39
80
 
40
- It's best to put your Dropbox credentials into a `dropbox.yml`, and pass the path to
41
- that file to `:dropbox_credentials`. One example of that YAML file:
81
+ This can be a hash or path to a YAML file containing the keys listed in the
82
+ example below. These are obtained from your Dropbox app settings and the
83
+ authorization Rake task.
84
+
85
+ Example `config/dropbox.yml`:
42
86
 
43
87
  ```erb
44
88
  app_key: <%= ENV["DROPBOX_APP_KEY"] %>
@@ -46,106 +90,83 @@ app_secret: <%= ENV["DROPBOX_APP_SECRET"] %>
46
90
  access_token: <%= ENV["DROPBOX_ACCESS_TOKEN"] %>
47
91
  access_token_secret: <%= ENV["DROPBOX_ACCESS_TOKEN_SECRET"] %>
48
92
  user_id: <%= ENV["DROPBOX_USER_ID"] %>
93
+ access_type: <%= ENV["DROPBOX_ACCESS_TYPE"] %>
49
94
  ```
50
95
 
51
- This is a good practice; Don't put your credentials directly in your YAML file.
52
- Instead set them in system environment variables, and then embed them here through ERB.
53
-
54
- Note that all credentials mentioned here are required.
96
+ It is good practice to not include the credentials directly in the YAML file.
97
+ Instead you can set them in environment variables and embed them with ERB. Note
98
+ `access_type` must be either `"dropbox"` or `"app_folder"` depending on the
99
+ access type of your app; see **Dropbox Setup** above.
55
100
 
56
- If you don't have your app key and secret yet, go to your [Dropbox apps](https://www.dropbox.com/developers/apps),
57
- and create a new app there, which will then provide you your app key and secret.
58
- Note that your app has to have the **Full Dropbox** access level (not the "App folder").
59
- This is because the uploaded files have to be stored in your `Public` folder.
101
+ You can also nest your credentials in environments (like in your `database.yml`):
60
102
 
61
- If you're a relatively new Dropbox user, you'll have to enable your `Public` folder first by visiting
62
- [this link](https://www.dropbox.com/enable_public_folder).
63
-
64
- After you obtain your app key and secret, you can obtain the rest of the credentials
65
- through the `dropbox:authorize` rake task, which is described in more detail at the bottom of the readme.
66
-
67
- You can also namespace your credentials in `development`, `testing` and `production` environments
68
- (just like you do in your `database.yml`).
103
+ ```erb
104
+ development:
105
+ app_key: "..."
106
+ ...
107
+ production:
108
+ app_key: "..."
109
+ ...
110
+ ```
69
111
 
70
112
  ### The `:dropbox_options` option
71
113
 
72
- You can pass it 3 options:
114
+ This is a hash containing any of the following options:
73
115
 
74
- - `:path` – A block, provides similar results as Paperclip's `:path` option
75
- - `:environment` – If you namespaced you credentials with environments, here you
76
- can set your environment if you're in a non-Rails application
77
- - `:unique_filename` – Boolean
116
+ - `:path` – Block, works similarly to Paperclip's `:path` option
117
+ - `:unique_filename` – Boolean, whether to generate unique names for files in
118
+ the absence of a custom `:path`
119
+ - `:environment` – String, the environment name to use for selecting namespaced
120
+ credentials in a non-Rails app
78
121
 
79
- The `:path` option works in this way; you give it a block, and the return value
80
- will be the path that the uploaded file will be saved to. The block yields attachment style,
81
- and is executed in the scope of the class' instance. For example, let's say you have
122
+ The `:path` option should be a block that returns a path that the uploaded file
123
+ should be saved to. The block yields the attachment style and is executed in the
124
+ scope of the model instance. For example:
82
125
 
83
126
  ```ruby
84
127
  class User < ActiveRecord::Base
85
128
  has_attached_file :avatar,
86
129
  :storage => :dropbox,
87
- :dropbox_credentials => "...",
130
+ :dropbox_credentials => "#{Rails.root}/config/dropbox.yml",
88
131
  :styles => { :medium => "300x300" },
89
132
  :dropbox_options => {
90
- :path => proc { |style| "#{style}/#{id}_#{avatar.original_filename}"}
133
+ :path => proc { |style| "#{style}/#{id}_#{avatar.original_filename}" }
91
134
  }
92
135
  end
93
136
  ```
94
137
 
95
- Let's say now that a new user is created with the ID of `23`, and a `photo.jpg` as his
96
- avatar. The following files would be saved to the Dropbox:
138
+ Let's say now that a new user is created with the ID of `23`, and a `photo.jpg`
139
+ as his avatar. The following files would be saved to the Dropbox:
97
140
 
98
141
  ```
99
142
  Public/original/23_photo.jpg
100
143
  Public/medium/23_photo_medium.jpg
101
144
  ```
102
145
 
103
- The other file is called `photo_medium.jpg` because style names (other than `original`)
104
- will always be appended to the filenames, for better management.
105
-
106
- Files in Dropbox inside a certain folder have to have **unique filenames**, otherwise exception
107
- `Paperclip::Storage::Dropbox::FileExists` is thrown. To help you with that, you
108
- can set
146
+ The other file is called `photo_medium.jpg` because style names (other than
147
+ `original`) will always be appended to the filenames, for better management.
109
148
 
110
- ```ruby
111
- # ...
112
- :dropbox_options => {
113
- :unique_filename => true
114
- }
115
- ```
149
+ Filenames within a Dropbox folder must be unique; uploading a file with a
150
+ duplicate name will throw error `Paperclip::Storage::Dropbox::FileExists`. If
151
+ you don't want to bother crafting your own unique filenames with the `:path`
152
+ option, you can instead set the `:unique_filename` option to true and it will
153
+ take care of that.
116
154
 
117
- That will set `:path` to something that will be unique.
155
+ ### URL options
118
156
 
119
- You can also pass in the `:download` option to attachment's `#url`:
157
+ When using `dropbox` access type, the `#url` method of attachments returns a
158
+ URL to a "landing page" that provides a preview of the file and a download link.
159
+ To make `#url` return a direct file download link, set the `:download` option as
160
+ a parameter:
120
161
 
121
162
  ```ruby
122
163
  user.avatar.url(:download => true)
123
164
  ```
124
165
 
125
- And that will return a download URL for that attachment (so, if a user clicks to
126
- that link, the file will be downloaded, as opposed to being opened in the browser).
127
-
128
- ### The `dropbox:authorize` rake task
129
-
130
- You just provide it your app key and secret:
131
-
132
- ```
133
- $ rake dropbox:authorize APP_KEY=your_app_key APP_SECRET=your_app_secret
134
- ```
135
-
136
- It will provide you an authorization URL which you have to visit, and after that
137
- it will output the rest of your credentials, which you just copy-paste wherever
138
- you need to.
139
-
140
- If you're in a non-Rails application, to get this rake task, you must require it in
141
- your `Rakefile`:
142
-
143
- ```ruby
144
- # Rakefile
145
- require "rake"
146
- require "paperclip/dropbox/rake"
147
- ```
166
+ When using `app_folder` access type, `#url` always returns a direct link, and
167
+ setting the `:download` option simply forces the file to be downloaded even if
168
+ the browser would normally just display it.
148
169
 
149
170
  ## License
150
171
 
151
- [MIT](https://github.com/janko-m/paperclip-dropbox/blob/master/LICENSE)
172
+ [MIT License](https://github.com/janko-m/paperclip-dropbox/blob/master/LICENSE)
@@ -2,7 +2,7 @@ module Paperclip
2
2
  module Dropbox
3
3
  class Railtie < Rails::Railtie
4
4
  rake_tasks do
5
- load "paperclip/dropbox/tasks/authorize.rake"
5
+ load "paperclip/dropbox/tasks.rake"
6
6
  end
7
7
  end
8
8
  end
@@ -5,15 +5,15 @@ module Paperclip
5
5
  module Rake
6
6
  extend self
7
7
 
8
- def authorize(app_key, app_secret)
8
+ def authorize(app_key, app_secret, access_type)
9
9
  session = create_new_session(app_key, app_secret)
10
10
 
11
- puts "\nVisit this URL: #{session.get_authorize_url}"
11
+ puts "Visit this URL: #{session.get_authorize_url}"
12
12
  print "And after you approved the authorization confirm it here (y/n): "
13
13
 
14
14
  assert_answer!
15
15
  session.get_access_token
16
- dropbox_client = DropboxClient.new(session, "dropbox")
16
+ dropbox_client = DropboxClient.new(session, access_type)
17
17
  account_info = dropbox_client.account_info
18
18
 
19
19
  puts <<-MESSAGE
@@ -23,7 +23,6 @@ Authorization was successful. Here you go:
23
23
  access_token: #{session.access_token.key}
24
24
  access_token_secret: #{session.access_token.secret}
25
25
  user_id: #{account_info["uid"]}
26
-
27
26
  MESSAGE
28
27
  end
29
28
 
@@ -1,12 +1,13 @@
1
- require "rake"
2
- require "paperclip/dropbox/rake" unless defined?(Paperclip::Dropbox::Rake)
1
+ require "paperclip/dropbox/rake"
3
2
 
4
3
  namespace :dropbox do
5
4
  desc "Obtains your Dropbox credentials"
6
5
  task :authorize do
7
6
  if ENV["APP_KEY"].nil? or ENV["APP_SECRET"].nil?
8
- raise ArgumentError, "usage: `rake dropbox:authorize APP_KEY=your_app_key APP_SECRET=your_app_secret`"
7
+ puts "USAGE: `rake dropbox:authorize APP_KEY=your_app_key APP_SECRET=your_app_secret` ACCESS_TYPE=your_access_type"
8
+ exit
9
9
  end
10
- Paperclip::Dropbox::Rake.authorize(ENV["APP_KEY"], ENV["APP_SECRET"])
10
+
11
+ Paperclip::Dropbox::Rake.authorize(ENV["APP_KEY"], ENV["APP_SECRET"], ENV["ACCESS_TYPE"] || "dropbox")
11
12
  end
12
13
  end
@@ -1,6 +1,5 @@
1
1
  require 'dropbox_sdk'
2
2
  require 'active_support/core_ext/hash/keys'
3
- require 'active_support/core_ext/hash/slice'
4
3
  require 'active_support/inflector/methods'
5
4
  require 'yaml'
6
5
  require 'erb'
@@ -38,30 +37,47 @@ module Paperclip
38
37
  end
39
38
 
40
39
  def exists?(style)
41
- !!dropbox_client.media(path(style))
40
+ metadata = dropbox_metadata(style)
41
+ !metadata.nil? && !metadata['is_deleted']
42
42
  rescue DropboxError
43
43
  false
44
44
  end
45
45
 
46
46
  def url(*args)
47
- unless blank?
47
+ if present?
48
48
  style = args.first.is_a?(Symbol) ? args.first : default_style
49
49
  options = args.last.is_a?(Hash) ? args.last : {}
50
50
  query = options[:download] ? "?dl=1" : ""
51
51
 
52
- File.join("http://dl.dropbox.com/u/#{user_id}", path_for_url(style) + query)
52
+ if app_folder_mode
53
+ dropbox_client.media(path(style))['url'] + query
54
+ else
55
+ File.join("http://dl.dropbox.com/u/#{user_id}", path_for_url(style) + query)
56
+ end
53
57
  end
54
58
  end
55
59
 
56
60
  def path(style)
57
- File.join("Public", path_for_url(style))
61
+ if app_folder_mode
62
+ path_for_url(style)
63
+ else
64
+ File.join("Public", path_for_url(style))
65
+ end
58
66
  end
59
67
 
60
68
  def path_for_url(style)
61
- result = instance.instance_exec(style, &file_path)
62
- result += extension if result !~ /\.\w{3,4}$/
69
+ path = instance.instance_exec(style, &file_path)
63
70
  style_suffix = (style != default_style ? "_#{style}" : "")
64
- result.sub(extension, "#{style_suffix}#{extension}")
71
+
72
+ if original_extension && path =~ /#{original_extension}$/
73
+ path.sub(original_extension, "#{style_suffix}#{original_extension}")
74
+ else
75
+ path + style_suffix + original_extension.to_s
76
+ end
77
+ end
78
+
79
+ def dropbox_metadata(style = default_style)
80
+ dropbox_client.metadata(path(style))
65
81
  end
66
82
 
67
83
  def copy_to_local_file(style, destination_path)
@@ -70,16 +86,29 @@ module Paperclip
70
86
  local_file.close
71
87
  end
72
88
 
89
+ def dropbox_client
90
+ @dropbox_client ||= begin
91
+ assert_required_keys
92
+ session = DropboxSession.new(@dropbox_credentials[:app_key], @dropbox_credentials[:app_secret])
93
+ session.set_access_token(@dropbox_credentials[:access_token], @dropbox_credentials[:access_token_secret])
94
+ DropboxClient.new(session, @dropbox_credentials[:access_type] || 'dropbox')
95
+ end
96
+ end
97
+
73
98
  private
74
99
 
75
- def extension
76
- original_filename[/\.\w{3,4}$/]
100
+ def original_extension
101
+ original_filename[/\.[^.]+$/]
77
102
  end
78
103
 
79
104
  def user_id
80
105
  @dropbox_credentials[:user_id]
81
106
  end
82
107
 
108
+ def app_folder_mode
109
+ @dropbox_credentials[:access_type] == 'app_folder'
110
+ end
111
+
83
112
  def file_path
84
113
  return @dropbox_options[:path] if @dropbox_options[:path]
85
114
 
@@ -90,19 +119,13 @@ module Paperclip
90
119
  end
91
120
  end
92
121
 
93
- def dropbox_client
94
- @dropbox_client ||= begin
95
- assert_required_keys
96
- session = DropboxSession.new(@dropbox_credentials[:app_key], @dropbox_credentials[:app_secret])
97
- session.set_access_token(@dropbox_credentials[:access_token], @dropbox_credentials[:access_token_secret])
98
- DropboxClient.new(session, "dropbox")
99
- end
100
- end
101
-
102
122
  def assert_required_keys
103
123
  [:app_key, :app_secret, :access_token, :access_token_secret, :user_id].each do |key|
104
124
  @dropbox_credentials.fetch(key)
105
125
  end
126
+ if @dropbox_credentials[:access_type] and not ['dropbox', 'app_folder'].include?(@dropbox_credentials[:access_type])
127
+ raise KeyError, ":access_type must be 'dropbox' or 'app_folder'"
128
+ end
106
129
  end
107
130
 
108
131
  def parse_credentials(credentials)
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |gem|
4
4
  gem.name = "paperclip-dropbox"
5
- gem.version = "1.0.1"
5
+ gem.version = "1.1.0"
6
6
  gem.platform = Gem::Platform::RUBY
7
7
 
8
8
  gem.homepage = "https://github.com/janko-m/paperclip-dropbox"
@@ -21,11 +21,12 @@ Gem::Specification.new do |gem|
21
21
  gem.add_dependency "paperclip", "~> 3.1"
22
22
  gem.add_dependency "dropbox-sdk", "~> 1.3"
23
23
 
24
- gem.add_development_dependency "rake", "~> 0.9"
24
+ gem.add_development_dependency "rake", ">= 0.9"
25
25
  gem.add_development_dependency "rspec", "~> 2.11"
26
26
  gem.add_development_dependency "vcr", "~> 2.2"
27
27
  gem.add_development_dependency "fakeweb", "~> 1.3"
28
28
  gem.add_development_dependency "activerecord", "~> 3.2"
29
29
  gem.add_development_dependency "rack-test", "~> 0.6"
30
30
  gem.add_development_dependency "sqlite3", "~> 1.3"
31
+ gem.add_development_dependency "rest-client", "~> 1.6"
31
32
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paperclip-dropbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-21 00:00:00.000000000 Z
12
+ date: 2012-12-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: paperclip
@@ -48,7 +48,7 @@ dependencies:
48
48
  requirement: !ruby/object:Gem::Requirement
49
49
  none: false
50
50
  requirements:
51
- - - ~>
51
+ - - ! '>='
52
52
  - !ruby/object:Gem::Version
53
53
  version: '0.9'
54
54
  type: :development
@@ -56,7 +56,7 @@ dependencies:
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  none: false
58
58
  requirements:
59
- - - ~>
59
+ - - ! '>='
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0.9'
62
62
  - !ruby/object:Gem::Dependency
@@ -155,6 +155,22 @@ dependencies:
155
155
  - - ~>
156
156
  - !ruby/object:Gem::Version
157
157
  version: '1.3'
158
+ - !ruby/object:Gem::Dependency
159
+ name: rest-client
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ~>
164
+ - !ruby/object:Gem::Version
165
+ version: '1.6'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ~>
172
+ - !ruby/object:Gem::Version
173
+ version: '1.6'
158
174
  description: Extends Paperclip with Dropbox storage.
159
175
  email:
160
176
  - janko.marohnic@gmail.com
@@ -164,7 +180,7 @@ extra_rdoc_files: []
164
180
  files:
165
181
  - lib/paperclip/dropbox/railtie.rb
166
182
  - lib/paperclip/dropbox/rake.rb
167
- - lib/paperclip/dropbox/tasks/authorize.rake
183
+ - lib/paperclip/dropbox/tasks.rake
168
184
  - lib/paperclip/dropbox.rb
169
185
  - lib/paperclip/storage/dropbox.rb
170
186
  - lib/paperclip-dropbox.rb
@@ -190,6 +206,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
190
206
  - - ! '>='
191
207
  - !ruby/object:Gem::Version
192
208
  version: '0'
209
+ segments:
210
+ - 0
211
+ hash: -208715218772659661
193
212
  requirements: []
194
213
  rubyforge_project:
195
214
  rubygems_version: 1.8.23
@@ -197,4 +216,3 @@ signing_key:
197
216
  specification_version: 3
198
217
  summary: Extends Paperclip with Dropbox storage.
199
218
  test_files: []
200
- has_rdoc: