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
@@ -0,0 +1,54 @@
1
+ require 'mini_mime'
2
+
3
+ class Shrine
4
+ module Plugins
5
+ # Includes the Shrine `derivatives` plugin with some configuration, and
6
+ # extra features. The metadata for shrine derivatives is stored in the same
7
+ # JSON as the main file.
8
+ #
9
+ # * default kithe storage location of :kithe_derivatives
10
+ #
11
+ # * nice metadata["filename"] for derivatives, instead of default shrine fairly
12
+ # random (filename ends up used by default in content-disposition headers when delivered)
13
+ #
14
+ # * Includes kithe_persisted_derivatives with #add_persisted_derivatives
15
+ # and #create_persisted_derivatives methods for concurrency-safe
16
+ # derivative persisting.
17
+ #
18
+ # ## Shrine derivatives references
19
+ #
20
+ # https://shrinerb.com/docs/plugins/derivatives
21
+ # https://shrinerb.com/docs/processing
22
+ class KitheDerivatives
23
+ def self.load_dependencies(uploader, *)
24
+ uploader.plugin :derivatives, storage: -> (derivative) do
25
+ # default derivatives storage to
26
+ :kithe_derivatives
27
+ end
28
+
29
+ uploader.plugin :kithe_persisted_derivatives
30
+ uploader.plugin :kithe_derivative_definitions
31
+ end
32
+
33
+ module InstanceMethods
34
+
35
+ # Override to fix "filename" metadata to be something reasonable, regardless
36
+ # of what if anything was the filename of the IO being attached. shrine S3 will
37
+ # insist on setting a default content-disposition with this filename.
38
+ def extract_metadata(io, derivative:nil, **context)
39
+ result = super
40
+
41
+ if derivative && context[:record] && result["mime_type"]
42
+ extension = MiniMime.lookup_by_content_type(result["mime_type"] || "")&.extension || "bin"
43
+ result["filename"] = "#{context[:record].friendlier_id}_#{derivative}.#{extension}"
44
+ end
45
+
46
+ result
47
+ end
48
+ end
49
+
50
+ end
51
+ register_plugin(:kithe_derivatives, KitheDerivatives)
52
+ end
53
+ end
54
+
@@ -0,0 +1,39 @@
1
+ class Shrine
2
+ module Plugins
3
+ # Custom kithe logic for determining mime type, using the shrine mime_type plugin.
4
+ #
5
+ # We start out using the `marcel` analyzer.
6
+ # Marcel analyzer is pure-ruby and fast. It's from Basecamp and is what
7
+ # ActiveStorage uses. It is very similar to :mimemagic (and uses mimemagic
8
+ # under the hood), but mimemagic seems not to be maintained with up to date
9
+ # magic db? https://github.com/minad/mimemagic/pull/66
10
+ #
11
+ # But marcel is not able to catch some of our MP3s as audio/mpeg. The
12
+ # `mediainfo` CLI is, and is one of the tools Harvard FITS uses.
13
+ # If marcel came up blank, AND we are configured to use mediainfo CLI
14
+ # (which by default we will be if it's available), we will try
15
+ # shelling out to mediainfo command line.
16
+ #
17
+ # https://github.com/MediaArea/MediaInfo
18
+ #
19
+ # Ensure that if mime-type can't be otherwise determined, it is assigned
20
+ # "application/octet-stream", basically the type for generic binary.
21
+ class KitheDetermineMimeType
22
+ def self.load_dependencies(uploader, *)
23
+ uploader.plugin :determine_mime_type, analyzer: -> (io, analyzers) do
24
+ mime_type = analyzers[:marcel].call(io)
25
+
26
+
27
+ if Kithe.use_mediainfo && mime_type == "application/octet-stream" || mime_type.blank?
28
+ mime_type = Kithe::MediainfoAnalyzer.new.call(io)
29
+ end
30
+
31
+ mime_type = "application/octet-stream" if mime_type.blank?
32
+
33
+ mime_type
34
+ end
35
+ end
36
+ end
37
+ register_plugin(:kithe_determine_mime_type, KitheDetermineMimeType)
38
+ end
39
+ end
@@ -0,0 +1,161 @@
1
+ class Shrine
2
+ module Plugins
3
+ # Some convenience methods for adding/changing derivatives in
4
+ # concurrency-safe manner:
5
+ #
6
+ # * Won't make a change if the underlying original has changed
7
+ # so doesn't match the one you wanted to remove.
8
+ # * Won't over-write changes made concurrently in the db by other processes
9
+ # * Will always make sure to clean up any temporary files on all error
10
+ # and falure conditions.
11
+ #
12
+ # Shrine has some building blocks for this, which we use, but it's a bit tricky
13
+ # to put them together to be generically reliable, as we think we've done here.
14
+ #
15
+ # All these methods will cause your Asset model to be saved, because of how
16
+ # the shrine atomic helpers work. So these methods will by default raise
17
+ # a TypeError if your Asset model has any unrelated outstanding changes,
18
+ # but you can tell it to save anyway with `allow_other_changes: true`.
19
+ #
20
+ # ## Shrine references:
21
+ #
22
+ # https://shrinerb.com/docs/plugins/derivatives
23
+ # https://shrinerb.com/docs/processing
24
+ class KithePersistedDerivatives
25
+ module AttacherMethods
26
+ # Like the shrine `add_derivatives` method, but also *persists* the
27
+ # derivatives (saves to db), in a realiably concurrency-safe way.
28
+ #
29
+ # Generally can take any options that shrine `add_derivatives`
30
+ # can take, including custom `storage` or `metadata` arguments.
31
+ #
32
+ # Like shrine add_derivatives, it will assume the files passed in are
33
+ # temporary, and delete them for you. If you want to disable this behavior:
34
+ #
35
+ # attacher.add_persisted_derivatives({key: io}, delete: false)
36
+ #
37
+ # In some cases the derivatives can't be persisted because the underlying
38
+ # database has changed such that they would not be applicable. In those
39
+ # cases `false` will be return value, otherwise returns the new derivatives
40
+ # just as shrine `add_derivatives`
41
+ #
42
+ # Because the concurrent-safe persistence method will save the associated model --
43
+ # and save without ActiveRecord validation -- it is not safe to
44
+ # add_persisted_derivatives on a model with other unsaved changes. The
45
+ # method will by default refuse to do so, throwing a TypeError. If you'd
46
+ # like to force it, pass `allow_other_changes: true` as an argument.
47
+ #
48
+ # Also takes care of deleting any replaced derivative files, that are no longer
49
+ # referenced by the model. Shrine by default does not do this:
50
+ # https://github.com/shrinerb/shrine/issues/468
51
+ #
52
+ # All deletions are inline. In general this could be a fairly expensive operation,
53
+ # it can be wise to do it in a bg job.
54
+ def add_persisted_derivatives(local_files, **options)
55
+ other_changes_allowed = !!options.delete(:allow_other_changes)
56
+ if record && !other_changes_allowed && record.changed?
57
+ raise TypeError.new("Can't safely add_persisted_derivatives on model with unsaved changes. Pass `allow_other_changes: true` to force.")
58
+ end
59
+
60
+ existing_derivative_files = nil
61
+
62
+ # upload to storage
63
+ new_derivatives = upload_derivatives(local_files, **options)
64
+
65
+ begin
66
+ atomic_persist do |reloaded_attacher|
67
+ # record so we can delete any replaced ones...
68
+ existing_derivative_files = map_derivative(reloaded_attacher.derivatives).collect { |path, file| file }
69
+
70
+ # make sure we don't override derivatives created in other jobs, by
71
+ # first using the current up-to-date derivatives from db,
72
+ # then merging our changes in on top.
73
+ set_derivatives(reloaded_attacher.derivatives)
74
+ merge_derivatives(new_derivatives)
75
+ end
76
+ rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound => e
77
+ # underlying file has changed or model has been deleted, inappropriate
78
+ # to add the derivatives, we can just silently drop them, but clean
79
+ # up after ourselves.
80
+ delete_derivatives(local_files) unless options[:delete] == false
81
+ delete_derivatives(new_derivatives)
82
+
83
+ return false
84
+ rescue StandardError => e
85
+ # unexpected error, clean up our files and re-raise
86
+ delete_derivatives(local_files) unless options[:delete] == false
87
+ delete_derivatives(new_derivatives)
88
+ raise e
89
+ end
90
+
91
+ # Take care of deleting from storage any derivatives that were replaced.
92
+ current_derivative_files = map_derivative(derivatives).collect { |path, file| file }
93
+ replaced_files = existing_derivative_files - current_derivative_files
94
+ delete_derivatives(replaced_files)
95
+
96
+ new_derivatives
97
+ end
98
+
99
+ # Like the shrine `create_derivatives` method, but persists the created derivatives
100
+ # to the database in a concurrency-safe way.
101
+ #
102
+ # Can take all options that shrine `create_derivatives` can take, including custom
103
+ # processors, custom storage key, and arbitrary custom processor arguments.
104
+ #
105
+ # asset.file_attacher.create_persisted_derivatives
106
+ # asset.file_attacher.create_persisted_derivatives(storage: :custom_key)
107
+ # asset.file_attacher.create_persisted_derivatives(:kithe_derivatives)
108
+ # asset.file_attacher.create_persisted_derivatives(:kithe_derivatives, some_arg: "value")
109
+ # asset.file_attacher.create_persisted_derivatives(:kithe_derivatives, alternate_source_file)
110
+ #
111
+ # Also has an `allow_other_changes` argument, see #add_persisted_derivatives.
112
+ def create_persisted_derivatives(*args, storage: nil, allow_other_changes: false, **options)
113
+ return false unless file
114
+
115
+ local_files = process_derivatives(*args, **options)
116
+ add_persisted_derivatives(local_files, storage: storage, allow_other_changes: allow_other_changes)
117
+ end
118
+
119
+ # Kind of like built-in Shrine #remove_derivatives, but also takes care of
120
+ # persisting AND deleting the removed derivative file from storage --
121
+ # all in concurrency-safe way, including not making sure to overwrite
122
+ # any unrelated derivatives someone else was adding.
123
+ #
124
+ # Can take the same sorts of path arguments as Shrine derivative #remove_derivatives
125
+ #
126
+ # asset.file_attacher.remove_persisted_derivatives(:small_thumb)
127
+ # asset.file_attacher.remove_persisted_derivatives(:small_thumb, :large_thumb)
128
+ # asset.file_attacher.remove_persisted_derivatives(:small_thumb, :large_thumb, allow_other_changes: true)
129
+ def remove_persisted_derivatives(*paths, **options)
130
+ return if paths.empty?
131
+
132
+ other_changes_allowed = !!options.delete(:allow_other_changes)
133
+ if record && !other_changes_allowed && record.changed?
134
+ raise TypeError.new("Can't safely add_persisted_derivatives on model with unsaved changes. Pass `allow_other_changes: true` to force.")
135
+ end
136
+
137
+ removed_derivatives = nil
138
+ atomic_persist do |reloaded_attacher|
139
+ set_derivatives(reloaded_attacher.derivatives)
140
+ removed_derivatives = remove_derivatives(*paths, delete: false)
141
+ end
142
+
143
+ if removed_derivatives
144
+ map_derivative(removed_derivatives) do |_, derivative|
145
+ derivative.delete if derivative
146
+ end
147
+ end
148
+
149
+ removed_derivatives
150
+ rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
151
+ # original was already deleted or changed, the derivatives wer'e trying to delete.
152
+ # It should be fine to do nothing, the process that deleted or changed
153
+ # the model should already have deleted all these derivatives.
154
+ # But we'll return false as a signel.
155
+ return false
156
+ end
157
+ end
158
+ end
159
+ register_plugin(:kithe_persisted_derivatives, KithePersistedDerivatives)
160
+ end
161
+ end
@@ -24,6 +24,10 @@ class Shrine
24
24
  # was convenient and avoided confusion to isolate wrapping in a class method that can be used
