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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '087115597d14a7b5ac33827a00f62fb37a37d7186deea82f3d6204e09a77d0b4'
4
- data.tar.gz: a5f87fdf80a1f521999fcb5a039bcd28831e1427f71e2590729327bac356f148
3
+ metadata.gz: cfaa6ce3c41abdc0779014cb028d3ae7acbbfee6124eb24abca06f52e6f60c30
4
+ data.tar.gz: 4f3951e52b4175c86933870933fd38c0c84dfcf11a5ad8cd18f1eb8c479305b0
5
5
  SHA512:
6
- metadata.gz: 57c51b54e947477b1f69426caa6adc9beb37ea72c25f6210874e2e1837c4ad0a98ad4d43322ee5fddfa2c6e7fb2f311b01c05f9d58435785a3774180225bf23c
7
- data.tar.gz: 1ed33f2a15c07ce47959e21345694815820ed8daa11cf04c8cfdfa9fb274f95ecc56cf326a87c07112adc765f04bd55970ab54efea7f8e44b8a14ca2f8508393
6
+ metadata.gz: 6fe1836bde7b33a29d95324afe745ceeecb59ca95c8952775cce39dd8233c8daf171da971cc4a7bb2691e8dad2b91d50766c9232e3dce5b333da6f14d2f8d6bd
7
+ data.tar.gz: de9bcd5fc0c22c18d9dde7ea9ec1bdc708e08035ee0f5d208131628d6d37bbc61574ae213b0ef69e6161c1fbdf334aad545c734614e10cee8f63f96666ed86dc
@@ -1,7 +1,7 @@
1
1
  module Kithe
2
2
  class CreateDerivativesJob < Job
3
- def perform(asset, mark_created: nil)
4
- asset.create_derivatives(mark_created: mark_created)
3
+ def perform(asset)
4
+ asset.create_derivatives
5
5
  end
6
6
  # This error typically occurs when several large assets, whose derivatives
7
7
  # take a long time to generate, are deleted immediately after ingest.
@@ -1,5 +1,5 @@
1
1
  class Kithe::Asset < Kithe::Model
2
- has_many :derivatives, foreign_key: "asset_id", inverse_of: "asset", dependent: :destroy # dependent destroy to get shrine destroy logic for assets
2
+ include Kithe::Asset::SetShrineUploader
3
3
 
4
4
  # These associations exist for hetereogenous eager-loading, but hide em.
5
5
  # They are defined as self-pointing below.
@@ -26,15 +26,12 @@ class Kithe::Asset < Kithe::Model
26
26
  :md5, :sha1, :sha512,
27
27
  to: :file, allow_nil: true
28
28
  delegate :stored?, to: :file_attacher
29
- delegate :set_promotion_directives, to: :file_attacher
29
+ delegate :set_promotion_directives, :promotion_directives, to: :file_attacher
30
30
 
31
- after_save :remove_invalid_derivatives
32
31
 
33
- # will be sent to file_attacher.promotion_directives=, provided by our
32
+ # will be sent to file_attacher.set_promotion_directives, provided by our
34
33
  # kithe_promotion_hooks shrine plugin.
35
- class_attribute :promotion_directives, instance_writer: false, default: {}
36
-
37
- class_attribute :derivative_definitions, instance_writer: false, default: []
34
+ class_attribute :promotion_directives, instance_accessor: false, default: {}
38
35
 
39
36
  # Callbacks are called by our kiteh_promotion_callbacks shrine plugin, around
40
37
  # shrine promotion. A before callback can cancel promotion with the usual
@@ -45,112 +42,94 @@ class Kithe::Asset < Kithe::Model
45
42
  before_promotion :refresh_metadata_before_promotion
46
43
  after_promotion :schedule_derivatives
47
44
 
48
-
49
-
50
- # Establish a derivative definition that will be used to create a derivative
51
- # when #create_derivatives is called, for instance automatically after promotion.
45
+ # A convenience to call file_attacher.create_persisted_derivatives (from :kithe_derivatives)
46
+ # to create derivatives with conncurrent access safety, with the :kithe_derivatives
47
+ # processor argument, to create derivatives defined using kithe_derivative_definitions.
52
48
  #
53
- # The most basic definition consists of a derivative key, and a ruby block that
54
- # takes the original file, transforms it, and returns a ruby File or other
55
- # (shrine-compatible) IO-like object. It will usually be done inside a custom Asset
56
- # class definition.
49
+ # This is designed for use with kithe_derivatives processor, and has options only relevant
50
+ # to it, although could be expanded to take a processor argument in the future if needed.
57
51
  #
