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,93 +1,224 @@
1
- # Active Record
1
+ ---
2
+ title: Active Record
3
+ ---
2
4
 
3
- The [`activerecord`][activerecord] plugin extends the "attachment" interface
4
- with support for ActiveRecord.
5
+ The [`activerecord`][activerecord] plugin adds [Active Record] integration to
6
+ the attachment interface. It is built on top of the [`model`][model] plugin.
5
7
 
6
8
  ```rb
7
- plugin :activerecord
9
+ Shrine.plugin :activerecord
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 an `ActiveRecord::Base` subclass
15
+ will:
13
16
 
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.
17
+ * add [model] attachment methods
18
+ * add [validations](#validations) and [callbacks](#callbacks) to tie attachment
19
+ process to the record lifecycle
17
20
 
18
- Note that ActiveRecord versions 3.x and 4.x have errors automatically silenced
19
- in hooks, which can make debugging more difficult, so it's recommended that you
20
- enable errors:
21
+ ```rb
22
+ class Photo < ActiveRecord::Base # has `image_data` column
23
+ include ImageUploader::Attachment(:image) # adds methods, callbacks & validations
24
+ end
25
+ ```
26
+ ```rb
27
+ photo = Photo.new
28
+
29
+ photo.image = file # cache attachment
30
+
31
+ photo.image #=> #<Shrine::UploadedFile id="bc2e13.jpg" storage=:cache ...>
32
+ photo.image_data #=> '{"id":"bc2e13.jpg","storage":"cache","metadata":{...}}'
33
+
34
+ photo.save # persist, promote attachment, then persist again
35
+
36
+ photo.image #=> #<Shrine::UploadedFile id="397eca.jpg" storage=:store ...>
37
+ photo.image_data #=> '{"id":"397eca.jpg","storage":"store","metadata":{...}}'
38
+
39
+ photo.destroy # delete attachment
40
+
41
+ photo.image.exists? #=> false
42
+ ```
43
+
44
+ ### Callbacks
45
+
46
+ #### After Save
47
+
48
+ After a record is saved and the transaction is committed, `Attacher#finalize`
49
+ is called, which promotes cached file to permanent storage and deletes previous
50
+ file if any.
21
51
 
22
52
  ```rb
23
- # This is the default in ActiveRecord 5
24
- ActiveRecord::Base.raise_in_transactional_callbacks = true
53
+ photo = Photo.new
54
+
55
+ photo.image = file
56
+ photo.image.storage_key #=> :cache
57
+
58
+ photo.save
59
+ photo.image.storage_key #=> :store
60
+ ```
61
+
62
+ #### After Destroy
63
+
64
+ After a record is destroyed and the transaction is committed,
65
+ `Attacher#destroy_attached` method is called, which deletes stored attached
66
+ file if any.
67
+
68
+ ```rb
69
+ photo = Photo.find(photo_id)
70
+ photo.image #=> #<Shrine::UploadedFile>
71
+ photo.image.exists? #=> true
72
+
73
+ photo.destroy
74
+ photo.image.exists? #=> false
25
75
  ```
26
76
 
27
- If you want to put promoting/deleting into a background job, see the
28
- `backgrounding` plugin.
77
+ #### Caveats
29
78
 
30
- Since attaching first saves the record with a cached attachment, then saves
31
- again with a stored attachment, you can detect this in callbacks:
79
+ Active Record currently has a [bug with transaction callbacks], so if you have
80
+ any "after commit" callbacks, make sure to include Shrine's attachment module
81
+ *after* they have all been defined.
82
+
83
+ #### Overriding callbacks
84
+
85
+ You can override any of the following attacher methods to modify callback
86
+ behaviour:
87
+
88
+ * `Attacher#activerecord_before_save`
89
+ * `Attacher#activerecord_after_save`
90
+ * `Attacher#activerecord_after_destroy`
32
91
 
33
92
  ```rb
34
- class User < ActiveRecord::Base
35
- include ImageUploader::Attachment.new(:avatar)
36
-
37
- before_save do
38
- if avatar_data_changed? && avatar_attacher.cached?
39
- # cached
40
- elsif avatar_data_changed? && avatar_attacher.stored?
41
- # promoted
42
- end
93
+ class Shrine::Attacher
94
+ def activerecord_after_save
95
+ super
96
+ # ...
43
97
  end
44
98
  end
45
99
  ```
46
100
 
47
- Note that ActiveRecord currently has a [bug with transaction callbacks], so if
48
- you have any "after commit" callbacks, make sure to include Shrine's attachment
49
- module *after* they have all been defined.
101
+ #### Skipping Callbacks
50
102
 
51
- If you don't want the attachment module to add any callbacks to the model, and
52
- would instead prefer to call these actions manually, you can disable callbacks:
103
+ If you don't want the attachment module to add any callbacks to your model, you
104
+ can set `:callbacks` to `false`:
53
105
 
54
106
  ```rb
55
107
  plugin :activerecord, callbacks: false
56
108
  ```
57
109
 
58
- ## Validations
110
+ ### Validations
59
111
 
60
- Additionally, any Shrine validation errors will be added to ActiveRecord's
61
- errors upon validation. Note that Shrine validation messages don't have to be
62
- strings, they can also be symbols or symbols and options, which allows them to
63
- be internationalized together with other ActiveRecord validation messages.
112
+ If you're using the [`validation`][validation] plugin, the attachment module
113
+ will automatically merge attacher errors with model errors.
64
114
 
65
115
  ```rb
66
- class MyUploader < Shrine
116
+ class ImageUploader < Shrine
67
117
  plugin :validation_helpers
68
118
 
69
119
  Attacher.validate do
70
- validate_max_size 256 * 1024**2, message: ->(max) { [:max_size, max: max] }
120
+ validate_max_size 10 * 1024 * 1024
71
121
  end
72
122
  end
73
123
  ```
124
+ ```rb
125
+ photo = Photo.new
126
+ photo.image = file
127
+ photo.valid?
128
+ photo.errors #=> { image: ["size must not be greater than 10.0 MB"] }
129
+ ```
130
+
131
+ #### Attachment Presence
74
132
 
75
- If you want to validate presence of the attachment, you can do it directly on
76
- the model.
133
+ If you want to validate presence of the attachment, you can use Active Record's
134
+ presence validator:
77
135
 
78
136
  ```rb
79
- class User < ActiveRecord::Base
80
- include ImageUploader::Attachment.new(:avatar)
81
- validates_presence_of :avatar
137
+ class Photo < ActiveRecord::Base
138
+ include ImageUploader::Attachment(:image)
139
+
140
+ validates_presence_of :image
82
141
  end
83
142
  ```
84
143
 
144
+ #### I18n
145
+
146
+ If you want Active Record to translate attacher error messages, you can use
147
+ symbols or arrays of symbols and options for validation errors:
148
+
149
+ ```rb
150
+ class ImageUploader < Shrine
151
+ plugin :validation_helpers
152
+
153
+ Attacher.validate do
154
+ validate_max_size 10 * 1024 * 1024, message: -> (max) { [:too_large, max: max] }
155
+ validate_mime_type %w[image/jpeg image/png], message: :not_image
156
+ end
157
+ end
158
+ ```
159
+ ```yml
160
+ en:
161
+ activerecord:
162
+ errors:
163
+ models:
164
+ photo:
165
+ attributes:
166
+ image:
167
+ max_size: "must not be larger than %{max_size} bytes"
168
+ not_image: "must be a common image format"
169
+ ```
170
+
171
+ #### Skipping Validations
172
+
85
173
  If don't want the attachment module to merge file validations errors into
86
- model errors, you can disable it:
174
+ model errors, you can set `:validations` to `false`:
87
175
 
88
176
  ```rb
89
177
  plugin :activerecord, validations: false
90
178
  ```
91
179
 
92
- [activerecord]: /lib/shrine/plugins/activerecord.rb
180
+ ## Attacher
181
+
182
+ You can also use `Shrine::Attacher` directly (with or without the
183
+ `Shrine::Attachment` module):
184
+
185
+ ```rb
186
+ class Photo < ActiveRecord::Base # has `image_data` column
187
+ end
188
+ ```
189
+ ```rb
190
+ photo = Photo.new
191
+ attacher = ImageUploader::Attacher.from_model(photo, :image)
192
+
193
+ attacher.assign(file) # cache
194
+
195
+ attacher.file #=> #<Shrine::UploadedFile id="bc2e13.jpg" storage=:cache ...>
196
+ photo.image_data #=> '{"id":"bc2e13.jpg","storage":"cache","metadata":{...}}'
197
+
198
+ photo.save # persist
199
+ attacher.finalize # promote
200
+ photo.save # persist
201
+
202
+ attacher.file #=> #<Shrine::UploadedFile id="397eca.jpg" storage=:store ...>
203
+ photo.image_data #=> '{"id":"397eca.jpg","storage":"store","metadata":{...}}'
204
+ ```
205
+
206
+ ### Persistence
207
+
208
+ The following persistence methods are added to `Shrine::Attacher`:
209
+
210
+ | Method | Description |
211
+ | :----- | :---------- |
212
+ | `Attacher#atomic_promote` | calls `Attacher#promote` and persists if the attachment hasn't changed |
213
+ | `Attacher#atomic_persist` | saves changes if the attachment hasn't changed |
214
+ | `Attacher#persist` | saves any changes to the underlying record |
215
+
216
+ See [persistence] docs for more details.
217
+
218
+ [activerecord]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/activerecord.rb
219
+ [Active Record]: https://guides.rubyonrails.org/active_record_basics.html
220
+ [model]: https://shrinerb.com/docs/plugins/model
221
+ [callbacks]: https://guides.rubyonrails.org/active_record_callbacks.html
93
222
  [bug with transaction callbacks]: https://github.com/rails/rails/issues/14493
