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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/app/jobs/kithe/create_derivatives_job.rb +2 -2
  3. data/app/models/kithe/asset.rb +82 -154
  4. data/app/models/kithe/asset/derivative_creator.rb +32 -62
  5. data/app/models/kithe/asset/derivative_definition.rb +12 -13
  6. data/app/models/kithe/asset/set_shrine_uploader.rb +64 -0
  7. data/app/models/kithe/collection.rb +0 -6
  8. data/app/models/kithe/model.rb +0 -21
  9. data/app/models/kithe/work.rb +0 -5
  10. data/app/uploaders/kithe/asset_uploader.rb +15 -78
  11. data/lib/kithe/version.rb +4 -1
  12. data/lib/shrine/plugins/kithe_checksum_signatures.rb +41 -0
  13. data/lib/shrine/plugins/kithe_controllable_backgrounding.rb +53 -0
  14. data/lib/shrine/plugins/kithe_derivative_definitions.rb +101 -0
  15. data/lib/shrine/plugins/kithe_derivatives.rb +54 -0
  16. data/lib/shrine/plugins/kithe_determine_mime_type.rb +39 -0
  17. data/lib/shrine/plugins/kithe_persisted_derivatives.rb +161 -0
  18. data/lib/shrine/plugins/kithe_promotion_callbacks.rb +4 -0
  19. data/lib/shrine/plugins/kithe_promotion_directives.rb +33 -3
  20. data/lib/shrine/plugins/kithe_storage_location.rb +53 -4
  21. data/lib/tasks/kithe_tasks.rake +22 -15
  22. data/spec/dummy/log/development.log +867 -0
  23. data/spec/dummy/log/test.log +26005 -0
  24. data/spec/models/kithe/asset/asset_derivatives_spec.rb +137 -0
  25. data/spec/models/kithe/asset/asset_promotion_hooks_spec.rb +26 -5
  26. data/spec/models/kithe/asset/set_shrine_uploader_spec.rb +39 -0
  27. data/spec/models/kithe/asset_spec.rb +9 -59
  28. data/spec/models/kithe/model_spec.rb +0 -32
  29. data/spec/shrine/kithe_accept_remote_url_spec.rb +49 -0
  30. data/spec/shrine/kithe_checksum_signatures_spec.rb +63 -0
  31. data/spec/shrine/kithe_derivative_definitions_spec.rb +303 -0
  32. data/spec/shrine/kithe_persisted_derivatives_spec.rb +424 -0
  33. data/spec/shrine/kithe_storage_location_spec.rb +43 -15
  34. data/spec/spec_helper.rb +7 -6
  35. data/spec/test_support/images/3x3_pixel.jpg +0 -0
  36. data/spec/test_support/shrine_spec_support.rb +2 -1
  37. metadata +23 -23
  38. data/app/models/kithe/asset/derivative_updater.rb +0 -119
  39. data/app/models/kithe/derivative.rb +0 -15
  40. data/app/uploaders/kithe/derivative_uploader.rb +0 -48
  41. data/spec/models/kithe/asset/asset_create_derivatives_spec.rb +0 -320
  42. 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:, storage_key:, proc:, content_type: nil, default_create: true)
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:,record:)
14
- if proc_accepts_record_keyword?
15
- proc.call(original_file, record: record)
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 applies_to?(asset)
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 == asset.content_type
24
+ return true if content_type == original_content_type
26
25
 
27
- return false if asset.content_type.nil?
26
+ return false if original_content_type.nil?
28
27
 
29
- return true if (content_type.kind_of?(Array) && content_type.include?(asset.content_type))
28
+ return true if (content_type.kind_of?(Array) && content_type.include?(original_content_type))
30
29
 
31
- content_type == asset.content_type.sub(%r{/.+\Z}, '')
30
+ content_type == original_content_type.sub(%r{/.+\Z}, '')
32
31
  end
33
32
 
34
33
  private
35
34
 
36
- def proc_accepts_record_keyword?
37
- proc.parameters.include?([:key, :record]) || proc.parameters.include?([:keyreq, :record])
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
@@ -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.
@@ -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 magicc-byte analyzer can't determine mime type, will fall back to `mediainfo`
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
- # promotion and deletion will (sometimes) be in background.
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
- # Normally we promote in background with backgrounding, but the set_promotion_directives
68
- # feature can be used to make promotion not happen at all, or happen in foreground.
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
- # Allows you to assign hashes like:
102
- # { "id" => "http://url", "storage" => "remote_url", headers: { "Authorization" => "Bearer whatever"}}
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
- # We want to store md5 and sha1 checksums (legacy compat), as well as
108
- # sha512 (more recent digital preservation recommendation: https://ocfl.io/draft/spec/#digests)
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
@@ -1,3 +1,6 @@
1
1
  module Kithe
2
- VERSION = '2.0.0-alpha2'
2
+ # not sure why rubygems turned our alphas into 2.0.0.pre.alpha1, inserting
3
+ # "pre". We need to do same thing with betas to get version orderings
4
+ # appropriate.
5
+ VERSION = '2.0.0.pre.beta1'
3
6
  end
@@ -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