25
25
  # anywhere, and only depends on args passed in, no implicit state anywhere.
26
26
  class KithePromotionCallbacks
27
+ def self.load_dependencies(uploader, *)
28
+ uploader.plugin :kithe_promotion_directives
29
+ end
30
+
27
31
  # promotion logic differs somewhat in different modes of use (bg or inline promotion),
28
32
  # so we extract the wrapping logic here. Exactly what the logic wrapped is can
29
33
  # differ.
@@ -89,15 +89,15 @@ class Shrine
89
89
  # some_model.save!
90
90
  def set_promotion_directives(hash)
91
91
  # ActiveJob sometimes has trouble if there are symbols in there, somewhat
92
- # unpredictably.
93
- hash = hash.collect { |k, v| [k.to_s, v === Symbol ? v.to_s : v.to_s]}.to_h
92
+ # unpredictably. And for other reasons, standardize on everything a string.
93
+ hash = hash.collect { |k, v| [k.to_s, v.to_s]}.to_h
94
94
 
95
95
  unrecognized = hash.keys.collect(&:to_sym) - KithePromotionDirectives.allowed_promotion_directives
96
96
  unless unrecognized.length == 0
97
97
  raise ArgumentError.new("Unrecognized promotion directive key: #{unrecognized.join('')}")
