kithe 2.0.0.pre.alpha2 → 2.0.0.pre.beta1
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.
- checksums.yaml +4 -4
- data/app/jobs/kithe/create_derivatives_job.rb +2 -2
- data/app/models/kithe/asset.rb +82 -154
- data/app/models/kithe/asset/derivative_creator.rb +32 -62
- data/app/models/kithe/asset/derivative_definition.rb +12 -13
- data/app/models/kithe/asset/set_shrine_uploader.rb +64 -0
- data/app/models/kithe/collection.rb +0 -6
- data/app/models/kithe/model.rb +0 -21
- data/app/models/kithe/work.rb +0 -5
- data/app/uploaders/kithe/asset_uploader.rb +15 -78
- data/lib/kithe/version.rb +4 -1
- data/lib/shrine/plugins/kithe_checksum_signatures.rb +41 -0
- data/lib/shrine/plugins/kithe_controllable_backgrounding.rb +53 -0
- data/lib/shrine/plugins/kithe_derivative_definitions.rb +101 -0
- data/lib/shrine/plugins/kithe_derivatives.rb +54 -0
- data/lib/shrine/plugins/kithe_determine_mime_type.rb +39 -0
- data/lib/shrine/plugins/kithe_persisted_derivatives.rb +161 -0
- data/lib/shrine/plugins/kithe_promotion_callbacks.rb +4 -0
- data/lib/shrine/plugins/kithe_promotion_directives.rb +33 -3
- data/lib/shrine/plugins/kithe_storage_location.rb +53 -4
- data/lib/tasks/kithe_tasks.rake +22 -15
- data/spec/dummy/log/development.log +867 -0
- data/spec/dummy/log/test.log +26005 -0
- data/spec/models/kithe/asset/asset_derivatives_spec.rb +137 -0
- data/spec/models/kithe/asset/asset_promotion_hooks_spec.rb +26 -5
- data/spec/models/kithe/asset/set_shrine_uploader_spec.rb +39 -0
- data/spec/models/kithe/asset_spec.rb +9 -59
- data/spec/models/kithe/model_spec.rb +0 -32
- data/spec/shrine/kithe_accept_remote_url_spec.rb +49 -0
- data/spec/shrine/kithe_checksum_signatures_spec.rb +63 -0
- data/spec/shrine/kithe_derivative_definitions_spec.rb +303 -0
- data/spec/shrine/kithe_persisted_derivatives_spec.rb +424 -0
- data/spec/shrine/kithe_storage_location_spec.rb +43 -15
- data/spec/spec_helper.rb +7 -6
- data/spec/test_support/images/3x3_pixel.jpg +0 -0
- data/spec/test_support/shrine_spec_support.rb +2 -1
- metadata +23 -23
- data/app/models/kithe/asset/derivative_updater.rb +0 -119
- data/app/models/kithe/derivative.rb +0 -15
- data/app/uploaders/kithe/derivative_uploader.rb +0 -48
- data/spec/models/kithe/asset/asset_create_derivatives_spec.rb +0 -320
- data/spec/models/kithe/derivative_spec.rb +0 -168
@@ -4,29 +4,57 @@ require 'shrine/plugins/kithe_storage_location'
|
|
4
4
|
describe Shrine::Plugins::KitheStorageLocation do
|
5
5
|
let(:uploader) { test_uploader { plugin :kithe_storage_location } }
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
describe "for main file" do
|
8
|
+
it "uploads with a record with id" do
|
9
|
+
uploaded_file = uploader.upload(fakeio, record: OpenStruct.new(id: "81060886-4f93-42e7-ace7-ab51399f4808"), name: :file)
|
9
10
|
|
10
|
-
|
11
|
-
|
11
|
+
expect(uploaded_file.id).to match %r{\Aasset/81060886-4f93-42e7-ace7-ab51399f4808/[0-9a-f]+}
|
12
|
+
end
|
12
13
|
|
13
|
-
|
14
|
-
|
14
|
+
it "has suffix with a record id and filename" do
|
15
|
+
uploaded_file = uploader.upload(fakeio(filename: "foo.jpg"), record: OpenStruct.new(id: "81060886-4f93-42e7-ace7-ab51399f4808"), name: :file)
|
15
16
|
|
16
|
-
|
17
|
-
|
17
|
+
expect(uploaded_file.id).to match %r{\Aasset/81060886-4f93-42e7-ace7-ab51399f4808/[0-9a-f]+\.jpg}
|
18
|
+
end
|
18
19
|
|
19
|
-
|
20
|
-
|
20
|
+
it "uploads with no record" do
|
21
|
+
uploaded_file = uploader.upload(fakeio, record: OpenStruct.new(id: "81060886-4f93-42e7-ace7-ab51399f4808"), name: :file)
|
21
22
|
|
22
|
-
|
23
|
-
|
23
|
+
expect(uploaded_file.id).to match %r{\Aasset/[0-9a-f]+}
|
24
|
+
end
|
24
25
|
|
25
|
-
|
26
|
-
|
26
|
+
it "uploads with record with no id" do
|
27
|
+
uploaded_file = uploader.upload(fakeio, record: OpenStruct.new(), name: :file)
|
27
28
|
|
28
|
-
|
29
|
+
expect(uploaded_file.id).to match %r{\Aasset/[0-9a-f]+}
|
30
|
+
end
|
29
31
|
end
|
30
32
|
|
33
|
+
describe "for shrine derivatives" do
|
34
|
+
let(:image_path) { Kithe::Engine.root.join("spec/test_support/images/1x1_pixel.jpg") }
|
35
|
+
|
36
|
+
let(:uploader) do
|
37
|
+
test_uploader do
|
38
|
+
plugin :kithe_storage_location
|
39
|
+
end
|
40
|
+
end
|
31
41
|
|
42
|
+
it "uses good path for derivative" do
|
43
|
+
uploaded_file = uploader.upload(fakeio("foo.jpg"), derivative: :fixed, record: OpenStruct.new(id: "81060886-4f93-42e7-ace7-ab51399f4808"), name: :file)
|
44
|
+
|
45
|
+
expect(uploaded_file.id).to match %r{\A81060886-4f93-42e7-ace7-ab51399f4808/fixed/[0-9a-f]+}
|
46
|
+
end
|
47
|
+
|
48
|
+
it "raises with no record" do
|
49
|
+
expect {
|
50
|
+
uploader.upload(fakeio("foo.jpg"), record: nil, derivative: :fixed, name: :file)
|
51
|
+
}.to raise_error(TypeError)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "raises with record with no id" do
|
55
|
+
expect {
|
56
|
+
uploader.upload(fakeio("foo.jpg"), record: OpenStruct.new, derivative: :fixed, name: :file)
|
57
|
+
}.to raise_error(TypeError)
|
58
|
+
end
|
59
|
+
end
|
32
60
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -132,13 +132,14 @@ end
|
|
132
132
|
require 'sane_patch'
|
133
133
|
SanePatch.patch("shrine", "< 3.2.2") do
|
134
134
|
require 'shrine/storage/memory'
|
135
|
+
|
135
136
|
class Shrine::Storage::Memory
|
136
|
-
def open(id,
|
137
|
-
|
138
|
-
|
137
|
+
def open(id, **)
|
138
|
+
io = StringIO.new(store.fetch(id))
|
139
|
+
io.set_encoding(io.string.encoding) # Ruby 2.7.0 – https://bugs.ruby-lang.org/issues/16497
|
140
|
+
io
|
141
|
+
rescue KeyError
|
142
|
+
raise Shrine::FileNotFound, "file #{id.inspect} not found on storage"
|
139
143
|
end
|
140
144
|
end
|
141
145
|
end
|
142
|
-
|
143
|
-
|
144
|
-
|
Binary file
|
@@ -59,8 +59,9 @@ require "stringio"
|
|
59
59
|
class FakeIO
|
60
60
|
attr_reader :original_filename, :content_type
|
61
61
|
|
62
|
-
def initialize(content, filename: nil, content_type: nil)
|
62
|
+
def initialize(content, filename: nil, content_type: nil, encoding: "BINARY")
|
63
63
|
@io = StringIO.new(content)
|
64
|
+
@io.set_encoding(encoding, encoding) # weird ruby workaround
|
64
65
|
@original_filename = filename
|
65
66
|
@content_type = content_type
|
66
67
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kithe
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0.pre.
|
4
|
+
version: 2.0.0.pre.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Rochkind
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-05-
|
11
|
+
date: 2020-05-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -272,20 +272,6 @@ dependencies:
|
|
272
272
|
- - ">="
|
273
273
|
- !ruby/object:Gem::Version
|
274
274
|
version: '0'
|
275
|
-
- !ruby/object:Gem::Dependency
|
276
|
-
name: shrine-memory
|
277
|
-
requirement: !ruby/object:Gem::Requirement
|
278
|
-
requirements:
|
279
|
-
- - ">="
|
280
|
-
- !ruby/object:Gem::Version
|
281
|
-
version: '0'
|
282
|
-
type: :development
|
283
|
-
prerelease: false
|
284
|
-
version_requirements: !ruby/object:Gem::Requirement
|
285
|
-
requirements:
|
286
|
-
- - ">="
|
287
|
-
- !ruby/object:Gem::Version
|
288
|
-
version: '0'
|
289
275
|
- !ruby/object:Gem::Dependency
|
290
276
|
name: webmock
|
291
277
|
requirement: !ruby/object:Gem::Requirement
|
@@ -341,10 +327,9 @@ files:
|
|
341
327
|
- app/models/kithe/asset.rb
|
342
328
|
- app/models/kithe/asset/derivative_creator.rb
|
343
329
|
- app/models/kithe/asset/derivative_definition.rb
|
344
|
-
- app/models/kithe/asset/
|
330
|
+
- app/models/kithe/asset/set_shrine_uploader.rb
|
345
331
|
- app/models/kithe/collection.rb
|
346
332
|
- app/models/kithe/config_base.rb
|
347
|
-
- app/models/kithe/derivative.rb
|
348
333
|
- app/models/kithe/mediainfo_analyzer.rb
|
349
334
|
- app/models/kithe/model.rb
|
350
335
|
- app/models/kithe/model_contains.rb
|
@@ -355,7 +340,6 @@ files:
|
|
355
340
|
- app/simple_form_enhancements/kithe/form_builder.rb
|
356
341
|
- app/simple_form_enhancements/kithe/repeatable_input_generator.rb
|
357
342
|
- app/uploaders/kithe/asset_uploader.rb
|
358
|
-
- app/uploaders/kithe/derivative_uploader.rb
|
359
343
|
- app/validators/array_inclusion_validator.rb
|
360
344
|
- config/locales/en.yml
|
361
345
|
- config/routes.rb
|
@@ -375,7 +359,13 @@ files:
|
|
375
359
|
- lib/kithe/sti_preload.rb
|
376
360
|
- lib/kithe/version.rb
|
377
361
|
- lib/shrine/plugins/kithe_accept_remote_url.rb
|
362
|
+
- lib/shrine/plugins/kithe_checksum_signatures.rb
|
363
|
+
- lib/shrine/plugins/kithe_controllable_backgrounding.rb
|
364
|
+
- lib/shrine/plugins/kithe_derivative_definitions.rb
|
365
|
+
- lib/shrine/plugins/kithe_derivatives.rb
|
366
|
+
- lib/shrine/plugins/kithe_determine_mime_type.rb
|
378
367
|
- lib/shrine/plugins/kithe_multi_cache.rb
|
368
|
+
- lib/shrine/plugins/kithe_persisted_derivatives.rb
|
379
369
|
- lib/shrine/plugins/kithe_promotion_callbacks.rb
|
380
370
|
- lib/shrine/plugins/kithe_promotion_directives.rb
|
381
371
|
- lib/shrine/plugins/kithe_storage_location.rb
|
@@ -448,19 +438,23 @@ files:
|
|
448
438
|
- spec/indexing/indexable_spec.rb
|
449
439
|
- spec/indexing/indexer_spec.rb
|
450
440
|
- spec/indexing/obj_extract_spec.rb
|
451
|
-
- spec/models/kithe/asset/
|
441
|
+
- spec/models/kithe/asset/asset_derivatives_spec.rb
|
452
442
|
- spec/models/kithe/asset/asset_promotion_hooks_spec.rb
|
443
|
+
- spec/models/kithe/asset/set_shrine_uploader_spec.rb
|
453
444
|
- spec/models/kithe/asset_spec.rb
|
454
445
|
- spec/models/kithe/collection_spec.rb
|
455
446
|
- spec/models/kithe/config_spec.rb
|
456
|
-
- spec/models/kithe/derivative_spec.rb
|
457
447
|
- spec/models/kithe/mediainfo_analyzer_spec.rb
|
458
448
|
- spec/models/kithe/model_spec.rb
|
459
449
|
- spec/models/kithe/parameters_spec.rb
|
460
450
|
- spec/models/kithe/representatives_spec.rb
|
461
451
|
- spec/models/kithe/work_spec.rb
|
462
452
|
- spec/rails_helper.rb
|
453
|
+
- spec/shrine/kithe_accept_remote_url_spec.rb
|
454
|
+
- spec/shrine/kithe_checksum_signatures_spec.rb
|
455
|
+
- spec/shrine/kithe_derivative_definitions_spec.rb
|
463
456
|
- spec/shrine/kithe_multi_cache_spec.rb
|
457
|
+
- spec/shrine/kithe_persisted_derivatives_spec.rb
|
464
458
|
- spec/shrine/kithe_storage_location_spec.rb
|
465
459
|
- spec/simple_form_enhancements/repeatable_input_generator_spec.rb
|
466
460
|
- spec/spec_helper.rb
|
@@ -468,6 +462,7 @@ files:
|
|
468
462
|
- spec/test_support/audio/ice_cubes.mp3
|
469
463
|
- spec/test_support/images/1x1_pixel.jpg
|
470
464
|
- spec/test_support/images/2x2_pixel.jpg
|
465
|
+
- spec/test_support/images/3x3_pixel.jpg
|
471
466
|
- spec/test_support/images/photo_800x586.jpg
|
472
467
|
- spec/test_support/shrine_spec_support.rb
|
473
468
|
- spec/test_support/temporary_class_for_specs.rb
|
@@ -561,23 +556,28 @@ test_files:
|
|
561
556
|
- spec/derivative_transformers/ffmpeg_transformer_spec.rb
|
562
557
|
- spec/models/kithe/collection_spec.rb
|
563
558
|
- spec/models/kithe/config_spec.rb
|
559
|
+
- spec/models/kithe/asset/asset_derivatives_spec.rb
|
564
560
|
- spec/models/kithe/asset/asset_promotion_hooks_spec.rb
|
565
|
-
- spec/models/kithe/asset/
|
561
|
+
- spec/models/kithe/asset/set_shrine_uploader_spec.rb
|
566
562
|
- spec/models/kithe/representatives_spec.rb
|
567
563
|
- spec/models/kithe/work_spec.rb
|
568
564
|
- spec/models/kithe/mediainfo_analyzer_spec.rb
|
569
565
|
- spec/models/kithe/parameters_spec.rb
|
570
|
-
- spec/models/kithe/derivative_spec.rb
|
571
566
|
- spec/models/kithe/asset_spec.rb
|
572
567
|
- spec/models/kithe/model_spec.rb
|
573
568
|
- spec/shrine/kithe_multi_cache_spec.rb
|
574
569
|
- spec/shrine/kithe_storage_location_spec.rb
|
570
|
+
- spec/shrine/kithe_persisted_derivatives_spec.rb
|
571
|
+
- spec/shrine/kithe_derivative_definitions_spec.rb
|
572
|
+
- spec/shrine/kithe_accept_remote_url_spec.rb
|
573
|
+
- spec/shrine/kithe_checksum_signatures_spec.rb
|
575
574
|
- spec/factories/kithe_works.rb
|
576
575
|
- spec/factories/kithe_collections.rb
|
577
576
|
- spec/factories/kithe_assets.rb
|
578
577
|
- spec/test_support/temporary_class_for_specs.rb
|
579
578
|
- spec/test_support/shrine_spec_support.rb
|
580
579
|
- spec/test_support/images/2x2_pixel.jpg
|
580
|
+
- spec/test_support/images/3x3_pixel.jpg
|
581
581
|
- spec/test_support/images/1x1_pixel.jpg
|
582
582
|
- spec/test_support/images/photo_800x586.jpg
|
583
583
|
- spec/test_support/audio/README.md
|
@@ -1,119 +0,0 @@
|
|
1
|
-
# A service object to add an IO stream as a derivative with a certain key, to a asset.
|
2
|
-
# Adds a Derivative database object for such. This class is normally only used from
|
3
|
-
# Asset#update_derivative, it's a helper object, you aren't expected to use it independently.
|
4
|
-
#
|
5
|
-
# This would be very straightforward if it weren't for taking account of a couple concurrency race
|
6
|
-
# conditions involving data integrity:
|
7
|
-
#
|
8
|
-
# 1. There should be only one derivative for a given asset/key pair. This is enforced by
|
9
|
-
# a DB constraint. If the record already exists, we want to update the current record,
|
10
|
-
# otherwise add a new one. We want to do this in a race-condition safe way, with possibly
|
11
|
-
# multiple processes editing db.
|
12
|
-
#
|
13
|
-
# 2. The DB should at no point in time contain a derivative generated for an _old_ version
|
14
|
-
# of the asset. If an asset#file is changed, it's existing derivatives need to be deleted,
|
15
|
-
# and this needs to happen in a race-condition safe way when something may be trying to
|
16
|
-
# add a derivative concurrently.
|
17
|
-
#
|
18
|
-
# I believe we have solved those challenges, but it leads to a bit tricky code. We use
|
19
|
-
# a kind of "optimistic" approach to (1) (try to insert, if you get a uniqueness violation
|
20
|
-
# try to find and use the record that's already there). And a "pessimistic" approach to (2),
|
21
|
-
# where we actually briefly take out a DB pessimistic lock to make sure the asset#file hasn't
|
22
|
-
# changed, and can't until we're done updating. (using sha512 as a marker, which is why you
|
23
|
-
# can't add an asset until it has a sha512 in it's metadata, usually post-promotion).
|
24
|
-
#
|
25
|
-
# If we made a given Asset objects's file bytestream immutable, this would all be a lot simpler;
|
26
|
-
# we wouldn't need to worry about (2) at all, and maybe not even (1). We might consider that, but
|
27
|
-
# for now we're tackling the hard way.
|
28
|
-
#
|
29
|
-
class Kithe::Asset::DerivativeUpdater
|
30
|
-
attr_reader :asset, :key, :io, :storage_key, :metadata, :max_optimistic_tries
|
31
|
-
|
32
|
-
def initialize(asset, key, io, storage_key: :kithe_derivatives, metadata: {})
|
33
|
-
@asset = asset
|
34
|
-
@key = key
|
35
|
-
@io = io
|
36
|
-
@storage_key = storage_key
|
37
|
-
@metadata = metadata
|
38
|
-
|
39
|
-
@max_optimistic_tries = 3
|
40
|
-
|
41
|
-
unless asset_has_persisted_sha512?
|
42
|
-
raise ArgumentError.new("Can not safely add derivative to an asset without a persisted sha512 value")
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def update
|
47
|
-
deriv = Kithe::Derivative.new(key: key.to_s, asset: asset)
|
48
|
-
|
49
|
-
# skip cache phase, right to specified storage, but with metadata extraction.
|
50
|
-
uploader = deriv.file_attacher.shrine_class.new(storage_key)
|
51
|
-
|
52
|
-
# add our derivative key to context when uploading, so Kithe::DerivativeUploader can
|
53
|
-
# use it if needed.
|
54
|
-
uploaded_file = uploader.upload(io, record: deriv, metadata: metadata, kithe_derivative_key: key)
|
55
|
-
optimistically_save_derivative(uploaded_file: uploaded_file, derivative: deriv)
|
56
|
-
end
|
57
|
-
|
58
|
-
# Attaches UploadedFile to Derivative and tries to save it -- if we get a
|
59
|
-
# unique constraint violation because a Derivative for that asset/key already existed,
|
60
|
-
# we fetch that alredy existing one from the db and update it's actual bytestream.
|
61
|
-
#
|
62
|
-
# This method calls itself recursively to do that. Gives up after max_optimistic_tries,
|
63
|
-
# at which point it'll just raise the constraint violation exception.
|
64
|
-
def optimistically_save_derivative(uploaded_file:, derivative:, tries: 0)
|
65
|
-
derivative.file_attacher.change(uploaded_file)
|
66
|
-
save_deriv_ensuring_unchanged_asset(derivative)
|
67
|
-
rescue ActiveRecord::RecordNotUnique => e
|
68
|
-
if tries < max_optimistic_tries
|
69
|
-
# find the one that's already there, try to attach our new file
|
70
|
-
# to that one
|
71
|
-
derivative = Kithe::Derivative.where(key: key.to_s, asset: asset).first || derivative
|
72
|
-
optimistically_save_derivative(uploaded_file: uploaded_file, derivative: derivative, tries: tries + 1)
|
73
|
-
else
|
74
|
-
uploaded_file.delete if uploaded_file
|
75
|
-
raise e
|
76
|
-
end
|
77
|
-
rescue StandardError => e
|
78
|
-
# aggressively clean up our file on errors!
|
79
|
-
uploaded_file.delete if uploaded_file
|
80
|
-
raise e
|
81
|
-
end
|
82
|
-
|
83
|
-
# Save a Derivative model with some fancy DB footwork to ensure at the time
|
84
|
-
# we save it, the original asset file it is based on is still in db unchanged,
|
85
|
-
# in a concurrency-safe way.
|
86
|
-
#
|
87
|
-
# We re-fetch to ensure asset still exists, with sha512 we expect. (kithe model ensures sha512
|
88
|
-
# exists in shrine metadata). With a pessmistic lock in a transaction. This ensures that at
|
89
|
-
# the point we save the new derivative, the db is still in a state where the original file
|
90
|
-
# the derivative relates to is still in the db.
|
91
|
-
#
|
92
|
-
# Can raise a ActiveRecord::RecordNotUnique, if derivative unique constraint is violated,
|
93
|
-
# that is handled above here.
|
94
|
-
def save_deriv_ensuring_unchanged_asset(deriv)
|
95
|
-
# fancy throw/catch keep our abort rescue from being in the transaction
|
96
|
-
catch(:kithe_unchanged_abort) do
|
97
|
-
Kithe::Asset.transaction do
|
98
|
-
# the file we're trying to add a derivative to doesn't exist anymore, forget it
|
99
|
-
unless asset.acquire_lock_on_sha
|
100
|
-
throw :kithe_unchanged_abort
|
101
|
-
end
|
102
|
-
|
103
|
-
deriv.save!
|
104
|
-
return deriv
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
# If we made it here, we've aborted
|
109
|
-
deriv.file.delete
|
110
|
-
return nil
|
111
|
-
end
|
112
|
-
|
113
|
-
def asset_has_persisted_sha512?
|
114
|
-
asset.persisted? && asset.sha512.present? &&
|
115
|
-
!( asset.file_data_changed? &&
|
116
|
-
asset.file_data_change.first.try(:dig, "metadata", "sha512") !=
|
117
|
-
asset.file_data_change.second.try(:dig, "metadata", "sha512"))
|
118
|
-
end
|
119
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
module Kithe
|
2
|
-
|
3
|
-
# Derivatives by default will be stored in Shrine storage :kithe_derivatives, so
|
4
|
-
# that should be registered in your app.
|
5
|
-
#
|
6
|
-
# Only one deriv can exist for a given asset_id/key pair, enforced by db constraint.
|
7
|
-
class Derivative < ApplicationRecord
|
8
|
-
# the fk is to kithe_models STI table, but we only intend for assets
|
9
|
-
belongs_to :asset, class_name: "Kithe::Asset"
|
10
|
-
|
11
|
-
include Kithe::DerivativeUploader::Attachment.new(:file, store: :kithe_derivatives)
|
12
|
-
|
13
|
-
delegate :content_type, :size, :height, :width, :url, to: :file, allow_nil: true
|
14
|
-
end
|
15
|
-
end
|
@@ -1,48 +0,0 @@
|
|
1
|
-
require 'mini_mime'
|
2
|
-
|
3
|
-
module Kithe
|
4
|
-
# The derivative uploader doesn't have to do too much, we don't even use
|
5
|
-
# promotion for derivatives, just writing directly to a storage.
|
6
|
-
#
|
7
|
-
# But it needs activerecord integration, and limited metadata automatic extraction.
|
8
|
-
class DerivativeUploader < Shrine
|
9
|
-
plugin :activerecord
|
10
|
-
|
11
|
-
plugin :determine_mime_type, analyzer: :marcel
|
12
|
-
|
13
|
-
# ignore error, often from storing a non-image file which can't have dimensions
|
14
|
-
# extracted. behavior consistent with shrine 2.x.
|
15
|
-
plugin :store_dimensions, on_error: :ignore
|
16
|
-
|
17
|
-
# Useful in case consumers want it, and doesn't harm anything to be available.
|
18
|
-
# https://github.com/shrinerb/shrine/blob/master/doc/plugins/rack_response.md
|
19
|
-
plugin :rack_response
|
20
|
-
|
21
|
-
# should this be in a plugin? location in file system based on original asset
|
22
|
-
# id and derivative key, as well as unique random file id from shrine.
|
23
|
-
def generate_location(io, context)
|
24
|
-
# assumes we're only used with Derivative model, that has an asset_id and key
|
25
|
-
asset_id = context[:record].asset_id
|
26
|
-
key = context[:record].key
|
27
|
-
original = super
|
28
|
-
[asset_id, key, original].compact.join("/")
|
29
|
-
end
|
30
|
-
|
31
|
-
|
32
|
-
# Override to fix "filename" metadata to be something reasonable, regardless
|
33
|
-
# of what if anything was the filename of the IO being attached. shrine S3 will
|
34
|
-
# insist on setting a default content-disposition with this filename.
|
35
|
-
def extract_metadata(io, context = {})
|
36
|
-
result = super
|
37
|
-
|
38
|
-
if context[:kithe_derivative_key] &&
|
39
|
-
context[:record]
|
40
|
-
extension = MiniMime.lookup_by_content_type(result["mime_type"] || "")&.extension
|
41
|
-
result["filename"] = "#{context[:record].asset.friendlier_id}_#{context[:kithe_derivative_key]}.#{extension}"
|
42
|
-
result["kithe_derivative_key"] = context[:kithe_derivative_key]
|
43
|
-
end
|
44
|
-
|
45
|
-
return result
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
@@ -1,320 +0,0 @@
|
|
1
|
-
# Make a new test file cause it's a buncha func
|
2
|
-
require 'rails_helper'
|
3
|
-
|
4
|
-
# Not sure how to get our
|
5
|
-
describe "Kithe::Asset derivative definitions", queue_adapter: :test do
|
6
|
-
let(:a_jpg_deriv_file) { Kithe::Engine.root.join("spec/test_support/images/2x2_pixel.jpg") }
|
7
|
-
|
8
|
-
temporary_class("TestAssetSubclass") do
|
9
|
-
deriv_src_path = a_jpg_deriv_file
|
10
|
-
Class.new(Kithe::Asset) do
|
11
|
-
define_derivative(:some_data) do |original_file|
|
12
|
-
StringIO.new("some one data")
|
13
|
-
end
|
14
|
-
|
15
|
-
define_derivative(:a_jpg) do |original_file|
|
16
|
-
FileUtils.cp(deriv_src_path,
|
17
|
-
Kithe::Engine.root.join("spec/test_support/images/2x2_pixel-TEMP.jpg"))
|
18
|
-
|
19
|
-
File.open(Kithe::Engine.root.join("spec/test_support/images/2x2_pixel-TEMP.jpg"))
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
let(:asset) do
|
25
|
-
TestAssetSubclass.create(title: "test",
|
26
|
-
file: File.open(Kithe::Engine.root.join("spec/test_support/images/1x1_pixel.jpg"))
|
27
|
-
).tap do |a|
|
28
|
-
# We want to promote without create_derivatives being automatically called
|
29
|
-
# as usual, so we can test create_derivatives manually.
|
30
|
-
a.file_attacher.set_promotion_directives(create_derivatives: false)
|
31
|
-
a.promote
|
32
|
-
|
33
|
-
# Precondition assumptions for our test setup to be valid
|
34
|
-
expect(a.file_attacher.stored?).to be(true)
|
35
|
-
expect(a.derivatives).to be_empty
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
it "builds derivatives" do
|
40
|
-
asset.create_derivatives
|
41
|
-
|
42
|
-
one_deriv = asset.derivatives.find { |d| d.key == "some_data" }
|
43
|
-
expect(one_deriv).to be_present
|
44
|
-
expect(one_deriv.file.read).to eq("some one data")
|
45
|
-
|
46
|
-
jpg_deriv = asset.derivatives.find {|d| d.key == "a_jpg"}
|
47
|
-
expect(jpg_deriv.file.read).to eq(File.read(a_jpg_deriv_file, encoding: "BINARY"))
|
48
|
-
end
|
49
|
-
|
50
|
-
it "sets #derivatives_created?" do
|
51
|
-
expect(asset.derivatives_created?).to be(false)
|
52
|
-
asset.create_derivatives
|
53
|
-
asset.reload
|
54
|
-
expect(asset.derivatives_created?).to be(true)
|
55
|
-
end
|
56
|
-
|
57
|
-
|
58
|
-
describe "Original deleted before derivatives can be created", queue_adapter: :inline do
|
59
|
-
let(:short_lived_asset) do
|
60
|
-
TestAssetSubclass.create!(title: "test",
|
61
|
-
file: File.open(Kithe::Engine.root.join("spec/test_support/images/1x1_pixel.jpg")))
|
62
|
-
end
|
63
|
-
it """catches the ActiveJob::DeserializationError
|
64
|
-
if the asset is no longer in the database
|
65
|
-
once the derivative creation job starts up.""" do
|
66
|
-
id_to_delete = short_lived_asset.id
|
67
|
-
Kithe::Derivative.where(asset_id: id_to_delete).delete_all
|
68
|
-
Kithe::Model.where(id: id_to_delete).delete_all
|
69
|
-
|
70
|
-
# short_lived_asset is no longer in the DB.
|
71
|
-
# Let's try and create derivatives for it:
|
72
|
-
expect do
|
73
|
-
Kithe::CreateDerivativesJob.perform_later(short_lived_asset)
|
74
|
-
end.not_to raise_error
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
describe "under normal operation", queue_adapter: :inline do
|
79
|
-
let(:asset) do
|
80
|
-
TestAssetSubclass.create!(title: "test",
|
81
|
-
file: File.open(Kithe::Engine.root.join("spec/test_support/images/1x1_pixel.jpg")))
|
82
|
-
end
|
83
|
-
it "automatically creates derivatives" do
|
84
|
-
expect(asset.derivatives.count).to eq(2)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
it "extracts limited metadata from derivative" do
|
89
|
-
asset.create_derivatives
|
90
|
-
|
91
|
-
jpg_deriv = asset.derivatives.find {|d| d.key == "a_jpg"}
|
92
|
-
expect(jpg_deriv.size).to eq(File.size(Kithe::Engine.root.join("spec/test_support/images/2x2_pixel.jpg")))
|
93
|
-
expect(jpg_deriv.width).to eq(2)
|
94
|
-
expect(jpg_deriv.height).to eq(2)
|
95
|
-
expect(jpg_deriv.content_type).to eq("image/jpeg")
|
96
|
-
end
|
97
|
-
|
98
|
-
it "deletes derivative file returned by block" do
|
99
|
-
asset.create_derivatives
|
100
|
-
|
101
|
-
expect(File.exist?(Kithe::Engine.root.join("spec/test_support/images/2x2_pixel-TEMP.jpg"))).not_to be(true)
|
102
|
-
end
|
103
|
-
|
104
|
-
it "by default saves in :kithe_derivatives storage" do
|
105
|
-
asset.create_derivatives
|
106
|
-
|
107
|
-
jpg_deriv = asset.derivatives.find {|d| d.key == "a_jpg"}
|
108
|
-
expect(jpg_deriv.file.storage_key).to eq(:kithe_derivatives)
|
109
|
-
end
|
110
|
-
|
111
|
-
|
112
|
-
describe "block arguments" do
|
113
|
-
let(:monitoring_proc) do
|
114
|
-
proc do |original_file, record:|
|
115
|
-
expect(original_file.kind_of?(File) || original_file.kind_of?(Tempfile)).to be(true)
|
116
|
-
expect(original_file.path).to be_present
|
117
|
-
expect(original_file.read).to eq(asset.file.read)
|
118
|
-
|
119
|
-
expect(record).to eq(asset)
|
120
|
-
|
121
|
-
nil
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
temporary_class("TestAssetSubclass") do
|
126
|
-
our_proc = monitoring_proc
|
127
|
-
Class.new(Kithe::Asset) do
|
128
|
-
define_derivative(:some_data, &our_proc)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
it "as expected" do
|
133
|
-
expect(monitoring_proc).to receive(:call).and_call_original
|
134
|
-
|
135
|
-
asset.create_derivatives
|
136
|
-
expect(asset.derivatives.length).to eq(0)
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
describe "custom storage_key" do
|
141
|
-
temporary_class("TestAssetSubclass") do
|
142
|
-
Class.new(Kithe::Asset) do
|
143
|
-
define_derivative(:some_data, storage_key: :store) do |original_file|
|
144
|
-
StringIO.new("some one data")
|
145
|
-
end
|
146
|
-
end
|
147
|
-
end
|
148
|
-
it "saves appropriately" do
|
149
|
-
asset.create_derivatives
|
150
|
-
|
151
|
-
deriv = asset.derivatives.first
|
152
|
-
|
153
|
-
expect(deriv).to be_present
|
154
|
-
expect(deriv.file.storage_key).to eq(:store)
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
describe "default_create false" do
|
159
|
-
let(:monitoring_proc) { proc { |asset| } }
|
160
|
-
|
161
|
-
temporary_class("TestAssetSubclass") do
|
162
|
-
p = monitoring_proc
|
163
|
-
Class.new(Kithe::Asset) do
|
164
|
-
define_derivative(:some_data, default_create: false, &p)
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
it "is not run automatically" do
|
169
|
-
expect(monitoring_proc).not_to receive(:call)
|
170
|
-
asset.create_derivatives
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
describe "only/except" do
|
175
|
-
let(:monitoring_proc1) { proc { |asset| StringIO.new("one") } }
|
176
|
-
let(:monitoring_proc2) { proc { |asset| StringIO.new("two") } }
|
177
|
-
let(:monitoring_proc3) { proc { |asset| StringIO.new("three") } }
|
178
|
-
|
179
|
-
temporary_class("TestAssetSubclass") do
|
180
|
-
p1, p2, p3 = monitoring_proc1, monitoring_proc2, monitoring_proc3
|
181
|
-
Class.new(Kithe::Asset) do
|
182
|
-
define_derivative(:one, default_create: false, &p1)
|
183
|
-
define_derivative(:two, &p2)
|
184
|
-
define_derivative(:three, &p3)
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
it "can call with only" do
|
189
|
-
expect(monitoring_proc1).to receive(:call).and_call_original
|
190
|
-
expect(monitoring_proc2).to receive(:call).and_call_original
|
191
|
-
expect(monitoring_proc3).not_to receive(:call)
|
192
|
-
|
193
|
-
asset.create_derivatives(only: [:one, :two])
|
194
|
-
|
195
|
-
expect(asset.derivatives.collect(&:key)).to eq(["one", "two"])
|
196
|
-
end
|
197
|
-
|
198
|
-
it "can call with except" do
|
199
|
-
expect(monitoring_proc1).not_to receive(:call)
|
200
|
-
expect(monitoring_proc2).to receive(:call).and_call_original
|
201
|
-
expect(monitoring_proc3).not_to receive(:call)
|
202
|
-
|
203
|
-
asset.create_derivatives(except: [:three])
|
204
|
-
|
205
|
-
expect(asset.derivatives.collect(&:key)).to eq(["two"])
|
206
|
-
end
|
207
|
-
|
208
|
-
it "can call with only and except" do
|
209
|
-
expect(monitoring_proc1).to receive(:call).and_call_original
|
210
|
-
expect(monitoring_proc2).not_to receive(:call)
|
211
|
-
expect(monitoring_proc3).not_to receive(:call)
|
212
|
-
|
213
|
-
asset.create_derivatives(only: [:one, :two], except: :two)
|
214
|
-
|
215
|
-
expect(asset.derivatives.collect(&:key)).to eq(["one"])
|
216
|
-
end
|
217
|
-
end
|
218
|
-
|
219
|
-
describe "content_type filters" do
|
220
|
-
temporary_class("TestAssetSubclass") do
|
221
|
-
Class.new(Kithe::Asset) do
|
222
|
-
define_derivative(:never_called, content_type: "nothing/nothing") { |o| StringIO.new("never") }
|
223
|
-
define_derivative(:gated_positive, content_type: "image/jpeg") { |o| StringIO.new("gated positive") }
|
224
|
-
define_derivative(:gated_positive_main_type, content_type: "image") { |o| StringIO.new("gated positive") }
|
225
|
-
end
|
226
|
-
end
|
227
|
-
|
228
|
-
it "does not call if content type does not match" do
|
229
|
-
asset.create_derivatives
|
230
|
-
expect(asset.derivatives.collect(&:key)).not_to include("never_called")
|
231
|
-
end
|
232
|
-
|
233
|
-
it "calls for exact content type match" do
|
234
|
-
asset.create_derivatives
|
235
|
-
expect(asset.derivatives.collect(&:key)).to include("gated_positive")
|
236
|
-
end
|
237
|
-
|
238
|
-
it "calls for main content type match" do
|
239
|
-
asset.create_derivatives
|
240
|
-
expect(asset.derivatives.collect(&:key)).to include("gated_positive_main_type")
|
241
|
-
end
|
242
|
-
|
243
|
-
describe "as array" do
|
244
|
-
temporary_class("TestAssetSubclass") do
|
245
|
-
Class.new(Kithe::Asset) do
|
246
|
-
define_derivative(:never_called, content_type: ["nothing/nothing", "also/nothing"]) { |o| StringIO.new("never") }
|
247
|
-
define_derivative(:gated_positive, content_type: ["image/jpeg", "something/else"]) { |o| StringIO.new("gated positive") }
|
248
|
-
end
|
249
|
-
end
|
250
|
-
it "calls for one match" do
|
251
|
-
asset.create_derivatives
|
252
|
-
expect(asset.derivatives.collect(&:key)).to eq(["gated_positive"])
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
|
-
describe "conflicting types" do
|
257
|
-
let(:unfiltered) { proc { |asset| StringIO.new("unfiltered") } }
|
258
|
-
let(:image) { proc { |asset| StringIO.new("image") } }
|
259
|
-
let(:image_jpeg) { proc { |asset| StringIO.new("image/jpeg") } }
|
260
|
-
|
261
|
-
|
262
|
-
temporary_class("TestAssetSubclass") do
|
263
|
-
u, i, ij = unfiltered, image, image_jpeg
|
264
|
-
Class.new(Kithe::Asset) do
|
265
|
-
define_derivative(:key, &u)
|
266
|
-
define_derivative(:key, content_type: "image/jpeg", &ij)
|
267
|
-
define_derivative(:key, content_type: "image", &i)
|
268
|
-
end
|
269
|
-
end
|
270
|
-
|
271
|
-
it "takes most specific" do
|
272
|
-
expect(unfiltered).not_to receive(:call)
|
273
|
-
expect(image).not_to receive(:call)
|
274
|
-
expect(image_jpeg).to receive(:call).and_call_original
|
275
|
-
|
276
|
-
asset.create_derivatives
|
277
|
-
expect(asset.derivatives.count).to eq(1)
|
278
|
-
|
279
|
-
deriv = asset.derivatives.first
|
280
|
-
expect(deriv.key).to eq("key")
|
281
|
-
expect(deriv.file.read). to eq("image/jpeg")
|
282
|
-
end
|
283
|
-
end
|
284
|
-
end
|
285
|
-
|
286
|
-
describe "lazy creation" do
|
287
|
-
before do
|
288
|
-
asset.class.derivative_definitions.collect(&:key).each do |key|
|
289
|
-
asset.update_derivative(key, StringIO.new("#{key} original"))
|
290
|
-
end
|
291
|
-
end
|
292
|
-
|
293
|
-
it "does not re-create" do
|
294
|
-
derivatives_pre_creation = asset.derivatives.collect(&:attributes)
|
295
|
-
|
296
|
-
asset.create_derivatives(lazy: true)
|
297
|
-
derivatives_post_creation = asset.derivatives.reload.collect(&:attributes)
|
298
|
-
|
299
|
-
expect(derivatives_post_creation).to eq(derivatives_pre_creation)
|
300
|
-
end
|
301
|
-
end
|
302
|
-
|
303
|
-
describe "#remove_derivative_definition!" do
|
304
|
-
it "can remove by string" do
|
305
|
-
original_keys = TestAssetSubclass.defined_derivative_keys
|
306
|
-
TestAssetSubclass.remove_derivative_definition!(original_keys.first.to_s)
|
307
|
-
expect(TestAssetSubclass.defined_derivative_keys).to eq(original_keys.slice(1..original_keys.length))
|
308
|
-
end
|
309
|
-
it "can remove by symbol" do
|
310
|
-
original_keys = TestAssetSubclass.defined_derivative_keys
|
311
|
-
TestAssetSubclass.remove_derivative_definition!(original_keys.first.to_sym)
|
312
|
-
expect(TestAssetSubclass.defined_derivative_keys).to eq(original_keys.slice(1..original_keys.length))
|
313
|
-
end
|
314
|
-
it "can remove multiple args" do
|
315
|
-
original_keys = TestAssetSubclass.defined_derivative_keys
|
316
|
-
TestAssetSubclass.remove_derivative_definition!(*original_keys)
|
317
|
-
expect(TestAssetSubclass.defined_derivative_keys).to eq([])
|
318
|
-
end
|
319
|
-
end
|
320
|
-
end
|