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
@@ -2,38 +2,37 @@
|
|
2
2
|
# class, it's what's created when you call Kithe::Asset#define_derivative
|
3
3
|
class Kithe::Asset::DerivativeDefinition
|
4
4
|
attr_reader :key, :content_type, :default_create, :proc, :storage_key
|
5
|
-
def initialize(key:,
|
6
|
-
@key = key
|
5
|
+
def initialize(key:, proc:, content_type: nil, default_create: true)
|
6
|
+
@key = key.to_sym
|
7
7
|
@content_type = content_type
|
8
|
-
@storage_key = storage_key
|
9
8
|
@default_create = default_create
|
10
9
|
@proc = proc
|
11
10
|
end
|
12
11
|
|
13
|
-
def call(original_file:,
|
14
|
-
if
|
15
|
-
proc.call(original_file,
|
12
|
+
def call(original_file:,attacher:)
|
13
|
+
if proc_accepts_keyword?(:attacher)
|
14
|
+
proc.call(original_file, attacher: attacher)
|
16
15
|
else
|
17
16
|
proc.call(original_file)
|
18
17
|
end
|
19
18
|
end
|
20
19
|
|
21
20
|
# Do content-type restrictions defined for this definition match a given asset?
|
22
|
-
def
|
21
|
+
def applies_to_content_type?(original_content_type)
|
23
22
|
return true if content_type.nil?
|
24
23
|
|
25
|
-
return true if content_type ==
|
24
|
+
return true if content_type == original_content_type
|
26
25
|
|
27
|
-
return false if
|
26
|
+
return false if original_content_type.nil?
|
28
27
|
|
29
|
-
return true if (content_type.kind_of?(Array) && content_type.include?(
|
28
|
+
return true if (content_type.kind_of?(Array) && content_type.include?(original_content_type))
|
30
29
|
|
31
|
-
content_type ==
|
30
|
+
content_type == original_content_type.sub(%r{/.+\Z}, '')
|
32
31
|
end
|
33
32
|
|
34
33
|
private
|
35
34
|
|
36
|
-
def
|
37
|
-
proc.parameters.include?([:key,
|
35
|
+
def proc_accepts_keyword?(kwarg)
|
36
|
+
proc.parameters.include?([:key, kwarg]) || proc.parameters.include?([:keyreq, kwarg]) || proc.parameters.find {|a| a.first == :keyrest}
|
38
37
|
end
|
39
38
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# Our Kithe::Asset model class is meant to be a superclass of a local application asset class, which we
|
2
|
+
# can call `Asset`, although an app can call it whatever they like.
|
3
|
+
#
|
4
|
+
# Kithe::Asset sets it's own shrine uploader class, with a typical shrine:
|
5
|
+
#
|
6
|
+
# include Kithe::AssetUploader::Attachment(:file)
|
7
|
+
#
|
8
|
+
# An application Asset subclass will inherit this uploader, which is convenient for getting
|
9
|
+
# started quickly. But an application will likely want to define its own local uploader
|
10
|
+
# class, to define it's own metadata, derivatives, and any other custom beahvior.
|
11
|
+
#
|
12
|
+
# There isn't an obvious built-into-shrine way to do that, but it turns out simply overriding
|
13
|
+
# class and instance `*_attacher` methods seems to work out well. See:
|
14
|
+
# https://discourse.shrinerb.com/t/model-sub-classes-with-uploader-sub-classes/208
|
15
|
+
#
|
16
|
+
# So a local application can define it's own shrine uploader, which is highly recommended to
|
17
|
+
# be a sub-class of Kithe::AssetUploader to ensure it has required and useful
|
18
|
+
# Kithe behavior:
|
19
|
+
#
|
20
|
+
# # ./app/uploaders/asset_uploader.rb
|
21
|
+
# class AssetUploader < Kithe::AssetUploader
|
22
|
+
# # maybe we want some custom metadata
|
23
|
+
# add_metadata :something do |io|
|
24
|
+
# whatever
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# And then set it in ti's custom local Asset class:
|
29
|
+
#
|
30
|
+
# # ./app/models/asset.rb
|
31
|
+
# class Asset < Kithe::Asset
|
32
|
+
# set_shrine_uploader(AssetUploader)
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# If a local app has it's own inheritance hieararchy of children below that (eg) Asset class,
|
36
|
+
# they can each (optionally) also override with a custom Uploader. It is recommended that
|
37
|
+
# the Uploader inheritance hieararchy match the model inheritance hieararchy, to have
|
38
|
+
# all behavior consistent. For instance:
|
39
|
+
#
|
40
|
+
# class AudioAssetUploader < AssetUploader
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# class AudioAsset < Asset
|
44
|
+
# set_shrine_uploader(AudioAssetUploader)
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
module Kithe::Asset::SetShrineUploader
|
48
|
+
extend ActiveSupport::Concern
|
49
|
+
|
50
|
+
class_methods do
|
51
|
+
def set_shrine_uploader(uploader_class)
|
52
|
+
subclass_attachment = uploader_class::Attachment.new(:file)
|
53
|
+
|
54
|
+
define_singleton_method :file_attacher do |**options|
|
55
|
+
subclass_attachment.send(:class_attacher, **options)
|
56
|
+
end
|
57
|
+
|
58
|
+
define_method :file_attacher do |**options|
|
59
|
+
subclass_attachment.send(:attacher, self, **options)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -1,14 +1,8 @@
|
|
1
1
|
class Kithe::Collection < Kithe::Model
|
2
|
-
# Collections don't have derivatives, but we want to allow Rails eager loading
|
3
|
-
# of association on hetereogenous fetches of Kithe::Model, so this is clever.
|
4
|
-
has_many :derivatives, -> { none }
|
5
|
-
private :derivatives, :derivatives=, :derivative_ids, :derivative_ids=
|
6
|
-
|
7
2
|
after_initialize do
|
8
3
|
self.kithe_model_type = "collection" if self.kithe_model_type.nil?
|
9
4
|
end
|
10
5
|
before_validation do
|
11
6
|
self.kithe_model_type = "collection" if self.kithe_model_type.nil?
|
12
7
|
end
|
13
|
-
|
14
8
|
end
|
data/app/models/kithe/model.rb
CHANGED
@@ -10,15 +10,6 @@ class Kithe::Model < ActiveRecord::Base
|
|
10
10
|
include AttrJson::Record::Dirty
|
11
11
|
include Kithe::Indexable
|
12
12
|
|
13
|
-
# A handy scope for eager-loading all representatives and all of their derivatives.
|
14
|
-
#
|
15
|
-
# Works on hetereogenous collections of Works and Assets -- the Assets need
|
16
|
-
# :derivatives directly referenced (since they don't really have a leaf_representative assoc),
|
17
|
-
# the works need :leaf_representative => :derivatives.
|
18
|
-
#
|
19
|
-
# Loading all three of these on result sets of hundreds of values is still relatively quick.
|
20
|
-
scope :with_representative_derivatives, -> { includes(:derivatives, leaf_representative: :derivatives) }
|
21
|
-
|
22
13
|
# While Rails STI means the actual specific class is in `type`, sometimes
|
23
14
|
# it can be convenient to fetch on a top category of Kithe::Model without using
|
24
15
|
# Rails STI.
|
@@ -100,18 +91,6 @@ class Kithe::Model < ActiveRecord::Base
|
|
100
91
|
in_memory
|
101
92
|
end
|
102
93
|
|
103
|
-
# hacky :(
|
104
|
-
def derivatives(*args)
|
105
|
-
raise TypeError.new("Only valid on Kithe::Asset") unless self.kind_of?(Kithe::Asset)
|
106
|
-
super
|
107
|
-
end
|
108
|
-
# hacky :(
|
109
|
-
def derivatives=(*args)
|
110
|
-
raise TypeError.new("Only valid on Kithe::Asset") unless self.kind_of?(Kithe::Asset)
|
111
|
-
super
|
112
|
-
end
|
113
|
-
|
114
|
-
|
115
94
|
# insist that leaf_representative is an Asset, otherwise return nil.
|
116
95
|
# nil means there is no _asset_ leaf, and lets caller rely on leaf being
|
117
96
|
# an asset.
|
data/app/models/kithe/work.rb
CHANGED
@@ -1,9 +1,4 @@
|
|
1
1
|
class Kithe::Work < Kithe::Model
|
2
|
-
# Works don't have derivatives, but we want to allow Rails eager loading
|
3
|
-
# of association on hetereogenous fetches of Kithe::Model, so this is clever.
|
4
|
-
has_many :derivatives, -> { none }
|
5
|
-
private :derivatives, :derivatives=, :derivative_ids, :derivative_ids=
|
6
|
-
|
7
2
|
after_initialize do
|
8
3
|
self.kithe_model_type = "work" if self.kithe_model_type.nil?
|
9
4
|
end
|
@@ -20,7 +20,7 @@ module Kithe
|
|
20
20
|
# FUTURE: Look at using client-side-calculated checksums to verify end-to-end.
|
21
21
|
# https://github.com/shrinerb/shrine/wiki/Using-Checksums-in-Direct-Uploads
|
22
22
|
#
|
23
|
-
# When
|
23
|
+
# When magic-byte analyzer can't determine mime type, will fall back to `mediainfo`
|
24
24
|
# CLI _if_ `Kithe.use_mediainfo` is true (defaults to true if mediainfo CLI is
|
25
25
|
# available). (We need better ways to customize uploader.)
|
26
26
|
class AssetUploader < Shrine
|
@@ -34,100 +34,37 @@ module Kithe
|
|
34
34
|
# promotion, possibly in the background.
|
35
35
|
plugin :refresh_metadata
|
36
36
|
|
37
|
-
# Marcel analyzer is pure-ruby and fast. It's from Basecamp and is what
|
38
|
-
# ActiveStorage uses. It is very similar to :mimemagic (and uses mimemagic
|
39
|
-
# under the hood), but mimemagic seems not to be maintained with up to date
|
40
|
-
# magic db? https://github.com/minad/mimemagic/pull/66
|
41
|
-
plugin :determine_mime_type, analyzer: -> (io, analyzers) do
|
42
|
-
mime_type = analyzers[:marcel].call(io)
|
43
|
-
|
44
|
-
# But marcel is not able to catch some of our MP3s as audio/mpeg,
|
45
|
-
# let's try mediainfo command line. mediainfo is one of the tools
|
46
|
-
# the Harvard Fits tool uses. https://github.com/MediaArea/MediaInfo
|
47
|
-
if Kithe.use_mediainfo && mime_type == "application/octet-stream" || mime_type.blank?
|
48
|
-
mime_type = Kithe::MediainfoAnalyzer.new.call(io)
|
49
|
-
end
|
50
|
-
|
51
|
-
mime_type = "application/octet-stream" if mime_type.blank?
|
52
|
-
|
53
|
-
mime_type
|
54
|
-
end
|
55
|
-
|
56
37
|
# Will save height and width to metadata for image types. (Won't for non-image types)
|
57
38
|
# ignore errors (often due to storing a non-image file), consistent with shrine 2.x behavior.
|
58
39
|
plugin :store_dimensions, on_error: :ignore
|
59
40
|
|
60
|
-
|
61
|
-
plugin :backgrounding
|
62
|
-
|
63
|
-
# Useful in case consumers want it, and doesn't harm anything to be available.
|
64
|
-
# https://github.com/shrinerb/shrine/blob/master/doc/plugins/rack_response.md
|
65
|
-
plugin :rack_response
|
41
|
+
plugin :infer_extension, inferrer: :mini_mime
|
66
42
|
|
67
|
-
#
|
68
|
-
|
69
|
-
# asset.file_attacher.set_promotion_directives(promote: false)
|
70
|
-
# asset.file_attacher.set_promotion_directives(promote: "inline")
|
71
|
-
Attacher.promote_block do
|
72
|
-
Kithe::TimingPromotionDirective.new(key: :promote, directives: self.promotion_directives) do |directive|
|
73
|
-
if directive.inline?
|
74
|
-
promote
|
75
|
-
elsif directive.background?
|
76
|
-
# What shrine normally expects for backgrounding, plus promotion_directives
|
77
|
-
Kithe::AssetPromoteJob.perform_later(self.class.name, record.class.name, record.id, name.to_s, file_data, self.promotion_directives)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
43
|
+
# Just leave it here for otheres please
|
44
|
+
plugin :add_metadata
|
81
45
|
|
82
|
-
# Delete using shrine backgrounding, but can be effected
|
83
|
-
# by promotion_directives[:delete], similar to promotion above.
|
84
|
-
# Yeah, not really a "promotion" directive, oh well.
|
85
|
-
Attacher.destroy_block do
|
86
|
-
Kithe::TimingPromotionDirective.new(key: :delete, directives: self.promotion_directives) do |directive|
|
87
|
-
if directive.inline?
|
88
|
-
destroy
|
89
|
-
elsif directive.background?
|
90
|
-
# What shrine normally expects for backgrounding
|
91
|
-
Kithe::AssetDeleteJob.perform_later(self.class.name, data)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
46
|
|
96
|
-
plugin :add_metadata
|
97
47
|
|
98
|
-
# Makes files stored as /asset/#{asset_pk}/#{random_uuid}.#{original_suffix}
|
99
|
-
plugin :kithe_storage_location
|
100
48
|
|
101
|
-
#
|
102
|
-
|
103
|
-
# (headers optional), for fetching remote urls on promotion. Useful with browse-everything.
|
104
|
-
# WARNING: There's no whitelist, will accept any url. Is this a problem?
|
105
|
-
plugin :kithe_accept_remote_url
|
49
|
+
# kithe-standard logic for sniffing mime type.
|
50
|
+
plugin :kithe_determine_mime_type
|
106
51
|
|
107
|
-
#
|
108
|
-
#
|
109
|
-
|
110
|
-
# We only calculate them on `store` action to avoid double-computation, and because for
|
111
|
-
# direct uploads/backgrounding, we haven't actually gotten the file in our hands to compute
|
112
|
-
# checksums until then anyway.
|
113
|
-
plugin :signature
|
114
|
-
add_metadata do |io, context|
|
115
|
-
if context[:action] != :cache
|
116
|
-
{
|
117
|
-
md5: calculate_signature(io, :md5),
|
118
|
-
sha1: calculate_signature(io, :sha1),
|
119
|
-
sha512: calculate_signature(io, :sha512)
|
120
|
-
}
|
121
|
-
end
|
122
|
-
end
|
123
|
-
metadata_method :md5, :sha1, :sha512
|
52
|
+
# Determines storage path/location/id, so files will be stored as:
|
53
|
+
# /asset/#{asset_pk}/#{random_uuid}.#{original_suffix}
|
54
|
+
plugin :kithe_storage_location
|
124
55
|
|
56
|
+
# Set up logic for shrine backgrounding, which in kithe can be set by promotion_directives
|
57
|
+
plugin :kithe_controllable_backgrounding
|
125
58
|
|
126
59
|
# Gives us (set_)promotion_directives methods on our attacher to
|
127
60
|
# house lifecycle directives, about whether promotion, deletion,
|
128
61
|
# derivatives happen in foreground, background, or not at all.
|
129
62
|
plugin :kithe_promotion_directives
|
130
63
|
|
64
|
+
# Makes our before/after promotion callbacks get called.
|
131
65
|
plugin :kithe_promotion_callbacks
|
66
|
+
|
67
|
+
# some configuration and convenience methods for shrine derivatives.
|
68
|
+
plugin :kithe_derivatives
|
132
69
|
end
|
133
70
|
end
|
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
|