kithe 2.0.0.pre.alpha2 → 2.0.2

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -4
  3. data/app/indexing/kithe/indexable/record_index_updater.rb +1 -1
  4. data/app/jobs/kithe/create_derivatives_job.rb +2 -2
  5. data/app/models/kithe/asset.rb +82 -154
  6. data/app/models/kithe/asset/derivative_creator.rb +32 -62
  7. data/app/models/kithe/asset/derivative_definition.rb +12 -13
  8. data/app/models/kithe/asset/set_shrine_uploader.rb +64 -0
  9. data/app/models/kithe/collection.rb +0 -6
  10. data/app/models/kithe/model.rb +0 -21
  11. data/app/models/kithe/work.rb +0 -5
  12. data/app/uploaders/kithe/asset_uploader.rb +15 -78
  13. data/lib/kithe.rb +22 -20
  14. data/{app/models → lib}/kithe/config_base.rb +6 -1
  15. data/lib/kithe/engine.rb +14 -3
  16. data/lib/kithe/indexable_settings.rb +1 -1
  17. data/lib/kithe/patch_fx.rb +39 -0
  18. data/lib/kithe/version.rb +4 -1
  19. data/lib/shrine/plugins/kithe_checksum_signatures.rb +41 -0
  20. data/lib/shrine/plugins/kithe_controllable_backgrounding.rb +53 -0
  21. data/lib/shrine/plugins/kithe_derivative_definitions.rb +101 -0
  22. data/lib/shrine/plugins/kithe_derivatives.rb +54 -0
  23. data/lib/shrine/plugins/kithe_determine_mime_type.rb +39 -0
  24. data/lib/shrine/plugins/kithe_persisted_derivatives.rb +161 -0
  25. data/lib/shrine/plugins/kithe_promotion_callbacks.rb +4 -0
  26. data/lib/shrine/plugins/kithe_promotion_directives.rb +33 -3
  27. data/lib/shrine/plugins/kithe_storage_location.rb +53 -4
  28. data/lib/tasks/kithe_tasks.rake +22 -15
  29. data/spec/dummy/app/models/plain_active_record.rb +3 -0
  30. data/spec/dummy/config/database.yml +6 -0
  31. data/spec/dummy/db/schema.rb +102 -0
  32. data/spec/dummy/log/development.log +3616 -0
  33. data/spec/dummy/log/test.log +86464 -0
  34. data/spec/dummy/tmp/development_secret.txt +1 -1
  35. data/spec/indexing/indexable_spec.rb +1 -1
  36. data/spec/models/kithe/asset/asset_derivatives_spec.rb +137 -0
  37. data/spec/models/kithe/asset/asset_promotion_hooks_spec.rb +26 -5
  38. data/spec/models/kithe/asset/set_shrine_uploader_spec.rb +39 -0
  39. data/spec/models/kithe/asset_spec.rb +9 -59
  40. data/spec/models/kithe/model_spec.rb +0 -32
  41. data/spec/models/kithe_spec.rb +10 -0
  42. data/spec/shrine/kithe_accept_remote_url_spec.rb +49 -0
  43. data/spec/shrine/kithe_checksum_signatures_spec.rb +63 -0
  44. data/spec/shrine/kithe_derivative_definitions_spec.rb +303 -0
  45. data/spec/shrine/kithe_persisted_derivatives_spec.rb +424 -0
  46. data/spec/shrine/kithe_storage_location_spec.rb +43 -15
  47. data/spec/spec_helper.rb +0 -19
  48. data/spec/test_support/images/3x3_pixel.jpg +0 -0
  49. data/spec/test_support/shrine_spec_support.rb +2 -1
  50. metadata +60 -36
  51. data/app/models/kithe/asset/derivative_updater.rb +0 -119
  52. data/app/models/kithe/derivative.rb +0 -15
  53. data/app/uploaders/kithe/derivative_uploader.rb +0 -48
  54. data/spec/dummy/db/structure.sql +0 -309
  55. data/spec/models/kithe/asset/asset_create_derivatives_spec.rb +0 -320
  56. data/spec/models/kithe/derivative_spec.rb +0 -168
