refile 0.5.5 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/lib/refile.rb +252 -27
  3. data/lib/refile/app.rb +55 -14
  4. data/lib/refile/attacher.rb +39 -40
  5. data/lib/refile/attachment.rb +28 -13
  6. data/lib/refile/attachment/active_record.rb +90 -1
  7. data/lib/refile/attachment_definition.rb +47 -0
  8. data/lib/refile/backend/s3.rb +1 -147
  9. data/lib/refile/backend_macros.rb +13 -5
  10. data/lib/refile/custom_logger.rb +3 -1
  11. data/lib/refile/file.rb +9 -0
  12. data/lib/refile/image_processing.rb +1 -143
  13. data/lib/refile/rails.rb +30 -0
  14. data/lib/refile/rails/attachment_helper.rb +27 -16
  15. data/lib/refile/signature.rb +5 -0
  16. data/lib/refile/simple_form.rb +17 -0
  17. data/lib/refile/version.rb +1 -1
  18. data/spec/refile/active_record_helper.rb +11 -0
  19. data/spec/refile/app_spec.rb +197 -20
  20. data/spec/refile/attachment/active_record_spec.rb +298 -1
  21. data/spec/refile/attachment_helper_spec.rb +39 -0
  22. data/spec/refile/attachment_spec.rb +53 -5
  23. data/spec/refile/backend_examples.rb +13 -2
  24. data/spec/refile/backend_macros_spec.rb +27 -6
  25. data/spec/refile/custom_logger_spec.rb +2 -3
  26. data/spec/refile/features/direct_upload_spec.rb +18 -0
  27. data/spec/refile/features/multiple_upload_spec.rb +122 -0
  28. data/spec/refile/features/normal_upload_spec.rb +5 -3
  29. data/spec/refile/features/presigned_upload_spec.rb +4 -0
  30. data/spec/refile/features/simple_form_spec.rb +8 -0
  31. data/spec/refile/fixtures/monkey.txt +1 -0
  32. data/spec/refile/fixtures/world.txt +1 -0
  33. data/spec/refile/spec_helper.rb +21 -11
  34. data/spec/refile_spec.rb +253 -24
  35. metadata +12 -303
  36. data/.gitignore +0 -27
  37. data/.rspec +0 -2
  38. data/.rubocop.yml +0 -68
  39. data/.travis.yml +0 -21
  40. data/.yardopts +0 -1
  41. data/CONTRIBUTING.md +0 -33
  42. data/Gemfile +0 -3
  43. data/History.md +0 -96
  44. data/LICENSE.txt +0 -22
  45. data/README.md +0 -651
  46. data/Rakefile +0 -19
  47. data/app/assets/javascripts/refile.js +0 -63
  48. data/config.ru +0 -8
  49. data/config/locales/en.yml +0 -8
  50. data/config/routes.rb +0 -5
  51. data/refile.gemspec +0 -42
  52. data/spec/refile/backend/s3_spec.rb +0 -11
  53. data/spec/refile/test_app.rb +0 -65
  54. data/spec/refile/test_app/app/assets/javascripts/application.js +0 -42
  55. data/spec/refile/test_app/app/controllers/application_controller.rb +0 -2
  56. data/spec/refile/test_app/app/controllers/direct_posts_controller.rb +0 -15
  57. data/spec/refile/test_app/app/controllers/home_controller.rb +0 -4
  58. data/spec/refile/test_app/app/controllers/normal_posts_controller.rb +0 -48
  59. data/spec/refile/test_app/app/controllers/presigned_posts_controller.rb +0 -31
  60. data/spec/refile/test_app/app/models/post.rb +0 -5
  61. data/spec/refile/test_app/app/views/direct_posts/new.html.erb +0 -20
  62. data/spec/refile/test_app/app/views/home/index.html.erb +0 -1
  63. data/spec/refile/test_app/app/views/layouts/application.html.erb +0 -14
  64. data/spec/refile/test_app/app/views/normal_posts/_form.html.erb +0 -28
  65. data/spec/refile/test_app/app/views/normal_posts/edit.html.erb +0 -1
  66. data/spec/refile/test_app/app/views/normal_posts/index.html +0 -5
  67. data/spec/refile/test_app/app/views/normal_posts/new.html.erb +0 -1
  68. data/spec/refile/test_app/app/views/normal_posts/show.html.erb +0 -19
  69. data/spec/refile/test_app/app/views/presigned_posts/new.html.erb +0 -16
  70. data/spec/refile/test_app/config/database.yml +0 -7
  71. data/spec/refile/test_app/config/routes.rb +0 -17
  72. data/spec/refile/test_app/public/favicon.ico +0 -0
