shrine 2.19.3 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +523 -41
  3. data/LICENSE.txt +1 -1
  4. data/README.md +83 -979
  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 +103 -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 +1156 -0
  20. data/doc/metadata.md +190 -109
  21. data/doc/multiple_files.md +93 -30
  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 +186 -101
  34. data/doc/plugins/derivatives.md +839 -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 +16 -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 +188 -170
  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 +5 -1
  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/release_notes/3.5.0.md +63 -0
  116. data/doc/release_notes/3.6.0.md +23 -0
  117. data/doc/retrieving_uploads.md +5 -2
  118. data/doc/securing_uploads.md +60 -37
  119. data/doc/storage/file_system.md +20 -3
  120. data/doc/storage/memory.md +19 -0
  121. data/doc/storage/s3.md +122 -78
  122. data/doc/testing.md +141 -133
  123. data/doc/upgrading_to_3.md +708 -0
  124. data/doc/validation.md +54 -90
  125. data/lib/shrine/attacher.rb +292 -169
  126. data/lib/shrine/attachment.rb +13 -46
  127. data/lib/shrine/plugins/_persistence.rb +93 -0
  128. data/lib/shrine/plugins/activerecord.rb +77 -34
  129. data/lib/shrine/plugins/add_metadata.rb +25 -17
  130. data/lib/shrine/plugins/atomic_helpers.rb +119 -0
  131. data/lib/shrine/plugins/backgrounding.rb +77 -113
  132. data/lib/shrine/plugins/cached_attachment_data.rb +6 -15
  133. data/lib/shrine/plugins/column.rb +102 -0
  134. data/lib/shrine/plugins/data_uri.rb +38 -36
  135. data/lib/shrine/plugins/default_storage.rb +45 -15
  136. data/lib/shrine/plugins/default_url.rb +12 -24
  137. data/lib/shrine/plugins/default_url_options.rb +3 -30
  138. data/lib/shrine/plugins/delete_raw.rb +10 -16
  139. data/lib/shrine/plugins/derivation_endpoint.rb +130 -171
  140. data/lib/shrine/plugins/derivatives.rb +645 -0
  141. data/lib/shrine/plugins/determine_mime_type.rb +9 -21
  142. data/lib/shrine/plugins/download_endpoint.rb +118 -133
  143. data/lib/shrine/plugins/dynamic_storage.rb +5 -11
  144. data/lib/shrine/plugins/entity.rb +158 -0
  145. data/lib/shrine/plugins/form_assign.rb +108 -0
  146. data/lib/shrine/plugins/included.rb +6 -6
  147. data/lib/shrine/plugins/infer_extension.rb +17 -20
  148. data/lib/shrine/plugins/instrumentation.rb +59 -43
  149. data/lib/shrine/plugins/keep_files.rb +3 -15
  150. data/lib/shrine/plugins/metadata_attributes.rb +28 -19
  151. data/lib/shrine/plugins/mirroring.rb +142 -0
  152. data/lib/shrine/plugins/model.rb +160 -0
  153. data/lib/shrine/plugins/module_include.rb +3 -3
  154. data/lib/shrine/plugins/multi_cache.rb +27 -0
  155. data/lib/shrine/plugins/presign_endpoint.rb +27 -28
  156. data/lib/shrine/plugins/pretty_location.rb +15 -9
  157. data/lib/shrine/plugins/processing.rb +22 -9
  158. data/lib/shrine/plugins/rack_file.rb +2 -42
  159. data/lib/shrine/plugins/rack_response.rb +21 -10
  160. data/lib/shrine/plugins/recache.rb +6 -5
  161. data/lib/shrine/plugins/refresh_metadata.rb +13 -11
  162. data/lib/shrine/plugins/remote_url.rb +49 -49
  163. data/lib/shrine/plugins/remove_attachment.rb +12 -6
  164. data/lib/shrine/plugins/remove_invalid.rb +19 -8
  165. data/lib/shrine/plugins/restore_cached_data.rb +13 -7
  166. data/lib/shrine/plugins/sequel.rb +86 -36
  167. data/lib/shrine/plugins/signature.rb +10 -16
  168. data/lib/shrine/plugins/store_dimensions.rb +35 -40
  169. data/lib/shrine/plugins/tempfile.rb +1 -3
  170. data/lib/shrine/plugins/type_predicates.rb +113 -0
  171. data/lib/shrine/plugins/upload_endpoint.rb +28 -24
  172. data/lib/shrine/plugins/upload_options.rb +14 -15
  173. data/lib/shrine/plugins/url_options.rb +31 -0
  174. data/lib/shrine/plugins/validation.rb +80 -0
  175. data/lib/shrine/plugins/validation_helpers.rb +35 -58
  176. data/lib/shrine/plugins/versions.rb +107 -87
  177. data/lib/shrine/plugins.rb +22 -0
  178. data/lib/shrine/storage/file_system.rb +46 -64
  179. data/lib/shrine/storage/linter.rb +42 -7
  180. data/lib/shrine/storage/memory.rb +49 -0
  181. data/lib/shrine/storage/s3.rb +173 -160
  182. data/lib/shrine/uploaded_file.rb +32 -32
  183. data/lib/shrine/version.rb +3 -3
  184. data/lib/shrine.rb +87 -150
  185. data/shrine.gemspec +11 -12
  186. metadata +92 -82
  187. data/doc/migrating_storage.md +0 -76
  188. data/doc/plugins/backup.md +0 -31
  189. data/doc/plugins/copy.md +0 -24
  190. data/doc/plugins/delete_promoted.md +0 -12
  191. data/doc/plugins/direct_upload.md +0 -172
  192. data/doc/plugins/hooks.md +0 -58
  193. data/doc/plugins/logging.md +0 -42
  194. data/doc/plugins/migration_helpers.md +0 -60
  195. data/doc/plugins/moving.md +0 -19
  196. data/doc/plugins/multi_delete.md +0 -20
  197. data/doc/plugins/parallelize.md +0 -16
  198. data/doc/plugins/parsed_json.md +0 -23
  199. data/doc/regenerating_versions.md +0 -143
  200. data/lib/shrine/plugins/background_helpers.rb +0 -5
  201. data/lib/shrine/plugins/backup.rb +0 -90
  202. data/lib/shrine/plugins/copy.rb +0 -50
  203. data/lib/shrine/plugins/delete_promoted.rb +0 -20
  204. data/lib/shrine/plugins/direct_upload.rb +0 -217
  205. data/lib/shrine/plugins/hooks.rb +0 -90
  206. data/lib/shrine/plugins/logging.rb +0 -142
  207. data/lib/shrine/plugins/migration_helpers.rb +0 -70
  208. data/lib/shrine/plugins/moving.rb +0 -57
  209. data/lib/shrine/plugins/multi_delete.rb +0 -32
  210. data/lib/shrine/plugins/parallelize.rb +0 -78
  211. data/lib/shrine/plugins/parsed_json.rb +0 -29