98
98
  end
99
99
 
100
- promotion_directives.merge!(hash)
100
+ context[:promotion_directives] = promotion_directives.merge(hash).freeze
101
101
  end
102
102
 
103
103
  # context[:promotion_directives], lazily initializing to hash for convenience.
@@ -105,6 +105,36 @@ class Shrine
105
105
  context[:promotion_directives] ||= {}
106
106
  end
107
107
  end
108
+
109
+ # VERY hacky way to try to preserve promotion_directives on Asset.reload.
110
+ #
111
+ # This may not be necessary in a future shrine version if shrine resolves
112
+ # issue. See: https://github.com/shrinerb/shrine/issues/463
113
+ #
114
+ # It is the activerecord plugin implementation that erases all shrine context
115
+ # (and thus our promotion directives) on reload.
116
+ # https://github.com/shrinerb/shrine/blob/b5fc2e1432e51e6fde87c120bc6cf6abeb286c68/lib/shrine/plugins/activerecord.rb#L56-L60
117
+ #
118
+ # It is quite tricky to override the activerecord plugin's own override, because
119
+ # of the way shrine does these overrides. We've figured out a pretty crazy way
120
+ # below.
121
+ module AttachmentMethods
122
+ def included(model)
123
+ super
124
+
125
+ original_reload = instance_method(:reload)
126
+
127
+ define_method :reload do |*args|
128
+ previous_promotion_directives = file_attacher.promotion_directives
129
+
130
+ result = original_reload.bind(self).call(*args)
131
+ file_attacher.set_promotion_directives(previous_promotion_directives)
132
+
133
+ result
134
+ end
135
+ end
136
+ end
137
+
108
138
  end