223
+ [validation]: https://shrinerb.com/docs/plugins/validation
224
+ [persistence]: https://shrinerb.com/docs/plugins/persistence
@@ -1,75 +1,155 @@
1
- # Add Metadata
1
+ ---
2
+ title: Add Metadata
3
+ ---
2
4
 
3
- The [`add_metadata`][add_metadata] plugin provides a convenient method for
4
- extracting and adding custom metadata values.
5
+ The [`add_metadata`][add_metadata] plugin allows adding custom metadata to
6
+ uploaded files.
5
7
 
6
8
  ```rb
7
- plugin :add_metadata
9
+ Shrine.plugin :add_metadata
10
+ ```
11
+
12
+ ## Metadata block
13
+
14
+ The `Shrine.add_metadata` method allows you to register a block that will get
15
+ executed on upload, where you can return custom metadata:
16
+
17
+ ```rb
18
+ require "pdf-reader" # https://github.com/yob/pdf-reader
8
19
 
9
- add_metadata :exif do |io, context|
10
- begin
11
- Exif::Data.new(io).to_h
12
- rescue Exif::NotReadable # not a valid image
13
- {}
20
+ class PdfUploader < Shrine
21
+ add_metadata :page_count do |io|
22
+ reader = PDF::Reader.new(io)
23
+ reader.page_count
14
24
  end
15
25
  end
16
26
  ```
17
27
 
18
- The above will add "exif" to the metadata hash, and also create the `#exif`
19
- reader method on Shrine::UploadedFile.
28
+ The above will add `page_count` key to the metadata hash, and also create the
29
+ `#page_count` reader method on the `Shrine::UploadedFile`.
20
30
 
21
31
  ```rb
22
- image.metadata["exif"]
32
+ uploaded_file.metadata["page_count"] #=> 30
23
33
  # or
24
- image.exif
34
+ uploaded_file.page_count #=> 30
35
+ ```
36
+
37
+ ### Skipping nil values
38
+
39
+ By default, if your block returns `nil` then the `nil` value will be stored into
40
+ metadata. If you do not want to store anything when your block returns nil, you
41
+ can use the `skip_nil: true` option:
42
+
43
+ ```rb
44
+ class PdfUploader < Shrine
45
+ add_metadata :pages, skip_nil: true do |io|
46
+ if is_pdf?(io)
47
+ reader = PDF::Reader.new(io)
48
+ reader.page_count
49
+ else
50
+ # If this is not a PDF, then the pages metadata will not be stored
51
+ nil
52
+ end
53
+ end
54
+ end
25
55
  ```
26
56
 
57
+ ### Multiple values
58
+
27
59
  You can also extract multiple metadata values at once, by using `add_metadata`
28
60
  without an argument and returning a hash of metadata.
29
61
 
30
62
  ```rb
31
- add_metadata do |io, context|
32
- begin
33
- data = Exif::Data.new(io)
34
- rescue Exif::NotReadable # not a valid image
35
- next {}
63
+ require "exif" # https://github.com/tonytonyjan/exif
64
+
65
+ class ImageUploader < Shrine
66
+ add_metadata do |io|
67
+ begin
68
+ data = Exif::Data.new(io)
69
+ rescue Exif::NotReadable # not a valid image
70
+ next {}
71
+ end
72
+
73
+ { "date_time" => data.date_time,
74
+ "flash" => data.flash,
75
+ "focal_length" => data.focal_length,
76
+ "exposure_time" => data.exposure_time }
36
77
  end
37
-
38
- { date_time: data.date_time,
39
- flash: data.flash,
40
- focal_length: data.focal_length,
41
- exposure_time: data.exposure_time }
42
78
  end
43
79
  ```
