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.
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