109
139
  register_plugin(:kithe_promotion_directives, KithePromotionDirectives)
110
140
  end
@@ -2,25 +2,74 @@ require 'shrine/storage/url'
2
2
 
3
3
  class Shrine
4
4
  module Plugins
5
- # Set file location to "asset/#{asset_uuid_id}/#{unique_file_id}" -- regardless of
6
- # asset sub-class, since they all have unique ids, just all under asset/.
5
+ # Set custom storage locations/paths for both the original file which is the main
6
+ # file in the shrine attachment at Asset#file, and any shrine derivatives.
7
+ #
8
+ # Shrine's default is to just put both of these at top-level `[randomID].suffix`. We
9
+ # instead:
10
+ #
11
+ # ## Original file
12
+ #
13
+ # Stored at `asset/#{asset_uuid_id}/#{unique_file_id}.suffix` -- regardless of
14
+ # asset sub-class, since they all have unique ids, just all under asset/. (In retrospect,
15
+ # maybe shoudl have left `asset/` off, and let consumer specify a prefix when configuring
16
+ # storage).
7
17
  #
8
18
  # If no Asset pk is available (direct upload or unsaved Asset), will be stored just
9
19
  # under "asset/#{unique_file_id}.#{suffix}"
10
20
  #
11
21
  # We are choosing to store under Asset UUID PK instead of friendlier_id, friendlier_id
12
22
  # is good for public URLs and UI, but actual PK is more reliable/immutable.
23
+ #
24
+ # ## Derivatives
25
+ #
26
+ # Stored at `#{asset_uuid_id}/derivative_key/#{unique_file_id}.suffix`.
27
+ #
28
+ # If asset uuid pk is not available, will raise a TypeError and refuse to store
29
+ # derivative. (This may have to be thought through more.)
30
+ #
31
+ # If you want an additional prefix, supply it hwen configuring kithe_derivatives
32
+ # storage.
13
33
  module KitheStorageLocation
14
34
  module InstanceMethods
15
- def generate_location(io, context)
35
+ def generate_location(io, derivative: nil, **context)
36
+ original = super
37
+
38
+ if derivative
39
+ _kithe_generate_derivative_location(io, original: original, derivative: derivative, **context)
40
+ else
41
+ _kithe_generate_main_location(io, original: original, **context)
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def _kithe_generate_main_location(io, original:, **context)
16
48
  # If it doesn't have a id, we're probably storing in cache, possibly as part
17
49
  # of direct upload endpoint. A better path will be created on store.
18
50
  id = context[:record].id if context[:record].respond_to?(:id)
19
51
 
20
- basename = super
52
+ basename = original
21
53
 
22
54
  ["asset", id, basename].compact.join("/")
23
55
  end