80
+ ```rb
81
+ uploaded_file.metadata #=>
82
+ # {
83
+ # ...
84
+ # "date_time" => "2019:07:20 16:16:08",
85
+ # "flash" => 16,
86
+ # "focal_length" => 26/1,
87
+ # "exposure_time" => 1/500,
88
+ # }
89
+ ```
44
90
 
45
91
  In this case Shrine won't automatically create reader methods for the extracted
46
- metadata on Shrine::UploadedFile, but you can create them via
47
- `#metadata_method`.
92
+ metadata, but you can create them via `Shrine.metadata_method`:
48
93
 
49
94
  ```rb
50
- metadata_method :date_time, :flash
95
+ class ImageUploader < Shrine
96
+ # ...
97
+ metadata_method :date_time, :flash
98
+ end
51
99
  ```
100
+ ```rb
101
+ uploaded_file.date_time #=> "2019:07:20 16:16:08"
102
+ uploaded_file.flash #=> 16
103
+ ```
104
+
105
+ ### Ensuring file
52
106
 
53
107
  The `io` might not always be a file object, so if you're using an analyzer
54
108
  which requires the source file to be on disk, you can use `Shrine.with_file` to
55
109
  ensure you have a file object.
56
110
 
57
111
  ```rb
58
- add_metadata do |io, context|
59
- movie = Shrine.with_file(io) { |file| FFMPEG::Movie.new(file.path) }
112
+ require "streamio-ffmpeg" # https://github.com/streamio/streamio-ffmpeg
113
+
114
+ class VideoUploader < Shrine
115
+ add_metadata do |io|
116
+ movie = Shrine.with_file(io) do |file|
117
+ FFMPEG::Movie.new(file.path)
118
+ end
119
+
120
+ { "duration" => movie.duration,
121
+ "bitrate" => movie.bitrate,
122
+ "resolution" => movie.resolution,
123
+ "frame_rate" => movie.frame_rate }
124
+ end
125
+ end
126
+ ```
127
+
128
+ ### Uploader options
129
+
130
+ Uploader options are also yielded to the block, you can access them for more
131
+ context:
60
132
 
61
- { "duration" => movie.duration,
62
- "bitrate" => movie.bitrate,
63
- "resolution" => movie.resolution,
64
- "frame_rate" => movie.frame_rate }
133
+ ```rb
134
+ add_metadata do |io, **options|
135
+ options #=>
136
+ # {
137
+ # record: #<Photo>,
138
+ # name: :image,
139
+ # action: :store,
140
+ # metadata: { ... },
141
+ # ...
142
+ # }
65
143
  end
66
144
  ```
67
145
 
68
- Any previously extracted metadata can be accessed via `context[:metadata]`:
146
+ #### Metadata
147
+
148
+ The `:metadata` option holds metadata that was extracted so far:
69
149
 
70
150
  ```rb
71
- add_metadata :foo do |io, context|
72
- context[:metadata] #=>
151
+ add_metadata :foo do |io, metadata:, **|
152
+ metadata #=>
73
153
  # {
74
154
  # "size" => 239823,
75
155
  # "filename" => "nature.jpg",
@@ -79,8 +159,8 @@ add_metadata :foo do |io, context|
79
159
  "foo"
80
160
  end
81
161
 
82
- add_metadata :bar do |io, context|
83
- context[:metadata] #=>
162
+ add_metadata :bar do |io, metadata:, **|
163
+ metadata #=>
84
164
  # {
85
165
  # "size" => 239823,
86
166
  # "filename" => "nature.jpg",
@@ -92,4 +172,25 @@ add_metadata :bar do |io, context|
92
172
  end
93
173
  ```
94
174
 
95
- [add_metadata]: /lib/shrine/plugins/add_metadata.rb
175
+ ## Updating metadata
176
+
177
+ If you just wish to add some custom metadata to existing uploads, you can do it
178
+ with `UploadedFile#add_metadata` (and write the changes back to the model):
179
+
180
+ ```rb
181
+ attacher.file.add_metadata("foo" => "bar")
182
+ attacher.write # write changes to the model attribute
183
+
184
+ attacher.file.metadata #=> { ..., "foo" => "bar" }
185
+ ```
186
+
187
+ You can also use the `Attacher#add_metadata` shorthand, which also takes care
188
+ of syncing the model:
189
+
190
+ ```rb
191
+ attacher.add_metadata("foo" => "bar")
192
+
193
+ attacher.file.metadata #=> { ..., "foo" => "bar" }
194
+ ```
195
+
196
+ [add_metadata]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/add_metadata.rb