shrine 3.0.0.rc → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of shrine might be problematic. Click here for more details.

Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -66
  3. data/README.md +39 -1061
  4. data/doc/advantages.md +151 -148
  5. data/doc/attacher.md +12 -30
  6. data/doc/carrierwave.md +150 -115
  7. data/doc/changing_derivatives.md +5 -11
  8. data/doc/changing_location.md +8 -5
  9. data/doc/changing_storage.md +5 -2
  10. data/doc/creating_persistence_plugins.md +9 -6
  11. data/doc/creating_plugins.md +42 -22
  12. data/doc/creating_storages.md +4 -1
  13. data/doc/design.md +7 -5
  14. data/doc/direct_s3.md +9 -4
  15. data/doc/external/articles.md +50 -0
  16. data/doc/external/extensions.md +46 -0
  17. data/doc/external/misc.md +17 -0
  18. data/doc/getting_started.md +1038 -0
  19. data/doc/metadata.md +5 -3
  20. data/doc/multiple_files.md +55 -29
  21. data/doc/paperclip.md +206 -163
  22. data/doc/plugins/activerecord.md +26 -6
  23. data/doc/plugins/add_metadata.md +4 -2
  24. data/doc/plugins/atomic_helpers.md +4 -2
  25. data/doc/plugins/backgrounding.md +83 -44
  26. data/doc/plugins/cached_attachment_data.md +4 -2
  27. data/doc/plugins/column.md +4 -2
  28. data/doc/plugins/data_uri.md +10 -6
  29. data/doc/plugins/default_storage.md +5 -3
  30. data/doc/plugins/default_url.md +4 -2
  31. data/doc/plugins/delete_raw.md +4 -2
  32. data/doc/plugins/derivation_endpoint.md +63 -39
  33. data/doc/plugins/derivatives.md +13 -50
  34. data/doc/plugins/determine_mime_type.md +6 -4
  35. data/doc/plugins/download_endpoint.md +6 -3
  36. data/doc/plugins/dynamic_storage.md +4 -2
  37. data/doc/plugins/entity.md +6 -4
  38. data/doc/plugins/form_assign.md +4 -2
  39. data/doc/plugins/included.md +4 -2
  40. data/doc/plugins/infer_extension.md +6 -4
  41. data/doc/plugins/instrumentation.md +5 -3
  42. data/doc/plugins/keep_files.md +9 -2
  43. data/doc/plugins/metadata_attributes.md +5 -3
  44. data/doc/plugins/mirroring.md +4 -2
  45. data/doc/plugins/model.md +6 -4
  46. data/doc/plugins/module_include.md +4 -2
  47. data/doc/plugins/multi_cache.md +4 -2
  48. data/doc/plugins/persistence.md +5 -3
  49. data/doc/plugins/presign_endpoint.md +6 -2
  50. data/doc/plugins/pretty_location.md +5 -3
  51. data/doc/plugins/processing.md +4 -2
  52. data/doc/plugins/rack_file.md +8 -2
  53. data/doc/plugins/rack_response.md +6 -2
  54. data/doc/plugins/recache.md +4 -2
  55. data/doc/plugins/refresh_metadata.md +5 -3
  56. data/doc/plugins/remote_url.md +26 -5
  57. data/doc/plugins/remove_attachment.md +4 -2
  58. data/doc/plugins/remove_invalid.md +10 -2
  59. data/doc/plugins/restore_cached_data.md +9 -3
  60. data/doc/plugins/sequel.md +26 -6
  61. data/doc/plugins/signature.md +6 -4
  62. data/doc/plugins/store_dimensions.md +6 -4
  63. data/doc/plugins/tempfile.md +4 -2
  64. data/doc/plugins/upload_endpoint.md +6 -2
  65. data/doc/plugins/upload_options.md +6 -4
  66. data/doc/plugins/url_options.md +4 -2
  67. data/doc/plugins/validation.md +7 -3
  68. data/doc/plugins/validation_helpers.md +13 -10
  69. data/doc/plugins/versions.md +4 -8
  70. data/doc/processing.md +27 -9
  71. data/doc/refile.md +119 -127
  72. data/doc/release_notes/1.0.0.md +4 -0
  73. data/doc/release_notes/1.1.0.md +4 -0
  74. data/doc/release_notes/1.2.0.md +4 -0
  75. data/doc/release_notes/1.3.0.md +4 -0
  76. data/doc/release_notes/1.4.0.md +4 -0
  77. data/doc/release_notes/1.4.1.md +4 -0
  78. data/doc/release_notes/1.4.2.md +4 -0
  79. data/doc/release_notes/2.0.0.md +4 -0
  80. data/doc/release_notes/2.0.1.md +4 -0
  81. data/doc/release_notes/2.1.0.md +4 -0
  82. data/doc/release_notes/2.1.1.md +4 -0
  83. data/doc/release_notes/2.10.0.md +4 -0
  84. data/doc/release_notes/2.10.1.md +4 -0
  85. data/doc/release_notes/2.11.0.md +4 -0
  86. data/doc/release_notes/2.12.0.md +4 -0
  87. data/doc/release_notes/2.13.0.md +4 -0
  88. data/doc/release_notes/2.14.0.md +5 -1
  89. data/doc/release_notes/2.15.0.md +10 -6
  90. data/doc/release_notes/2.16.0.md +4 -0
  91. data/doc/release_notes/2.17.0.md +4 -0
  92. data/doc/release_notes/2.18.0.md +4 -0
  93. data/doc/release_notes/2.19.0.md +7 -4
  94. data/doc/release_notes/2.2.0.md +4 -0
  95. data/doc/release_notes/2.3.0.md +4 -0
  96. data/doc/release_notes/2.3.1.md +4 -0
  97. data/doc/release_notes/2.4.0.md +4 -0
  98. data/doc/release_notes/2.4.1.md +4 -0
  99. data/doc/release_notes/2.5.0.md +4 -0
  100. data/doc/release_notes/2.6.0.md +4 -0
  101. data/doc/release_notes/2.6.1.md +4 -0
  102. data/doc/release_notes/2.7.0.md +4 -0
  103. data/doc/release_notes/2.8.0.md +4 -0
  104. data/doc/release_notes/2.9.0.md +4 -0
  105. data/doc/release_notes/3.0.0.md +120 -38
  106. data/doc/retrieving_uploads.md +4 -1
  107. data/doc/securing_uploads.md +4 -1
  108. data/doc/storage/file_system.md +12 -4
  109. data/doc/storage/s3.md +4 -2
  110. data/doc/testing.md +27 -41
  111. data/doc/upgrading_to_3.md +105 -26
  112. data/doc/validation.md +8 -6
  113. data/lib/shrine/attacher.rb +2 -2
  114. data/lib/shrine/attachment.rb +7 -10
  115. data/lib/shrine/plugins/activerecord.rb +10 -10
  116. data/lib/shrine/plugins/add_metadata.rb +1 -3
  117. data/lib/shrine/plugins/atomic_helpers.rb +6 -8
  118. data/lib/shrine/plugins/backgrounding.rb +4 -6
  119. data/lib/shrine/plugins/cached_attachment_data.rb +1 -3
  120. data/lib/shrine/plugins/column.rb +2 -4
  121. data/lib/shrine/plugins/data_uri.rb +1 -3
  122. data/lib/shrine/plugins/default_storage.rb +1 -3
  123. data/lib/shrine/plugins/default_url.rb +1 -3
  124. data/lib/shrine/plugins/delete_raw.rb +1 -3
  125. data/lib/shrine/plugins/derivation_endpoint.rb +3 -4
  126. data/lib/shrine/plugins/derivatives.rb +2 -4
  127. data/lib/shrine/plugins/determine_mime_type.rb +1 -3
  128. data/lib/shrine/plugins/download_endpoint.rb +1 -3
  129. data/lib/shrine/plugins/dynamic_storage.rb +1 -3
  130. data/lib/shrine/plugins/entity.rb +25 -9
  131. data/lib/shrine/plugins/form_assign.rb +1 -3
  132. data/lib/shrine/plugins/included.rb +1 -3
  133. data/lib/shrine/plugins/infer_extension.rb +1 -3
  134. data/lib/shrine/plugins/instrumentation.rb +1 -3
  135. data/lib/shrine/plugins/keep_files.rb +1 -3
  136. data/lib/shrine/plugins/metadata_attributes.rb +1 -3
  137. data/lib/shrine/plugins/mirroring.rb +2 -1
  138. data/lib/shrine/plugins/model.rb +2 -4
  139. data/lib/shrine/plugins/module_include.rb +1 -3
  140. data/lib/shrine/plugins/multi_cache.rb +3 -3
  141. data/lib/shrine/plugins/presign_endpoint.rb +1 -3
  142. data/lib/shrine/plugins/pretty_location.rb +1 -3
  143. data/lib/shrine/plugins/processing.rb +1 -3
  144. data/lib/shrine/plugins/rack_file.rb +1 -3
  145. data/lib/shrine/plugins/rack_response.rb +1 -3
  146. data/lib/shrine/plugins/recache.rb +1 -3
  147. data/lib/shrine/plugins/refresh_metadata.rb +1 -3
  148. data/lib/shrine/plugins/remote_url.rb +1 -3
  149. data/lib/shrine/plugins/remove_attachment.rb +1 -3
  150. data/lib/shrine/plugins/remove_invalid.rb +1 -3
  151. data/lib/shrine/plugins/restore_cached_data.rb +1 -3
  152. data/lib/shrine/plugins/sequel.rb +10 -12
  153. data/lib/shrine/plugins/signature.rb +1 -3
  154. data/lib/shrine/plugins/store_dimensions.rb +1 -3
  155. data/lib/shrine/plugins/tempfile.rb +1 -3
  156. data/lib/shrine/plugins/upload_endpoint.rb +1 -3
  157. data/lib/shrine/plugins/upload_options.rb +1 -3
  158. data/lib/shrine/plugins/url_options.rb +1 -3
  159. data/lib/shrine/plugins/validation.rb +1 -3
  160. data/lib/shrine/plugins/validation_helpers.rb +1 -3
  161. data/lib/shrine/plugins/versions.rb +1 -3
  162. data/lib/shrine/storage/file_system.rb +1 -1
  163. data/lib/shrine/storage/linter.rb +1 -1
  164. data/lib/shrine/storage/memory.rb +2 -1
  165. data/lib/shrine/storage/s3.rb +3 -3
  166. data/lib/shrine/version.rb +1 -1
  167. metadata +8 -4