56
+
57
+ # Usually NOT in the same bucket/prefix as the originals/main attachments.
58
+ # You can set a prefix yourself in your shrine storage config if you want them
59
+ # on the same bucket, and probably should.
60
+ def _kithe_generate_derivative_location(io, original:, derivative:, record:, **context)
61
+ # for now to be save, insist the record exist and have an id so we can get the
62
+ # correct derivative location. This is consistent with kithe 1.x behavior. We can
63
+ # enhance later maybe.
64
+ unless record && record.id
65
+ raise TypeError.new("Can't determine correct derivative location without a persisted record. Record: #{record}")
66
+ end
67
+ unless derivative && original
68
+ raise ArgumentError.new("Missing required argument")
69
+ end
70
+
71
+ [record.id, derivative, original].join("/")
72
+ end
24
73
  end
25
74
  end
26
75
  register_plugin(:kithe_storage_location, KitheStorageLocation)
@@ -8,11 +8,10 @@ namespace :kithe do
8
8
  options = {}
9
9
  OptionParser.new do |opts|
10
10
  opts.banner = "Usage: ./bin/rake kithe:create_derivatives -- [options]"
11
- opts.on("--derivatives TYPES", "comma-seperated list of type keys") { |ids| options[:derivative_keys] = ids.split(",")}
12
- opts.on("--lazy","Lazy create") { options[:lazy] = true }
13
- opts.on("--asset-id FRIENDLIER_IDS", "comma-seperated list of asset (friendlier) ids") { |ids| options[:asset_ids] = ids.split(",") }
14
- opts.on("--work-id FRIENDLIER_IDS", "comma-seperated list of work (friendlier) ids") { |ids| options[:work_ids] = ids.split(",") }
15
- opts.on("--mark-derivatives-created", "set derivatives_created? flag on assets") { |ids| options[:mark_derivatives_created] = true }
11
+ opts.on("--derivatives=TYPES", "comma-seperated list of type keys") { |ids| options[:derivative_keys] = ids.split(",")}
12
+ opts.on("--lazy", "Lazy create") { options[:lazy] = true }
13
+ opts.on("--asset-id=FRIENDLIER_IDS", "comma-seperated list of asset (friendlier) ids") { |ids| options[:asset_ids] = ids.split(",") }
14
+ opts.on("--work-id=FRIENDLIER_IDS", "comma-seperated list of work (friendlier) ids") { |ids| options[:work_ids] = ids.split(",") }
16
15
  end.tap do |parser|
17
16
  parser.parse!(parser.order(ARGV) {})
18
17
  end
@@ -22,17 +21,20 @@ namespace :kithe do
22
21
  scope = scope.joins(:parent).where("parents_kithe_models.friendlier_id": options[:work_ids])
23
22
  end
24
23
  scope = scope.where(friendlier_id: options[:asset_ids]) if options[:asset_ids]
25
- scope = scope.includes(:derivatives) if options[:lazy]
26
24
 
27
25
  progress_bar = ProgressBar.create(total: scope.count, format: Kithe::STANDARD_PROGRESS_BAR_FORMAT)
28
26
 
29
27
  scope.find_each do |asset|
30
- progress_bar.title = asset.friendlier_id
31
- asset.create_derivatives(
32
- only: options[:derivative_keys],
33
- lazy: !!options[:lazy],
34
- mark_created: options[:mark_derivatives_created]
35
- )
28
+ begin
29
+ progress_bar.title = asset.friendlier_id
30
+ asset.create_derivatives(
31
+ only: options[:derivative_keys],
32
+ lazy: !!options[:lazy]
33
+ )
34
+ rescue Shrine::FileNotFound => e
35
+ progress_bar.log("original missing for #{asset.friendlier_id}")
36
+ # it's cool, skip it
37
+ end
36
38
  progress_bar.increment
37
39
  end
38
40
  end
@@ -43,9 +45,14 @@ namespace :kithe do
43
45
  task :lazy_defaults => :environment do
44
46
  progress_bar = ProgressBar.create(total: Kithe::Asset.count, format: Kithe::STANDARD_PROGRESS_BAR_FORMAT)
45
47
 
46
- Kithe::Asset.includes(:derivatives).find_each do |asset|
47
- progress_bar.title = asset.friendlier_id
48
- asset.create_derivatives(lazy: true)
48
+ Kithe::Asset.find_each do |asset|
49
+ begin
50
+ progress_bar.title = asset.friendlier_id
51
+ asset.create_derivatives(lazy: true)
52
+ rescue Shrine::FileNotFound => e
53
+ progress_bar.log("original missing for #{asset.friendlier_id}")
54
+ # it's cool, skip it
55
+ end
49
56
  progress_bar.increment
50
57
  end
51
58
  end