58
- # class Asset < Kithe::Asset
59
- # define_derivative :thumbnail do |original_file|
60
- # end
61
- # end
52
+ # Create derivatives for every definition added to uploader/attacher with kithe_derivatives
53
+ # `define_derivative`. Ordinarily will create a definition for every definition
54
+ # that has not been marked `default_create: false`.
62
55
  #
63
- # The original_file passed in will be a ruby File object that is already open for reading. If
64
- # you need a local file path for your transformation, just use `original_file.path`.
56
+ # But you can also pass `only` and/or `except` to customize the list of definitions to be created,
57
+ # possibly including some that are `default_create: false`.
65
58
  #
66
- # The return value can be any IO-like object. If it is a ruby File or Tempfile,
67
- # that temporary file will be deleted for you after the derivative has been created. If you
68
- # have to make any intermediate files, you are responsible for cleaning them up. Ruby stdlib
69
- # Tempfile and Dir.mktmpdir may be useful.
59
+ # create_derivatives should be idempotent. If it has failed having only created some derivatives,
60
+ # you can always just run it again.
70
61
  #
71
- # If in order to do your transformation you need additional information about the original,
72
- # just add a `record:` keyword argument to your block, and the Asset object will be passed in:
62
+ # Will normally re-create derivatives (per existing definitions) even if they already exist,
63
+ # but pass `lazy: false` to skip creating if a derivative with a given key already exists.
64
+ # This will use the asset `derivatives` association, so if you are doing this in bulk for several
65
+ # assets, you should eager-load the derivatives association for efficiency.
73
66
  #
74
- # define_derivative :thumbnail do |original_file, record:|
75
- # record.width, record.height, record.content_type # etc
76
- # end
67
+ # ## Avoiding eager-download
77
68
  #
78
- # Derivatives are normally uploaded to the Shrine storage labeled :kithe_derivatives,
79
- # but a definition can specify an alternate Shrine storage id. (specified shrine storage key
80
- # is applied on derivative creation; if you change it with existing derivatives, they should
81
- # remain, and be accessible, where they were created; there is no built-in solution at present
82
- # for moving them).
69
+ # Additionally, this has a feature to 'trick' shrine into not eager downloading
70
+ # the original before our kithe_derivatives processor has a chance to decide if it
71
+ # needs to create any derivatives (based on `lazy` arg), making no-op
72
+ # create_derivatives so much faster and more efficient, which matters when doing
73
+ # bulk operations say with our rake task.
83
74
  #
84
- # define_derivative :thumbnail, storage_key: :my_thumb_storage do |original| # ...
75
+ # kithe_derivatives processor must then do a Shrine.with_file to make sure
76
+ # it's a local file, when needed.
85
77
  #
86
- # You can also set `default_create: false` if you want a particular definition not to be
87
- # included in a no-arg `asset.create_derivatives` that is normally triggered on asset creation.
78
+ # See https://github.com/shrinerb/shrine/issues/470
88
79
  #
89
- # And you can set content_type to either a specific type like `image/jpeg` (or array of such) or a general type
90
- # like `image`, if you want to define a derivative generation routine for only certain types.
91
- # If multiple blocks for the same key are defined, with different content_type restrictions,
92
- # the most specific one will be used. That is, for a JPG, `image/jpeg` beats `image` beats no restriction.
93
- def self.define_derivative(key, storage_key: :kithe_derivatives, content_type: nil, default_create: true, &block)
94
- # Make sure we dup the array to handle sub-classes on class_attribute
95
- self.derivative_definitions = self.derivative_definitions.dup.push(
96
- DerivativeDefinition.new(
97
- key: key,
98
- storage_key: storage_key,
99
- content_type: content_type,
100
- default_create: default_create,
101
- proc: block
102
- )
103
- )
104
- end
80
+ def create_derivatives(only: nil, except: nil, lazy: false)
81
+ source = file
82
+ return false unless source
105
83
 
106
- # Returns all derivative keys with a definition, as array of strings
107
- def self.defined_derivative_keys
108
- self.derivative_definitions.collect(&:key).uniq.collect(&:to_s)
109
- end
84
+ #local_files = file_attacher.process_derivatives(:kithe_derivatives, only: only, except: except, lazy: lazy)
85
+ local_files = _process_kithe_derivatives_without_download(source, only: only, except: except, lazy: lazy)
110
86
 
111
- # If you have a subclass that has inherited derivative definitions, you can
112
- # remove them -- only by key, will remove any definitions with that key regardless
113
- # of content_type restrictions.
114
- #
115
- # This could be considered rather bad OO design, you might want to consider
116
- # a different class hieararchy where you don't have to do this. But it's here.
117
- def self.remove_derivative_definition!(*keys)
118
- keys = keys.collect(&:to_sym)
119
- self.derivative_definitions = self.derivative_definitions.reject do |defn|
120
- keys.include?(defn.key.to_sym)
121
- end
87
+ file_attacher.add_persisted_derivatives(local_files)
122
88
  end
123
89
 
124
- # Create derivatives for every definition added with `define_derivative. Ordinarily
125
- # will create a definition for every definition that has not been marked `default_create: false`.
90
+ # Working around Shrine's insistence on pre-downloading original before calling derivative processor.
91
+ # We want to avoid that, so when our `lazy` argument is in use, original does not get eagerly downloaded,
92
+ # but only gets downloaded if needed to make derivatives.
126
93
  #
127
- # But you can also pass `only` and/or `except` to customize the list of definitions to be created,
128
- # possibly including some that are `default_create: false`.
94
+ # This is a somewhat hacky way to do that, loking at the internals of shrine `process_derivatives`,
95
+ # and pulling them out to skip the parts we don't want. We also lose shrine instrumentation
96
+ # around this action.
129
97
  #
130
- # create_derivatives should be idempotent. If it has failed having only created some derivatives,
131
- # you can always just run it again.
98
+ # See: https://github.com/shrinerb/shrine/issues/470
132
99
  #
133
- # Will normally re-create derivatives (per existing definitions) even if they already exist,
134
- # but pass `lazy: false` to skip creating if a derivative with a given key already exists.
135
- # This will use the asset `derivatives` association, so if you are doing this in bulk for several
136
- # assets, you should eager-load the derivatives association for efficiency.
137
- def create_derivatives(only: nil, except: nil, lazy: false, mark_created: nil)
138
- DerivativeCreator.new(derivative_definitions, self, only: only, except: except, lazy: lazy, mark_created: mark_created).call
100
+ # If that were resolved, the 'ordinary' shrine thing would be to replace calls
101
+ # to this local private method with:
102
+ #
103
+ # file_attacher.process_derivatives(:kithe_derivatives, only: only, except: except, lazy: lazy)
104
+ #
105
+ private def _process_kithe_derivatives_without_download(source, **options)
106
+ processor = file_attacher.class.derivatives_processor(:kithe_derivatives)
107
+ local_files = file_attacher.instance_exec(source, **options, &processor)
139
108
  end
140
109
 
141
- # Adds an associated derivative with key and io bytestream specified.
110
+ # Just a convennience for file_attacher.add_persisted_derivatives (from :kithe_derivatives),
111
+ # feel free to use that if you want to add more than one etc. By default stores to
112
+ # :kithe_derivatives, just as `add_persisted_derivatives` does.
113
+ #
114
+ # Note that just like shrine's own usual `add_derivative(s)`, it assumes any files
115
+ # you pass it are meant to be temporary and will delete them, unless you pass
116
+ # `delete: false`.
117
+ #
118
+ # Adds an associated derivative with key and io bytestream specified,
119
+ # doing so in a way that is safe from race conditions under multi-process
120
+ # concurrency.
121
+ #
142
122
  # Normally you don't use this with derivatives defined with `define_derivative`,
143
123
  # this is used by higher-level API. But if you'd like to add a derivative not defined
144
124
  # with `define_derivative`, or for any other reason would like to manually add
145
- # a derivative, this is public API meant for that.
125
+ # a derivative.
146
126
  #
147
- # Ensures safe from race conditions under multi-thread/process concurrency, to
148
- # make sure any existing derivative with same key is atomically replaced,
149
- # and if the asset#file is changed to a different bytestream (compared to what's in memory
150
- # for this asset), we don't end up saving a derivative based on old one.
127
+ # Can specify any options normally allowed for kithe `add_persisted_derivatives`,
128
+ # which are also generally any allowed for shrine `add_derivative`.
151
129
  #
152
- # Can specify any metadata values to be force set on the Derivative#file, and
153
- # a specific Shrine storage key (defaults to :kithe_derivatives shrine storage)
130
+ # asset.update_derivative("big_thumb", File.open(something))
131
+ # asset.update_derivative("something", File.open(something), delete: false)
132
+ # asset.update_derivative("something", File.open(something), storage_key: :registered_storage, metadata: { "foo": "bar" })
154
133
  #
155
134
  # @param key derivative-type identifying key
156
135
  # @param io An IO-like object (according to Shrine), bytestream for the derivative
@@ -158,30 +137,20 @@ class Kithe::Asset < Kithe::Model
158
137
  # @param metadata an optional hash of key/values to set as default metadata for the Derivative#file
159
138
  # shrine object.
160
139
  #
161
- # @return [Derivative] the Derivative created, or nil if it was not created because no longer
140
+ # @return [Shrine::UploadedFile] the Derivative created, or false if it was not created because no longer
162
141
  # applicable (underlying Asset#file has changed in db)
163
- def update_derivative(key, io, storage_key: :kithe_derivatives, metadata: {})
164
- DerivativeUpdater.new(self, key, io, storage_key: storage_key, metadata: metadata).update.tap do |result|
165
- self.derivatives.reset if result
166
- end
142
+ def update_derivative(key, io, **options)
143
+ result = update_derivatives({ key => io }, **options)
144
+ result && result.values.first
167
145
  end
168
146
 
169
- def remove_derivative(key)
170
- if association(:derivatives).loaded?
171
- derivatives.find_all { |d| d.key == key.to_s }.each do |deriv|
172
- derivatives.delete(deriv)
173
- end
174
- else
175
- Kithe::Derivative.where(key: key.to_s, asset: self).each do |deriv|
176
- deriv.destroy!
177
- end
178
- end
147
+ def update_derivatives(deriv_hash, **options)
148
+ file_attacher.add_persisted_derivatives(deriv_hash, **options)
179
149
  end
180
150
 
181
- # Just finds the Derivative object matching supplied key. if you're going to be calling
182
- # this on a list of Asset objects, make sure to preload :derivatives association.
183
- def derivative_for(key)
184
- derivatives.find {|d| d.key == key.to_s }
151
+ # just a convenience for kithe remove_persisted_derivatives
152
+ def remove_derivatives(*keys)
153
+ file_attacher.remove_persisted_derivatives(*keys)
185
154
  end
186
155
 
187
156
  # Runs the shrine promotion step, that we normally have in backgrounding, manually
@@ -205,41 +174,6 @@ class Kithe::Asset < Kithe::Model
205
174
  file_attacher.atomic_promote(**context)
206
175
  end
207
176
 
208
- # The derivative creator sets metadata when it's created all derivatives
209
- # defined as `default_create`. So we can tell you if it's done or not.
210
- def derivatives_created?
211
- if file
212
- !!file.metadata["derivatives_created"]
213
- end
214
- end
215
-
216
- # Take out a DB lock on the asset with unchanged sha512 saved in metadata. If a lock
217
- # can't be acquired -- which would be expected to be because the asset has already changed
218
- # and has a new sha for some reason -- returns nil.
219
- #
220
- # Useful for making a change to an asset making sure it applies to a certain original file.
221
- #
222
- # Needs to be done in a transaction, and you should keep the transaction SMALL AS POSSIBLE.
223
- # We can't check to make sure you're in a transaction reliably because of Rails transactional
224
- # tests, you gotta do it!
225
- #
226
- # This method is mostly intended for internal Kithe use, cause it's a bit tricky.
227
- def acquire_lock_on_sha
228
- raise ArgumentError.new("Can't acquire lock without sha512 in metadata") if self.sha512.blank?
229
-
230
- Kithe::Asset.where(id: self.id).where("file_data -> 'metadata' ->> 'sha512' = ?", self.sha512).lock.first
231
- end
232
-
233
- # Intentionally only true if there WAS a sha512 before AND it's changed.
234
- # Allowing false on nil previous sha512 allows certain conditions, mostly only
235
- # in testing, where you want to assign a derivative to not-yet-promoted file.
236
- def saved_change_to_file_sha?
237
- saved_change_to_file_data? &&
238
- saved_change_to_file_data.first.try(:dig, "metadata", "sha512") != nil &&
239
- saved_change_to_file_data.first.try(:dig, "metadata", "sha512") !=
240
- saved_change_to_file_data.second.try(:dig, "metadata", "sha512")
241
- end
242
-
243
177
  # An Asset is it's own representative
244
178
  def representative
245
179
  self
@@ -252,8 +186,10 @@ class Kithe::Asset < Kithe::Model
252
186
 
253
187
  def initialize(*args)
254
188
  super
255
- if promotion_directives.present?
256
- file_attacher.set_promotion_directives(promotion_directives)
189
+
190
+ # copy class-level global promotion directives as initial instance value
191
+ if self.class.promotion_directives.present?
192
+ self.set_promotion_directives(self.class.promotion_directives)
257
193
  end
258
194
  end
259
195
 
@@ -261,7 +197,7 @@ class Kithe::Asset < Kithe::Model
261
197
 
262
198
  # called by after_promotion hook
263
199
  def schedule_derivatives
264
- return unless self.derivative_definitions.present? # no need to schedule if we don't have any
200
+ return unless self.file_attacher.kithe_derivative_definitions.present? # no need to schedule if we don't have any
265
201
 
266
202
  Kithe::TimingPromotionDirective.new(key: :create_derivatives, directives: file_attacher.promotion_directives) do |directive|
267
203
  if directive.inline?
@@ -276,12 +212,4 @@ class Kithe::Asset < Kithe::Model
276
212
  def refresh_metadata_before_promotion
277
213
  file.refresh_metadata!(promoting: true)
278
214
  end
279
-
280
- # Meant to be called in after_save hook, looks at activerecord dirty tracking in order
281
- # to removes all derivatives if the asset sha512 has changed
282
- def remove_invalid_derivatives
283
- if saved_change_to_file_sha?
284
- derivatives.destroy_all
285
- end
286
- end
287
215
  end
@@ -1,6 +1,6 @@
1
1
  # Creates derivatives from definitions stored on an Asset class
2
2
  class Kithe::Asset::DerivativeCreator
3
- attr_reader :definitions, :asset, :only, :except, :lazy, :mark_created
3
+ attr_reader :definitions, :shrine_attacher, :only, :except, :lazy, :source_io
4
4
 
5
5
  # A helper class that provides the implementation for Kithe::Asset#create_derivatives,
6
6
  # normally only expected to be called from there.
@@ -13,57 +13,62 @@ class Kithe::Asset::DerivativeCreator
13
13
  # (deleted) if it is a File or Tempfile object.
14
14
  #
15
15
  # @param definitions an array of DerivativeDefinition
16
- # @param asset an Asset instance
16
+ # @param shrine_attacher a shrine attacher instance holding attachment state from an individual
17
+ # model, from eg `asset.file_attacher`
17
18
  # @param only array of definition keys, only execute these (doesn't matter if they are `default_create` or not)
18
19
  # @param except array of definition keys, exclude these from definitions of derivs to be created
19
20
  # @param lazy (default false), Normally we will create derivatives for all applicable definitions,
20
21
  # overwriting any that already exist for a given key. If the definition has changed, a new
21
22
  # derivative created with new definition will overwrite existing. However, if you pass lazy false,
22
23
  # it'll skip derivative creation if the derivative already exists, which can save time
23
- # if you are only intending to create missing derivatives. With lazy:false, the asset
24
- # derivatives association will be consulted, so should be eager-loaded if you are going
25
- # to be calling on multiple assets.
26
- # @param mark_created [Boolean] if true will set shrine metadata indicating we've done
27
- # derivative creation phase, so Asset#derivatives_created? will return true. Defaults to nil,
28
- # meaning true if and only if `only` is nil -- mark created if creating default derivatives.
29
- def initialize(definitions, asset, only:nil, except:nil, lazy: false, mark_created: :not_set)
24
+ # if you are only intending to create missing derivatives.
25
+ def initialize(definitions, source_io:, shrine_attacher:, only:nil, except:nil, lazy: false)
30
26
  @definitions = definitions
31
- @asset = asset
32
- @only = only && Array(only)
33
- @except = except && Array(except)
27
+ @source_io = source_io
28
+ @shrine_attacher = shrine_attacher
29
+ @only = only && Array(only).collect(&:to_sym)
30
+ @except = except && Array(except).collect(&:to_sym)
34
31
  @lazy = !!lazy
35
- @mark_created = mark_created.nil? ? (only.nil? && except.nil?) : !! mark_created
32
+
33
+ unless shrine_attacher.kind_of?(Shrine::Attacher)
34
+ raise ArgumentError.new("expect second arg Shrine::Attacher not #{shrine_attacher.class}")
35
+ end
36
36
  end
37
37
 
38
38
  def call
39
- return unless asset.file.present? # if no file, can't create derivatives
39
+ return unless shrine_attacher.file.present? # if no file, can't create derivatives
40
40
 
41
41
  definitions_to_create = applicable_definitions
42
+
42
43
  if lazy
43
- existing_derivative_keys = asset.derivatives.collect(&:key).collect(&:to_s)
44
+ existing_derivative_keys = shrine_attacher.derivatives.keys
44
45
  definitions_to_create.reject! do |defn|
45
- existing_derivative_keys.include?(defn.key.to_s)
46
+ existing_derivative_keys.include?(defn.key)
46
47
  end
47
48
  end
48
49
 
49
- return unless definitions_to_create.present?
50
+ return {} unless definitions_to_create.present?
51
+
52
+ derivatives = {}
50
53
 
51
- # Note, MAY make a superfluous copy and/or download of original file, ongoing
52
- # discussion https://github.com/shrinerb/shrine/pull/329#issuecomment-443615868
53
- # https://github.com/shrinerb/shrine/pull/332
54
- Shrine.with_file(asset.file) do |original_file|
54
+ # Make sure we have this as a local file, because many processors
55
+ # require it, for instance for shelling out to command line.
56
+ # This might trigger a download, but note we are only doing it if we have
57
+ # `definitions_to_create`, otherwise we already returned.
58
+ shrine_attacher.shrine_class.with_file(source_io) do |source_io_as_file|
55
59
  definitions_to_create.each do |defn|
56
- deriv_bytestream = defn.call(original_file: original_file, record: asset)
60
+ deriv_bytestream = defn.call(original_file: source_io_as_file, attacher: shrine_attacher)
57
61
 
58
62
  if deriv_bytestream
59
- asset.update_derivative(defn.key, deriv_bytestream, storage_key: defn.storage_key)
60
- cleanup_returned_io(deriv_bytestream)
63
+ derivatives[defn.key] = deriv_bytestream
61
64
  end
62
65
 
63
- original_file.rewind
66
+ # may not need this but it doesn't hurt...
67
+ source_io.rewind
64
68
  end
65
- mark_derivatives_created! if mark_created
66
69
  end
70
+
71
+ derivatives
67
72
  end
68
73
 
69
74
  private
@@ -83,7 +88,7 @@ class Kithe::Asset::DerivativeCreator
83
88
  candidates = definitions.find_all do |defn|
84
89
  (only.nil? ? defn.default_create : only.include?(defn.key)) &&
85
90
  (except.nil? || ! except.include?(defn.key)) &&
86
- defn.applies_to?(asset)
91
+ defn.applies_to_content_type?(shrine_attacher.file.content_type)
87
92
  end
88
93
 
89
94
  # Now we gotta filter out any duplicate keys based on our priority rules, but keep
@@ -107,39 +112,4 @@ class Kithe::Asset::DerivativeCreator
107
112
  # Now we uniq keeping last defn
108
113
  candidates.reverse.uniq {|d| d.key }.reverse
109
114
  end
110
-
111
- def cleanup_returned_io(io)
112
- if io.respond_to?(:close!)
113
- # it's a Tempfile, clean it up now
114
- io.close!
115
- elsif io.is_a?(File)
116
- # It's a File, close it and delete it.
117
- io.close
118
- File.unlink(io.path)
119
- end
120
- end
121
-
122
- # Sets kithe asset metadata "derivatives_created" to `true`, so
123
- # code can know that we're finished creating all `default_create`
124
- # derivatives.
125
- #
126
- # Uses a db-level atomic jsonb update and db-locking to make sure it can do this
127
- # without overwriting any other metadata changes, safely.
128
- def mark_derivatives_created!
129
- asset.transaction do
130
- unless asset.acquire_lock_on_sha
131
- # asset bytestream has changed
132
- return nil
133
- end
134
-
135
- sql = <<~SQL
136
- UPDATE "#{Kithe::Asset.table_name}"
137
- SET file_data = jsonb_set(file_data, '{metadata, derivatives_created}', 'true')
138
- WHERE id = '#{asset.id}'
139
- SQL
140
-
141
- #ActiveRecord::Base.connection.exec_update("update table set f1=#{ActiveRecord::Base.sanitize(f1)}")
142
- Kithe::Asset.connection.execute(sql)
143
- end
144
- end
145
115
  end