kithe 2.0.0.pre.alpha2 → 2.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -4
- data/app/indexing/kithe/indexable/record_index_updater.rb +1 -1
- 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.rb +22 -20
- data/{app/models → lib}/kithe/config_base.rb +6 -1
- data/lib/kithe/engine.rb +14 -3
- data/lib/kithe/indexable_settings.rb +1 -1
- data/lib/kithe/patch_fx.rb +39 -0
- 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/app/models/plain_active_record.rb +3 -0
- data/spec/dummy/config/database.yml +6 -0
- data/spec/dummy/db/schema.rb +102 -0
- data/spec/dummy/log/development.log +3616 -0
- data/spec/dummy/log/test.log +86464 -0
- data/spec/dummy/tmp/development_secret.txt +1 -1
- data/spec/indexing/indexable_spec.rb +1 -1
- 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/models/kithe_spec.rb +10 -0
- 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 +0 -19
- data/spec/test_support/images/3x3_pixel.jpg +0 -0
- data/spec/test_support/shrine_spec_support.rb +2 -1
- metadata +60 -36
- 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/dummy/db/structure.sql +0 -309
- data/spec/models/kithe/asset/asset_create_derivatives_spec.rb +0 -320
- data/spec/models/kithe/derivative_spec.rb +0 -168
data/lib/kithe/version.rb
CHANGED
@@ -0,0 +1,41 @@
|
|
1
|
+
class Shrine
|
2
|
+
module Plugins
|
3
|
+
# Using the shrine signature and add_metadata plugins, ensure that the shrine standard
|
4
|
+
# digest/checksum signatures are recorded in metadata.
|
5
|
+
#
|
6
|
+
# This plugin is NOT included in Kithe::AssetUploader by default, include it in your
|
7
|
+
# local uploader if desired.
|
8
|
+
#
|
9
|
+
# We want to store md5 and sha1 checksums (legacy compat), as well as
|
10
|
+
# sha512 (more recent digital preservation recommendation: https://ocfl.io/draft/spec/#digests)
|
11
|
+
#
|
12
|
+
# We only calculate them only on promotion action (not cache action), to avoid needlessly
|
13
|
+
# expensive double-computation, and because for direct uploads/backgrounding, we haven't
|
14
|
+
# actually gotten the file in our hands to compute checksums until then anyway.
|
15
|
+
#
|
16
|
+
# the add_metadata plugin's `metadata_method` is used to make md5, sha1, and sha512 methods
|
17
|
+
# available on the Attacher. (They also end up delegated from the Asset model)
|
18
|
+
class KitheChecksumSignatures
|
19
|
+
def self.load_dependencies(uploader, *)
|
20
|
+
uploader.plugin :add_metadata
|
21
|
+
uploader.plugin :signature
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.configure(uploader, opts = {})
|
25
|
+
uploader.class_eval do
|
26
|
+
add_metadata do |io, derivative:nil, **context|
|
27
|
+
if context[:action] != :cache && derivative.nil?
|
28
|
+
{
|
29
|
+
md5: calculate_signature(io, :md5),
|
30
|
+
sha1: calculate_signature(io, :sha1),
|
31
|
+
sha512: calculate_signature(io, :sha512)
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
metadata_method :md5, :sha1, :sha512
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
register_plugin(:kithe_checksum_signatures, KitheChecksumSignatures)
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class Shrine
|
2
|
+
module Plugins
|
3
|
+
|
4
|
+
# Set up shrine `backgrounding`, where promotion and deletion can happen in a background job.
|
5
|
+
#
|
6
|
+
# https://shrinerb.com/docs/getting-started#backgrounding
|
7
|
+
# https://shrinerb.com/docs/plugins/backgrounding
|
8
|
+
#
|
9
|
+
# By default, kithe does promotion and deletion in kithe-provided ActiveJob classes.
|
10
|
+
#
|
11
|
+
# But this plugin implements code to let you use kithe_promotion_directives to make them happen
|
12
|
+
# inline instead, or disable them.
|
13
|
+
#
|
14
|
+
# asset.file_attacher.set_promotion_directives(promote: false)
|
15
|
+
# asset.file_attacher.set_promotion_directives(promote: :inline)
|
16
|
+
# asset.file_attacher.set_promotion_directives(promote: "inline")
|
17
|
+
#
|
18
|
+
# asset.file_attacher.set_promotion_directives(delete: :inline)
|
19
|
+
class KitheControllableBackgrounding
|
20
|
+
def self.load_dependencies(uploader, *)
|
21
|
+
uploader.plugin :backgrounding
|
22
|
+
uploader.plugin :kithe_promotion_directives
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.configure(uploader, options = {})
|
26
|
+
|
27
|
+
# promote using shrine backgrounding, but can be effected by promotion_directives[:promote]
|
28
|
+
uploader::Attacher.promote_block do
|
29
|
+
Kithe::TimingPromotionDirective.new(key: :promote, directives: self.promotion_directives) do |directive|
|
30
|
+
if directive.inline?
|
31
|
+
promote
|
32
|
+
elsif directive.background?
|
33
|
+
# What shrine normally expects for backgrounding, plus promotion_directives
|
34
|
+
Kithe::AssetPromoteJob.perform_later(self.class.name, record.class.name, record.id, name.to_s, file_data, self.promotion_directives)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
uploader::Attacher.destroy_block do
|
40
|
+
Kithe::TimingPromotionDirective.new(key: :delete, directives: self.promotion_directives) do |directive|
|
41
|
+
if directive.inline?
|
42
|
+
destroy
|
43
|
+
elsif directive.background?
|
44
|
+
# What shrine normally expects for backgrounding
|
45
|
+
Kithe::AssetDeleteJob.perform_later(self.class.name, data)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
register_plugin(:kithe_controllable_backgrounding, KitheControllableBackgrounding)
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
class Shrine
|
2
|
+
module Plugins
|
3
|
+
class KitheDerivativeDefinitions
|
4
|
+
def self.configure(uploader, *opts)
|
5
|
+
# use Rails class_attribute to conveniently have a class-level place
|
6
|
+
# to store our derivative definitions that are inheritable and overrideable.
|
7
|
+
# We store it on the Attacher class, because that's where shrine
|
8
|
+
# puts derivative processor definitions, so seems appropriate.
|
9
|
+
uploader::Attacher.class_attribute :kithe_derivative_definitions, instance_writer: false, default: []
|
10
|
+
|
11
|
+
# Register our derivative processor, that will create our registered derivatives,
|
12
|
+
# with our custom options.
|
13
|
+
uploader::Attacher.derivatives(:kithe_derivatives) do |original, **options|
|
14
|
+
Kithe::Asset::DerivativeCreator.new(self.class.kithe_derivative_definitions,
|
15
|
+
source_io: original,
|
16
|
+
shrine_attacher: self,
|
17
|
+
only: options[:only],
|
18
|
+
except: options[:except],
|
19
|
+
lazy: options[:lazy]
|
20
|
+
).call
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module AttacherClassMethods
|
25
|
+
# Establish a derivative definition that will be used to create a derivative
|
26
|
+
# when #create_derivatives is called, for instance automatically after promotion.
|
27
|
+
#
|
28
|
+
# The most basic definition consists of a derivative key, and a ruby block that
|
29
|
+
# takes the original file, transforms it, and returns a ruby File or other
|
30
|
+
# (shrine-compatible) IO-like object. It will usually be done inside a custom Asset
|
31
|
+
# class definition.
|
32
|
+
#
|
33
|
+
# class Asset < Kithe::Asset
|
34
|
+
# define_derivative :thumbnail do |original_file|
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# The original_file passed in will be a ruby File object that is already open for reading. If
|
39
|
+
# you need a local file path for your transformation, just use `original_file.path`.
|
40
|
+
#
|
41
|
+
# The return value can be any IO-like object. If it is a ruby File or Tempfile,
|
42
|
+
# that temporary file will be deleted for you after the derivative has been created. If you
|
43
|
+
# have to make any intermediate files, you are responsible for cleaning them up. Ruby stdlib
|
44
|
+
# Tempfile and Dir.mktmpdir may be useful.
|
45
|
+
#
|
46
|
+
# If in order to do your transformation you need additional information about the original,
|
47
|
+
# just add a `record:` keyword argument to your block, and the Asset object will be passed in:
|
48
|
+
#
|
49
|
+
# define_derivative :thumbnail do |original_file, record:|
|
50
|
+
# record.width, record.height, record.content_type # etc
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# Derivatives are normally uploaded to the Shrine storage labeled :kithe_derivatives,
|
54
|
+
# but a definition can specify an alternate Shrine storage id. (specified shrine storage key
|
55
|
+
# is applied on derivative creation; if you change it with existing derivatives, they should
|
56
|
+
# remain, and be accessible, where they were created; there is no built-in solution at present
|
57
|
+
# for moving them).
|
58
|
+
#
|
59
|
+
# define_derivative :thumbnail, storage_key: :my_thumb_storage do |original| # ...
|
60
|
+
#
|
61
|
+
# You can also set `default_create: false` if you want a particular definition not to be
|
62
|
+
# included in a no-arg `asset.create_derivatives` that is normally triggered on asset creation.
|
63
|
+
#
|
64
|
+
# And you can set content_type to either a specific type like `image/jpeg` (or array of such) or a general type
|
65
|
+
# like `image`, if you want to define a derivative generation routine for only certain types.
|
66
|
+
# If multiple blocks for the same key are defined, with different content_type restrictions,
|
67
|
+
# the most specific one will be used. That is, for a JPG, `image/jpeg` beats `image` beats no restriction.
|
68
|
+
def define_derivative(key, content_type: nil, default_create: true, &block)
|
69
|
+
# Make sure we dup the array to handle sub-classes on class_attribute
|
70
|
+
self.kithe_derivative_definitions = self.kithe_derivative_definitions.dup.push(
|
71
|
+
Kithe::Asset::DerivativeDefinition.new(
|
72
|
+
key: key,
|
73
|
+
content_type: content_type,
|
74
|
+
default_create: default_create,
|
75
|
+
proc: block
|
76
|
+
)
|
77
|
+
).freeze
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns all derivative keys registered with a definition, as array of strings
|
81
|
+
def defined_derivative_keys
|
82
|
+
self.kithe_derivative_definitions.collect(&:key).uniq.collect(&:to_s)
|
83
|
+
end
|
84
|
+
|
85
|
+
# If you have a subclass that has inherited derivative definitions, you can
|
86
|
+
# remove them -- only by key, will remove any definitions with that key regardless
|
87
|
+
# of content_type restrictions.
|
88
|
+
#
|
89
|
+
# This could be considered rather bad OO design, you might want to consider
|
90
|
+
# a different class hieararchy where you don't have to do this. But it's here.
|
91
|
+
def remove_derivative_definition!(*keys)
|
92
|
+
keys = keys.collect(&:to_sym)
|
93
|
+
self.kithe_derivative_definitions = self.kithe_derivative_definitions.reject do |defn|
|
94
|
+
keys.include?(defn.key.to_sym)
|
95
|
+
end.freeze
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
register_plugin(:kithe_derivative_definitions, KitheDerivativeDefinitions)
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'mini_mime'
|
2
|
+
|
3
|
+
class Shrine
|
4
|
+
module Plugins
|
5
|
+
# Includes the Shrine `derivatives` plugin with some configuration, and
|
6
|
+
# extra features. The metadata for shrine derivatives is stored in the same
|
7
|
+
# JSON as the main file.
|
8
|
+
#
|
9
|
+
# * default kithe storage location of :kithe_derivatives
|
10
|
+
#
|
11
|
+
# * nice metadata["filename"] for derivatives, instead of default shrine fairly
|
12
|
+
# random (filename ends up used by default in content-disposition headers when delivered)
|
13
|
+
#
|
14
|
+
# * Includes kithe_persisted_derivatives with #add_persisted_derivatives
|
15
|
+
# and #create_persisted_derivatives methods for concurrency-safe
|
16
|
+
# derivative persisting.
|
17
|
+
#
|
18
|
+
# ## Shrine derivatives references
|
19
|
+
#
|
20
|
+
# https://shrinerb.com/docs/plugins/derivatives
|
21
|
+
# https://shrinerb.com/docs/processing
|
22
|
+
class KitheDerivatives
|
23
|
+
def self.load_dependencies(uploader, *)
|
24
|
+
uploader.plugin :derivatives, storage: -> (derivative) do
|
25
|
+
# default derivatives storage to
|
26
|
+
:kithe_derivatives
|
27
|
+
end
|
28
|
+
|
29
|
+
uploader.plugin :kithe_persisted_derivatives
|
30
|
+
uploader.plugin :kithe_derivative_definitions
|
31
|
+
end
|
32
|
+
|
33
|
+
module InstanceMethods
|
34
|
+
|
35
|
+
# Override to fix "filename" metadata to be something reasonable, regardless
|
36
|
+
# of what if anything was the filename of the IO being attached. shrine S3 will
|
37
|
+
# insist on setting a default content-disposition with this filename.
|
38
|
+
def extract_metadata(io, derivative:nil, **context)
|
39
|
+
result = super
|
40
|
+
|
41
|
+
if derivative && context[:record] && result["mime_type"]
|
42
|
+
extension = MiniMime.lookup_by_content_type(result["mime_type"] || "")&.extension || "bin"
|
43
|
+
result["filename"] = "#{context[:record].friendlier_id}_#{derivative}.#{extension}"
|
44
|
+
end
|
45
|
+
|
46
|
+
result
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
register_plugin(:kithe_derivatives, KitheDerivatives)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class Shrine
|
2
|
+
module Plugins
|
3
|
+
# Custom kithe logic for determining mime type, using the shrine mime_type plugin.
|
4
|
+
#
|
5
|
+
# We start out using the `marcel` analyzer.
|
6
|
+
# Marcel analyzer is pure-ruby and fast. It's from Basecamp and is what
|
7
|
+
# ActiveStorage uses. It is very similar to :mimemagic (and uses mimemagic
|
8
|
+
# under the hood), but mimemagic seems not to be maintained with up to date
|
9
|
+
# magic db? https://github.com/minad/mimemagic/pull/66
|
10
|
+
#
|
11
|
+
# But marcel is not able to catch some of our MP3s as audio/mpeg. The
|
12
|
+
# `mediainfo` CLI is, and is one of the tools Harvard FITS uses.
|
13
|
+
# If marcel came up blank, AND we are configured to use mediainfo CLI
|
14
|
+
# (which by default we will be if it's available), we will try
|
15
|
+
# shelling out to mediainfo command line.
|
16
|
+
#
|
17
|
+
# https://github.com/MediaArea/MediaInfo
|
18
|
+
#
|
19
|
+
# Ensure that if mime-type can't be otherwise determined, it is assigned
|
20
|
+
# "application/octet-stream", basically the type for generic binary.
|
21
|
+
class KitheDetermineMimeType
|
22
|
+
def self.load_dependencies(uploader, *)
|
23
|
+
uploader.plugin :determine_mime_type, analyzer: -> (io, analyzers) do
|
24
|
+
mime_type = analyzers[:marcel].call(io)
|
25
|
+
|
26
|
+
|
27
|
+
if Kithe.use_mediainfo && mime_type == "application/octet-stream" || mime_type.blank?
|
28
|
+
mime_type = Kithe::MediainfoAnalyzer.new.call(io)
|
29
|
+
end
|
30
|
+
|
31
|
+
mime_type = "application/octet-stream" if mime_type.blank?
|
32
|
+
|
33
|
+
mime_type
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
register_plugin(:kithe_determine_mime_type, KitheDetermineMimeType)
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
class Shrine
|
2
|
+
module Plugins
|
3
|
+
# Some convenience methods for adding/changing derivatives in
|
4
|
+
# concurrency-safe manner:
|
5
|
+
#
|
6
|
+
# * Won't make a change if the underlying original has changed
|
7
|
+
# so doesn't match the one you wanted to remove.
|
8
|
+
# * Won't over-write changes made concurrently in the db by other processes
|
9
|
+
# * Will always make sure to clean up any temporary files on all error
|
10
|
+
# and falure conditions.
|
11
|
+
#
|
12
|
+
# Shrine has some building blocks for this, which we use, but it's a bit tricky
|
13
|
+
# to put them together to be generically reliable, as we think we've done here.
|
14
|
+
#
|
15
|
+
# All these methods will cause your Asset model to be saved, because of how
|
16
|
+
# the shrine atomic helpers work. So these methods will by default raise
|
17
|
+
# a TypeError if your Asset model has any unrelated outstanding changes,
|
18
|
+
# but you can tell it to save anyway with `allow_other_changes: true`.
|
19
|
+
#
|
20
|
+
# ## Shrine references:
|
21
|
+
#
|
22
|
+
# https://shrinerb.com/docs/plugins/derivatives
|
23
|
+
# https://shrinerb.com/docs/processing
|
24
|
+
class KithePersistedDerivatives
|
25
|
+
module AttacherMethods
|
26
|
+
# Like the shrine `add_derivatives` method, but also *persists* the
|
27
|
+
# derivatives (saves to db), in a realiably concurrency-safe way.
|
28
|
+
#
|
29
|
+
# Generally can take any options that shrine `add_derivatives`
|
30
|
+
# can take, including custom `storage` or `metadata` arguments.
|
31
|
+
#
|
32
|
+
# Like shrine add_derivatives, it will assume the files passed in are
|
33
|
+
# temporary, and delete them for you. If you want to disable this behavior:
|
34
|
+
#
|
35
|
+
# attacher.add_persisted_derivatives({key: io}, delete: false)
|
36
|
+
#
|
37
|
+
# In some cases the derivatives can't be persisted because the underlying
|
38
|
+
# database has changed such that they would not be applicable. In those
|
39
|
+
# cases `false` will be return value, otherwise returns the new derivatives
|
40
|
+
# just as shrine `add_derivatives`
|
41
|
+
#
|
42
|
+
# Because the concurrent-safe persistence method will save the associated model --
|
43
|
+
# and save without ActiveRecord validation -- it is not safe to
|
44
|
+
# add_persisted_derivatives on a model with other unsaved changes. The
|
45
|
+
# method will by default refuse to do so, throwing a TypeError. If you'd
|
46
|
+
# like to force it, pass `allow_other_changes: true` as an argument.
|
47
|
+
#
|
48
|
+
# Also takes care of deleting any replaced derivative files, that are no longer
|
49
|
+
# referenced by the model. Shrine by default does not do this:
|
50
|
+
# https://github.com/shrinerb/shrine/issues/468
|
51
|
+
#
|
52
|
+
# All deletions are inline. In general this could be a fairly expensive operation,
|
53
|
+
# it can be wise to do it in a bg job.
|
54
|
+
def add_persisted_derivatives(local_files, **options)
|
55
|
+
other_changes_allowed = !!options.delete(:allow_other_changes)
|
56
|
+
if record && !other_changes_allowed && record.changed?
|
57
|
+
raise TypeError.new("Can't safely add_persisted_derivatives on model with unsaved changes. Pass `allow_other_changes: true` to force.")
|
58
|
+
end
|
59
|
+
|
60
|
+
existing_derivative_files = nil
|
61
|
+
|
62
|
+
# upload to storage
|
63
|
+
new_derivatives = upload_derivatives(local_files, **options)
|
64
|
+
|
65
|
+
begin
|
66
|
+
atomic_persist do |reloaded_attacher|
|
67
|
+
# record so we can delete any replaced ones...
|
68
|
+
existing_derivative_files = map_derivative(reloaded_attacher.derivatives).collect { |path, file| file }
|
69
|
+
|
70
|
+
# make sure we don't override derivatives created in other jobs, by
|
71
|
+
# first using the current up-to-date derivatives from db,
|
72
|
+
# then merging our changes in on top.
|
73
|
+
set_derivatives(reloaded_attacher.derivatives)
|
74
|
+
merge_derivatives(new_derivatives)
|
75
|
+
end
|
76
|
+
rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound => e
|
77
|
+
# underlying file has changed or model has been deleted, inappropriate
|
78
|
+
# to add the derivatives, we can just silently drop them, but clean
|
79
|
+
# up after ourselves.
|
80
|
+
delete_derivatives(local_files) unless options[:delete] == false
|
81
|
+
delete_derivatives(new_derivatives)
|
82
|
+
|
83
|
+
return false
|
84
|
+
rescue StandardError => e
|
85
|
+
# unexpected error, clean up our files and re-raise
|
86
|
+
delete_derivatives(local_files) unless options[:delete] == false
|
87
|
+
delete_derivatives(new_derivatives)
|
88
|
+
raise e
|
89
|
+
end
|
90
|
+
|
91
|
+
# Take care of deleting from storage any derivatives that were replaced.
|
92
|
+
current_derivative_files = map_derivative(derivatives).collect { |path, file| file }
|
93
|
+
replaced_files = existing_derivative_files - current_derivative_files
|
94
|
+
delete_derivatives(replaced_files)
|
95
|
+
|
96
|
+
new_derivatives
|
97
|
+
end
|
98
|
+
|
99
|
+
# Like the shrine `create_derivatives` method, but persists the created derivatives
|
100
|
+
# to the database in a concurrency-safe way.
|
101
|
+
#
|
102
|
+
# Can take all options that shrine `create_derivatives` can take, including custom
|
103
|
+
# processors, custom storage key, and arbitrary custom processor arguments.
|
104
|
+
#
|
105
|
+
# asset.file_attacher.create_persisted_derivatives
|
106
|
+
# asset.file_attacher.create_persisted_derivatives(storage: :custom_key)
|
107
|
+
# asset.file_attacher.create_persisted_derivatives(:kithe_derivatives)
|
108
|
+
# asset.file_attacher.create_persisted_derivatives(:kithe_derivatives, some_arg: "value")
|
109
|
+
# asset.file_attacher.create_persisted_derivatives(:kithe_derivatives, alternate_source_file)
|
110
|
+
#
|
111
|
+
# Also has an `allow_other_changes` argument, see #add_persisted_derivatives.
|
112
|
+
def create_persisted_derivatives(*args, storage: nil, allow_other_changes: false, **options)
|
113
|
+
return false unless file
|
114
|
+
|
115
|
+
local_files = process_derivatives(*args, **options)
|
116
|
+
add_persisted_derivatives(local_files, storage: storage, allow_other_changes: allow_other_changes)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Kind of like built-in Shrine #remove_derivatives, but also takes care of
|
120
|
+
# persisting AND deleting the removed derivative file from storage --
|
121
|
+
# all in concurrency-safe way, including not making sure to overwrite
|
122
|
+
# any unrelated derivatives someone else was adding.
|
123
|
+
#
|
124
|
+
# Can take the same sorts of path arguments as Shrine derivative #remove_derivatives
|
125
|
+
#
|
126
|
+
# asset.file_attacher.remove_persisted_derivatives(:small_thumb)
|
127
|
+
# asset.file_attacher.remove_persisted_derivatives(:small_thumb, :large_thumb)
|
128
|
+
# asset.file_attacher.remove_persisted_derivatives(:small_thumb, :large_thumb, allow_other_changes: true)
|
129
|
+
def remove_persisted_derivatives(*paths, **options)
|
130
|
+
return if paths.empty?
|
131
|
+
|
132
|
+
other_changes_allowed = !!options.delete(:allow_other_changes)
|
133
|
+
if record && !other_changes_allowed && record.changed?
|
134
|
+
raise TypeError.new("Can't safely add_persisted_derivatives on model with unsaved changes. Pass `allow_other_changes: true` to force.")
|
135
|
+
end
|
136
|
+
|
137
|
+
removed_derivatives = nil
|
138
|
+
atomic_persist do |reloaded_attacher|
|
139
|
+
set_derivatives(reloaded_attacher.derivatives)
|
140
|
+
removed_derivatives = remove_derivatives(*paths, delete: false)
|
141
|
+
end
|
142
|
+
|
143
|
+
if removed_derivatives
|
144
|
+
map_derivative(removed_derivatives) do |_, derivative|
|
145
|
+
derivative.delete if derivative
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
removed_derivatives
|
150
|
+
rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
|
151
|
+
# original was already deleted or changed, the derivatives wer'e trying to delete.
|
152
|
+
# It should be fine to do nothing, the process that deleted or changed
|
153
|
+
# the model should already have deleted all these derivatives.
|
154
|
+
# But we'll return false as a signel.
|
155
|
+
return false
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
register_plugin(:kithe_persisted_derivatives, KithePersistedDerivatives)
|
160
|
+
end
|
161
|
+
end
|