paperclip-dropbox 1.0.1 → 1.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.
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: