shrine 2.19.4 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (209) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +485 -43
  3. data/LICENSE.txt +1 -1
  4. data/README.md +81 -977
  5. data/doc/advantages.md +231 -204
  6. data/doc/attacher.md +304 -153
  7. data/doc/carrierwave.md +297 -226
  8. data/doc/changing_derivatives.md +308 -0
  9. data/doc/changing_location.md +102 -21
  10. data/doc/changing_storage.md +110 -0
  11. data/doc/creating_persistence_plugins.md +132 -0
  12. data/doc/creating_plugins.md +43 -23
  13. data/doc/creating_storages.md +19 -5
  14. data/doc/design.md +147 -97
  15. data/doc/direct_s3.md +38 -28
  16. data/doc/external/articles.md +63 -0
  17. data/doc/external/extensions.md +53 -0
  18. data/doc/external/misc.md +32 -0
  19. data/doc/getting_started.md +1115 -0
  20. data/doc/metadata.md +190 -109
  21. data/doc/multiple_files.md +62 -34
  22. data/doc/paperclip.md +384 -262
  23. data/doc/plugins/activerecord.md +177 -46
  24. data/doc/plugins/add_metadata.md +139 -38
  25. data/doc/plugins/atomic_helpers.md +217 -0
  26. data/doc/plugins/backgrounding.md +156 -98
  27. data/doc/plugins/cached_attachment_data.md +7 -5
  28. data/doc/plugins/column.md +121 -0
  29. data/doc/plugins/data_uri.md +23 -22
  30. data/doc/plugins/default_storage.md +36 -10
  31. data/doc/plugins/default_url.md +30 -13
  32. data/doc/plugins/delete_raw.md +4 -2
  33. data/doc/plugins/derivation_endpoint.md +162 -101
  34. data/doc/plugins/derivatives.md +829 -0
  35. data/doc/plugins/determine_mime_type.md +4 -2
  36. data/doc/plugins/download_endpoint.md +64 -8
  37. data/doc/plugins/dynamic_storage.md +5 -3
  38. data/doc/plugins/entity.md +263 -0
  39. data/doc/plugins/form_assign.md +55 -0
  40. data/doc/plugins/included.md +31 -8
  41. data/doc/plugins/infer_extension.md +21 -10
  42. data/doc/plugins/instrumentation.md +38 -16
  43. data/doc/plugins/keep_files.md +14 -17
  44. data/doc/plugins/metadata_attributes.md +42 -13
  45. data/doc/plugins/mirroring.md +118 -0
  46. data/doc/plugins/model.md +210 -0
  47. data/doc/plugins/module_include.md +4 -2
  48. data/doc/plugins/multi_cache.md +24 -0
  49. data/doc/plugins/persistence.md +101 -0
  50. data/doc/plugins/presign_endpoint.md +9 -4
  51. data/doc/plugins/pretty_location.md +16 -3
  52. data/doc/plugins/processing.md +4 -2
  53. data/doc/plugins/rack_file.md +8 -2
  54. data/doc/plugins/rack_response.md +6 -2
  55. data/doc/plugins/recache.md +4 -2
  56. data/doc/plugins/refresh_metadata.md +49 -9
  57. data/doc/plugins/remote_url.md +84 -47
  58. data/doc/plugins/remove_attachment.md +27 -6
  59. data/doc/plugins/remove_invalid.md +21 -6
  60. data/doc/plugins/restore_cached_data.md +11 -3
  61. data/doc/plugins/sequel.md +159 -35
  62. data/doc/plugins/signature.md +16 -5
  63. data/doc/plugins/store_dimensions.md +14 -2
  64. data/doc/plugins/tempfile.md +4 -2
  65. data/doc/plugins/type_predicates.md +96 -0
  66. data/doc/plugins/upload_endpoint.md +13 -13
  67. data/doc/plugins/upload_options.md +6 -4
  68. data/doc/plugins/{default_url_options.md → url_options.md} +9 -7
  69. data/doc/plugins/validation.md +97 -0
  70. data/doc/plugins/validation_helpers.md +16 -13
  71. data/doc/plugins/versions.md +15 -19
  72. data/doc/processing.md +438 -221
  73. data/doc/refile.md +185 -167
  74. data/doc/release_notes/1.0.0.md +4 -0
  75. data/doc/release_notes/1.1.0.md +6 -2
  76. data/doc/release_notes/1.2.0.md +4 -0
  77. data/doc/release_notes/1.3.0.md +4 -0
  78. data/doc/release_notes/1.4.0.md +4 -0
  79. data/doc/release_notes/1.4.1.md +4 -0
  80. data/doc/release_notes/1.4.2.md +4 -0
  81. data/doc/release_notes/2.0.0.md +4 -0
  82. data/doc/release_notes/2.0.1.md +4 -0
  83. data/doc/release_notes/2.1.0.md +4 -0
  84. data/doc/release_notes/2.1.1.md +4 -0
  85. data/doc/release_notes/2.10.0.md +4 -0
  86. data/doc/release_notes/2.10.1.md +4 -0
  87. data/doc/release_notes/2.11.0.md +4 -0
  88. data/doc/release_notes/2.12.0.md +4 -0
  89. data/doc/release_notes/2.13.0.md +4 -0
  90. data/doc/release_notes/2.14.0.md +5 -1
  91. data/doc/release_notes/2.15.0.md +11 -7
  92. data/doc/release_notes/2.16.0.md +4 -0
  93. data/doc/release_notes/2.17.0.md +4 -0
  94. data/doc/release_notes/2.18.0.md +4 -0
  95. data/doc/release_notes/2.19.0.md +6 -3
  96. data/doc/release_notes/2.2.0.md +4 -0
  97. data/doc/release_notes/2.3.0.md +4 -0
  98. data/doc/release_notes/2.3.1.md +4 -0
  99. data/doc/release_notes/2.4.0.md +4 -0
  100. data/doc/release_notes/2.4.1.md +4 -0
  101. data/doc/release_notes/2.5.0.md +4 -0
  102. data/doc/release_notes/2.6.0.md +4 -0
  103. data/doc/release_notes/2.6.1.md +4 -0
  104. data/doc/release_notes/2.7.0.md +4 -0
  105. data/doc/release_notes/2.8.0.md +4 -0
  106. data/doc/release_notes/2.9.0.md +4 -0
  107. data/doc/release_notes/3.0.0.md +981 -0
  108. data/doc/release_notes/3.0.1.md +22 -0
  109. data/doc/release_notes/3.1.0.md +73 -0
  110. data/doc/release_notes/3.2.0.md +96 -0
  111. data/doc/release_notes/3.2.1.md +31 -0
  112. data/doc/release_notes/3.2.2.md +14 -0
  113. data/doc/release_notes/3.3.0.md +105 -0
  114. data/doc/release_notes/3.4.0.md +35 -0
  115. data/doc/retrieving_uploads.md +4 -1
  116. data/doc/securing_uploads.md +60 -37
  117. data/doc/storage/file_system.md +20 -3
  118. data/doc/storage/memory.md +19 -0
  119. data/doc/storage/s3.md +117 -83
  120. data/doc/testing.md +124 -144
  121. data/doc/upgrading_to_3.md +710 -0
  122. data/doc/validation.md +54 -90
  123. data/lib/shrine/attacher.rb +287 -171
  124. data/lib/shrine/attachment.rb +13 -46
  125. data/lib/shrine/plugins/_persistence.rb +93 -0
  126. data/lib/shrine/plugins/activerecord.rb +77 -34
  127. data/lib/shrine/plugins/add_metadata.rb +25 -17
  128. data/lib/shrine/plugins/atomic_helpers.rb +119 -0
  129. data/lib/shrine/plugins/backgrounding.rb +77 -113
  130. data/lib/shrine/plugins/cached_attachment_data.rb +6 -15
  131. data/lib/shrine/plugins/column.rb +102 -0
  132. data/lib/shrine/plugins/data_uri.rb +38 -36
  133. data/lib/shrine/plugins/default_storage.rb +45 -15
  134. data/lib/shrine/plugins/default_url.rb +12 -24
  135. data/lib/shrine/plugins/default_url_options.rb +3 -30
  136. data/lib/shrine/plugins/delete_raw.rb +10 -16
  137. data/lib/shrine/plugins/derivation_endpoint.rb +89 -134
  138. data/lib/shrine/plugins/derivatives.rb +637 -0
  139. data/lib/shrine/plugins/determine_mime_type.rb +9 -21
  140. data/lib/shrine/plugins/download_endpoint.rb +109 -133
  141. data/lib/shrine/plugins/dynamic_storage.rb +5 -11
  142. data/lib/shrine/plugins/entity.rb +152 -0
  143. data/lib/shrine/plugins/form_assign.rb +108 -0
  144. data/lib/shrine/plugins/included.rb +6 -6
  145. data/lib/shrine/plugins/infer_extension.rb +13 -20
  146. data/lib/shrine/plugins/instrumentation.rb +54 -42
  147. data/lib/shrine/plugins/keep_files.rb +3 -15
  148. data/lib/shrine/plugins/metadata_attributes.rb +28 -19
  149. data/lib/shrine/plugins/mirroring.rb +142 -0
  150. data/lib/shrine/plugins/model.rb +158 -0
  151. data/lib/shrine/plugins/module_include.rb +3 -3
  152. data/lib/shrine/plugins/multi_cache.rb +27 -0
  153. data/lib/shrine/plugins/presign_endpoint.rb +18 -22
  154. data/lib/shrine/plugins/pretty_location.rb +15 -9
  155. data/lib/shrine/plugins/processing.rb +22 -9
  156. data/lib/shrine/plugins/rack_file.rb +2 -42
  157. data/lib/shrine/plugins/rack_response.rb +15 -10
  158. data/lib/shrine/plugins/recache.rb +6 -5
  159. data/lib/shrine/plugins/refresh_metadata.rb +13 -11
  160. data/lib/shrine/plugins/remote_url.rb +49 -49
  161. data/lib/shrine/plugins/remove_attachment.rb +10 -6
  162. data/lib/shrine/plugins/remove_invalid.rb +19 -8
  163. data/lib/shrine/plugins/restore_cached_data.rb +13 -7
  164. data/lib/shrine/plugins/sequel.rb +86 -36
  165. data/lib/shrine/plugins/signature.rb +10 -16
  166. data/lib/shrine/plugins/store_dimensions.rb +35 -40
  167. data/lib/shrine/plugins/tempfile.rb +1 -3
  168. data/lib/shrine/plugins/type_predicates.rb +113 -0
  169. data/lib/shrine/plugins/upload_endpoint.rb +25 -23
  170. data/lib/shrine/plugins/upload_options.rb +14 -15
  171. data/lib/shrine/plugins/url_options.rb +31 -0
  172. data/lib/shrine/plugins/validation.rb +80 -0
  173. data/lib/shrine/plugins/validation_helpers.rb +34 -57
  174. data/lib/shrine/plugins/versions.rb +107 -87
  175. data/lib/shrine/plugins.rb +22 -0
  176. data/lib/shrine/storage/file_system.rb +46 -64
  177. data/lib/shrine/storage/linter.rb +42 -7
  178. data/lib/shrine/storage/memory.rb +49 -0
  179. data/lib/shrine/storage/s3.rb +154 -158
  180. data/lib/shrine/uploaded_file.rb +28 -30
  181. data/lib/shrine/version.rb +3 -3
  182. data/lib/shrine.rb +86 -149
  183. data/shrine.gemspec +9 -10
  184. metadata +79 -83
  185. data/doc/migrating_storage.md +0 -76
  186. data/doc/plugins/backup.md +0 -31
  187. data/doc/plugins/copy.md +0 -24
  188. data/doc/plugins/delete_promoted.md +0 -12
  189. data/doc/plugins/direct_upload.md +0 -172
  190. data/doc/plugins/hooks.md +0 -58
  191. data/doc/plugins/logging.md +0 -42
  192. data/doc/plugins/migration_helpers.md +0 -60
  193. data/doc/plugins/moving.md +0 -19
  194. data/doc/plugins/multi_delete.md +0 -20
  195. data/doc/plugins/parallelize.md +0 -16
  196. data/doc/plugins/parsed_json.md +0 -23
  197. data/doc/regenerating_versions.md +0 -143
  198. data/lib/shrine/plugins/background_helpers.rb +0 -5
  199. data/lib/shrine/plugins/backup.rb +0 -90
  200. data/lib/shrine/plugins/copy.rb +0 -50
  201. data/lib/shrine/plugins/delete_promoted.rb +0 -20
  202. data/lib/shrine/plugins/direct_upload.rb +0 -217
  203. data/lib/shrine/plugins/hooks.rb +0 -90
  204. data/lib/shrine/plugins/logging.rb +0 -142
  205. data/lib/shrine/plugins/migration_helpers.rb +0 -70
  206. data/lib/shrine/plugins/moving.rb +0 -57
  207. data/lib/shrine/plugins/multi_delete.rb +0 -32
  208. data/lib/shrine/plugins/parallelize.rb +0 -78
  209. data/lib/shrine/plugins/parsed_json.rb +0 -29
@@ -1,4 +1,6 @@
1
- # Rack File
1
+ ---
2
+ title: Rack File
3
+ ---
2
4
 
3
5
  The [`rack_file`][rack_file] plugin enables uploaders to accept Rack uploaded
4
6
  file hashes for uploading.
@@ -7,6 +9,8 @@ file hashes for uploading.
7
9
  plugin :rack_file
8
10
  ```
9
11
 
12
+ ## Usage
13
+
10
14
  When a file is uploaded to your Rack application using the
11
15
  `multipart/form-data` parameter encoding, Rack converts the uploaded file to a
12
16
  hash.
@@ -33,6 +37,8 @@ user.avatar = file_hash
33
37
  attacher.assign(file_hash)
34
38
  ```
35
39
 
40
+ ## API
41
+
36
42
  Internally the Rack uploaded file hash will be converted into an IO object
37
43
  using `Shrine.rack_file`, which you can also use directly:
38
44
 
@@ -48,4 +54,4 @@ Note that this plugin is not needed in Rails applications, as Rails already
48
54
  wraps the Rack uploaded file hash into an `ActionDispatch::Http::UploadedFile`
49
55
  object.
50
56
 
51
- [rack_file]: /lib/shrine/plugins/rack_file.rb
57
+ [rack_file]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/rack_file.rb
@@ -1,4 +1,6 @@
1
- # Rack Response
1
+ ---
2
+ title: Rack Response
3
+ ---
2
4
 
3
5
  The [`rack_response`][rack_response] plugin allows you to convert an
4
6
  `UploadedFile` object into a triple consisting of status, headers, and body,
@@ -8,6 +10,8 @@ suitable for returning as a response in a Rack-based application.
8
10
  plugin :rack_response
9
11
  ```
10
12
 
13
+ ## Usage
14
+
11
15
  To convert a `Shrine::UploadedFile` into a Rack response, simply call
12
16
  `#to_rack_response`:
13
17
 
@@ -107,6 +111,6 @@ uploaded_file.open(
107
111
  uploaded_file.to_rack_response
108
112
  ```
109
113
 
110
- [rack_response]: /lib/shrine/plugins/rack_response.rb
114
+ [rack_response]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/rack_response.rb
111
115
  [range requests]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests
112
116
  [Rack::Sendfile]: https://www.rubydoc.info/github/rack/rack/Rack/Sendfile
@@ -1,4 +1,6 @@
1
- # Re-cache
1
+ ---
2
+ title: Re-cache
3
+ ---
2
4
 
3
5
  The [`recache`][recache] plugin allows you to process your attachment after
4
6
  validations succeed, but before the attachment is promoted. This is useful for
@@ -26,4 +28,4 @@ you're using the attacher directly, you can call it manually:
26
28
  attacher.recache if attacher.changed?
27
29
  ```
28
30
 
29
- [recache]: /lib/shrine/plugins/recache.rb
31
+ [recache]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/recache.rb
@@ -1,4 +1,6 @@
1
- # Refresh Metadata
1
+ ---
2
+ title: Refresh Metadata
3
+ ---
2
4
 
3
5
  The [`refresh_metadata`][refresh_metadata] plugin allows you to re-extract
4
6
  metadata from an uploaded file.
@@ -7,27 +9,65 @@ metadata from an uploaded file.
7
9
  plugin :refresh_metadata
8
10
  ```
9
11
 
10
- It provides `UploadedFile#refresh_metadata!` method, which triggers metadata
11
- extraction (calls `Shrine#extract_metadata`) with the uploaded file opened for
12
- reading, and updates the existing metadata hash with the results.
12
+ It provides `#refresh_metadata!` method, which triggers metadata extraction
13
+ (calls `Shrine#extract_metadata`) with the uploaded file opened for reading,
14
+ and updates the existing metadata hash with the results. This can be done
15
+ on the `Shrine::Attacher` or the `Shrine::UploadedFile` level.
16
+
17
+ ## Attacher
18
+
19
+ Calling `#refresh_metadata!` on a `Shrine::Attacher` object will re-extract
20
+ metadata of the attached file, and when used with a [model], it will write new
21
+ file data back into the attachment attribute.
22
+
23
+ ```rb
24
+ attacher.refresh_metadata!
25
+ attacher.file.metadata # re-extracted metadata
26
+ attacher.record.file_data #=> '{ ... data with updated metadata ... }'
27
+ ```
28
+
29
+ The `Attacher#context` hash will be forwarded to metadata extraction, as well
30
+ as any options that you pass in.
31
+
32
+ ```rb
33
+ # via context
34
+ attacher.context[:foo] = "bar"
35
+ attacher.refresh_metadata! # passes `{ foo: "bar" }` options to metadata extraction
36
+
37
+ # via arguments
38
+ attacher.refresh_metadata!(foo: "bar") # passes `{ foo: "bar" }` options to metadata extraction
39
+ ```
40
+
41
+ ## Uploaded File
42
+
43
+ The `#refresh_metadata!` method can be called on a `Shrine::UploadedFile` object
44
+ as well.
13
45
 
14
46
  ```rb
15
47
  uploaded_file.refresh_metadata!
16
48
  uploaded_file.metadata # re-extracted metadata
17
49
  ```
18
50
 
19
- For remote storages this will make an HTTP request to open the file for
20
- reading, but only the portion of the file needed for extracting each metadata
21
- value will be downloaded.
51
+ If the uploaded file is not open, it is opened before and closed after metadata
52
+ extraction. For remote storage services this will make an HTTP request.
53
+ However, only the portion of the file needed for extracting metadata will be
54
+ downloaded.
22
55
 
23
56
  If the uploaded file is already open, it is passed to metadata extraction as
24
57
  is.
25
58
 
26
59
  ```rb
27
60
  uploaded_file.open do
28
- uploaded_file.refresh_metadata!
61
+ uploaded_file.refresh_metadata! # uses the already opened file
29
62
  # ...
30
63
  end
31
64
  ```
32
65
 
33
- [refresh_metadata]: /lib/shrine/plugins/refresh_metadata.rb
66
+ Any options passed in will be forwarded to metadata extraction:
67
+
68
+ ```rb
69
+ uploaded_file.refresh_metadata!(foo: "bar") # passes `{ foo: "bar" }` options to metadata extraction
70
+ ```
71
+
72
+ [refresh_metadata]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/refresh_metadata.rb
73
+ [model]: https://shrinerb.com/docs/plugins/model
@@ -1,4 +1,6 @@
1
- # Remote URL
1
+ ---
2
+ title: Remote URL
3
+ ---
2
4
 
3
5
  The [`remote_url`][remote_url] plugin allows you to attach files from a remote
4
6
  location.
@@ -7,82 +9,117 @@ location.
7
9
  plugin :remote_url, max_size: 20*1024*1024
8
10
  ```
9
11
 
10
- If for example your attachment is called "avatar", this plugin will add
11
- `#avatar_remote_url` and `#avatar_remote_url=` methods to your model.
12
+ ## Usage
12
13
 
13
- ```rb
14
- user.avatar #=> nil
15
- user.avatar_remote_url = "http://example.com/cool-image.png"
16
- user.avatar #=> #<Shrine::UploadedFile>
14
+ The plugin will add the `#<name>_remote_url` writer to your model, which
15
+ downloads the remote file and uploads it to temporary storage.
17
16
 
18
- user.avatar.mime_type #=> "image/png"
19
- user.avatar.size #=> 43423
20
- user.avatar.original_filename #=> "cool-image.png"
17
+ ```rb
18
+ photo.image_remote_url = "http://example.com/cool-image.png"
19
+ photo.image.mime_type #=> "image/png"
20
+ photo.image.size #=> 43423
21
+ photo.image.original_filename #=> "cool-image.png"
21
22
  ```
22
23
 
23
- You can also use `#remote_url=` and `#remote_url` methods directly on the
24
- `Shrine::Attacher`:
24
+ If you're using `Shrine::Attacher` directly, you can use
25
+ `Attacher#assign_remote_url`:
25
26
 
26
27
  ```rb
27
- attacher.remote_url = "http://example.com/cool-image.png"
28
+ attacher.assign_remote_url("http://example.com/cool-image.png")
29
+ attacher.file.mime_type #=> "image/png"
30
+ attacher.file.size #=> 43423
31
+ attacher.file.original_filename #=> "cool-image.png"
28
32
  ```
29
33
 
34
+ ## Downloader
35
+
30
36
  By default, the file will be downloaded using `Down.download` from the [Down]
31
- gem. This will use the `Down::NetHttp` backend by default, which is a wrapper
37
+ gem. This will use the [Down::NetHttp] backend by default, which is a wrapper
32
38
  around [open-uri].
33
39
 
34
- ## Dynamic options
40
+ You can pass options to the downloader via the `:downloader` option:
35
41
 
36
- You can dynamically pass options to the downloader by using
37
- `Attacher#assign_remote_url`:
42
+ ```rb
43
+ attacher.assign_remote_url url, downloader: {
44
+ headers: { "Authorization" => "Basic ..." },
45
+ read_timeout: 30, open_timeout: 30,
46
+ max_redirects: 5,
47
+ # ...
48
+ }
49
+ ```
50
+
51
+ You can also change the downloader:
38
52
 
39
53
  ```rb
40
- attacher.assign_remote_url(url, downloader: { 'Authorization' => 'Basic ...' })
54
+ # Gemfile
55
+ gem "http"
56
+ ```
57
+ ```rb
58
+ require "down/http"
59
+
60
+ plugin :remote_url, downloader: -> (url, **options) {
61
+ Down::Http.download(url, **options) do |client|
62
+ client.follow(max_hops: 2).timeout(connect: 2, read: 2)
63
+ end
64
+ }
41
65
  ```
42
66
 
43
- You can also pass any other `Shrine#upload` options:
67
+ Any `Down::NotFound` and `Down::TooLarge` exceptions will be rescued and
68
+ converted into validation errors. If you want to convert any other exceptions
69
+ into validation errors, you can raise them as
70
+ `Shrine::Plugins::RemoteUrl::DownloadError`:
44
71
 
45
72
  ```rb
46
- attacher.assign_remote_url(url, metadata: { "mime_type" => "text/plain" })
73
+ plugin :remote_url, downloader: -> (url, **options) {
74
+ begin
75
+ RestClient.get(url)
76
+ rescue RestClient::ExceptionWithResponse => error
77
+ raise Shrine::Plugins::RemoteUrl::DownloadError, "remote file not found"
78
+ end
79
+ }
47
80
  ```
48
81
 
49
- ## Maximum size
82
+ ### Calling downloader
50
83
 
51
- It's a good practice to limit the maximum filesize of the remote file:
84
+ You can call the downloader directly with `Shrine.remote_url`:
52
85
 
53
86
  ```rb
54
- plugin :remote_url, max_size: 20*1024*1024 # 20 MB
87
+ # or YourUploader.remote_url(...)
88
+ file = Shrine.remote_url("https://example.com/image.jpg")
89
+ file #=> #<Tempfile:...>
55
90
  ```
56
91
 
57
- Now if a file that is bigger than 20MB is assigned, download will be terminated
58
- as soon as it gets the "Content-Length" header, or the size of currently
59
- downloaded content surpasses the maximum size. However, if for whatever reason
60
- you don't want to limit the maximum file size, you can set `:max_size` to nil:
92
+ You can pass additional options as well:
61
93
 
62
94
  ```rb
63
- plugin :remote_url, max_size: nil
95
+ # or YourUploader.remote_url(...)
96
+ Shrine.remote_url("https://example.com/image.jpg", headers: { "Cookie" => "..." })
64
97
  ```
65
98
 
66
- ## Custom downloader
99
+ ## Uploader options
67
100
 
68
- If you want to customize how the file is downloaded, you can override the
69
- `:downloader` parameter and provide your own implementation. For example, you
70
- can use the [http.rb] Down backend for downloading:
101
+ Any additional options passed to `Attacher#assign_remote_url` will be forwarded
102
+ to `Attacher#assign` (and `Shrine#upload`):
71
103
 
72
104
  ```rb
73
- # Gemfile
74
- gem "http"
105
+ attacher.assign_remote_url(url, metadata: { "mime_type" => "text/plain" })
75
106
  ```
107
+
108
+ ## Maximum size
109
+
110
+ It's a good practice to limit the maximum filesize of the remote file:
111
+
76
112
  ```rb
77
- require "down/http"
113
+ plugin :remote_url, max_size: 20*1024*1024 # 20 MB
114
+ ```
78
115
 
79
- down = Down::Http.new do |client|
80
- client
81
- .follow(max_hops: 2)
82
- .timeout(connect: 2, read: 2)
83
- end
116
+ Now if a file that is bigger than 20MB is assigned, download will be terminated
117
+ as soon as it gets the "Content-Length" header, or the size of currently
118
+ downloaded content surpasses the maximum size. However, if for whatever reason
119
+ you don't want to limit the maximum file size, you can set `:max_size` to nil:
84
120
 
85
- plugin :remote_url, max_size: 20*1024*1024, downloader: down.method(:download)
121
+ ```rb
122
+ plugin :remote_url, max_size: nil
86
123
  ```
87
124
 
88
125
  ## Errors
@@ -103,11 +140,10 @@ file ID, and pair that with the `backgrounding` plugin.
103
140
 
104
141
  ## File extension
105
142
 
106
- When attaching from a remote URL, the uploaded file location will have the
107
- extension inferred from the URL. However, some URLs might not have an
108
- extension, in which case the uploaded file location also won't have the
109
- extension. If you want the upload location to always have an extension, you can
110
- load the `infer_extension` plugin to infer it from the MIME type.
143
+ When attaching from a remote URL, the uploaded file location will inherit the
144
+ extension from the URL. However, some URLs might not have an extension. To
145
+ handle this case, you can use the `infer_extension` plugin to infer the
146
+ extension from the MIME type.
111
147
 
112
148
  ```rb
113
149
  plugin :infer_extension
@@ -156,8 +192,9 @@ Or disable logging altogether:
156
192
  plugin :remote_url, log_subscriber: nil
157
193
  ```
158
194
 
159
- [remote_url]: /lib/shrine/plugins/remote_url.rb
195
+ [remote_url]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/remote_url.rb
160
196
  [Down]: https://github.com/janko/down
197
+ [Down::NetHttp]: https://github.com/janko/down#downnethttp
161
198
  [open-uri]: https://ruby-doc.org/stdlib/libdoc/open-uri/rdoc/OpenURI.html
162
199
  [http.rb]: https://github.com/httprb/http
163
200
  [shrine-url]: https://github.com/shrinerb/shrine-url
@@ -1,4 +1,6 @@
1
- # Remove Attachment
1
+ ---
2
+ title: Remove Attachment
3
+ ---
2
4
 
3
5
  The [`remove_attachment`][remove_attachment] plugin allows you to delete
4
6
  attachments through checkboxes on the web form.
@@ -7,12 +9,31 @@ attachments through checkboxes on the web form.
7
9
  plugin :remove_attachment
8
10
  ```
9
11
 
10
- If for example your attachment is called "avatar", this plugin will add
11
- `#remove_avatar` and `#remove_avatar=` methods to your model. This allows you
12
- to add a form field for removing attachments:
12
+ The plugin adds the `#remove_<name>` accessor to your model, which removes the
13
+ attached file if it receives a truthy value:
13
14
 
14
15
  ```rb
15
- form.check_box :remove_avatar
16
+ photo.image #=> #<Shrine::UploadedFile>
17
+ photo.remove_image = 'true'
18
+ photo.image #=> nil
16
19
  ```
17
20
 
18
- [remove_attachment]: /lib/shrine/plugins/remove_attachment.rb
21
+ This allows you to add a checkbox form field for removing attachments:
22
+
23
+ ```rb
24
+ form_for photo do |f|
25
+ # ...
26
+ f.check_box :remove_image
27
+ end
28
+ ```
29
+
30
+ If you're using the `Shrine::Attacher` directly, you can use the
31
+ `Attacher#remove` accessor:
32
+
33
+ ```rb
34
+ attacher.file #=> #<Shrine::UploadedFile>
35
+ attacher.remove = '1'
36
+ attacher.file #=> nil
37
+ ```
38
+
39
+ [remove_attachment]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/remove_attachment.rb
@@ -1,12 +1,27 @@
1
- # Remove Invalid
1
+ ---
2
+ title: Remove Invalid
3
+ ---
2
4
 
3
- The [`remove_invalid`][remove_invalid] plugin automatically deletes a new
4
- assigned file if it was invalid and deassigns it from the record. If there was
5
- a previous file attached, it will be assigned back, otherwise no attachment
6
- will be assigned.
5
+ The [`remove_invalid`][remove_invalid] plugin automatically deletes and
6
+ deassigns a new assigned file if it was invalid. If there was a previous file
7
+ attached, it will be assigned back.
7
8
 
8
9
  ```rb
9
10
  plugin :remove_invalid
10
11
  ```
11
12
 
12
- [remove_invalid]: /lib/shrine/plugins/remove_invalid.rb
13
+ ```rb
14
+ # without previous file
15
+ photo.image #=> nil
16
+ photo.image = file # validation fails, assignment is reverted
17
+ photo.valid? #=> false
18
+ photo.image #=> nil
19
+
20
+ # with previous file
21
+ photo.image #=> #<Shrine::UploadedFile id="foo" ...>
22
+ photo.image = file # validation fails, assignment is reverted
23
+ photo.valid? #=> false
24
+ photo.image #=> #<Shrine::UploadedFile id="foo" ...>
25
+ ```
26
+
27
+ [remove_invalid]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/remove_invalid.rb
@@ -1,4 +1,6 @@
1
- # Restore Cached Data
1
+ ---
2
+ title: Restore Cached Data
3
+ ---
2
4
 
3
5
  The [`restore_cached_data`][restore_cached_data] plugin re-extracts metadata
4
6
  when assigning already cached files, i.e. when the attachment has been retained
@@ -10,7 +12,13 @@ extracted on the client side.
10
12
  ```rb
11
13
  plugin :restore_cached_data
12
14
  ```
15
+ ```rb
16
+ photo.image = { "id" => "path/to/image.jpg", "storage" => "cache", "metadata" => {} }
17
+ photo.image.metadata #=> { "size" => 4823763, "mime_type" => "image/jpeg", ... }
18
+ ```
13
19
 
14
- It uses the `refresh_metadata` plugin to re-extract metadata.
20
+ It uses the [`refresh_metadata`][refresh_metadata] plugin to re-extract
21
+ metadata.
15
22
 
16
- [restore_cached_data]: /lib/shrine/plugins/restore_cached_data.rb
23
+ [restore_cached_data]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/restore_cached_data.rb
24
+ [refresh_metadata]: https://shrinerb.com/docs/plugins/refresh_metadata
@@ -1,67 +1,191 @@
1
- # Sequel
1
+ ---
2
+ title: Sequel
3
+ ---
2
4
 
3
- The [`sequel`][sequel] plugin extends the "attachment" interface with support
4
- for Sequel.
5
+ The [`sequel`][sequel] plugin adds [Sequel] integration to the attachment
6
+ interface. It is built on top of the [`model`][model] plugin.
5
7
 
6
8
  ```rb
7
- plugin :sequel
9
+ Shrine.plugin :sequel
8
10
  ```
9
11
 
10
- ## Callbacks
12
+ ## Attachment
11
13
 
12
- Now the attachment module will add additional callbacks to the model:
14
+ Including a `Shrine::Attachment` module into a `Sequel::Model` subclass will:
13
15
 
14
- * "before save" Used by the `recache` plugin.
15
- * "after commit" (save) Promotes the attachment, deletes replaced ones.
16
- * "after commit" (destroy) – Deletes the attachment.
16
+ * add [model] attachment methods
17
+ * add [validations](#validations) and [hooks](#hooks) to tie attachment process
18
+ to the record lifecycle
17
19
 
18
- If you want to put promoting/deleting into a background job, see the
19
- `backgrounding` plugin.
20
+ ```rb
21
+ class Photo < Sequel::Model # has `image_data` column
22
+ include ImageUploader::Attachment(:image) # adds methods, callbacks & validations
23
+ end
24
+ ```
25
+ ```rb
26
+ photo = Photo.new
27
+
28
+ photo.image = file # cache attachment
29
+
30
+ photo.image #=> #<Shrine::UploadedFile id="bc2e13.jpg" storage=:cache ...>
31
+ photo.image_data #=> '{"id":"bc2e13.jpg","storage":"cache","metadata":{...}}'
32
+
33
+ photo.save # persist, promote attachment, then persist again
34
+
35
+ photo.image #=> #<Shrine::UploadedFile id="397eca.jpg" storage=:store ...>
36
+ photo.image_data #=> '{"id":"397eca.jpg","storage":"store","metadata":{...}}'
20
37
 
21
- Since attaching first saves the record with a cached attachment, then saves
22
- again with a stored attachment, you can detect this in callbacks:
38
+ photo.destroy # delete attachment
39
+
40
+ photo.image.exists? #=> false
41
+ ```
42
+
43
+ ### Hooks
44
+
45
+ #### After Save
46
+
47
+ After a record is saved and the transaction is committed, `Attacher#finalize`
48
+ is called, which promotes cached file to permanent storage and deletes previous
49
+ file if any.
23
50
 
24
51
  ```rb
25
- class User < Sequel::Model
26
- include ImageUploader::Attachment.new(:avatar)
52
+ photo = Photo.new
27
53
 
28
- def before_save
29
- super
54
+ photo.image = file
55
+ photo.image.storage_key #=> :cache
30
56
 
31
- if changed_columns.include?(:avatar) && avatar_attacher.cached?
32
- # cached
33
- elsif changed_columns.include?(:avatar) && avatar_attacher.stored?
34
- # promoted
35
- end
57
+ photo.save
58
+ photo.image.storage_key #=> :store
59
+ ```
60
+
61
+ #### After Destroy
62
+
63
+ After a record is destroyed and the transaction is committed,
64
+ `Attacher#destroy_attached` method is called, which deletes stored attached
65
+ file if any.
66
+
67
+ ```rb
68
+ photo = Photo.find(photo_id)
69
+ photo.image #=> #<Shrine::UploadedFile>
70
+ photo.image.exists? #=> true
71
+
72
+ photo.destroy
73
+ photo.image.exists? #=> false
74
+ ```
75
+
76
+ #### Overriding hooks
77
+
78
+ You can override any of the following attacher methods to modify callback
79
+ behaviour:
80
+
81
+ * `Attacher#sequel_before_save`
82
+ * `Attacher#sequel_after_save`
83
+ * `Attacher#sequel_after_destroy`
84
+
85
+ ```rb
86
+ class Shrine::Attacher
87
+ def sequel_after_save
88
+ super
89
+ # ...
36
90
  end
37
91
  end
38
92
  ```
39
93
 
40
- If you don't want the attachment module to add any callbacks to the model, and
41
- would instead prefer to call these actions manually, you can disable callbacks:
94
+ #### Skipping Hooks
95
+
96
+ If you don't want the attachment module to add any hooks to your model, you can
97
+ set `:hooks` to `false`:
98
+
99
+ ```rb
100
+ plugin :sequel, hooks: false
101
+ ```
102
+
103
+ ### Validations
104
+
105
+ If you're using the [`validation`][validation] plugin, the attachment module
106
+ will automatically merge attacher errors with model errors.
42
107
 
43
108
  ```rb
44
- plugin :sequel, callbacks: false
109
+ class ImageUploader < Shrine
110
+ plugin :validation_helpers
111
+
112
+ Attacher.validate do
113
+ validate_max_size 10 * 1024 * 1024
114
+ end
115
+ end
116
+ ```
117
+ ```rb
118
+ photo = Photo.new
119
+ photo.image = file
120
+ photo.valid?
121
+ photo.errors #=> { image: ["size must not be greater than 10.0 MB"] }
45
122
  ```
46
123
 
47
- ## Validations
124
+ #### Attachment Presence
48
125
 
49
- Additionally, any Shrine validation errors will added to Sequel's errors upon
50
- validation. Note that if you want to validate presence of the attachment, you
51
- can do it directly on the model.
126
+ If you want to validate presence of the attachment, you can use Sequel's
127
+ presence validator:
52
128
 
53
129
  ```rb
54
- class User < Sequel::Model
55
- include ImageUploader::Attachment.new(:avatar)
56
- validates_presence_of :avatar
130
+ class Photo < Sequel::Model
131
+ include ImageUploader::Attachment(:image)
132
+
133
+ def validate
134
+ super
135
+ validates_presence :image
136
+ end
57
137
  end
58
138
  ```
59
139
 
60
- If don't want the attachment module to merge file validations errors into model
61
- errors, you can disable it:
140
+ #### Skipping Validations
141
+
142
+ If don't want the attachment module to merge file validations errors into
143
+ model errors, you can set `:validations` to `false`:
62
144
 
63
145
  ```rb
64
146
  plugin :sequel, validations: false
65
147
  ```
66
148
 
67
- [sequel]: /lib/shrine/plugins/sequel.rb
149
+ ## Attacher
150
+
151
+ You can also use `Shrine::Attacher` directly (with or without the
152
+ `Shrine::Attachment` module):
153
+
154
+ ```rb
155
+ class Photo < Sequel::Model # has `image_data` column
156
+ end
157
+ ```
158
+ ```rb
159
+ photo = Photo.new
160
+ attacher = ImageUploader::Attacher.from_model(photo, :image)
161
+
162
+ attacher.assign(file) # cache
163
+
164
+ attacher.file #=> #<Shrine::UploadedFile id="bc2e13.jpg" storage=:cache ...>
165
+ photo.image_data #=> '{"id":"bc2e13.jpg","storage":"cache","metadata":{...}}'
166
+
167
+ photo.save # persist
168
+ attacher.finalize # promote
169
+ photo.save # persist
170
+
171
+ attacher.file #=> #<Shrine::UploadedFile id="397eca.jpg" storage=:store ...>
172
+ photo.image_data #=> '{"id":"397eca.jpg","storage":"store","metadata":{...}}'
173
+ ```
174
+
175
+ ### Persistence
176
+
177
+ The following persistence methods are added to `Shrine::Attacher`:
178
+
179
+ | Method | Description |
180
+ | :----- | :---------- |
181
+ | `Attacher#atomic_promote` | calls `Attacher#promote` and persists if the attachment hasn't changed |
182
+ | `Attacher#atomic_persist` | saves changes if the attachment hasn't changed |
183
+ | `Attacher#persist` | saves any changes to the underlying record |
184
+
185
+ See [persistence] docs for more details.
186
+
187
+ [sequel]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/sequel.rb
188
+ [Sequel]: https://sequel.jeremyevans.net/
189
+ [model]: https://shrinerb.com/docs/plugins/model
190
+ [validation]: https://shrinerb.com/docs/plugins/validation
191
+ [persistence]: https://shrinerb.com/docs/plugins/persistence