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
@@ -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
- it "uploads with a record with id" do
8
- uploaded_file = uploader.upload(fakeio, record: OpenStruct.new(id: "81060886-4f93-42e7-ace7-ab51399f4808"), name: :file)
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
- expect(uploaded_file.id).to match %r{\Aasset/81060886-4f93-42e7-ace7-ab51399f4808/[0-9a-f]+}
11
- end
11
+ expect(uploaded_file.id).to match %r{\Aasset/81060886-4f93-42e7-ace7-ab51399f4808/[0-9a-f]+}
12
+ end
12
13
 
13
- it "has suffix with a record id and filename" do
14
- uploaded_file = uploader.upload(fakeio(filename: "foo.jpg"), record: OpenStruct.new(id: "81060886-4f93-42e7-ace7-ab51399f4808"), name: :file)
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
- expect(uploaded_file.id).to match %r{\Aasset/81060886-4f93-42e7-ace7-ab51399f4808/[0-9a-f]+\.jpg}
17
- end
17
+ expect(uploaded_file.id).to match %r{\Aasset/81060886-4f93-42e7-ace7-ab51399f4808/[0-9a-f]+\.jpg}
18
+ end
18
19
 
19
- it "uploads with no record" do
20
- uploaded_file = uploader.upload(fakeio, record: OpenStruct.new(id: "81060886-4f93-42e7-ace7-ab51399f4808"), name: :file)
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
- expect(uploaded_file.id).to match %r{\Aasset/[0-9a-f]+}
23
- end
23
+ expect(uploaded_file.id).to match %r{\Aasset/[0-9a-f]+}
24
+ end
24
25
 
25
- it "uploads with record with no id" do
26
- uploaded_file = uploader.upload(fakeio, record: OpenStruct.new(), name: :file)
26
+ it "uploads with record with no id" do
27
+ uploaded_file = uploader.upload(fakeio, record: OpenStruct.new(), name: :file)
27
28
 
28
- expect(uploaded_file.id).to match %r{\Aasset/[0-9a-f]+}
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
@@ -123,22 +123,3 @@ RSpec.configure do |config|
123
123
  =end
124
124
  end
125
125
 
126
- # Workaround ruby 2.7.0 StringIO enccoding weirdness, which hopefully
127
- # will be fixed in shrine 3.x before we get there. if you can remove this patch
128
- # and tests still pass, you're good.
129
- #
130
- # https://github.com/shrinerb/shrine/pull/443
131
- #
132
- require 'sane_patch'
133
- SanePatch.patch("shrine", "< 3.2.2") do
134
- require 'shrine/storage/memory'
135
- class Shrine::Storage::Memory
136
- def open(id, *)
137
- str = store.fetch(id)
138
- StringIO.new(str).set_encoding(str.encoding, str.encoding)
139
- end
140
- end
141
- end
142
-
143
-
144
-
@@ -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.alpha2
4
+ version: 2.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Rochkind
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-18 00:00:00.000000000 Z
11
+ date: 2021-01-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: 5.2.1
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '6.1'
22
+ version: '6.2'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,7 @@ dependencies:
29
29
  version: 5.2.1
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '6.1'
32
+ version: '6.2'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: attr_json
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -182,6 +182,26 @@ dependencies:
182
182
  - - ">="
183
183
  - !ruby/object:Gem::Version
184
184
  version: '0'
185
+ - !ruby/object:Gem::Dependency
186
+ name: fx
187
+ requirement: !ruby/object:Gem::Requirement
188
+ requirements:
189
+ - - ">="
190
+ - !ruby/object:Gem::Version
191
+ version: 0.5.0
192
+ - - "<"
193
+ - !ruby/object:Gem::Version
194
+ version: '1'
195
+ type: :runtime
196
+ prerelease: false
197
+ version_requirements: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: 0.5.0
202
+ - - "<"
203
+ - !ruby/object:Gem::Version
204
+ version: '1'
185
205
  - !ruby/object:Gem::Dependency
186
206
  name: traject
187
207
  requirement: !ruby/object:Gem::Requirement
@@ -272,20 +292,6 @@ dependencies:
272
292
  - - ">="
273
293
  - !ruby/object:Gem::Version
274
294
  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
295
  - !ruby/object:Gem::Dependency
290
296
  name: webmock
291
297
  requirement: !ruby/object:Gem::Requirement
@@ -314,7 +320,7 @@ dependencies:
314
320
  - - "<"
315
321
  - !ruby/object:Gem::Version
316
322
  version: '2'
317
- description:
323
+ description:
318
324
  email:
319
325
  - jrochkind@sciencehistory.org
320
326
  executables: []
@@ -341,10 +347,8 @@ files:
341
347
  - app/models/kithe/asset.rb
342
348
  - app/models/kithe/asset/derivative_creator.rb