data/doc/testing.md CHANGED
@@ -1,4 +1,9 @@
1
- # Testing with Shrine
1
+ ---
2
+ title: Testing with Shrine
3
+ ---
4
+
5
+ import Tabs from '@theme/Tabs';
6
+ import TabItem from '@theme/TabItem';
2
7
 
3
8
  The goal of this guide is to provide some useful tips for testing file
4
9
  attachments implemented with Shrine in your application.
@@ -6,9 +11,9 @@ attachments implemented with Shrine in your application.
6
11
  ## Callbacks
7
12
 
8
13
  When you first try to test file attachments, you might experience that files
9
- are simply not being promoted (uploaded from temporary to permanent storage).
10
- This is because your tests are likely setup to be wrapped inside database
11
- transactions, and that doesn't work with Shrine callbacks.
14
+ are not being promoted to permanent storage. This is because your tests are
15
+ likely setup to be wrapped inside database transactions, and that doesn't work
16
+ with Shrine callbacks.
12
17
 
13
18
  Specifically, Shrine uses "after commit" callbacks for promoting and deleting
14
19
  attached files. This means that if your tests are wrapped inside transactions,
@@ -18,7 +23,7 @@ happens only after the test has already finished.
18
23
  ```rb
19
24
  # Promoting will happen only after the test transaction commits
20
25
  it "can attach images" do
21
- photo = Photo.create(image: image_file)
26
+ photo = Photo.create(image: file)
22
27
  photo.image.storage_key #=> :cache (we expected it to be promoted to permanent storage)
23
28
  end
24
29
  ```
@@ -37,15 +42,10 @@ end
37
42
  ## Storage
