refile 0.4.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of refile might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -2
- data/.travis.yml +2 -0
- data/.yardopts +1 -0
- data/CONTRIBUTING.md +33 -0
- data/History.md +9 -0
- data/README.md +67 -16
- data/app/assets/javascripts/refile.js +19 -17
- data/lib/refile.rb +36 -6
- data/lib/refile/app.rb +15 -12
- data/lib/refile/attacher.rb +119 -49
- data/lib/refile/attachment.rb +29 -16
- data/lib/refile/attachment/active_record.rb +5 -2
- data/lib/refile/backend/file_system.rb +61 -1
- data/lib/refile/backend/s3.rb +66 -0
- data/lib/refile/custom_logger.rb +46 -0
- data/lib/refile/file.rb +32 -1
- data/lib/refile/image_processing.rb +72 -3
- data/lib/refile/rails.rb +2 -8
- data/lib/refile/rails/attachment_helper.rb +77 -19
- data/lib/refile/signature.rb +16 -1
- data/lib/refile/type.rb +28 -0
- data/lib/refile/version.rb +1 -1
- data/refile.gemspec +1 -1
- data/spec/refile/active_record_helper.rb +27 -0
- data/spec/refile/attachment/active_record_spec.rb +92 -0
- data/spec/refile/attachment_spec.rb +153 -28
- data/spec/refile/custom_logger_spec.rb +22 -0
- data/spec/refile/features/direct_upload_spec.rb +19 -2
- data/spec/refile/features/normal_upload_spec.rb +41 -11
- data/spec/refile/features/presigned_upload_spec.rb +1 -2
- data/spec/refile/rails/attachment_helper_spec.rb +1 -1
- data/spec/refile/test_app.rb +16 -14
- data/spec/refile/test_app/app/controllers/direct_posts_controller.rb +1 -1
- data/spec/refile/test_app/app/controllers/normal_posts_controller.rb +1 -1
- data/spec/refile/test_app/app/controllers/presigned_posts_controller.rb +1 -1
- data/spec/refile/test_app/app/views/direct_posts/new.html.erb +4 -0
- data/spec/refile/test_app/app/views/normal_posts/show.html.erb +5 -3
- metadata +27 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: efeeacf3670d33b7a4c22cdb6f55cdddba524a0b
|
4
|
+
data.tar.gz: 8e4f7bc973f1347606a251d9a15a7f3983f04e13
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 01f6f2eb954e55add137dc351da2829295a7de8a6e5290bbad27f70b05c039f3b3db4e84adf6b9c44a415d305b2fbb28a74f6c8eec55fb09c386a9e270ba7708
|
7
|
+
data.tar.gz: f20d8cc03bbced04056c291cb06620af4a257e1ad582b9e4a594a14137e0d9abbeb2b9b2c7109a76f0bc4c99dd4385530c43074fce728cafb7530a6bd2f81c74
|
data/.rubocop.yml
CHANGED
@@ -11,10 +11,13 @@ Metrics/ParameterLists:
|
|
11
11
|
Max: 8
|
12
12
|
|
13
13
|
Metrics/AbcSize:
|
14
|
-
|
14
|
+
Enabled: false
|
15
15
|
|
16
16
|
Metrics/CyclomaticComplexity:
|
17
|
-
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
Metrics/PerceivedComplexity:
|
20
|
+
Enabled: false
|
18
21
|
|
19
22
|
Style/AlignParameters:
|
20
23
|
EnforcedStyle: with_fixed_indentation
|
@@ -54,3 +57,6 @@ Style/SignalException:
|
|
54
57
|
|
55
58
|
Lint/EndAlignment:
|
56
59
|
AlignWith: variable
|
60
|
+
|
61
|
+
Lint/HandleExceptions:
|
62
|
+
Enabled: false
|
data/.travis.yml
CHANGED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--hide-api private --hide-void-return --markup markdown
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,33 @@
|
|
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/History.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
# 0.5.0
|
2
|
+
|
3
|
+
Release date: 2015-01-09
|
4
|
+
|
5
|
+
- [ADDED] Can add custom types for easier content type validations
|
6
|
+
- [ADDED] Can persist filename, size and content type
|
7
|
+
- [CHANGED] The `cache_id` field is no longer necessary and no longer need to be permitted in the controller
|
8
|
+
- [CHANGED] Improved logging
|
9
|
+
|
1
10
|
# 0.4.2
|
2
11
|
|
3
12
|
Release date: 2014-12-27
|
data/README.md
CHANGED
@@ -1,11 +1,17 @@
|
|
1
1
|
# Refile
|
2
|
+
|
2
3
|
[![Gem Version](https://badge.fury.io/rb/refile.svg)](http://badge.fury.io/rb/refile)
|
3
4
|
[![Build Status](https://travis-ci.org/elabs/refile.svg?branch=master)](https://travis-ci.org/elabs/refile)
|
4
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)
|
5
7
|
|
6
8
|
Refile is a modern file upload library for Ruby applications. It is simple, yet
|
7
|
-
powerful.
|
8
|
-
|
9
|
+
powerful.
|
10
|
+
|
11
|
+
Links:
|
12
|
+
|
13
|
+
- [API documentation](http://www.rubydoc.info/gems/refile)
|
14
|
+
- [Source Code](https://github.com/elabs/refile)
|
9
15
|
|
10
16
|
Features:
|
11
17
|
|
@@ -25,6 +31,16 @@ gem "mini_magick"
|
|
25
31
|
gem "refile", require: ["refile/rails", "refile/image_processing"]
|
26
32
|
```
|
27
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
|
+
|
28
44
|
Use the `attachment` method to use Refile in a model:
|
29
45
|
|
30
46
|
``` ruby
|
@@ -52,7 +68,7 @@ Set up strong parameters:
|
|
52
68
|
|
53
69
|
``` ruby
|
54
70
|
def user_params
|
55
|
-
params.require(:user).permit(:profile_image
|
71
|
+
params.require(:user).permit(:profile_image)
|
56
72
|
end
|
57
73
|
```
|
58
74
|
|
@@ -70,7 +86,7 @@ Refile consists of several parts:
|
|
70
86
|
2. Model attachments: map files to model columns
|
71
87
|
3. A Rack application: streams files and accepts uploads
|
72
88
|
4. Rails helpers: conveniently generate markup in your views
|
73
|
-
|
89
|
+
5. A JavaScript library: facilitates direct uploads
|
74
90
|
|
75
91
|
Let's look at each of these in more detail!
|
76
92
|
|
@@ -140,8 +156,8 @@ For example:
|
|
140
156
|
Refile.cache = Refile::Backend::S3.new(max_size: 10.megabytes, ...)
|
141
157
|
```
|
142
158
|
|
143
|
-
The Refile gem ships with [S3](lib/refile/
|
144
|
-
[FileSystem](lib/refile/
|
159
|
+
The Refile gem ships with [S3](lib/refile/backend/s3.rb) and
|
160
|
+
[FileSystem](lib/refile/backend/file_system.rb) backends. Additional backends
|
145
161
|
are provided by other gems.
|
146
162
|
|
147
163
|
- [Fog](https://github.com/elabs/refile-fog) provides support for a ton of
|
@@ -299,10 +315,10 @@ example.
|
|
299
315
|
## 4. Rails helpers
|
300
316
|
|
301
317
|
Refile provides the `attachment_field` form helper which generates a file field
|
302
|
-
as well as a hidden field
|
303
|
-
|
304
|
-
|
305
|
-
|
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`.
|
306
322
|
|
307
323
|
``` erb
|
308
324
|
<%= form_for @user do |form| %>
|
@@ -314,7 +330,7 @@ Will generate something like:
|
|
314
330
|
|
315
331
|
``` html
|
316
332
|
<form action="/users" enctype="multipart/form-data" method="post">
|
317
|
-
<input name="user[
|
333
|
+
<input name="user[profile_image]" type="hidden">
|
318
334
|
<input name="user[profile_image]" type="file">
|
319
335
|
</form>
|
320
336
|
```
|
@@ -363,6 +379,8 @@ simply include it like this:
|
|
363
379
|
```
|
364
380
|
|
365
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.
|
366
384
|
|
367
385
|
Now mark the field for direct upload:
|
368
386
|
|
@@ -476,8 +494,26 @@ production mode.
|
|
476
494
|
### Browser compatibility
|
477
495
|
|
478
496
|
Refile's JavaScript library requires HTML5 features which are unavailable on
|
479
|
-
IE9 and earlier versions. All other major browsers are supported.
|
480
|
-
|
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 :profile_image_filename
|
510
|
+
add_column :profile_image_size
|
511
|
+
add_column :profile_image_content_type
|
512
|
+
end
|
513
|
+
end
|
514
|
+
```
|
515
|
+
|
516
|
+
These columns will now be filled automatically.
|
481
517
|
|
482
518
|
## File type validations
|
483
519
|
|
@@ -496,7 +532,7 @@ attachment :cv, extension: "pdf"
|
|
496
532
|
attachment :profile_image, content_type: "image/jpeg"
|
497
533
|
```
|
498
534
|
|
499
|
-
You can also provide a list of content
|
535
|
+
You can also provide a list of content types or extensions:
|
500
536
|
|
501
537
|
``` ruby
|
502
538
|
attachment :cv, extension: ["pdf", "doc"]
|
@@ -513,6 +549,21 @@ attachment :profile_image, type: :image
|
|
513
549
|
When a user uploads a file with an invalid extension or content type and
|
514
550
|
submits the form, they'll be presented with a validation error.
|
515
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
|
+
|
516
567
|
## Removing attached files
|
517
568
|
|
518
569
|
File input fields unfortunately do not have the option of removing an already
|
@@ -535,7 +586,7 @@ Don't forget to permit this attribute in your controller:
|
|
535
586
|
|
536
587
|
``` ruby
|
537
588
|
def user_params
|
538
|
-
params.require(:user).permit(:profile_image, :
|
589
|
+
params.require(:user).permit(:profile_image, :remove_profile_image)
|
539
590
|
end
|
540
591
|
```
|
541
592
|
|
@@ -563,7 +614,7 @@ Then permit this field in your controller:
|
|
563
614
|
|
564
615
|
``` ruby
|
565
616
|
def user_params
|
566
|
-
params.require(:user).permit(:profile_image, :
|
617
|
+
params.require(:user).permit(:profile_image, :remote_profile_image_url)
|
567
618
|
end
|
568
619
|
```
|
569
620
|
|
@@ -1,25 +1,25 @@
|
|
1
1
|
(function() {
|
2
2
|
"use strict";
|
3
3
|
|
4
|
-
if(!document.addEventListener) { return }
|
4
|
+
if(!document.addEventListener) { return; } // IE8
|
5
5
|
|
6
|
-
document.addEventListener("change", function(
|
7
|
-
|
8
|
-
|
6
|
+
document.addEventListener("change", function(changeEvent) {
|
7
|
+
var input = changeEvent.target;
|
8
|
+
if(input.tagName === "INPUT" && input.type === "file" && input.getAttribute("data-direct")) {
|
9
9
|
if(!input.files) { return; } // IE9, bail out if file API is not supported.
|
10
|
+
|
10
11
|
var file = input.files[0];
|
12
|
+
var metadataField = input.previousSibling;
|
11
13
|
|
12
14
|
var dispatchEvent = function(name, detail) {
|
13
15
|
var ev = document.createEvent('CustomEvent');
|
14
16
|
ev.initCustomEvent(name, true, false, detail);
|
15
17
|
input.dispatchEvent(ev);
|
16
|
-
}
|
18
|
+
};
|
17
19
|
|
18
20
|
if(file) {
|
19
|
-
var url =
|
20
|
-
|
21
|
-
var fields = JSON.parse(e.target.getAttribute("data-fields"));
|
22
|
-
}
|
21
|
+
var url = input.getAttribute("data-url");
|
22
|
+
var fields = JSON.parse(input.getAttribute("data-fields") || "null");
|
23
23
|
|
24
24
|
var data = new FormData();
|
25
25
|
|
@@ -31,12 +31,14 @@
|
|
31
31
|
data.append(input.getAttribute("data-as"), file);
|
32
32
|
|
33
33
|
var xhr = new XMLHttpRequest();
|
34
|
-
xhr.addEventListener("load", function(
|
35
|
-
input.classList.remove("uploading")
|
34
|
+
xhr.addEventListener("load", function() {
|
35
|
+
input.classList.remove("uploading");
|
36
36
|
dispatchEvent("upload:complete", xhr.responseText);
|
37
37
|
if((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
|
38
38
|
var id = input.getAttribute("data-id") || JSON.parse(xhr.responseText).id;
|
39
|
-
|
39
|
+
if(metadataField) {
|
40
|
+
metadataField.value = JSON.stringify({ id: id, filename: file.name, content_type: file.type, size: file.size });
|
41
|
+
}
|
40
42
|
input.removeAttribute("name");
|
41
43
|
dispatchEvent("upload:success", xhr.responseText);
|
42
44
|
} else {
|
@@ -44,17 +46,17 @@
|
|
44
46
|
}
|
45
47
|
});
|
46
48
|
|
47
|
-
xhr.upload.addEventListener("progress", function(
|
48
|
-
if (
|
49
|
-
dispatchEvent("upload:progress",
|
49
|
+
xhr.upload.addEventListener("progress", function(progressEvent) {
|
50
|
+
if (progressEvent.lengthComputable) {
|
51
|
+
dispatchEvent("upload:progress", progressEvent);
|
50
52
|
}
|
51
53
|
});
|
52
54
|
|
53
55
|
xhr.open("POST", url, true);
|
54
56
|
xhr.send(data);
|
55
57
|
|
56
|
-
input.classList.add("uploading")
|
57
|
-
dispatchEvent("upload:start");
|
58
|
+
input.classList.add("uploading");
|
59
|
+
dispatchEvent("upload:start", xhr);
|
58
60
|
}
|
59
61
|
}
|
60
62
|
});
|
data/lib/refile.rb
CHANGED
@@ -1,19 +1,20 @@
|
|
1
1
|
require "uri"
|
2
2
|
require "fileutils"
|
3
3
|
require "tempfile"
|
4
|
-
require "rest_client"
|
5
4
|
require "logger"
|
6
5
|
require "mime/types"
|
7
6
|
|
8
7
|
module Refile
|
8
|
+
# @api private
|
9
9
|
class Invalid < StandardError; end
|
10
|
+
|
11
|
+
# @api private
|
10
12
|
class Confirm < StandardError
|
11
13
|
def message
|
12
14
|
"are you sure? this will remove all files in the backend, call as \
|
13
15
|
`clear!(:confirm)` if you're sure you want to do this"
|
14
16
|
end
|
15
17
|
end
|
16
|
-
ONE_YEAR_IN_SECONDS = 31_557_600
|
17
18
|
|
18
19
|
class << self
|
19
20
|
# A shortcut to the instance of the Rack application. This should be
|
@@ -51,16 +52,23 @@ module Refile
|
|
51
52
|
attr_accessor :allow_origin
|
52
53
|
|
53
54
|
# Value for Cache-Control: max-age=<value in seconds> header
|
55
|
+
#
|
56
|
+
# @return [Integer]
|
54
57
|
attr_accessor :content_max_age
|
55
58
|
|
56
|
-
# Where should the rack application be mounted?
|
57
|
-
#
|
59
|
+
# Where should the rack application be mounted? The default is 'attachments'.
|
60
|
+
#
|
61
|
+
# @return [String]
|
58
62
|
attr_accessor :mount_point
|
59
63
|
|
60
64
|
# Should the rack application be automounted in a Rails app?
|
61
|
-
#
|
65
|
+
#
|
62
66
|
# If set to false then Refile.app should be mounted in the Rails application
|
63
67
|
# routes.rb with the options `at: Refile.mount_point, as: :refile_app`
|
68
|
+
#
|
69
|
+
# The default is true.
|
70
|
+
#
|
71
|
+
# @return [Boolean]
|
64
72
|
attr_accessor :automount
|
65
73
|
|
66
74
|
# A global registry of backends.
|
@@ -80,6 +88,14 @@ module Refile
|
|
80
88
|
@processors ||= {}
|
81
89
|
end
|
82
90
|
|
91
|
+
# A global registry of types. Currently, types are simply aliases for a set
|
92
|
+
# of content types, but their functionality may expand in the future.
|
93
|
+
#
|
94
|
+
# @return [Hash{Symbol => Refile::Type}]
|
95
|
+
def types
|
96
|
+
@types ||= {}
|
97
|
+
end
|
98
|
+
|
83
99
|
# Adds a processor. The processor must respond to `call`, both receiving
|
84
100
|
# and returning an IO-like object. Alternatively a block can be given to
|
85
101
|
# this method which also receives and returns an IO-like object.
|
@@ -106,6 +122,7 @@ module Refile
|
|
106
122
|
# @param [Proc, nil] processor The processor, must respond to `call` and.
|
107
123
|
# @yield [Refile::File] The file to modify
|
108
124
|
# @yieldreturn [IO] An IO-like object representing the processed file
|
125
|
+
# @return [void]
|
109
126
|
def processor(name, processor = nil, &block)
|
110
127
|
processor ||= block
|
111
128
|
processors[name.to_s] = processor
|
@@ -170,6 +187,11 @@ module Refile
|
|
170
187
|
true
|
171
188
|
end
|
172
189
|
|
190
|
+
# Extract the filename from an uploadable object. If the filename cannot be
|
191
|
+
# determined, this method will return `nil`.
|
192
|
+
#
|
193
|
+
# @param [IO] uploadable The uploadable object to extract the filename from
|
194
|
+
# @return [String, nil] The extracted filename
|
173
195
|
def extract_filename(uploadable)
|
174
196
|
path = if uploadable.respond_to?(:original_filename)
|
175
197
|
uploadable.original_filename
|
@@ -179,6 +201,11 @@ module Refile
|
|
179
201
|
::File.basename(path) if path
|
180
202
|
end
|
181
203
|
|
204
|
+
# Extract the content type from an uploadable object. If the content type
|
205
|
+
# cannot be determined, this method will return `nil`.
|
206
|
+
#
|
207
|
+
# @param [IO] uploadable The uploadable object to extract the content type from
|
208
|
+
# @return [String, nil] The extracted content type
|
182
209
|
def extract_content_type(uploadable)
|
183
210
|
if uploadable.respond_to?(:content_type)
|
184
211
|
uploadable.content_type
|
@@ -194,10 +221,12 @@ module Refile
|
|
194
221
|
|
195
222
|
require "refile/version"
|
196
223
|
require "refile/signature"
|
224
|
+
require "refile/type"
|
197
225
|
require "refile/attacher"
|
198
226
|
require "refile/attachment"
|
199
227
|
require "refile/random_hasher"
|
200
228
|
require "refile/file"
|
229
|
+
require "refile/custom_logger"
|
201
230
|
require "refile/app"
|
202
231
|
require "refile/backend/file_system"
|
203
232
|
end
|
@@ -208,5 +237,6 @@ Refile.configure do |config|
|
|
208
237
|
config.logger = Logger.new(STDOUT)
|
209
238
|
config.mount_point = "attachments"
|
210
239
|
config.automount = true
|
211
|
-
config.content_max_age =
|
240
|
+
config.content_max_age = 60 * 60 * 24 * 365
|
241
|
+
config.types[:image] = Refile::Type.new(:image, content_type: %w[image/jpeg image/gif image/png])
|
212
242
|
end
|