343
349
  - app/models/kithe/asset/derivative_definition.rb
344
- - app/models/kithe/asset/derivative_updater.rb
350
+ - app/models/kithe/asset/set_shrine_uploader.rb
345
351
  - app/models/kithe/collection.rb
346
- - app/models/kithe/config_base.rb
347
- - app/models/kithe/derivative.rb
348
352
  - app/models/kithe/mediainfo_analyzer.rb
349
353
  - app/models/kithe/model.rb
350
354
  - app/models/kithe/model_contains.rb
@@ -355,7 +359,6 @@ files:
355
359
  - app/simple_form_enhancements/kithe/form_builder.rb
356
360
  - app/simple_form_enhancements/kithe/repeatable_input_generator.rb
357
361
  - app/uploaders/kithe/asset_uploader.rb
358
- - app/uploaders/kithe/derivative_uploader.rb
359
362
  - app/validators/array_inclusion_validator.rb
360
363
  - config/locales/en.yml
361
364
  - config/routes.rb
@@ -370,12 +373,20 @@ files:
370
373
  - lib/kithe.rb
371
374
  - lib/kithe/blacklight_tools/bulk_loading_search_service.rb
372
375
  - lib/kithe/blacklight_tools/search_service_bulk_load.rb
376
+ - lib/kithe/config_base.rb
373
377
  - lib/kithe/engine.rb
374
378
  - lib/kithe/indexable_settings.rb
379
+ - lib/kithe/patch_fx.rb
375
380
  - lib/kithe/sti_preload.rb
376
381
  - lib/kithe/version.rb
377
382
  - lib/shrine/plugins/kithe_accept_remote_url.rb
383
+ - lib/shrine/plugins/kithe_checksum_signatures.rb
384
+ - lib/shrine/plugins/kithe_controllable_backgrounding.rb
385
+ - lib/shrine/plugins/kithe_derivative_definitions.rb
386
+ - lib/shrine/plugins/kithe_derivatives.rb
387
+ - lib/shrine/plugins/kithe_determine_mime_type.rb
378
388
  - lib/shrine/plugins/kithe_multi_cache.rb
389
+ - lib/shrine/plugins/kithe_persisted_derivatives.rb
379
390
  - lib/shrine/plugins/kithe_promotion_callbacks.rb
380
391
  - lib/shrine/plugins/kithe_promotion_directives.rb
381
392
  - lib/shrine/plugins/kithe_storage_location.rb
@@ -395,6 +406,7 @@ files:
395
406
  - spec/dummy/app/jobs/application_job.rb
396
407
  - spec/dummy/app/mailers/application_mailer.rb
397
408
  - spec/dummy/app/models/application_record.rb
409
+ - spec/dummy/app/models/plain_active_record.rb
398
410
  - spec/dummy/app/views/layouts/application.html.erb
399
411
  - spec/dummy/app/views/layouts/mailer.html.erb
400
412
  - spec/dummy/app/views/layouts/mailer.text.erb
@@ -431,7 +443,7 @@ files:
431
443
  - spec/dummy/config/routes.rb
432
444
  - spec/dummy/config/spring.rb
433
445
  - spec/dummy/config/storage.yml
434
- - spec/dummy/db/structure.sql
446
+ - spec/dummy/db/schema.rb
435
447
  - spec/dummy/log/development.log
436
448
  - spec/dummy/log/test.log
437
449
  - spec/dummy/package.json
@@ -448,19 +460,24 @@ files:
448
460
  - spec/indexing/indexable_spec.rb
449
461
  - spec/indexing/indexer_spec.rb
450
462
  - spec/indexing/obj_extract_spec.rb
451
- - spec/models/kithe/asset/asset_create_derivatives_spec.rb
463
+ - spec/models/kithe/asset/asset_derivatives_spec.rb
452
464
  - spec/models/kithe/asset/asset_promotion_hooks_spec.rb
465
+ - spec/models/kithe/asset/set_shrine_uploader_spec.rb
453
466
  - spec/models/kithe/asset_spec.rb
454
467
  - spec/models/kithe/collection_spec.rb
455
468
  - spec/models/kithe/config_spec.rb
456
- - spec/models/kithe/derivative_spec.rb
457
469
  - spec/models/kithe/mediainfo_analyzer_spec.rb
458
470
  - spec/models/kithe/model_spec.rb
459
471
  - spec/models/kithe/parameters_spec.rb
460
472
  - spec/models/kithe/representatives_spec.rb
461
473
  - spec/models/kithe/work_spec.rb
474
+ - spec/models/kithe_spec.rb
462
475
  - spec/rails_helper.rb
476
+ - spec/shrine/kithe_accept_remote_url_spec.rb
477
+ - spec/shrine/kithe_checksum_signatures_spec.rb
478
+ - spec/shrine/kithe_derivative_definitions_spec.rb
463
479
  - spec/shrine/kithe_multi_cache_spec.rb
