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.

Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -2
  3. data/.travis.yml +2 -0
  4. data/.yardopts +1 -0
  5. data/CONTRIBUTING.md +33 -0
  6. data/History.md +9 -0
  7. data/README.md +67 -16
  8. data/app/assets/javascripts/refile.js +19 -17
  9. data/lib/refile.rb +36 -6
  10. data/lib/refile/app.rb +15 -12
  11. data/lib/refile/attacher.rb +119 -49
  12. data/lib/refile/attachment.rb +29 -16
  13. data/lib/refile/attachment/active_record.rb +5 -2
  14. data/lib/refile/backend/file_system.rb +61 -1
  15. data/lib/refile/backend/s3.rb +66 -0
  16. data/lib/refile/custom_logger.rb +46 -0
  17. data/lib/refile/file.rb +32 -1
  18. data/lib/refile/image_processing.rb +72 -3
  19. data/lib/refile/rails.rb +2 -8
  20. data/lib/refile/rails/attachment_helper.rb +77 -19
  21. data/lib/refile/signature.rb +16 -1
  22. data/lib/refile/type.rb +28 -0
  23. data/lib/refile/version.rb +1 -1
  24. data/refile.gemspec +1 -1
  25. data/spec/refile/active_record_helper.rb +27 -0
  26. data/spec/refile/attachment/active_record_spec.rb +92 -0
  27. data/spec/refile/attachment_spec.rb +153 -28
  28. data/spec/refile/custom_logger_spec.rb +22 -0
  29. data/spec/refile/features/direct_upload_spec.rb +19 -2
  30. data/spec/refile/features/normal_upload_spec.rb +41 -11
  31. data/spec/refile/features/presigned_upload_spec.rb +1 -2
  32. data/spec/refile/rails/attachment_helper_spec.rb +1 -1
  33. data/spec/refile/test_app.rb +16 -14
  34. data/spec/refile/test_app/app/controllers/direct_posts_controller.rb +1 -1
  35. data/spec/refile/test_app/app/controllers/normal_posts_controller.rb +1 -1
  36. data/spec/refile/test_app/app/controllers/presigned_posts_controller.rb +1 -1
  37. data/spec/refile/test_app/app/views/direct_posts/new.html.erb +4 -0
  38. data/spec/refile/test_app/app/views/normal_posts/show.html.erb +5 -3
  39. metadata +27 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a3a2b57d57c5951f0c24c51726efb70cce7d81ff
4
- data.tar.gz: 39063e4b10ac86030aa75ab30463b1798b02b91a
3
+ metadata.gz: efeeacf3670d33b7a4c22cdb6f55cdddba524a0b
4
+ data.tar.gz: 8e4f7bc973f1347606a251d9a15a7f3983f04e13
5
5
  SHA512:
6
- metadata.gz: 091a57dc29f9fc7cef0f9aa240446f48076a6a321d60cfdb9491104a91ec196a105ed3476a74f8f05f3fee7fdc1c796807710dd9b7eb3b642d961493ee505bbe
7
- data.tar.gz: 9fb6d682ea8fc32b0a5a3321912e5315f5ac836b88d14636731fd2e068917f32a4621a02f2759acf4fc9d52ddebfe90235b7b46abda11bc502c62b23591f315f
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
- Max: 20
14
+ Enabled: false
15
15
 
16
16
  Metrics/CyclomaticComplexity:
17
- Max: 7 # TODO: reduce
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
@@ -8,6 +8,8 @@ rvm:
8
8
  gemfile:
9
9
  - Gemfile
10
10
 
11
+ cache: bundler
12
+
11
13
  sudo: false
12
14
 
13
15
  before_script:
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. Refile is an attempt by CarrierWave's original author to fix the
8
- design mistakes and overengineering in CarrierWave.
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, :profile_image_cache_id)
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
- 4. A JavaScript library: facilitates direct uploads
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/backends/s3.rb) and
144
- [FileSystem](lib/refile/backends/file_system.rb) backends. Additional backends
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, suffixed with `cache_id`. This field keeps track of
303
- the file in case it is not yet permanently stored, for example if validations
304
- fail. It is also used for direct and presigned uploads. For this reason it is
305
- highly recommended to use `attachment_field` instead of `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`.
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[profile_image_cache_id]" type="hidden">
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. Note though
480
- that it has not yet been extensively tested.
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 type or extensions:
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, :profile_image_cache_id, :remove_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, :profile_image_cache_id, :remote_profile_image_url)
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 }; // IE8
4
+ if(!document.addEventListener) { return; } // IE8
5
5
 
6
- document.addEventListener("change", function(e) {
7
- if(e.target.tagName === "INPUT" && e.target.type === "file" && e.target.getAttribute("data-direct")) {
8
- var input = e.target;
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 = e.target.getAttribute("data-url");
20
- if(e.target.getAttribute("data-fields")) {
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(e) {
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
- input.previousSibling.value = id;
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(e) {
48
- if (e.lengthComputable) {
49
- dispatchEvent("upload:progress", e);
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
- # The default is 'attachments'
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
- # The default is true.
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 = Refile::ONE_YEAR_IN_SECONDS
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