@@ -1,4 +1,6 @@
1
- # Extracting Metadata
1
+ ---
2
+ title: Extracting Metadata
3
+ ---
2
4
 
3
5
  Before a file is uploaded, Shrine automatically extracts metadata from it, and
4
6
  stores them in the `Shrine::UploadedFile` object.
@@ -386,5 +388,5 @@ end
386
388
  [MiniMagick]: https://github.com/minimagick/minimagick
387
389
  [ruby-vips]: https://github.com/libvips/ruby-vips
388
390
  [tus server]: https://github.com/janko/tus-ruby-server
389
- [determine_mime_type]: /doc/plugins/determine_mime_type.md#readme
390
- [backgrounding]: /doc/plugins/backgrounding.md#readme
391
+ [determine_mime_type]: https://shrinerb.com/docs/plugins/determine_mime_type
392
+ [backgrounding]: https://shrinerb.com/docs/plugins/backgrounding
@@ -1,4 +1,7 @@
1
- # Multiple Files
1
+ ---
2
+ id: multiple-files
3
+ title: Multiple Files
4
+ ---
2
5
 
3
6
  There are times when you want to allow users to attach multiple files to a
4
7
  single resource, like an album having many photos or a playlist having many
@@ -11,7 +14,7 @@ relationship with the main table, and files will be attached on the records in
11
14
  the new table. That way each record from the main table can implicitly have
12
15
  multiple attachments through the associated records.
13
16
 
14
- ```
17
+ ```plaintext
15
18
  album1
16
19
  photo1
17
20
  - attachment1
@@ -64,8 +67,9 @@ files (or attachments) table will be the photos table.
64
67
  Let's create a table for the main resource and attachments, and add a foreign
65
68
  key in the attachment table for the main table:
66
69
 
70
+ <!--DOCUSAURUS_CODE_TABS-->
71
+ <!--Sequel-->
67
72
  ```rb
68
- # with Sequel:
69
73
  Sequel.migration do
70
74
  change do
71
75
  create_table :albums do
@@ -80,8 +84,9 @@ Sequel.migration do
80
84
  end
81
85
  end
82
86
  end
83
-
84
- # with Active Record:
87
+ ```
88
+ <!--ActiveRecord-->
89
+ ```rb
85
90
  class CreateAlbumsAndPhotos < ActiveRecord::Migration
86
91
  def change
87
92
  create_table :albums do |t|
@@ -97,21 +102,25 @@ class CreateAlbumsAndPhotos < ActiveRecord::Migration
97
102
  end
98
103
  end
99
104
  ```
105
+ <!--END_DOCUSAURUS_CODE_TABS-->
100
106
 
101
107
  In the Photo model, create a Shrine attachment attribute named `image`
102
108
  (`:image` matches the `_data` column prefix above):
103
109
 
110
+ <!--DOCUSAURUS_CODE_TABS-->
111
+ <!--Sequel-->
104
112
  ```rb
105
- # with Sequel:
106
113
  class Photo < Sequel::Model
107
114
  include ImageUploader::Attachment(:image)
108
115
  end
109
-
110
- # with Active Record:
116
+ ```
117
+ <!--ActiveRecord-->
118
+ ```rb
111
119
  class Photo < ActiveRecord::Base
112
120
  include ImageUploader::Attachment(:image)
113
121
  end
114
122
  ```
123
+ <!--END_DOCUSAURUS_CODE_TABS-->
115
124
 
116
125
  ### 2. Declare nested attributes
117
126
 
@@ -120,8 +129,9 @@ Using nested attributes is the easiest way to implement any dynamic
120
129
  relationship to the photos table, and allow it to directly accept attributes
121
130
  for the associated photo records by enabling nested attributes:
122
131
 
132
+ <!--DOCUSAURUS_CODE_TABS-->
133
+ <!--Sequel-->
123
134
  ```rb
124
- # with Sequel:
125
135
  class Album < Sequel::Model
126
136
  one_to_many :photos
127
137
  plugin :association_dependencies, photos: :destroy # destroy photos when album is destroyed
@@ -129,13 +139,23 @@ class Album < Sequel::Model
129
139
  plugin :nested_attributes
130
140
  nested_attributes :photos, destroy: true
131
141
  end
132
-
133
- # with Active Record:
142
+ ```
143
+ <!--ActiveRecord-->
144
+ ```rb
134
145
  class Album < ActiveRecord::Base
135
146
  has_many :photos, dependent: :destroy
136
147
  accepts_nested_attributes_for :photos, allow_destroy: true
137
148
  end
138
149
  ```