480
+ - spec/shrine/kithe_persisted_derivatives_spec.rb
464
481
  - spec/shrine/kithe_storage_location_spec.rb
465
482
  - spec/simple_form_enhancements/repeatable_input_generator_spec.rb
466
483
  - spec/spec_helper.rb
@@ -468,6 +485,7 @@ files:
468
485
  - spec/test_support/audio/ice_cubes.mp3
469
486
  - spec/test_support/images/1x1_pixel.jpg
470
487
  - spec/test_support/images/2x2_pixel.jpg
488
+ - spec/test_support/images/3x3_pixel.jpg
471
489
  - spec/test_support/images/photo_800x586.jpg
472
490
  - spec/test_support/shrine_spec_support.rb
473
491
  - spec/test_support/temporary_class_for_specs.rb
@@ -476,7 +494,7 @@ homepage: https://github.com/sciencehistory/kithe
476
494
  licenses:
477
495
  - MIT
478
496
  metadata: {}
479
- post_install_message:
497
+ post_install_message:
480
498
  rdoc_options: []
481
499
  require_paths:
482
500
  - lib
@@ -487,18 +505,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
487
505
  version: '2.5'
488
506
  required_rubygems_version: !ruby/object:Gem::Requirement
489
507
  requirements:
490
- - - ">"
508
+ - - ">="
491
509
  - !ruby/object:Gem::Version
492
- version: 1.3.1
510
+ version: '0'
493
511
  requirements: []
494
- rubyforge_project:
495
- rubygems_version: 2.7.6
496
- signing_key:
512
+ rubygems_version: 3.0.3
513
+ signing_key:
497
514
  specification_version: 4
498
515
  summary: Shareable tools/components for building a digital collections app in Rails.
499
516
  test_files:
500
517
  - spec/spec_helper.rb
501
518
  - spec/dummy/app/mailers/application_mailer.rb
519
+ - spec/dummy/app/models/plain_active_record.rb
502
520
  - spec/dummy/app/models/application_record.rb
503
521
  - spec/dummy/app/jobs/application_job.rb
504
522
  - spec/dummy/app/controllers/application_controller.rb
@@ -553,7 +571,7 @@ test_files:
553
571
  - spec/dummy/public/404.html
554
572
  - spec/dummy/public/apple-touch-icon-precomposed.png
555
573
  - spec/dummy/package.json
556
- - spec/dummy/db/structure.sql
574
+ - spec/dummy/db/schema.rb
557
575
  - spec/dummy/log/test.log
558
576
  - spec/dummy/log/development.log
559
577
  - spec/dummy/tmp/development_secret.txt
@@ -561,23 +579,29 @@ test_files:
561
579
  - spec/derivative_transformers/ffmpeg_transformer_spec.rb
562
580
  - spec/models/kithe/collection_spec.rb
563
581
  - spec/models/kithe/config_spec.rb
582
+ - spec/models/kithe/asset/asset_derivatives_spec.rb
564
583
  - spec/models/kithe/asset/asset_promotion_hooks_spec.rb
565
- - spec/models/kithe/asset/asset_create_derivatives_spec.rb
584
+ - spec/models/kithe/asset/set_shrine_uploader_spec.rb
566
585
  - spec/models/kithe/representatives_spec.rb
567
586
  - spec/models/kithe/work_spec.rb
568
587
  - spec/models/kithe/mediainfo_analyzer_spec.rb
569
588
  - spec/models/kithe/parameters_spec.rb
570
- - spec/models/kithe/derivative_spec.rb
571
589
  - spec/models/kithe/asset_spec.rb
572
590
  - spec/models/kithe/model_spec.rb
591
+ - spec/models/kithe_spec.rb
573
592
  - spec/shrine/kithe_multi_cache_spec.rb
574
593
  - spec/shrine/kithe_storage_location_spec.rb
594
+ - spec/shrine/kithe_persisted_derivatives_spec.rb
595
+ - spec/shrine/kithe_derivative_definitions_spec.rb
596
+ - spec/shrine/kithe_accept_remote_url_spec.rb
597
+ - spec/shrine/kithe_checksum_signatures_spec.rb
575
598
  - spec/factories/kithe_works.rb
576
599
  - spec/factories/kithe_collections.rb
577
600
  - spec/factories/kithe_assets.rb
578
601
  - spec/test_support/temporary_class_for_specs.rb
579
602
  - spec/test_support/shrine_spec_support.rb
580
603
  - spec/test_support/images/2x2_pixel.jpg
604
+ - spec/test_support/images/3x3_pixel.jpg
581
605
  - spec/test_support/images/1x1_pixel.jpg
582
606
  - spec/test_support/images/photo_800x586.jpg
583
607
  - 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