38
43
 
39
44
  If you're using FileSystem storage and your tests run in a single process,
40
- you can switch to [memory storage][shrine-memory], which is both faster and
41
- doesn't require you to clean up anything between tests.
45
+ you can switch to `Shrine::Storage::Memory`, which is both faster and doesn't
46
+ require you to clean up anything between tests.
42
47
 
43
48
  ```rb
44
- # Gemfile
45
- gem "shrine-memory"
46
- ```
47
- ```rb
48
- # test/test_helper.rb
49
49
  require "shrine/storage/memory"
50
50
 
51
51
  Shrine.storages = {
@@ -100,189 +100,197 @@ subdomains when generating URLs.
100
100
 
101
101
  ## Test data
102
102
 
103
- If you're creating test data dynamically using libraries like [factory_bot],
104
- you can have the test file assigned dynamically when the record is created:
103
+ We want to keep our tests fast, so when we're setting up files for tests, we
104
+ want to avoid expensive operations such as file processing and metadata
105
+ extraction.
106
+
107
+ We can create a helper method that will create attached file data for us, and
108
+ use that with our factories/fixtures.
105
109
 
106
110
  ```rb
107
- factory :photo do
108
- image { File.open("test/files/image.jpg") }
109
- end
110
- ```
111
+ module TestData
112
+ module_function
111
113
 
112
- On the other hand, if you're setting up test data using Rails' YAML fixtures,
113
- you unfortunately won't be able to use them for assigning files. This is
114
- because Rails fixtures only allow assigning primitive data types, and don't
115
- allow you to specify Shrine attributes - you can only assign to columns
116
- directly.
114
+ def image_data
115
+ attacher = Shrine::Attacher.new
116
+ attacher.set(uploaded_image)
117
117
 
118
- ## Background jobs
118
+ # if you're processing derivatives
119
+ attacher.set_derivatives(
120
+ large: uploaded_image,
121
+ medium: uploaded_image,
122
+ small: uploaded_image,
123
+ )
119
124
 
120
- If you're using background jobs with Shrine, you probably want to make them
121
- synchronous in tests. Your favourite backgrounding library should already
122
- support this, examples:
125
+ attacher.column_data # or attacher.data in case of postgres jsonb column
126
+ end
123
127
 
124
- ```rb
125
- # Sidekiq
126
- require "sidekiq/testing"
127
- Sidekiq::Testing.inline!
128
+ def uploaded_image
129
+ file = File.open("test/files/image.jpg", binmode: true)
130
+
131
+ # for performance we skip metadata extraction and assign test metadata
132
+ uploaded_file = Shrine.upload(file, :store, metadata: false)
133
+ uploaded_file.metadata.merge!(
134
+ "size" => File.size(file.path),
135
+ "mime_type" => "image/jpeg",
136
+ "filename" => "test.jpg",
137
+ )
138
+
139
+ uploaded_file
140
+ end
141
+ end
128
142
  ```
129
143
 
144
+ <Tabs>
145
+ <TabItem value="factory_bot" label="FactoryBot">
146
+
130
147
  ```rb
131
- # SuckerPunch
132
- require "sucker_punch/testing/inline"
148
+ factory :photo do
149
+ image_data { TestData.image_data }
150
+ end
133
151
  ```
134
152
 
153
+ </TabItem>
154
+ <TabItem value="fixtures" label="Rails YAML fixtures">
155
+
156
+ ```erb
157
+ photo:
158
+ image_data: <%= TestData.image_data %>
159
+ ```
160
+
161
+ </TabItem>
162
+ </Tabs>
163
+
164
+ ## Unit tests
165
+
166
+ For testing attachment in your unit tests, you can assign plain `File` objects:
167
+
135
168
  ```rb
136
- # ActiveJob
137
- ActiveJob::Base.queue_adapter = :inline
169
+ RSpec.describe ImageUploader do
170
+ let(:image) { photo.image }
171
+ let(:derivatives) { photo.image_derivatives }
172
+ let(:photo) { Photo.create(image: File.open("test/files/image.png", "rb")) }
173
+
174
+ it "extracts metadata" do
175
+ expect(image.mime_type).to eq("image/png")
176
+ expect(image.extension).to eq("png")
177
+ expect(image.size).to be_instance_of(Integer)
178
+ expect(image.width).to be_instance_of(Integer)
179
+ expect(image.height).to be_instance_of(Integer)
180
+ end
181
+
182
+ it "generates derivatives" do
183
+ expect(derivatives[:small]).to be_kind_of(Shrine::UploadedFile)
184
+ expect(derivatives[:medium]).to be_kind_of(Shrine::UploadedFile)
185
+ expect(derivatives[:large]).to be_kind_of(Shrine::UploadedFile)
186
+ end
187
+ end
138
188
  ```
139
189
 
140
190
  ## Acceptance tests
141
191
 
142
192
  In acceptance tests you're testing your app end-to-end, and you likely want to
143
- also test file attachments here. There are a variety of libraries that you
144
- might be using for your acceptance tests.
193
+ also test file attachments here. Here are examples for some common use cases:
145
194
 
146
- ### Capybara
147
-
148
- If you're testing with the [Capybara] acceptance test framework, you can use
149
- [`#attach_file`] to select a file from your filesystem in the form:
195
+ <Tabs>
196
+ <TabItem value="capybara" label="Capybara">
150
197
 
151
198
  ```rb
152
199
  attach_file("#image-field", "test/files/image.jpg")
153
200
  ```
154
201
 
155
- ### Rack::Test
156
-
157
- Regular routing tests in Rails use [Rack::Test], in which case you can create
158
- `Rack::Test::UploadedFile` objects and pass them as form parameters:
202
+ </TabItem>
203
+ <TabItem value="rack-test" label="rack-test">
159
204
 
160
205
  ```rb
161
- post "/photos", photo: {image: Rack::Test::UploadedFile.new("test/files/image.jpg", "image/jpeg")}
206
+ post "/photos", photo: {
207
+ image: Rack::Test::UploadedFile.new("test/files/image.jpg", "image/jpeg")
208
+ }
162
209
  ```
163
210
 
164
- ### Rack::TestApp
211
+ </TabItem>
212
+ </Tabs>
165
213
 
166
- With [Rack::TestApp] you can create multipart file upload requests by using the
167
- `:multipart` option and passing a `File` object:
214
+ If you want to test requests with cached attachment data, you can do so as
215
+ follows:
168
216
 
169
217
  ```rb
170
- http.post "/photos", multipart: {"photo[image]" => File.open("test/files/image.jpg")}
218
+ cached_file = Shrine.upload(file, :cache)
219
+
220
+ post "/photos", photo: { image: cached_file.to_json }
171
221
  ```
172
222
 
173
- ## Attachment
223
+ ## Background jobs
174
224
 
175
- Even though all the file attachment logic is usually encapsulated in your
176
- uploader classes, in general it's still best to test this logic through models.
225
+ If you're using background jobs with Shrine, you probably want to make them
226
+ synchronous in tests. See your backgrounding library docs for how to make jobs
227
+ synchronous.
177
228
 
178
- In your controller the attachment attribute using the uploaded file from the
179
- controller, in Rails case it's an `ActionDispatch::Http::UploadedFile`.
180
- However, you can also assign plain `File` objects, or any other kind of IO-like
181
- objects.
229
+ <Tabs>
230
+ <TabItem value="activejob" label="Active Job">
182
231
 
183
232
  ```rb
184
- describe ImageUploader do
185
- it "generates image thumbnails" do
186
- photo = Photo.create(image: File.open("test/files/image.png"))
187
- assert_equal [:small, :medium, :large], photo.image.keys
188
- end
189
- end
233
+ ActiveJob::Base.queue_adapter = :inline
190
234
  ```
191
235
 
192
- If you want test with an IO object that closely resembles the kind of IO that
193
- is assigned by your web framework, you can use this:
236
+ </TabItem>
237
+ <TabItem value="sidekiq" label="Sidekiq">
194
238
 
195
239
  ```rb
196
- require "forwardable"
197
- require "stringio"
198
-
199
- class FakeIO
200
- attr_reader :original_filename, :content_type
201
-
202
- def initialize(content, filename: nil, content_type: nil)
203
- @io = StringIO.new(content)
204
- @original_filename = filename
205
- @content_type = content_type
206
- end
207
-
208
- extend Forwardable
209
- delegate %i[read rewind eof? close size] => :@io
210
- end
240
+ require "sidekiq/testing"
241
+ Sidekiq::Testing.inline!
211
242
  ```
212
243
 
244
+ </TabItem>
245
+ <TabItem value="sucker_punch" label="SuckerPunch">
246
+
213
247
  ```rb
214
- describe ImageUploader do
215
- it "generates image thumbnails" do
216
- photo = Photo.create(image: FakeIO.new(File.read("test/files/image.png")))
217
- assert_equal [:small, :medium, :large], photo.image.keys
218
- end
219
- end
248
+ require "sucker_punch/testing/inline"
220
249
  ```
221
250
 
222
- ## Processing
223
-
224
- In tests you usually don't want to perform processing, or at least don't want
225
- it to be performed by default (only when you're actually testing it).
226
-
227
- If you're processing only single files, you can override the `Shrine#process`
228
- method in tests to return nil:
251
+ </TabItem>
252
+ </Tabs>
229
253
 
230
- ```rb
231
- class ImageUploader
232
- def process(io, context)
233
- # don't do any processing
234
- end
235
- end
236
- ```
254
+ ## Processing
237
255
 
238
- If you're processing versions, you can override `Shrine#process` to simply
239
- return a hash of unprocessed original files:
256
+ If you're testing your attachment flow which includes processing [derivatives],
257
+ you might want to disable the processing for certain tests. You can do this by
258
+ temporarily overriding the processor:
240
259
 
241
260
  ```rb
242
- class ImageUploader
243
- def process(io, context)
244
- if context[:action] == :store
245
- {small: io.download, medium: io.download, large: io.download}
261
+ module TestMode
262
+ module_function
263
+
264
+ def disable_processing(attacher, processor_name = :default)
265
+ attacher.class.instance_exec do
266
+ original_processor = derivatives_processor
267
+ derivatives_processor(processor_name) { Hash.new }
268
+ yield
269
+ derivatives_processor(processor_name, &original_processor)
246
270
  end
247
271
  end
248
272
  end
249
273
  ```
250
-
251
- However, it's even better to design your processing code in such a way that
252
- it's easier to swap out in tests. In your *application* code you could extract
253
- processing into a single `#call`-able object, and register it inside uploader
254
- generic `opts` hash.
255
-
256
274
  ```rb
257
- class ImageUploader < Shrine
258
- opts[:processor] = ImageThumbnailsGenerator
259
-
260
- process(:store) do |io, context|
261
- opts[:processor].call(io, context)
262
- end
275
+ TestMode.disable_processing(Photo.image_attacher) do
276
+ photo = Photo.new
277
+ photo.file = File.open("test/files/image.png", "rb")
278
+ photo.save
263
279
  end
264
280
  ```
265
281
 
266
- Now in your tests you can easily swap out `ImageThumbnailsGenerator` with
267
- "fake" processing, which just returns the result in correct format (single file
268
- or hash of versions). Since the only requirement of the processor is that it
269
- responds to `#call`, we can just swap it out for a proc or a lambda:
282
+ ## Testing direct upload
283
+
284
+ If you'd like to unit-test direct upload on the server side, you can
285
+ emulate it by uploading a file to `cache` and then assigning it to the record.
270
286
 
271
287
  ```rb
272
- ImageUploader.opts[:processor] = proc do |io, context|
273
- # return unprocessed file(s)
274
- end
288
+ cached_file = Shrine.upload(some_file, :cache)
289
+ record.attachment = cached_file.to_json
275
290
  ```
276
291
 
277
- This also has the benefit of allowing you to test `ImageThumbnailsGenerator` in
278
- isolation.
279
-
280
292
  [DatabaseCleaner]: https://github.com/DatabaseCleaner/database_cleaner
281
- [shrine-memory]: https://github.com/shrinerb/shrine-memory
282
- [factory_bot]: https://github.com/thoughtbot/factory_bot
283
- [Capybara]: https://github.com/jnicklas/capybara
284
293
  [`#attach_file`]: http://www.rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Actions#attach_file-instance_method
285
- [Rack::Test]: https://github.com/brynary/rack-test
286
- [Rack::TestApp]: https://github.com/kwatch/rack-test_app
287
294
  [aws-sdk-ruby stubs]: http://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/ClientStubs.html
288
295
  [MinIO]: https://min.io/
296
+ [derivatives]: https://shrinerb.com/docs/plugins/derivatives