kithe 2.0.0.pre.alpha2 → 2.0.0.pre.beta1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|