150
+ <!--Mongoid-->
151
+ ```rb
152
+ class Album
153
+ include Mongoid::Document
154
+ embeds_many :photos
155
+ accepts_nested_attributes_for :photos
156
+ end
157
+ ```
158
+ <!--END_DOCUSAURUS_CODE_TABS-->
139
159
 
140
160
  Documentation on nested attributes:
141
161
 
@@ -152,20 +172,9 @@ already created photos, so that the same form can be used for updating the
152
172
  album/photos as well (they will be submitted under the
153
173
  `album[photos_attributes]` parameter).
154
174
 
175
+ <!--DOCUSAURUS_CODE_TABS-->
176
+ <!--Rails form builder-->
155
177
  ```rb
156
- # with Forme:
157
- form @album, action: "/photos", enctype: "multipart/form-data" do |f|
158
- f.input :title
159
- f.subform :photos do # adds new `album[photos_attributes]` parameter
160
- f.input :image, type: :hidden, value: f.obj.cached_image_data
161
- f.input :image, type: :file
162
- f.input :_delete, type: :checkbox unless f.obj.new?
163
- end
164
- f.input "files[]", type: :file, attr: { multiple: true }, obj: nil
165
- f.button "Create"
166
- end
167
-
168
- # with Rails form builder:
169
178
  form_for @album, html: { enctype: "multipart/form-data" } do |f|
170
179
  f.text_field :title
171
180
  f.fields_for :photos do |p| # adds new `album[photos_attributes]` parameter
@@ -177,6 +186,20 @@ form_for @album, html: { enctype: "multipart/form-data" } do |f|
177
186
  f.submit "Create"
178
187
  end
179
188
  ```
189
+ <!--Forme-->
190
+ ```rb
191
+ form @album, action: "/photos", enctype: "multipart/form-data" do |f|
192
+ f.input :title
193
+ f.subform :photos do # adds new `album[photos_attributes]` parameter
194
+ f.input :image, type: :hidden, value: f.obj.cached_image_data
195
+ f.input :image, type: :file
196
+ f.input :_delete, type: :checkbox unless f.obj.new?
197
+ end
198
+ f.input "files[]", type: :file, attr: { multiple: true }, obj: nil
199
+ f.button "Create"
200
+ end
201
+ ```
202
+ <!--END_DOCUSAURUS_CODE_TABS-->
180
203
 
181
204
  In your controller you should still be able to assign all the attributes to the
182
205
  album, just remember to whitelist the new parameter for the nested attributes,
@@ -261,18 +284,21 @@ class ImageUploader < Shrine
261
284
  end
262
285
  end
263
286
  ```
287
+ <!--DOCUSAURUS_CODE_TABS-->
288
+ <!--Sequel-->
264
289
  ```rb
265
- # with Sequel:
266
290
  class Album < Sequel::Model
267
291
  # ... (nested_attributes already enables validating associated photos) ...
268
292
  end
269
-
270
- # with ActiveRecord:
293
+ ```
294
+ <!--ActiveRecord-->
295
+ ```rb
271
296
  class Album < ActiveRecord::Base
272
297
  # ...
273
298
  validates_associated :photos
274
299
  end
275
300
  ```
301
+ <!--END_DOCUSAURUS_CODE_TABS-->
276
302
 
277
303
  Note that by default only metadata set on the client side will be available for
278
304
  validations. Shrine will not automatically run metadata extraction for directly
@@ -294,8 +320,8 @@ attributes feature gives you for free.
294
320
  [`Sequel::Model.nested_attributes`]: http://sequel.jeremyevans.net/rdoc-plugins/classes/Sequel/Plugins/NestedAttributes.html
295
321
  [`ActiveRecord::Base.accepts_nested_attributes_for`]: http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
296
322
  [`Mongoid::Document.accepts_nested_attributes_for`]: https://docs.mongodb.com/mongoid/master/tutorials/mongoid-nested-attributes/
297
- [`upload_endpoint`]: /doc/plugins/upload_endpoint.md#readme
298
- [`presign_endpoint`]: /doc/plugins/presign_endpoint.md#readme
323
+ [`upload_endpoint`]: https://shrinerb.com/docs/plugins/upload_endpoint
324
+ [`presign_endpoint`]: https://shrinerb.com/docs/plugins/presign_endpoint
299
325
  [Uppy]: https://uppy.io
300
326
  [direct app uploads]: https://github.com/shrinerb/shrine/wiki/Adding-Direct-App-Uploads
301
327
  [direct S3 uploads]: https://github.com/shrinerb/shrine/wiki/Adding-Direct-S3-Uploads
@@ -1,4 +1,6 @@
1
- # Shrine for Paperclip Users
1
+ ---
2
+ title: Shrine for Paperclip Users
3
+ ---
2
4
 
3
5
  This guide is aimed at helping Paperclip users transition to Shrine, and it
4
6
  consists of three parts:
@@ -7,9 +9,52 @@ consists of three parts:
7
9
  2. Instructions how to migrate and existing app that uses Paperclip to Shrine
8
10
  3. Extensive reference of Paperclip's interface with Shrine equivalents
9
11
 
10
- ## Storages
12
+ ## Overview
11
13
 
12
- In Paperclip the storage is configure inside the global options:
14
+ ### Uploader
15
+
16
+ In Paperclip, the attachment logic is configured directly inside Active Record
17
+ models:
18
+
19
+ ```rb
20
+ class Photo < ActiveRecord::Base
21
+ has_attached_file :image,
22
+ preserve_files: true,
23
+ default_url: "/images/:style/missing.png"
24
+
25
+ validated_attachment_content_type :image, content_type: "image/jpeg"
26
+ end
27
+ ```
28
+
29
+ Shrine takes a more object-oriented approach, by encapsulating attachment logic
30
+ in "uploader" classes:
31
+
32
+ ```rb
33
+ class ImageUploader < Shrine
34
+ plugin :keep_files
35
+ plugin :default_url
36
+ plugin :validation_helpers
37
+
38
+ Attacher.default_url do |derivative: nil, **|
39
+ "/images/#{derivative}/missing.png" if derivative
40
+ end
41
+
42
+ Attacher.validate do
43
+ validate_mime_type %w[image/jpeg]
44
+ end
45
+ end
46
+ ```
47
+ ```rb
48
+ class Photo < ActiveRecord::Base
49
+ include ImageUploader::Attachment(:image)
50
+ end
51
+ ```
52
+
53
+ ### Storage
54
+
55
+ Paperclip storage is configured together with other attachment options. Also,
56
+ the storage implementations themselves are mixed into the attachment class,
57
+ which couples them to the attachment flow.
13
58
 
14
59
  ```rb
15
60
  class Photo < ActiveRecord::Base
@@ -24,7 +69,8 @@ class Photo < ActiveRecord::Base
24
69
  end
25
70
  ```
26
71
 
27
- In contrast, a Shrine storage is just a class which you configure individually:
72
+ Shrine storage objects are configured separately and are decoupled from
73
+ attachment:
28
74
 
29
75
  ```rb
30
76
  Shrine.storages[:store] = Shrine::Storage::S3.new(
@@ -35,10 +81,8 @@ Shrine.storages[:store] = Shrine::Storage::S3.new(
35
81
  )
36
82
  ```
37
83
 
38
- Paperclip doesn't have a concept of "temporary" storage, so it cannot retain
39
- uploaded files in case of validation errors, and [direct S3 uploads] cannot be
40
- implemented in a safe way. Shrine uses separate "temporary" and "permanent"
41
- storage for attaching files:
84
+ Shrine also has a concept of "temporary" storage, which enables retaining
85
+ uploaded files in case of validation errors and [direct uploads].
42
86
 
43
87
  ```rb
44
88
  Shrine.storages = {
@@ -47,43 +91,79 @@ Shrine.storages = {
47
91
  }
48
92
  ```
49
93
 
50
- ## Uploaders
94
+ ### Persistence
51
95
 
52
- While in Paperclip you define all your uploading logic inside your models,
53
- Shrine takes a more object-oriented approach and lets you define uploading logic
54
- inside "uploader" classes:
96
+ When using Paperclip, the attached file data will be persisted into several
97
+ columns:
98
+
99
+ * `<name>_file_name`
100
+ * `<name>_content_type`
101
+ * `<name>_file_size`
102
+ * `<name>_updated_at`
103
+ * `<name>_created_at` (optional)
104
+ * `<name>_fingerprint` (optional)
105
+
106
+ In contrast, Shrine uses a single `<name>_data` column to store data in JSON
107
+ format:
55
108
 
56
109
  ```rb
57
- class Photo < ActiveRecord::Base
58
- has_attached_file :image
59
- end
110
+ {
111
+ "id": "path/to/image.jpg",
112
+ "storage": "store",
113
+ "metadata": {
114
+ "filename": "nature.jpg",
115
+ "size": 4739472,
116
+ "mime_type": "image/jpeg"
117
+ }
118
+ }
60
119
  ```
61
-
62
120
  ```rb
63
- class ImageUploader < Shrine
64
- # ...
65
- end
121
+ photo.image.id #=> "path/to/image.jpg"
122
+ photo.image.storage_key #=> :store
123
+ photo.image.metadata #=> { "filename" => "...", "size" => ..., "mime_type" => "..." }
66
124
 
67
- class Photo < ActiveRecord::Base
68
- include ImageUploader::Attachment(:image)
69
- end
125
+ photo.image.original_filename #=> "nature.jpg"
126
+ photo.image.size #=> 4739472
127
+ photo.image.mime_type #=> "image/jpeg"
70
128
  ```
71
129
 
72
- Among other things, this allows you to use uploader classes standalone, which
73
- gives you more power:
130
+ This column can be queried if it's made a JSON column. Alternatively, you can
131
+ use the [`metadata_attributes`][metadata_attributes] plugin to save metadata
132
+ into separate columns.
133
+
134
+ #### ORM
135
+
136
+ While Paperclip works only with Active Record, Shrine is designed to integrate
137
+ with any persistence library (there are integrations for [Active
138
+ Record][activerecord], [Sequel][sequel], [ROM][rom], [Hanami][hanami] and
139
+ [Hanami][mongoid]), and can also be used standalone:
74
140
 
75
141
  ```rb
76
- uploaded_file = ImageUploader.upload(File.open("nature.jpg"), :store)
77
- uploaded_file #=> #<Shrine::UploadedFile>
78
- uploaded_file.url #=> "https://my-bucket.s3.amazonaws.com/store/kfds0lg9rer.jpg"
142
+ attacher = ImageUploader::Attacher.new
143
+ attacher.attach File.open("nature.jpg")
144
+ attacher.file #=> #<Shrine::UploadedFile @id="f4ba5bdbf366ef0b.jpg" ...>
145
+ attacher.url #=> "https://my-bucket.s3.amazonaws.com/f4ba5bdbf366ef0b.jpg"
146
+ attacher.data #=> { "id" => "f4ba5bdbf366ef0b.jpg", "storage" => "store", "metadata" => { ... } }
79
147
  ```
80
148
 
81
- ### Processing
149
+ #### Location
150
+
151
+ Paperclip persists only the filename of the uploaded file, and recalculates the
152
+ full location dynamically based on location configuration. This can be
153
+ dangerous, because if some component of the location happens to change, all
154
+ existing links might become invalid.
82
155
 
83
- In contrast to Paperclip's static options, in Shrine you define and perform
84
- processing on instance-level. You also have the descriptive method names
85
- provided by the [image_processing] gem.
156
+ To avoid this, Shrine persists the full location on attachment, and uses it
157
+ when generating file URL. So, even if you change how file locations are
158
+ generated, existing files that are on old locations will still remain
159
+ accessible.
160
+
161
+ ### Processing
86
162
 
163
+ In Shrine, processing is defined and performed on the instance level, which
164
+ gives you more control. You're also not coupled to ImageMagick, e.g. you can
165
+ use [libvips] instead (both integrations are provided by the [image_processing]
166
+ gem).
87
167
 
88
168
  ```rb
89
169
  class Photo < ActiveRecord::Base
@@ -114,25 +194,51 @@ class ImageUploader < Shrine
114
194
  end
115
195
  ```
116
196
 
197
+ Shrine is agnostic as to how you're performing your processing, so you can
198
+ easily use any other processing tools. You can also combine different
199
+ processors for different versions.
200
+
201
+ #### Retrieving versions
202
+
203
+ When retrieving versions, Paperclip returns a list of declared styles which
204
+ may or may not have been generated. In contrast, Shrine persists data of
205
+ uploaded processed files into the database (including any extracted metadata),
206
+ which then becomes the source of truth on which versions have been generated.
207
+
208
+ ```rb
209
+ photo.image #=> #<Shrine::UploadedFile @id="original.jpg" ...>
210
+ photo.image_derivatives #=> {}
211
+
212
+ photo.image_derivatives! # triggers processing
213
+ photo.image_derivatives #=>
214
+ # {
215
+ # large: #<Shrine::UploadedFile @id="large.jpg" @metadata={"size"=>873232, ...} ...>,
216
+ # medium: #<Shrine::UploadedFile @id="medium.jpg" @metadata={"size"=>94823, ...} ...>,
217
+ # small: #<Shrine::UploadedFile @id="small.jpg" @metadata={"size"=>37322, ...} ...>,
218
+ # }
219
+ ```
220
+
117
221
  #### Reprocessing versions
118
222
 
119
223
  Shrine doesn't have a built-in way of regenerating versions, because that has
120
- to be written and optimized differently depending on whether you're adding or
121
- removing a version, what ORM are you using, how many records there are in the
122
- database etc. The [Managing Derivatives] guide provides some useful tips on
123
- this task.
224
+ to be written and optimized differently depending on what versions have changed
225
+ which persistence library you're using, how many records there are in the table
226
+ etc.
227
+
228
+ However, there is an extensive guide for [Managing Derivatives], which provides
229
+ instructions on how to make these changes safely and with zero downtime.
124
230
 
125
- ### Validations
231
+ ### Validation
126
232
 
127
- Validations are also defined inside the uploader on the instance-level, which
128
- allows you to do conditional validations:
233
+ File validation in Shrine is also instance-level, which allows using
234
+ conditionals:
129
235
 
130
236
  ```rb
131
237
  class Photo < ActiveRecord::Base
132
238
  has_attached_file :image
133
239
  validates_attachment :image,
134
- content_type: {content_type: %w[image/jpeg image/png image/gif]},
135
- size: {in: 0..10.megabytes}
240
+ size: { in: 0..10.megabytes },
241
+ content_type: { content_type: %w[image/jpeg image/png image/webp] }
136
242
  end
137
243
  ```
138
244
 
@@ -141,114 +247,53 @@ class ImageUploader < Shrine
141
247
  plugin :validation_helpers
142
248
 
143
249
  Attacher.validate do
144
- validate_mime_type %w[image/jpeg image/gif image/png]
145
250
  validate_max_size 10*1024*1024
251
+
252
+ if validate_mime_type %w[image/jpeg image/png image/webp]
253
+ validate_max_dimensions [5000, 5000]
254
+ end
146
255
  end
147
256
  end
148
257
  ```
149
258
 
150
- #### MIME type spoofing
151
-
152
- Paperclip detects MIME type spoofing, in the way that it extracts the MIME type
153
- from file contents using the `file` command and MimeMagic, compares it to the
154
- value that the `mime-types` gem determined from file extension, and raises a
155
- validation error if these two values mismatch.
156
-
157
- However, this turned out to be very problematic, leading to a lot of valid
158
- files being classified as "spoofed", because of the differences of MIME
159
- type databases between the `mime-types` gem, `file` command, and MimeMagic.
160
-
161
- Shrine takes a different approach here. By default it will extract MIME
162
- type from file extension, but it has a plugin for determining MIME type from
163
- file contents, which by default uses the `file` command:
164
-
165
- ```rb
166
- Shrine.plugin :determine_mime_type
167
- ```
168
-
169
- However, it doesn't try to compare this value with the one from file extension,
170
- it just means that now this value will be used for your MIME type validations.
171
- With this approach you can still prevent malicious files from being attached,
172
- but without the possibility of false negatives.
173
-
174
- ### Logging
175
-
176
- In Paperclip you enable logging by setting `Paperclip.options[:log] = true`,
177
- however, this only logs ImageMagick commands. Shrine has full logging support,
178
- which measures processing, uploading and deleting individually:
179
-
180
- ```rb
181
- Shrine.plugin :instrumentation
182
- ```
183
- ```
184
- Metadata (32ms) – {:storage=>:store, :io=>StringIO, :uploader=>Shrine}
185
- Upload (1523ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :io=>StringIO, :upload_options=>{}, :uploader=>Shrine}
186
- Exists (755ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :uploader=>Shrine}
187
- Download (1002ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :download_options=>{}, :uploader=>Shrine}
188
- Delete (700ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :uploader=>Shrine}
189
- ```
190
-
191
- ## Attachments
259
+ #### Custom metadata
192
260
 
193
- While Paperclip is designed to only integrate with ActiveRecord, Shrine is
194
- designed to be completely generic and integrate with any ORM. It ships with
195
- plugins for ActiveRecord and Sequel:
261
+ With Shrine you can also extract and validate any custom metadata:
196
262
 
197
263
  ```rb
198
- Shrine.plugin :activerecord # if you're using ActiveRecord
199
- Shrine.plugin :sequel # if you're using Sequel
200
- ```
264
+ class VideoUploader < Shrine
265
+ plugin :add_metadata
266
+ plugin :validation
201
267
 
202
- Instead of giving you class methods for defining attachments, in Shrine you
203
- generate attachment modules which you simply include in your models, which
204
- gives your models similar set of methods that Paperclip gives:
268
+ add_metadata :duration do |io|
269
+ FFMPEG::Movie.new(io.path).duration
270
+ end
205
271
 
206
- ```rb
207
- class Photo < Sequel::Model
208
- include ImageUploader::Attachment(:image)
272
+ Attacher.validate do
273
+ if file.duration > 5*60*60
274
+ errors << "must not be longer than 5 hours"
275
+ end
276
+ end
209
277
  end
210
278
  ```
211
279
 
212
- ### Attachment column
280
+ #### MIME type spoofing
213
281
 
214
- Unlike in Paperclip which requires you to have 4 `<attachment>_*` columns, in
215
- Shrine you only need to have a single `<attachment>_data` text column (in the
216
- above case `image_data`), and all information will be stored there.
282
+ Paperclip attempts to detect MIME type spoofing, which turned out to be
283
+ unreliable due to differences in MIME type databases between different ruby
284
+ libraries.
217
285
 
218
- ```rb
219
- photo.image_data #=>
220
- # {
221
- # "storage" => "store",
222
- # "id" => "photo/1/image/0d9o8dk42.png",
223
- # "metadata" => {
224
- # "filename" => "nature.png",
225
- # "size" => 49349138,
226
- # "mime_type" => "image/png"
227
- # }
228
- # }
286
+ Shrine on the other hand simply allows you to determine MIME type from file
287
+ content, which you can then validate.
229
288
 
230
- photo.image.original_filename #=> "nature.png"
231
- photo.image.size #=> 49349138
232
- photo.image.mime_type #=> "image/png"
289
+ ```rb
290
+ Shrine.plugin :determine_mime_type, analyzer: :marcel
233
291
  ```
234
-
235
- Unlike Paperclip, Shrine will store this information for each processed
236
- version, making them first-class citizens:
237
-
238
292
  ```rb
239
- photo.image #=> #<Shrine::UploadedFile>
240
- photo.image.width #=> 800
241
-
242
- photo.image(:thumb) #=> #<Shrine::UploadedFile>
243
- photo.image(:thumb).width #=> 300
293
+ file = uploader.upload StringIO.new("<?php ... ?>")
294
+ file.mime_type #=> "application/x-php"
244
295
  ```
245
296
 
246
- Also, since Paperclip stores only the filename, it has to recalculate the full
247
- location each time it wants to generate the URL. That makes it really difficult
248
- to move files to a new location, because changing how the location is generated
249
- will now cause incorrect URLs to be generated for all existing files. Shrine
250
- calculates the whole location only once and saves it to the column.
251
-
252
297
  ## Migrating from Paperclip
253
298
 
254
299
  You have an existing app using Paperclip and you want to transfer it to Shrine.
@@ -476,10 +521,18 @@ Shrine has this functionality in the `determine_mime_type` plugin.
476
521
  This section explains the equivalent of Paperclip attachment's methods, in
477
522
  Shrine this is an instance of `Shrine::UploadedFile`.
478
523
 
479
- #### `#url`, `#styles`
524
+ #### `#url`
480
525
 
481
- If you're generating versions in Shrine, the attachment will be a hash of
482
- uploaded files:
526
+ In Shrine you can generate URLs with `#<name>_url`:
527
+
528
+ ```rb
529
+ photo.image_url #=> "https://example.com/path/to/original.jpg"
530
+ photo.image_url(:large) #=> "https://example.com/path/to/large.jpg"
531
+ ```
532
+
533
+ #### `#styles`
534
+
535
+ In Shrine you can use `#<name>_derivatives` to retrieve a list of versions:
483
536
 
484
537
  ```rb
485
538
  photo.image_derivatives #=>
@@ -489,9 +542,9 @@ photo.image_derivatives #=>
489
542
  # large: #<Shrine::UploadedFile>,
490
543
  # }
491
544
 
492
- photo.image(:small).url #=> "..."
545
+ photo.image_derivatives[:small] #=> #<Shrine::UploadedFile>
493
546
  # or
494
- photo.image_url(:small) #=> "..."
547
+ photo.image(:small) #=> #<Shrine::UploadedFile>
495
548
  ```
496
549
 
497
550
  #### `#path`
@@ -510,7 +563,7 @@ guide provides some useful tips on how to do this.
510
563
 
511
564
  ### `Paperclip::Storage::S3`
512
565
 
513
- The built-in [`Shrine::Storage::S3`] storage is a direct replacement for
566
+ The built-in [`Shrine::Storage::S3`][S3] storage is a direct replacement for
514
567
  `Paperclip::Storage::S3`.
515
568
 
516
569
  #### `:s3_credentials`, `:s3_region`, `:bucket`
@@ -527,36 +580,21 @@ Shrine::Storage::S3.new(
527
580
  )
528
581
  ```
529
582
 
530
- #### `:s3_headers`
583
+ #### `:s3_headers`, `:s3_permissions`, `:s3_metadata`
531
584
 
532
- The object data can be configured via the `:upload_options` hash:
585
+ These can be configured via the `:upload_options` option:
533
586
 
534
587
  ```rb
535
- Shrine::Storage::S3.new(upload_options: { content_disposition: "attachment" }, **options)
536
- ```
537
-
538
- You can use the `upload_options` plugin to set upload options dynamically.
539
-
540
- #### `:s3_permissions`
541
-
542
- The object permissions can be configured with the `:acl` upload option:
543
-
544
- ```rb
545
- Shrine::Storage::S3.new(upload_options: { acl: "private" }, **options)
546
- ```
547
-
548
- You can use the `upload_options` plugin to set upload options dynamically.
549
-
550
- #### `:s3_metadata`
551
-
552
- The object metadata can be configured with the `:metadata` upload option:
553
-
554
- ```rb
555
- Shrine::Storage::S3.new(upload_options: { metadata: { "key" => "value" } }, **options)
588
+ Shrine::Storage::S3.new(
589
+ upload_options: {
590
+ content_disposition: "attachment", # headers
591
+ acl: "private", # permissions
592
+ metadata: { "key" => "value" }, # metadata
593
+ },
594
+ **options
595
+ )
556
596
  ```
557
597
 
558
- You can use the `upload_options` plugin to set upload options dynamically.
559
-
560
598
  #### `:s3_protocol`, `:s3_host_alias`, `:s3_host_name`
561
599
 
562
600
  The `#url` method accepts a `:host` option for specifying a CDN host. You can
@@ -580,8 +618,13 @@ s3.upload(io, "object/destination/path")
580
618
  The Shrine storage has no replacement for the `:url` Paperclip option, and it
581
619
  isn't needed.
582
620
 
583
- [file]: http://linux.die.net/man/1/file
584
- [Managing Derivatives]: /doc/changing_derivatives.md#readme
585
- [direct S3 uploads]: /doc/direct_s3.md#readme
586
- [`Shrine::Storage::S3`]: /doc/storage/s3.md#readme
621
+ [Managing Derivatives]: https://shrinerb.com/docs/changing-derivatives
622
+ [direct uploads]: https://shrinerb.com/docs/getting-started#direct-uploads
623
+ [S3]: https://shrinerb.com/docs/storage/s3
587
624
  [image_processing]: https://github.com/janko/image_processing
625
+ [libvips]: http://libvips.github.io/libvips/
626
+ [activerecord]: https://shrinerb.com/docs/plugins/activerecord
627
+ [sequel]: https://shrinerb.com/docs/plugins/sequel
628
+ [rom]: https://github.com/shrinerb/shrine-rom
629
+ [hanami]: https://github.com/katafrakt/hanami-shrine
630
+ [mongoid]: https://github.com/shrinerb/shrine-mongoid