@@ -0,0 +1,63 @@
1
+ require 'rails_helper'
2
+ require 'shrine/plugins/kithe_checksum_signatures'
3
+
4
+ # This kithe plugin is optional, let's make sure it works how we expect
5
+ describe Shrine::Plugins::KitheChecksumSignatures, queue_adpater: :inline do
6
+ temporary_class("ChecksumUploader") do
7
+ Class.new(Kithe::AssetUploader) do
8
+ plugin :kithe_checksum_signatures
9
+ end
10
+ end
11
+
12
+ temporary_class("ChecksumAsset") do
13
+ Class.new(Kithe::Asset) do
14
+ set_shrine_uploader(ChecksumUploader)
15
+ end
16
+ end
17
+
18
+ around do |example|
19
+ original = Kithe::Asset.promotion_directives
20
+ Kithe::Asset.promotion_directives = { promote: :inline }
21
+
22
+ example.run
23
+
24
+ Kithe::Asset.promotion_directives = original
25
+ end
26
+
27
+ it "provides checksum metadata after promotion" do
28
+ asset = ChecksumAsset.create!(title: "test", file: StringIO.new("test"))
29
+ asset.reload
30
+
31
+ expect(asset).to be_stored
32
+ expect(asset.file.metadata.slice("md5", "sha1", "sha512")).to all(be_present)
33
+ end
34
+
35
+ describe "without promotion" do
36
+ around do |example|
37
+ original = Kithe::Asset.promotion_directives
38
+ Kithe::Asset.promotion_directives = { promote: false }
39
+
40
+ example.run
41
+
42
+ Kithe::Asset.promotion_directives = original
43
+ end
44
+
45
+ it "does not extract checksum metadata on cache" do
46
+ asset = ChecksumAsset.create!(title: "test", file: StringIO.new("test"))
47
+ asset.reload
48
+
49
+ expect(asset).not_to be_stored
50
+ expect(asset.file.metadata.slice("md5", "sha1", "sha512")).not_to include(be_present)
51
+ end
52
+ end
53
+
54
+ describe "derivatives" do
55
+ it "do not get checksum metadata" do
56
+ asset = ChecksumAsset.create!(title: "test", file: StringIO.new("test"))
57
+ asset.update_derivative("test", StringIO.new("test deriv"))
58
+
59
+ expect(asset.file_derivatives[:test]).to be_present
60
+ expect(asset.file_derivatives[:test].metadata.slice("md5", "sha1", "sha512")).not_to include(be_present)
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,303 @@
1
+ require 'rails_helper'
2
+
3
+ # We just test with a Kithe::Asset class, too much trouble to try to isolate, not
4
+ # worth it I think.
5
+ describe "Shrine::Plugins::KitheDerivativeDefinitions", queue_adapter: :test do
6
+ # promotion inline, disable auto derivatives
7
+ around do |example|
8
+ original = Kithe::Asset.promotion_directives
9
+ Kithe::Asset.promotion_directives = { promote: :inline, create_derivatives: false }
10
+
11
+ example.run
12
+ Kithe::Asset.promotion_directives = original
13
+ end
14
+
15
+ let(:a_jpg_deriv_file) { Kithe::Engine.root.join("spec/test_support/images/2x2_pixel.jpg") }
16
+
17
+ temporary_class("CustomUploader") do
18
+ deriv_src_path = a_jpg_deriv_file
19
+
20
+ Class.new(Kithe::AssetUploader) do
21
+ self::Attacher.define_derivative(:some_data) do |original_file|
22
+ StringIO.new("some one data")
23
+ end
24
+
25
+ self::Attacher.define_derivative(:a_jpg) do |original_file|
26
+ FileUtils.cp(deriv_src_path,
27
+ Kithe::Engine.root.join("spec/test_support/images/2x2_pixel-TEMP.jpg"))
28
+
29
+ File.open(Kithe::Engine.root.join("spec/test_support/images/2x2_pixel-TEMP.jpg"))
30
+ end
31
+ end
32
+ end
33
+
34
+ temporary_class("CustomAsset") do
35
+ Class.new(Kithe::Asset) do
36
+ set_shrine_uploader(CustomUploader)
37
+ end
38
+ end
39
+
40
+ let(:asset) do
41
+ CustomAsset.create(title: "test",
42
+ file: File.open(Kithe::Engine.root.join("spec/test_support/images/1x1_pixel.jpg"))
43
+ )
44
+ end
45
+
46
+ it "normalizes string key to symbol" do
47
+ CustomUploader::Attacher.define_derivative("started_string") { |io| }
48
+ expect(CustomUploader::Attacher.kithe_derivative_definitions.collect(&:key)).to include(:started_string)
49
+ end
50
+
51
+ it "builds derivatives" do
52
+ asset.file_attacher.create_derivatives(:kithe_derivatives)
53
+
54
+ one_deriv = asset.file_derivatives[:some_data]
55
+ expect(one_deriv).to be_present
56
+ expect(one_deriv.read).to eq("some one data")
57
+
58
+ jpg_deriv = asset.file_derivatives[:a_jpg]
59
+ expect(jpg_deriv.read).to eq(File.binread(a_jpg_deriv_file))
60
+ expect(jpg_deriv.storage_key).to eq(:kithe_derivatives)
61
+ end
62
+
63
+ describe "attacher argument to block" do
64
+ let(:monitoring_proc) do
65
+ proc do |original_file, attacher:|
66
+ expect(original_file.kind_of?(File) || original_file.kind_of?(Tempfile)).to be(true)
67
+ expect(original_file.path).to be_present
68
+ expect(original_file.read).to eq(asset.file.read)
69
+
70
+ expect(attacher).to be_present
71
+ expect(attacher).to be_kind_of(Shrine::Attacher)
72
+ expect(attacher.record).to eq(asset)
73
+ expect(attacher.file).to be_kind_of(Shrine::UploadedFile)
74
+ nil
75
+ end
76
+ end
77
+
78
+ before do
79
+ # hacky confusing way to set this up for testing, sorry.
80
+ CustomUploader::Attacher.kithe_derivative_definitions = []
81
+ CustomUploader::Attacher.define_derivative(:some_data, &monitoring_proc)
82
+ end
83
+
84
+ it "as expected" do
85
+ expect(monitoring_proc).to receive(:call).and_call_original
86
+
87
+ asset.file_attacher.create_derivatives(:kithe_derivatives)
88
+ expect(asset.file_derivatives.length).to eq(0)
89
+ end
90
+
91
+ describe "as **kwargs" do
92
+ let(:monitoring_proc) do
93
+ proc do |original_file, **kwargs|
94
+ expect(original_file.kind_of?(File) || original_file.kind_of?(Tempfile)).to be(true)
95
+ expect(original_file.path).to be_present
96
+ expect(original_file.read).to eq(asset.file.read)
97
+
98
+ # It really has to be a local file with a path, not just an IO.
99
+ # Kithe wants to guarantee this, whether or not shrine does.
100
+ expect(original_file).to respond_to(:path)
101
+
102
+ expect(kwargs[:attacher]).to be_present
103
+ expect(kwargs[:attacher]).to eq(asset.file_attacher)
104
+
105
+ nil
106
+ end
107
+ end
108
+
109
+ it "as expected" do
110
+ expect(monitoring_proc).to receive(:call).and_call_original
111
+
112
+ asset.file_attacher.create_derivatives(:kithe_derivatives)
113
+ expect(asset.file_derivatives.length).to eq(0)
114
+ end
115
+ end
116
+ end
117
+
118
+
119
+ describe "default_create false" do
120
+ let(:monitoring_proc) { proc { |asset| } }
121
+
122
+ before do
123
+ # hacky confusing way to set this up for testing, sorry.
124
+ CustomUploader::Attacher.kithe_derivative_definitions = []
125
+ CustomUploader::Attacher.define_derivative(:some_data, default_create: false, &monitoring_proc)
126
+ end
127
+
128
+ it "is not run automatically" do
129
+ expect(monitoring_proc).not_to receive(:call)
130
+ asset.file_attacher.create_derivatives(:kithe_derivatives)
131
+ end
132
+ end
133
+
134
+ describe "only/except" do
135
+ let(:monitoring_proc1) { proc { |asset| StringIO.new("one") } }
136
+ let(:monitoring_proc2) { proc { |asset| StringIO.new("two") } }
137
+ let(:monitoring_proc3) { proc { |asset| StringIO.new("three") } }
138
+
139
+ before do
140
+ # hacky confusing way to set this up for testing, sorry.
141
+ CustomUploader::Attacher.kithe_derivative_definitions = []
142
+ CustomUploader::Attacher.define_derivative(:one, default_create: false, &monitoring_proc1)
143
+ CustomUploader::Attacher.define_derivative(:two, &monitoring_proc2)
144
+ CustomUploader::Attacher.define_derivative(:three, &monitoring_proc2)
145
+ end
146
+
147
+ it "can call with only" do
148
+ expect(monitoring_proc1).to receive(:call).and_call_original
149
+ expect(monitoring_proc2).to receive(:call).and_call_original
150
+ expect(monitoring_proc3).not_to receive(:call)
151
+
152
+ # except string or symbol
153
+ asset.file_attacher.create_derivatives(:kithe_derivatives, only: [:one, "two"])
154
+
155
+ expect(asset.file_derivatives.keys).to match([:one, :two])
156
+ end
157
+
158
+ it "can call with except" do
159
+ expect(monitoring_proc1).not_to receive(:call)
160
+ expect(monitoring_proc2).to receive(:call).and_call_original
161
+ expect(monitoring_proc3).not_to receive(:call)
162
+
163
+ asset.file_attacher.create_derivatives(:kithe_derivatives, except: [:three])
164
+
165
+ # :one was default_create:false, and we said except: :three
166
+ expect(asset.file_derivatives.keys).to eq([:two])
167
+ end
168
+
169
+ it "can call with except as string" do
170
+ expect(monitoring_proc1).not_to receive(:call)
171
+ expect(monitoring_proc2).to receive(:call).and_call_original
172
+ expect(monitoring_proc3).not_to receive(:call)
173
+
174
+ asset.file_attacher.create_derivatives(:kithe_derivatives, except: ["three"])
175
+
176
+ # :one was default_create:false, and we said except: :three
177
+ expect(asset.file_derivatives.keys).to eq([:two])
178
+ end
179
+
180
+ it "can call with only and except" do
181
+ expect(monitoring_proc1).to receive(:call).and_call_original
182
+ expect(monitoring_proc2).to receive(:call).and_call_original
183
+ expect(monitoring_proc3).not_to receive(:call)
184
+
185
+ # string version of except why not
186
+ asset.file_attacher.create_derivatives(:kithe_derivatives, only: [:one, :two], except: :three)
187
+
188
+ expect(asset.file_derivatives.keys).to eq([:one, :two])
189
+ end
190
+ end
191
+
192
+
193
+ describe "content_type filters" do
194
+ before do
195
+ # hacky confusing way to set this up for testing, sorry.
196
+ CustomUploader::Attacher.kithe_derivative_definitions = []
197
+ CustomUploader::Attacher.define_derivative(:never_called, content_type: "nothing/nothing") { |o| StringIO.new("never") }
198
+ CustomUploader::Attacher.define_derivative(:gated_positive, content_type: "image/jpeg") { |o| StringIO.new("gated positive") }
199
+ CustomUploader::Attacher.define_derivative(:gated_positive_main_type, content_type: "image") { |o| StringIO.new("gated positive") }
200
+ end
201
+
202
+ it "does not call if content type does not match" do
203
+ asset.file_attacher.create_derivatives(:kithe_derivatives)
204
+ expect(asset.file_derivatives.keys).not_to include(:never_called)
205
+ end
206
+
207
+ it "calls for exact content type match" do
208
+ asset.file_attacher.create_derivatives(:kithe_derivatives)
209
+ expect(asset.file_derivatives.keys).to include(:gated_positive)
210
+ end
211
+
212
+ it "calls for main content type match" do
213
+ asset.file_attacher.create_derivatives(:kithe_derivatives)
214
+ expect(asset.file_derivatives.keys).to include(:gated_positive_main_type)
215
+ end
216
+
217
+ describe "as array" do
218
+ before do
219
+ # hacky confusing way to set this up for testing, sorry.
220
+ CustomUploader::Attacher.kithe_derivative_definitions = []
221
+ CustomUploader::Attacher.define_derivative(:never_called, content_type: ["nothing/nothing", "also/nothing"]) { |o| StringIO.new("never") }
222
+ CustomUploader::Attacher.define_derivative(:gated_positive, content_type: ["image/jpeg", "something/else"]) { |o| StringIO.new("gated positive") }
223
+ end
224
+
225
+ it "calls for one match" do
226
+ asset.file_attacher.create_derivatives(:kithe_derivatives)
227
+ expect(asset.file_derivatives.keys).to eq([:gated_positive])
228
+ end
229
+ end
230
+
231
+ describe "conflicting types" do
232
+ let(:unfiltered) { proc { |asset| StringIO.new("unfiltered") } }
233
+ let(:image) { proc { |asset| StringIO.new("image") } }
234
+ let(:image_jpeg) { proc { |asset| StringIO.new("image/jpeg") } }
235
+
236
+ before do
237
+ # hacky confusing way to set this up for testing, sorry.
238
+ CustomUploader::Attacher.kithe_derivative_definitions = []
239
+ CustomUploader::Attacher.define_derivative(:key, &unfiltered)
240
+ CustomUploader::Attacher.define_derivative(:key, content_type: "image/jpeg", &image_jpeg)
241
+ CustomUploader::Attacher.define_derivative(:key, content_type: "image", &image)
242
+ end
243
+
244
+ it "takes most specific" do
245
+ expect(unfiltered).not_to receive(:call)
246
+ expect(image).not_to receive(:call)
247
+ expect(image_jpeg).to receive(:call).and_call_original
248
+
249
+ asset.file_attacher.create_derivatives(:kithe_derivatives)
250
+ expect(asset.file_derivatives.count).to eq(1)
251
+
252
+
253
+ expect(asset.file_derivatives.keys).to eq([:key])
254
+ expect(asset.file_derivatives[:key].read). to eq("image/jpeg")
255
+ end
256
+ end
257
+ end
258
+
259
+ describe "lazy creation" do
260
+ before do
261
+ # Create existing derivatives for existing definitions, which we assume exist
262
+ expect(CustomUploader::Attacher.kithe_derivative_definitions).to be_present
263
+
264
+ CustomUploader::Attacher.kithe_derivative_definitions.collect(&:key).each do |key|
265
+ asset.file_attacher.add_persisted_derivatives({key => StringIO.new("#{key} original")})
266
+ end
267
+ end
268
+
269
+ it "does not re-create" do
270
+ derivatives_pre_creation = asset.file_derivatives
271
+
272
+ asset.file_attacher.create_derivatives(:kithe_derivatives, lazy: true)
273
+ derivatives_post_creation = asset.file_derivatives
274
+
275
+ expect(derivatives_post_creation).to eq(derivatives_pre_creation)
276
+ end
277
+ end
278
+
279
+ describe "#remove_derivative_definition!" do
280
+ let(:defined_derivative_key) do
281
+ CustomUploader::Attacher.defined_derivative_keys.first.tap do |key|
282
+ expect(key).to be_present
283
+ end
284
+ end
285
+
286
+ it "can remove by string" do
287
+ expect {
288
+ CustomUploader::Attacher.remove_derivative_definition!(defined_derivative_key.to_s)
289
+ }.to change { CustomUploader::Attacher.defined_derivative_keys.count }.from(2).to(1)
290
+ end
291
+
292
+ it "can remove by symbol" do
293
+ expect {
294
+ CustomUploader::Attacher.remove_derivative_definition!(defined_derivative_key.to_sym)
295
+ }.to change { CustomUploader::Attacher.defined_derivative_keys.count }.from(2).to(1)
296
+ end
297
+
298
+ it "can remove multiple args" do
299
+ CustomUploader::Attacher.remove_derivative_definition!(*CustomUploader::Attacher.defined_derivative_keys)
300
+ expect(CustomUploader::Attacher.defined_derivative_keys).to eq([])
301
+ end
302
+ end
303
+ end
@@ -0,0 +1,424 @@
1
+ require 'rails_helper'
2
+ require 'shrine/plugins/kithe_persisted_derivatives'
3
+
4
+ # We just test with a Kithe::Asset class, too much trouble to try to isolate, not
5
+ # worth it I think.
6
+ describe Shrine::Plugins::KithePersistedDerivatives, queue_adapter: :test do
7
+ # promotion inline, disable auto derivatives
8
+ around do |example|
9
+ original = Kithe::Asset.promotion_directives
10
+ Kithe::Asset.promotion_directives = { promote: :inline, create_derivatives: false }
11
+
12
+ example.run
13
+ Kithe::Asset.promotion_directives = original
14
+ end
15
+
16
+ # Need to make a new copy, because shrine likes deleting derivatives!
17
+ def sample_deriv_file!(path = Kithe::Engine.root.join("spec/test_support/images/1x1_pixel.jpg"))
18
+ tempfile = Tempfile.new
19
+ IO.copy_stream(File.open(path), tempfile)
20
+
21
+ tempfile
22
+ end
23
+
24
+ let(:sample_orig_path) { Kithe::Engine.root.join("spec/test_support/images/2x2_pixel.jpg") }
25
+ let(:another_sample_orig_path) { Kithe::Engine.root.join("spec/test_support/images/1x1_pixel.jpg") }
26
+
27
+ let(:asset) { Kithe::Asset.create!(title: "test", file: File.open(sample_orig_path))}
28
+
29
+ describe "#add_persisted_derivatives" do
30
+ it "can add and persist" do
31
+ asset.file_attacher.add_persisted_derivatives(sample: sample_deriv_file!)
32
+
33
+ expect(asset.changed?).to be(false)
34
+ expect(asset.file_derivatives[:sample]).to be_present
35
+ expect(asset.file_derivatives[:sample].storage_key).to eq(:kithe_derivatives)
36
+ expect(asset.file_derivatives[:sample].exists?).to be(true)
37
+
38
+ # sanity check
39
+ asset.reload
40
+ expect(asset.file_derivatives[:sample]).to be_present
41
+ end
42
+
43
+ it "can avoid deleting deriv file" do
44
+ file = sample_deriv_file!
45
+ asset.file_attacher.add_persisted_derivatives({sample: file}, delete: false)
46
+
47
+ expect(File.exists?(file.path)).to eq(true)
48
+ end
49
+
50
+ it "can use custom storage" do
51
+ file = sample_deriv_file!
52
+ asset.file_attacher.add_persisted_derivatives({sample: file}, storage: :cache)
53
+
54
+ asset.reload
55
+ expect(asset.file_derivatives[:sample]).to be_present
56
+ expect(asset.file_derivatives[:sample].storage_key).to eq(:cache)
57
+ end
58
+
59
+ it "can supply custom metadata" do
60
+ file = sample_deriv_file!
61
+ asset.file_attacher.add_persisted_derivatives({sample: file}, metadata: { extra: "value" })
62
+
63
+ asset.reload
64
+
65
+ expect(asset.file_derivatives[:sample].metadata["extra"]).to eq("value")
66
+ # and still has default metadata
67
+ expect(asset.file_derivatives[:sample].metadata["size"]).to be_present
68
+ end
69
+
70
+ describe "model with unsaved changes" do
71
+ before do
72
+ asset.title = "changed title"
73
+ end
74
+
75
+ it "will refuse" do
76
+ expect {
77
+ asset.file_attacher.add_persisted_derivatives(sample: sample_deriv_file!)
78
+ }.to raise_error(TypeError)
79
+ end
80
+
81
+ it "can be forced" do
82
+ asset.file_attacher.add_persisted_derivatives({sample: sample_deriv_file!}, allow_other_changes: true)
83
+
84
+ expect(asset.changed?).to be(false)
85
+ asset.reload
86
+ expect(asset.title).to eq("changed title")
87
+ expect(asset.file_derivatives[:sample]).to be_present
88
+ end
89
+ end
90
+
91
+ describe "unsaved model" do
92
+ let(:asset) { Kithe::Asset.new(title: "test", file: File.open(sample_orig_path)) }
93
+
94
+ it "refuses even with other_changes: true" do
95
+ expect {
96
+ asset.file_attacher.add_persisted_derivatives({sample: sample_deriv_file!}, allow_other_changes: true)
97
+ }.to raise_error(TypeError)
98
+ end
99
+
100
+ describe "with an existing derivative" do
101
+ before do
102
+ asset.save!
103
+ asset.file_attacher.add_derivative("key", StringIO.new("existing"))
104
+ asset.save!
105
+ end
106
+
107
+ it "replaces" do
108
+ existing_storage = asset.file_derivatives[:key]
109
+ expect(existing_storage.exists?).to be(true)
110
+
111
+ asset.file_attacher.add_persisted_derivatives({"key" => StringIO.new("new")})
112
+ expect(asset.file_derivatives[:key].read).to eq("new")
113
+ expect(existing_storage.exists?).to be(false)
114
+ end
115
+ end
116
+ end
117
+
118
+ describe "Original deleted before derivatives can be created" do
119
+ before do
120
+ # delete it out from under us
121
+ Kithe::Model.where(id: asset.id).delete_all
122
+ end
123
+
124
+ it "doesn't complain and cleans up file" do
125
+
126
+ # asset is no longer in the DB.
127
+ # Let's try and create derivatives for it:
128
+ file = sample_deriv_file!
129
+ local_file_path = file.path
130
+
131
+ expect(asset.file_attacher.add_persisted_derivatives(sample: file)).to be(false)
132
+
133
+ expect(File.exist?(local_file_path)).to be(false)
134
+ end
135
+ end
136
+
137
+ describe "Original changed before derivatives can be created" do
138
+ before do
139
+ asset # load
140
+
141
+ # Change what's in the db for this asset, without changing the in-memory
142
+ # asset
143
+ another_copy = Kithe::Asset.find(asset.id)
144
+ another_copy.file = File.open(another_sample_orig_path)
145
+ another_copy.save!
146
+ end
147
+
148
+ it "doesn't add derivative and cleans up file" do
149
+ # asset in DB has different file attachment
150
+ # Let's try and create derivatives for it:
151
+ file = sample_deriv_file!
152
+ local_file_path = file.path
153
+
154
+ expect(asset.file_attacher.add_persisted_derivatives(new_try: file)).to be(false)
155
+
156
+
157
+ expect(asset.changed?).to be(false)
158
+ expect(asset.file_derivatives[:new_try]).to be_nil
159
+ asset.reload
160
+ expect(asset.file_derivatives[:new_try]).to be_nil
161
+
162
+ expect(File.exist?(local_file_path)).to be(false)
163
+ end
164
+ end
165
+
166
+ describe "concurrent derivative changes" do
167
+ let(:original_one) { sample_deriv_file!(Kithe::Engine.root.join("spec/test_support/images/1x1_pixel.jpg")) }
168
+ let(:original_two) { sample_deriv_file!(Kithe::Engine.root.join("spec/test_support/images/2x2_pixel.jpg")) }
169
+
170
+ let(:new_two) { sample_deriv_file!(Kithe::Engine.root.join("spec/test_support/images/3x3_pixel.jpg")) }
171
+ let(:new_three) { sample_deriv_file!(Kithe::Engine.root.join("spec/test_support/images/3x3_pixel.jpg")) }
172
+
173
+ before do
174
+ # make sure our test is setup to test what we want
175
+ original_two.rewind; new_two.rewind
176
+ expect(original_two.read).not_to eq(new_two.read)
177
+ original_two.rewind; new_two.rewind
178
+
179
+ asset # load
180
+
181
+ # change what derivatives are in db for this asset, without changing
182
+ # the in-memory asset
183
+ another_copy = Kithe::Asset.find(asset.id)
184
+ another_copy.file_attacher.add_derivatives({
185
+ one: original_one,
186
+ two: original_two
187
+ }, delete: false)
188
+ another_copy.save!
189
+
190
+ # Make sure we set up what we expected
191
+ another_copy.reload
192
+ expect(another_copy.file_derivatives[:one]).to be_present
193
+ expect(another_copy.file_derivatives[:two]).to be_present
194
+ end
195
+
196
+ it "merges changes in safely" do
197
+ expect(asset.file_derivatives.keys).to be_empty
198
+
199
+ asset.file_attacher.add_persisted_derivatives({two: new_two, three: new_three}, delete: false)
200
+
201
+ expect(asset.changed?).to be(false)
202
+ expect(asset.file_derivatives.keys).to match([:one, :two, :three])
203
+ expect(asset.file_derivatives[:two].read).to eq(File.binread(new_two.path))
204
+ expect(asset.file_derivatives[:three].read).to eq(File.binread(new_three.path))
205
+ end
206
+ end
207
+ end
208
+
209
+ describe "#create_persisted_derivatives" do
210
+ temporary_class("AssetSubclassUploader") do
211
+ call_fakeio = method(:fakeio) # weird closure issue
212
+
213
+ Class.new(Kithe::AssetUploader) do
214
+ self::Attacher.derivatives do |original, **options|
215
+ {
216
+ one: call_fakeio.("one"),
217
+ original_reflected: call_fakeio.(original.read)
218
+ }
219
+ end
220
+
221
+ self::Attacher.derivatives(:options) do |original, **options|
222
+ {
223
+ options_one: call_fakeio.("one"),
224
+ options_reflected: call_fakeio.(options.to_s)
225
+ }
226
+ end
227
+ end
228
+ end
229
+
230
+ temporary_class("AssetSubclass") do
231
+ Class.new(Kithe::Asset) do
232
+ set_shrine_uploader(AssetSubclassUploader)
233
+ end
234
+ end
235
+
236
+ let(:asset) { AssetSubclass.create!(title: "test", file: File.open(sample_orig_path))}
237
+
238
+ it "creates derivatives" do
239
+ # we're not gonna test concurrency safety, counting on add_persisted_derivatives
240
+ # for that, but let's make sure
241
+ expect(asset.file_attacher).to receive(:add_persisted_derivatives).and_call_original
242
+
243
+ asset.file_attacher.create_persisted_derivatives
244
+
245
+ expect(asset.changed?).to be(false)
246
+ expect(asset.file_derivatives.keys).to match([:one, :original_reflected])
247
+ asset.reload
248
+ expect(asset.file_derivatives[:one].read).to eq("one")
249
+ expect(asset.file_derivatives[:one].storage_key).to eq(:kithe_derivatives)
250
+ expect(asset.file_derivatives[:original_reflected].read).to eq(File.binread(sample_orig_path))
251
+ expect(asset.file_derivatives[:original_reflected].storage_key).to eq(:kithe_derivatives)
252
+ end
253
+
254
+ describe "with custom source" do
255
+ let(:string_io_source) { StringIO.new("pretend data") }
256
+
257
+ it "works with custom source as StringIO" do
258
+ asset.file_attacher.create_persisted_derivatives(:options, string_io_source)
259
+ expect(asset.file_derivatives).to be_present
260
+ end
261
+
262
+ it "works with custom source StringIO as only arg" do
263
+ asset.file_attacher.create_persisted_derivatives(string_io_source)
264
+
265
+ string_io_source.rewind
266
+ expect(asset.file_derivatives[:original_reflected].read).to eq(string_io_source.read)
267
+ end
268
+ end
269
+
270
+ describe "for an asset with nil file_data" do
271
+ let(:asset) { AssetSubclass.create!(title: "blank") }
272
+
273
+ it "no-ops" do
274
+ expect(asset.file_attacher.create_persisted_derivatives).to be(false)
275
+ end
276
+ end
277
+
278
+ describe "for an asset with missing original" do
279
+ let(:asset) { AssetSubclass.create!(title: "blank", file_data: { storage: "cache", id: "not_found"}) }
280
+
281
+ it "raises" do
282
+ expect(asset.file.exists?).to be(false)
283
+
284
+ # Not necessarily what kithe 1.x did, but this seems better...
285
+ expect {
286
+ asset.file_attacher.create_persisted_derivatives
287
+ }.to raise_error(Shrine::FileNotFound)
288
+ end
289
+ end
290
+
291
+ it "can call custom processor" do
292
+ asset.file_attacher.create_persisted_derivatives(:options)
293
+
294
+ expect(asset.file_derivatives.keys).to match([:options_one, :options_reflected])
295
+ end
296
+
297
+ it "can pass processor options" do
298
+ asset.file_attacher.create_persisted_derivatives(:options, arg1: "value1", arg2: "value2")
299
+
300
+ expect(asset.file_derivatives[:options_reflected].read).to eq({arg1: "value1", arg2: "value2"}.to_s)
301
+ end
302
+
303
+ it "can customize :storage" do
304
+ asset.file_attacher.create_persisted_derivatives(:options, storage: :cache, arg1: "value1", arg2: "value2")
305
+
306
+ expect(asset.file_derivatives[:options_reflected].read).to eq({arg1: "value1", arg2: "value2"}.to_s)
307
+ expect(asset.file_derivatives[:options_reflected].storage_key).to eq(:cache)
308
+ end
309
+
310
+ describe "model with unsaved changes" do
311
+ before do
312
+ asset.title = "changed title"
313
+ end
314
+
315
+ it "will refuse" do
316
+ expect {
317
+ asset.file_attacher.create_persisted_derivatives
318
+ }.to raise_error(TypeError)
319
+ end
320
+
321
+ it "can be forced" do
322
+ asset.file_attacher.create_persisted_derivatives(allow_other_changes: true)
323
+
324
+ expect(asset.changed?).to be(false)
325
+ asset.reload
326
+ expect(asset.title).to eq("changed title")
327
+ expect(asset.file_derivatives.keys).to match([:one, :original_reflected])
328
+ end
329
+ end
330
+ end
331
+
332
+ describe "#remove_persisted_derivatives" do
333
+ before do
334
+ asset.file_attacher.add_persisted_derivatives(
335
+ sample1: fakeio("sample 1"),
336
+ sample2: fakeio("sample 2")
337
+ )
338
+ end
339
+
340
+ it "can remove" do
341
+ removed = asset.file_attacher.remove_persisted_derivatives(:sample1)
342
+
343
+ expect(asset.changed?).to eq(false)
344
+ expect(asset.file_derivatives.keys).to eq([:sample2])
345
+ asset.reload
346
+ expect(asset.file_derivatives.keys).to eq([:sample2])
347
+
348
+ expect(removed).to be_kind_of(Array)
349
+ expect(removed.length).to eq(1)
350
+ expect(removed.first).to be_kind_of(Shrine::UploadedFile)
351
+ expect(removed.first.exists?).to be(false)
352
+ end
353
+
354
+ describe "if someone else removed first" do
355
+ before do
356
+ another_copy = asset.class.find(asset.id)
357
+ another_copy.file_attacher.remove_derivative(:sample1)
358
+ another_copy.save!
359
+ another_copy.reload
360
+ expect(another_copy.file_derivatives.keys).to eq([:sample2])
361
+
362
+ expect(asset.file_derivatives.keys).to match([:sample1, :sample2])
363
+ end
364
+
365
+ it "doesn't complain" do
366
+ asset.file_attacher.remove_persisted_derivatives(:sample1)
367
+ expect(asset.changed?).to eq(false)
368
+ expect(asset.file_derivatives.keys).to eq([:sample2])
369
+ end
370
+ end
371
+
372
+ describe "someone else added another derivative" do
373
+ before do
374
+ another_copy = asset.class.find(asset.id)
375
+ another_copy.file_attacher.add_persisted_derivatives({:sample3 => fakeio("sample 3")})
376
+ another_copy.reload
377
+ expect(another_copy.file_derivatives.keys).to match([:sample1, :sample2, :sample3])
378
+
379
+ expect(asset.file_derivatives.keys).to match([:sample1, :sample2])
380
+ end
381
+
382
+ it "deletes without deleting newly added" do
383
+ asset.file_attacher.remove_persisted_derivatives(:sample1)
384
+
385
+ expect(asset.changed?).to eq(false)
386
+ expect(asset.file_derivatives.keys).to eq([:sample2, :sample3])
387
+ end
388
+ end
389
+
390
+ describe "model deleted from under us" do
391
+ before do
392
+ another_copy = asset.class.find(asset.id)
393
+ another_copy.destroy!
394
+ end
395
+
396
+ it "silently no-ops" do
397
+ result = asset.file_attacher.remove_persisted_derivatives(:sample1)
398
+ expect(result).to eq(false)
399
+ end
400
+ end
401
+
402
+ describe "model with unsaved changes" do
403
+ before do
404
+ asset.title = "changed title"
405
+ end
406
+
407
+ it "will refuse" do
408
+ expect {
409
+ asset.file_attacher.remove_persisted_derivatives(:sample1)
410
+ }.to raise_error(TypeError)
411
+ end
412
+
413
+ it "can be forced" do
414
+ asset.file_attacher.remove_persisted_derivatives(:sample1, allow_other_changes: true)
415
+
416
+ expect(asset.changed?).to be(false)
417
+ asset.reload
418
+ expect(asset.title).to eq("changed title")
419
+ expect(asset.file_derivatives.keys).to eq([:sample2])
420
+ end
421
+ end
422
+
423
+ end
424
+ end