@@ -1,21 +0,0 @@
1
- language: ruby
2
-
3
- rvm:
4
- - 2.1
5
- - 2.2
6
- - ruby-head
7
-
8
- gemfile:
9
- - Gemfile
10
-
11
- cache: bundler
12
-
13
- sudo: false
14
-
15
- before_script:
16
- - export DISPLAY=:99.0
17
- - sh -e /etc/init.d/xvfb start
18
-
19
- matrix:
20
- allow_failures:
21
- - rvm: ruby-head
data/.yardopts DELETED
@@ -1 +0,0 @@
1
- --hide-api private --hide-void-return --markup markdown
@@ -1,33 +0,0 @@
1
- ## Security issues
2
-
3
- If you have found a security related issue, please do not file an issue on
4
- GitHub or send a PR addressing the issue. Contact
5
- [Jonas](mailto:jonas.nicklas@gmail.com) directly. You will be given public
6
- credit for your disclosure.
7
-
8
- ## Reporting issues
9
-
10
- Please try to answer the following questions in your bug report:
11
-
12
- - What did you do?
13
- - What did you expect to happen?
14
- - What happened instead?
15
-
16
- Make sure to include as much relevant information as possible. Ruby version,
17
- Refile version, OS and any stack traces you have are very valuable.
18
-
19
- ## Pull Requests
20
-
21
- - **Add tests!** Your patch won't be accepted if it doesn't have tests.
22
-
23
- - **Document any change in behaviour**. Make sure the README and any other
24
- relevant documentation are kept up-to-date.
25
-
26
- - **Create topic branches**. Please don't ask us to pull from your master branch.
27
-
28
- - **One pull request per feature**. If you want to do more than one thing, send
29
- multiple pull requests.
30
-
31
- - **Send coherent history**. Make sure each individual commit in your pull
32
- request is meaningful. If you had to make multiple intermediate commits while
33
- developing, please squash them before sending them to us.
data/Gemfile DELETED
@@ -1,3 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- gemspec
data/History.md DELETED
@@ -1,96 +0,0 @@
1
- # 0.5.5
2
-
3
- Release date: 2015-05-19
4
-
5
- - [FIXED] Upgrade rest-client version due to security concerns.
6
-
7
- # 0.5.3
8
-
9
- Release date: 2015-01-18
10
-
11
- - [FIXED] More stringent checks for ID validity.
12
- - [CHANGED] `Refile.attachment_url` not uses `Refile.mount_point` as the prefix by default.
13
-
14
- # 0.5.2
15
-
16
- Release date: 2015-01-13
17
-
18
- - [ADDED] Can generate URLs without using the Rails helper via `Refile.attachment_url`
19
- - [FIXED] Regression in `attachment_image_tag`, was not using `Refile.host`.
20
- - [FIXED] Record without file can be updated when content type and filename are not persisted
21
- - [FIXED] Remove `id` attribute from hidden field, so it doesn't get confused with the file field
22
-
23
- # 0.5.1
24
-
25
- Release date: 2015-01-11
26
-
27
- - [FIXED] Set content type from extension properly
28
- - [FIXED] Support animated GIFs when changing format
29
-
30
- # 0.5.0
31
-
32
- Release date: 2015-01-09
33
-
34
- - [ADDED] Can add custom types for easier content type validations
35
- - [ADDED] Can persist filename, size and content type
36
- - [CHANGED] The `cache_id` field is no longer necessary and no longer need to be permitted in the controller
37
- - [CHANGED] Improved logging
38
-
39
- # 0.4.2
40
-
41
- Release date: 2014-12-27
42
-
43
- - [FIXED] Regression in S3 backend
44
-
45
- # 0.4.1
46
-
47
- Release date: 2014-12-26
48
-
49
- - [CHANGED] Improved IO performance
50
- - [FIXED] Work around a bug in Ruby 2.2
51
-
52
- # 0.4.0
53
-
54
- Release date: 2014-12-26
55
-
56
- - [ADDED] Pass through additional args to S3
57
- - [ADDED] Rack app sets far future expiry headers
58
- - [ADDED] Sinatra app supports CORS preflight requests
59
- - [ADDED] Helpers can take `host` option
60
- - [ADDED] File type validations
61
- - [ADDED] attachment_field accept attribute is set from file type restrictions
62
- - [CHANGED] Dynamically generated methods in attachments are included via Module
63
- - [CHANGED] Rack app replaced with Sinatra app
64
- - [FIXED] Various content type fixes in Sinatra app
65
- - [FIXED] Don't set id of record if it is frozen
66
-
67
- # 0.3.0
68
-
69
- Release date: 2014-12-14
70
-
71
- - [ADDED] Can upload files via URL
72
-
73
- # 0.2.5
74
-
75
- Release date: 2014-12-12
76
-
77
- - [ADDED] CarrierWave style `remove_` attribute
78
- - [ADDED] Files are deleted after model is destroyed
79
- - [FIXED] Spec files can be required by external gems
80
- - [FIXED] Refile should work inside other Rails engines
81
-
82
- # 0.2.4
83
-
84
- Release date: 2014-12-08
85
-
86
- - [ADDED] Supports format option with image processing
87
-
88
- # 0.2.3
89
-
90
- Release date: 2014-12-08
91
-
92
- - [ADDED] Support for passing format to processors
93
- - [FIXED] Support IE10
94
- - [FIXED] Gracefully degrade on IE9, IE8 and IE7
95
- - [FIXED] Success event is fired at the appropriate time
96
- - [FIXED] Works with apps which don't define root_url
@@ -1,22 +0,0 @@
1
- Copyright (c) 2014 Jonas Nicklas
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.
data/README.md DELETED
@@ -1,651 +0,0 @@
1
- # Refile
2
-
3
- [![Gem Version](https://badge.fury.io/rb/refile.svg)](http://badge.fury.io/rb/refile)
4
- [![Build Status](https://travis-ci.org/elabs/refile.svg?branch=master)](https://travis-ci.org/elabs/refile)
5
- [![Code Climate](https://codeclimate.com/github/elabs/refile/badges/gpa.svg)](https://codeclimate.com/github/elabs/refile)
6
- [![Inline docs](http://inch-ci.org/github/elabs/refile.svg?branch=master)](http://inch-ci.org/github/elabs/refile)
7
-
8
- Refile is a modern file upload library for Ruby applications. It is simple, yet
9
- powerful.
10
-
11
- Links:
12
-
13
- - [API documentation](http://www.rubydoc.info/gems/refile)
14
- - [Source Code](https://github.com/elabs/refile)
15
-
16
- Features:
17
-
18
- - Configurable backends, file system, S3, etc...
19
- - Convenient integration with ORMs
20
- - On the fly manipulation of images and other files
21
- - Streaming IO for fast and memory friendly uploads
22
- - Works across form redisplays, i.e. when validations fail, even on S3
23
- - Effortless direct uploads, even to S3
24
-
25
- ## Quick start, Rails
26
-
27
- Add the gem:
28
-
29
- ``` ruby
30
- gem "mini_magick"
31
- gem "refile", require: ["refile/rails", "refile/image_processing"]
32
- ```
33
-
34
- We're requiring both Refile's Rails integration and image processing via the
35
- [MiniMagick](https://github.com/minimagick/minimagick) gem, which requires
36
- [ImageMagick](http://imagemagick.org/) to be installed. To install it simply
37
- run:
38
-
39
- ``` sh
40
- brew install imagemagick # OS X
41
- sudo apt-get install imagemagick # Ubuntu
42
- ```
43
-
44
- Use the `attachment` method to use Refile in a model:
45
-
46
- ``` ruby
47
- class User < ActiveRecord::Base
48
- attachment :profile_image
49
- end
50
- ```
51
-
52
- Generate a migration:
53
-
54
- ``` sh
55
- rails generate migration add_profile_image_to_users profile_image_id:string
56
- rake db:migrate
57
- ```
58
-
59
- Add an attachment field to your form:
60
-
61
- ``` erb
62
- <%= form_for @user do |form| %>
63
- <%= form.attachment_field :profile_image %>
64
- <% end %>
65
- ```
66
-
67
- Set up strong parameters:
68
-
69
- ``` ruby
70
- def user_params
71
- params.require(:user).permit(:profile_image)
72
- end
73
- ```
74
-
75
- And start uploading! Finally show the file in your view:
76
-
77
- ``` erb
78
- <%= image_tag attachment_url(@user, :profile_image, :fill, 300, 300) %>
79
- ```
80
-
81
- ## How it works
82
-
83
- Refile consists of several parts:
84
-
85
- 1. Backends: cache and persist files
86
- 2. Model attachments: map files to model columns
87
- 3. A Rack application: streams files and accepts uploads
88
- 4. Rails helpers: conveniently generate markup in your views
89
- 5. A JavaScript library: facilitates direct uploads
90
-
91
- Let's look at each of these in more detail!
92
-
93
- ## 1. Backend
94
-
95
- Files are uploaded to a backend. The backend assigns an ID to this file, which
96
- will be unique for this file within the backend.
97
-
98
- Let's look at a simple example of using the backend:
99
-
100
- ``` ruby
101
- backend = Refile::Backend::FileSystem.new("tmp")
102
-
103
- file = backend.upload(StringIO.new("hello"))
104
- file.id # => "b205bc..."
105
- file.read # => "hello"
106
-
107
- backend.get(file.id).read # => "hello"
108
- ```
109
-
110
- As you may notice, backends are "flat". Files do not have directories, nor do
111
- they have names or permissions, they are only identified by their ID.
112
-
113
- Refile has a global registry of backends, accessed through `Refile.backends`.
114
-
115
- There are two "special" backends, which are only really special in that they
116
- are the default backends for attachments. They are `cache` and `store`.
117
-
118
- The cache is intended to be transient. Files are added here before they are
119
- meant to be permanently stored. Usually files are then moved to the store for
120
- permanent storage, but this isn't always the case.
121
-
122
- Suppose for example that a user uploads a file in a form and receives a
123
- validation error. In that case the file has been temporarily stored in the
124
- cache. The user might decide to fix the error and resubmit, at which point the
125
- file will be promoted to the store. On the other hand, the user might simply
126
- give up and leave, now the file is left in the cache for later cleanup.
127
-
128
- Refile has convenient accessors for setting the `cache` and `store`, so for
129
- example you can switch to the S3 backend like this:
130
-
131
- ``` ruby
132
- # config/initializers/refile.rb
133
- require "refile/backend/s3"
134
-
135
- aws = {
136
- access_key_id: "xyz",
137
- secret_access_key: "abc",
138
- bucket: "my-bucket",
139
- }
140
- Refile.cache = Refile::Backend::S3.new(prefix: "cache", **aws)
141
- Refile.store = Refile::Backend::S3.new(prefix: "store", **aws)
142
- ```
143
-
144
- And add to your Gemfile:
145
- ```ruby
146
- gem "aws-sdk"
147
- ```
148
-
149
- Try this in the quick start example above and your files are now uploaded to
150
- S3.
151
-
152
- Backends also provide the option of restricting the size of files they accept.
153
- For example:
154
-
155
- ``` ruby
156
- Refile.cache = Refile::Backend::S3.new(max_size: 10.megabytes, ...)
157
- ```
158
-
159
- The Refile gem ships with [S3](lib/refile/backend/s3.rb) and
160
- [FileSystem](lib/refile/backend/file_system.rb) backends. Additional backends
161
- are provided by other gems.
162
-
163
- - [Fog](https://github.com/elabs/refile-fog) provides support for a ton of
164
- different cloud storage providers, including Google Storage and Rackspace
165
- CloudFiles.
166
- - [Postgresql](https://github.com/krists/refile-postgres)
167
- - [In Memory](https://github.com/jnicklas/refile-memory)
168
-
169
- ### Uploadable
170
-
171
- The `upload` method on backends can be called with a variety of objects. It
172
- requires that the object passed to it behaves similarly to Ruby IO objects, in
173
- particular it must implement the methods `size`, `read(length = nil, buffer =
174
- nil)`, `eof?` and `close`. All of `File`, `Tempfile`,
175
- `ActionDispath::UploadedFile` and `StringIO` implement this interface, however
176
- `String` does not. If you want to upload a file from a `String` you must wrap
177
- it in a `StringIO` first.
178
-
179
- ## 2. Attachments
180
-
181
- You've already seen the `attachment` method:
182
-
183
- ``` ruby
184
- class User < ActiveRecord::Base
185
- attachment :profile_image
186
- end
187
- ```
188
-
189
- Calling `attachment` generates a getter and setter with the given name. When
190
- you assign a file to the setter, it is uploaded to the cache:
191
-
192
- ``` ruby
193
- User.new
194
-
195
- # with a ActionDispatch::UploadedFile
196
- user.profile_image = params[:file]
197
-
198
- # with a regular File object
199
- File.open("/some/path", "rb") do |file|
200
- user.profile_image = file
201
- end
202
-
203
- # or a StringIO
204
- user.profile_image = StringIO.new("hello world")
205
-
206
- user.profile_image.id # => "fec421..."
207
- user.profile_image.read # => "hello world"
208
- ```
209
-
210
- When you call `save` on the record, the uploaded file is transferred from the
211
- cache to the store. Where possible, Refile does this move efficiently. For example
212
- if both `cache` and `store` are on the same S3 account, instead of downloading
213
- the file and uploading it again, Refile will simply issue a copy command to S3.
214
-
215
- ### Other ORMs
216
-
217
- Refile is built to integrate with ORMs other than ActiveRecord, but this being
218
- a very young gem, such integrations do not yet exist. Take a look at the [ActiveRecord
219
- integration](lib/refile/attachment/active_record.rb), building your own should
220
- not be too difficult.
221
-
222
- ### Pure Ruby classes
223
-
224
- You can also use attachments in pure Ruby classes like this:
225
-
226
- ``` ruby
227
- class User
228
- extend Refile::Attachment
229
-
230
- attr_accessor :profile_image_id
231
-
232
- attachment :profile_image
233
- end
234
- ```
235
-
236
- ## 3. Rack Application
237
-
238
- Refile includes a Rack application (an endpoint, not a middleware), written in
239
- Sinatra. This application streams files from backends and can even accept file
240
- uploads and upload them to backends.
241
-
242
- **Important:** Unlike other file upload solutions, Refile always streams your files through your
243
- application. It cannot generate URLs to your files. This means that you should
244
- **always** put a CDN or other HTTP cache in front of your application. Serving
245
- files through your app takes a lot of resources and you want it to happen rarely.
246
-
247
- Setting this up is actually quite simple, you can use the same CDN you would use
248
- for your application's static assets. [This blog post](http://www.happybearsoftware.com/use-cloudfront-and-the-rails-asset-pipeline-to-speed-up-your-app.html)
249
- explains how to set this up (bonus: faster static assets!). Once you've set this
250
- up, simply configure Refile to use your CDN:
251
-
252
- ``` ruby
253
- Refile.host = "//your-dist-url.cloudfront.net"
254
- ```
255
-
256
- Using a [protocol-relative URL](http://www.paulirish.com/2010/the-protocol-relative-url/) for `Refile.host` is recommended.
257
-
258
- ### Mounting
259
-
260
- If you are using Rails and have required [refile/rails.rb](lib/refile/rails.rb),
261
- then the Rack application is mounted for you at `/attachments`. You should be able
262
- to see this when you run `rake routes`.
263
-
264
- You could also run the application on its own, it doesn't need to be mounted to
265
- work.
266
-
267
- ### Retrieving files
268
-
269
- Files can be retrieved from the application by calling:
270
-
271
- ```
272
- GET /attachments/:backend_name/:id/:filename
273
- ```
274
-
275
- The `:filename` serves no other purpose than generating a nice name when the user
276
- downloads the file, it does not in any way affect the downloaded file. For caching
277
- purposes you should always use the same filename for the same file. The Rails helpers
278
- default this to the name of the column.
279
-
280
- ### Processing
281
-
282
- Refile provides on the fly processing of files. You can trigger it by calling
283
- a URL like this:
284
-
285
- ```
286
- GET /attachments/:backend_name/:processor_name/*args/:id/:filename
287
- ```
288
-
289
- Suppose we have uploaded a file:
290
-
291
- ``` ruby
292
- Refile.cache.upload(StringIO.new("hello")).id # => "a4e8ce"
293
- ```
294
-
295
- And we've defined a processor like this:
296
-
297
- ``` ruby
298
- Refile.processor :reverse do |file|
299
- StringIO.new(file.read.reverse)
300
- end
301
- ```
302
-
303
- Then you could do the following.
304
-
305
- ``` sh
306
- curl http://127.0.0.1:3000/attachments/cache/reverse/a4e8ce/some_file.txt
307
- elloh
308
- ```
309
-
310
- Refile calls `call` on the processor and passes in the retrieved file, as well
311
- as all additional arguments sent through the URL. See the
312
- [built in image processors](lib/refile/image_processing.rb) for a more advanced
313
- example.
314
-
315
- ## 4. Rails helpers
316
-
317
- Refile provides the `attachment_field` form helper which generates a file field
318
- as well as a hidden field. This field keeps track of the file in case it is not
319
- yet permanently stored, for example if validations fail. It is also used for
320
- direct and presigned uploads. For this reason it is highly recommended to use
321
- `attachment_field` instead of `file_field`.
322
-
323
- ``` erb
324
- <%= form_for @user do |form| %>
325
- <%= form.attachment_field :profile_image %>
326
- <% end %>
327
- ```
328
-
329
- Will generate something like:
330
-
331
- ``` html
332
- <form action="/users" enctype="multipart/form-data" method="post">
333
- <input name="user[profile_image]" type="hidden">
334
- <input name="user[profile_image]" type="file">
335
- </form>
336
- ```
337
-
338
- The `attachment_url` helper can then be used for generating URLs for the uploaded
339
- files:
340
-
341
- ``` erb
342
- <%= link_to "Image", attachment_url(@user, :profile_image) %>
343
- ```
344
-
345
- Any additional arguments to it are included in the URL as processor arguments:
346
-
347
- ``` erb
348
- <%= link_to "Image", attachment_url(@user, :profile_image, :fill, 300, 300) %>
349
- ```
350
-
351
- There's also a helper for generating image tags:
352
-
353
- ``` erb
354
- <%= attachment_image_tag(@user, :profile_image, :fill, 300, 300) %>
355
- ```
356
-
357
- With this helper you can specify an image which is used as a fallback in case
358
- no file has been uploaded:
359
-
360
- ``` erb
361
- <%= attachment_image_tag(@user, :profile_image, :fill, 300, 300, fallback: "default.png") %>
362
- ```
363
-
364
- ## 5. JavaScript library
365
-
366
- Refile's JavaScript library is small but powerful.
367
-
368
- Uploading files is slow, so anything we can do to speed up the process is going
369
- to lead to happier users. One way to cheat is to start uploading files directly
370
- after the user has chosen a file, instead of waiting until they hit the submit
371
- button. This provides a significantly better user experience. Implementing this
372
- is usually tricky, but thankfully Refile makes it very easy.
373
-
374
- First, load the JavaScript file. If you're using the asset pipeline, you can
375
- simply include it like this:
376
-
377
- ``` javascript
378
- //= require refile
379
- ```
380
-
381
- Otherwise you can grab a copy [here](https://raw.githubusercontent.com/elabs/refile/master/app/assets/javascripts/refile.js).
382
- Be sure to always update your copy of this file when you upgrade to the latest
383
- Refile version.
384
-
385
- Now mark the field for direct upload:
386
-
387
- ``` erb
388
- <%= form.attachment_field :profile_image, direct: true %>
389
- ```
390
-
391
- There is no step 3 ;)
392
-
393
- The file is now uploaded to the `cache` immediately after the user chooses a file.
394
- If you try this in the browser, you'll notice that an AJAX request is fired as
395
- soon as you choose a file. Then when you submit to the server, the file is no
396
- longer submitted, only its id.
397
-
398
- If you want to improve the experience of this, the JavaScript library fires
399
- a couple of custom DOM events. These events bubble, so you can also listen for
400
- them on the form for example:
401
-
402
- ``` javascript
403
- form.addEventListener("upload:start", function() {
404
- // ...
405
- });
406
-
407
- form.addEventListener("upload:success", function() {
408
- // ...
409
- });
410
-
411
- input.addEventListener("upload:progress", function() {
412
- // ...
413
- });
414
- ```
415
-
416
- You can also listen for them with jQuery, even with event delegation:
417
-
418
- ``` javascript
419
- $(document).on("upload:start", "form", function(e) {
420
- // ...
421
- });
422
- ```
423
-
424
- This way you could for example disable the submit button until all files have
425
- uploaded:
426
-
427
- ``` javascript
428
- $(document).on("upload:start", "form", function(e) {
429
- $(this).find("input[type=submit]").attr("disabled", true)
430
- });
431
-
432
- $(document).on("upload:complete", "form", function(e) {
433
- if(!$(this).find("input.uploading").length) {
434
- $(this).find("input[type=submit]").removeAttr("disabled")
435
- }
436
- });
437
- ```
438
-
439
- ### Presigned uploads
440
-
441
- Amazon S3 supports uploads directly from the browser to S3 buckets. With this
442
- feature you can bypass your application entirely; uploads never hit your application
443
- at all. Unfortunately the default configuration of S3 buckets does not allow
444
- cross site AJAX requests from posting to buckets. Fixing this is easy though.
445
-
446
- - Open the AWS S3 console and locate your bucket
447
- - Right click on it and choose "Properties"
448
- - Open the "Permission" section
449
- - Click "Add CORS Configuration"
450
-
451
- The default configuration only allows "GET", you'll want to allow "POST" as
452
- well. You'll also want to permit the "Content-Type" and "Origin" headers.
453
-
454
- It could look something like this:
455
-
456
- ``` xml
457
- <CORSConfiguration>
458
- <CORSRule>
459
- <AllowedOrigin>*</AllowedOrigin>
460
- <AllowedMethod>GET</AllowedMethod>
461
- <AllowedMethod>POST</AllowedMethod>
462
- <MaxAgeSeconds>3000</MaxAgeSeconds>
463
- <AllowedHeader>Authorization</AllowedHeader>
464
- <AllowedHeader>Content-Type</AllowedHeader>
465
- <AllowedHeader>Origin</AllowedHeader>
466
- </CORSRule>
467
- </CORSConfiguration>
468
- ```
469
-
470
- If you're paranoid you can restrict the allowed origin to only your domain, but
471
- since your bucket is only writable with authentication anyway, this shouldn't
472
- be necessary.
473
-
474
- Note that you do not need to, and in fact you shouldn't, make your bucket world
475
- writable.
476
-
477
- Once you've put in the new configuration, click "Save".
478
-
479
- Now you can enable presigned uploads:
480
-
481
- ``` erb
482
- <%= form.attachment_field :profile_image, presigned: true %>
483
- ```
484
-
485
- You can also enable both direct and presigned uploads, and it'll fall back to
486
- direct uploads if presigned uploads aren't available. This is useful if you're
487
- using the FileSystem backend in development or test mode and the S3 backend in
488
- production mode.
489
-
490
- ``` erb
491
- <%= form.attachment_field :profile_image, direct: true, presigned: true %>
492
- ```
493
-
494
- ### Browser compatibility
495
-
496
- Refile's JavaScript library requires HTML5 features which are unavailable on
497
- IE9 and earlier versions. All other major browsers are supported.
498
-
499
- ## Additional metadata
500
-
501
- In the quick start example above, we chose to only store the file id, but often
502
- it is useful to store the file's filename, size and content type as well.
503
- Refile makes it easy to extract this data and store it alongside the id. All you
504
- need to do is add columns for these:
505
-
506
- ``` ruby
507
- class StoreMetadata < ActiveRecord::Migration
508
- def change
509
- add_column :users, :profile_image_filename, :string
510
- add_column :users, :profile_image_size, :integer
511
- add_column :users, :profile_image_content_type, :string
512
- end
513
- end
514
- ```
515
-
516
- These columns will now be filled automatically.
517
-
518
- ## File type validations
519
-
520
- Refile can check that attached files have a given content type or extension.
521
- This allows you to warn users if they try to upload an invalid file.
522
-
523
- **Important:** You should regard this as a convenience feature for your users,
524
- not a security feature. Both file extension and content type can easily be
525
- spoofed.
526
-
527
- In order to limit attachments to an extension or content type, you can provide
528
- them like this:
529
-
530
- ``` ruby
531
- attachment :cv, extension: "pdf"
532
- attachment :profile_image, content_type: "image/jpeg"
533
- ```
534
-
535
- You can also provide a list of content types or extensions:
536
-
537
- ``` ruby
538
- attachment :cv, extension: ["pdf", "doc"]
539
- attachment :profile_image, content_type: ["image/jpeg", "image/png", "image/gif"]
540
- ```
541
-
542
- Since the combination of JPEG, PNG and GIF is so common, you can also specify
543
- this more succinctly like this:
544
-
545
- ``` ruby
546
- attachment :profile_image, type: :image
547
- ```
548
-
549
- When a user uploads a file with an invalid extension or content type and
550
- submits the form, they'll be presented with a validation error.
551
-
552
- If you use a particular content type or set of content types frequently
553
- you can define your own types like this:
554
-
555
- ``` ruby
556
- Refile.types[:document] = Refile::Type.new(:document,
557
- content_type: %w[text/plain application/pdf]
558
- )
559
- ```
560
-
561
- Now you can use them like this:
562
-
563
- ``` ruby
564
- attachment :profile_image, type: :document
565
- ```
566
-
567
- ## Removing attached files
568
-
569
- File input fields unfortunately do not have the option of removing an already
570
- uploaded file. This is problematic when editing a model which has a file attached
571
- and the user wants to remove this file. To work around this, Refile automatically
572
- adds an attribute to your model when you use the `attachment` method, which is
573
- designed to be used with a checkbox in a form.
574
-
575
- ``` erb
576
- <%= form_for @user do |form| %>
577
- <%= form.label :profile_image %>
578
- <%= form.attachment_field :profile_image %>
579
-
580
- <%= form.check_box :remove_profile_image %>
581
- <%= form.label :remove_profile_image %>
582
- <% end %>
583
- ```
584
-
585
- Don't forget to permit this attribute in your controller:
586
-
587
- ``` ruby
588
- def user_params
589
- params.require(:user).permit(:profile_image, :remove_profile_image)
590
- end
591
- ```
592
-
593
- Now when you check this checkbox and submit the form, the previously attached
594
- file will be removed.
595
-
596
- ## Fetching remote files by URL
597
-
598
- You might want to give you users the option of uploading a file by its URL.
599
- This could be either just via a textfield or through some other interface.
600
- Refile makes it easy to fetch this file and upload it. Just add a field like
601
- this:
602
-
603
- ``` erb
604
- <%= form_for @user do |form| %>
605
- <%= form.label :profile_image, "Attach image" %>
606
- <%= form.attachment_field :profile_image %>
607
-
608
- <%= form.label :remote_profile_image_url, "Or specify URL" %>
609
- <%= form.text_field :remote_profile_image_url %>
610
- <% end %>
611
- ```
612
-
613
- Then permit this field in your controller:
614
-
615
- ``` ruby
616
- def user_params
617
- params.require(:user).permit(:profile_image, :remote_profile_image_url)
618
- end
619
- ```
620
-
621
- Refile will now fetch the file from the given URL, following redirects if
622
- needed.
623
-
624
- ## Cache expiry
625
-
626
- Files will accumulate in your cache, and you'll probably want to remove them
627
- after some time.
628
-
629
- The FileSystem backend does not currently provide any method of doing this. PRs
630
- welcome ;)
631
-
632
- On S3 this can be conveniently handled through lifecycle rules. Exactly how
633
- depends a bit on your setup. If you are using the suggested setup of having
634
- one bucket with `cache` and `store` being directories in that bucket (or prefixes
635
- in S3 parlance), then follow the following steps, otherwise adapt them to your
636
- needs:
637
-
638
- - Open the AWS S3 console and locate your bucket
639
- - Right click on it and choose "Properties"
640
- - Open the "Lifecycle" section
641
- - Click "Add rule"
642
- - Choose "Apply the rule to: A prefix"
643
- - Enter "cache/" as the prefix (trailing slash!)
644
- - Click "Configure rule"
645
- - For "Action on Objects" you'll probably want to choose "Permanently Delete Only"
646
- - Choose whatever number of days you're comfortable with, I chose "1"
647
- - Click "Review" and finally "Create and activate Rule"
648
-
649
- ## License
650
-
651
- [MIT](LICENSE.txt)