module Shrine::Plugins::Derivatives::AttacherMethods
Attributes
Public Class Methods
Adds the ability to accept derivatives.
# File lib/shrine/plugins/derivatives.rb, line 108 def initialize(derivatives: {}, **options) super(**options) @derivatives = derivatives @derivatives_mutex = Mutex.new if shrine_class.derivatives_options[:mutex] end
Public Instance Methods
Uploads a given file and adds it to the derivatives hash.
attacher.derivatives #=> # { # thumb: #<Shrine::UploadedFile>, # } attacher.add_derivative(:cropped, cropped) attacher.derivatives #=> # { # thumb: #<Shrine::UploadedFile>, # cropped: #<Shrine::UploadedFile>, # }
# File lib/shrine/plugins/derivatives.rb, line 235 def add_derivative(name, file, **options) add_derivatives({ name => file }, **options) derivatives[name] end
Uploads given hash of files and adds uploaded files to the derivatives hash.
attacher.derivatives #=> # { # thumb: #<Shrine::UploadedFile>, # } attacher.add_derivatives({ cropped: cropped }) attacher.derivatives #=> # { # thumb: #<Shrine::UploadedFile>, # cropped: #<Shrine::UploadedFile>, # }
# File lib/shrine/plugins/derivatives.rb, line 217 def add_derivatives(files, **options) new_derivatives = upload_derivatives(files, **options) merge_derivatives(new_derivatives) new_derivatives end
Clears derivatives when attachment changes.
attacher.derivatives #=> { thumb: #<Shrine::UploadedFile> } attacher.change(file) attacher.derivatives #=> {}
# File lib/shrine/plugins/derivatives.rb, line 462 def change(*) result = super set_derivatives({}) result end
Calls processor and adds returned derivatives.
Attacher.derivatives_processor :my_processor do |original| # ... end attacher.create_derivatives(:my_processor)
# File lib/shrine/plugins/derivatives.rb, line 199 def create_derivatives(*args, storage: nil, **options) files = process_derivatives(*args, **options) add_derivatives(files, storage: storage) end
Adds derivative data into the hash.
attacher.attach(io) attacher.add_derivatives({ thumb: thumb }) attacher.data #=> # { # "id" => "...", # "storage" => "store", # "metadata" => { ... }, # "derivatives" => { # "thumb" => { # "id" => "...", # "storage" => "store", # "metadata" => { ... }, # } # } # }
# File lib/shrine/plugins/derivatives.rb, line 416 def data result = super if derivatives.any? result ||= {} result["derivatives"] = map_derivative(derivatives, transform_keys: :to_s) do |_, derivative| derivative.data end end result end
Deletes given hash of uploaded files.
attacher.delete_derivatives({ thumb: uploaded_file }) uploaded_file.exists? #=> false
# File lib/shrine/plugins/derivatives.rb, line 384 def delete_derivatives(derivatives = self.derivatives) map_derivative(derivatives) { |_, derivative| derivative.delete } end
Sets a hash of derivatives.
attacher.derivatives = { thumb: Shrine.uploaded_file(...) } attacher.derivatives #=> { thumb: #<Shrine::UploadedFile ...> }
# File lib/shrine/plugins/derivatives.rb, line 472 def derivatives=(derivatives) unless derivatives.is_a?(Hash) fail ArgumentError, "expected derivatives to be a Hash, got #{derivatives.inspect}" end @derivatives = derivatives end
In addition to deleting the main file it also deletes any derivatives.
attacher.add_derivatives({ thumb: thumb }) attacher.derivatives[:thumb].exists? #=> true attacher.destroy attacher.derivatives[:thumb].exists? #=> false
# File lib/shrine/plugins/derivatives.rb, line 187 def destroy super delete_derivatives end
Convenience method for accessing derivatives.
photo.image_derivatives[:thumb] #=> #<Shrine::UploadedFile> # can be shortened to photo.image(:thumb) #=> #<Shrine::UploadedFile>
# File lib/shrine/plugins/derivatives.rb, line 120 def get(*path) return super if path.empty? get_derivatives(*path) end
Convenience method for accessing derivatives.
photo.image_derivatives.dig(:thumbnails, :large) # can be shortened to photo.image_derivatives(:thumbnails, :large)
# File lib/shrine/plugins/derivatives.rb, line 131 def get_derivatives(*path) return derivatives if path.empty? path = derivative_path(path) derivatives.dig(*path) end
Loads derivatives from data generated by ‘Attacher#data`.
attacher.load_data({ "id" => "...", "storage" => "store", "metadata" => { ... }, "derivatives" => { "thumb" => { "id" => "...", "storage" => "store", "metadata" => { ... }, } } }) attacher.file #=> #<Shrine::UploadedFile> attacher.derivatives #=> { thumb: #<Shrine::UploadedFile> }
# File lib/shrine/plugins/derivatives.rb, line 445 def load_data(data) data ||= {} data = data.dup derivatives_data = data.delete("derivatives") || data.delete(:derivatives) || {} @derivatives = shrine_class.derivatives(derivatives_data) data = nil if data.empty? super(data) end
Iterates through nested derivatives and maps results.
attacher.map_derivative(derivatives) { |path, file| ... }
# File lib/shrine/plugins/derivatives.rb, line 483 def map_derivative(derivatives, **options, &block) shrine_class.map_derivative(derivatives, **options, &block) end
Deep merges given uploaded derivatives with current derivatives.
attacher.derivatives #=> { one: #<Shrine::UploadedFile> } attacher.merge_derivatives({ two: uploaded_file }) attacher.derivatives #=> { one: #<Shrine::UploadedFile>, two: #<Shrine::UploadedFile> }
# File lib/shrine/plugins/derivatives.rb, line 303 def merge_derivatives(new_derivatives) derivatives_synchronize do merged_derivatives = deep_merge_derivatives(derivatives, new_derivatives) set_derivatives(merged_derivatives) end end
Downloads the attached file and calls the specified processor.
Attacher.derivatives_processor :thumbnails do |original| processor = ImageProcessing::MiniMagick.source(original) { small: processor.resize_to_limit!(300, 300), medium: processor.resize_to_limit!(500, 500), large: processor.resize_to_limit!(800, 800), } end attacher.process_derivatives(:thumbnails) #=> { small: #<File:...>, medium: #<File:...>, large: #<File:...> }
# File lib/shrine/plugins/derivatives.rb, line 278 def process_derivatives(processor_name = :default, source = nil, **options) # handle receiving only source file without a processor unless processor_name.respond_to?(:to_sym) source = processor_name processor_name = :default end source ||= file! processor_settings = self.class.derivatives_processor_settings(processor_name) || {} if processor_settings[:download] shrine_class.with_file(source) do |file| _process_derivatives(processor_name, file, **options) end else _process_derivatives(processor_name, source, **options) end end
In addition to promoting the main file, also promotes any cached derivatives. This is useful when these derivatives are being created as part of a direct upload.
attacher.assign(io) attacher.add_derivative(:thumb, file, storage: :cache) attacher.promote attacher.stored?(attacher.derivatives[:thumb]) #=> true
# File lib/shrine/plugins/derivatives.rb, line 162 def promote(**options) super promote_derivatives create_derivatives if create_derivatives_on_promote? end
Uploads any cached derivatives to permanent storage.
# File lib/shrine/plugins/derivatives.rb, line 169 def promote_derivatives(**options) stored_derivatives = map_derivative(derivatives) do |path, derivative| if cached?(derivative) upload_derivative(path, derivative, **options) else derivative end end set_derivatives(stored_derivatives) unless derivatives == stored_derivatives end
Removes derivative with specified name from the derivatives hash.
attacher.derivatives #=> { one: #<Shrine::UploadedFile>, two: #<Shrine::UploadedFile> } attacher.remove_derivative(:one) #=> #<Shrine::UploadedFile> (removed derivative) attacher.derivatives #=> { two: #<Shrine::UploadedFile> }
Nested derivatives are also supported:
attacher.derivatives #=> { nested: { one: #<Shrine::UploadedFile>, two: #<Shrine::UploadedFile> } } attacher.remove_derivative([:nested, :one]) #=> #<Shrine::UploadedFile> (removed derivative) attacher.derivatives #=> { nested: { one: #<Shrine::UploadedFile> } }
The :delete option can be passed for deleting removed derivative:
attacher.derivatives #=> { one: #<Shrine::UploadedFile>, two: #<Shrine::UploadedFile> } derivative = attacher.remove_derivatives(:two, delete: true) derivative.exists? #=> false
# File lib/shrine/plugins/derivatives.rb, line 376 def remove_derivative(path, **options) remove_derivatives(path, **options).first end
Removes derivatives with specified name from the derivatives hash.
attacher.derivatives #=> { one: #<Shrine::UploadedFile>, two: #<Shrine::UploadedFile>, three: #<Shrine::UploadedFile> } attacher.remove_derivatives(:two, :three) #=> [#<Shrine::UploadedFile>, #<Shrine::UploadedFile>] (removed derivatives) attacher.derivatives #=> { one: #<Shrine::UploadedFile> }
Nested derivatives are also supported:
attacher.derivatives #=> { nested: { one: #<Shrine::UploadedFile>, two: #<Shrine::UploadedFile>, three: #<Shrine::UploadedFile> } } attacher.remove_derivatives([:nested, :two], [:nested, :three]) #=> [#<Shrine::UploadedFile>, #<Shrine::UploadedFile>] (removed derivatives) attacher.derivatives #=> { nested: { one: #<Shrine::UploadedFile> } }
The :delete option can be passed for deleting removed derivatives:
attacher.derivatives #=> { one: #<Shrine::UploadedFile>, two: #<Shrine::UploadedFile>, three: #<Shrine::UploadedFile> } two, three = attacher.remove_derivatives(:two, :three, delete: true) two.exists? #=> false three.exists? #=> false
# File lib/shrine/plugins/derivatives.rb, line 341 def remove_derivatives(*paths, delete: false) removed_derivatives = paths.map do |path| path = Array(path) if path.one? derivatives.delete(path.first) else derivatives.dig(*path[0..-2]).delete(path[-1]) end end set_derivatives derivatives delete_derivatives(removed_derivatives) if delete removed_derivatives end
Sets the given hash of uploaded files as derivatives.
attacher.set_derivatives({ thumb: uploaded_file }) attacher.derivatives #=> { thumb: #<Shrine::UploadedFile> }
# File lib/shrine/plugins/derivatives.rb, line 392 def set_derivatives(derivatives) self.derivatives = derivatives set file # trigger model write derivatives end
Uploads the given file and deletes it afterwards.
hash = attacher.upload_derivative(:thumb, thumb) hash[:thumb] #=> #<Shrine::UploadedFile>
# File lib/shrine/plugins/derivatives.rb, line 254 def upload_derivative(path, file, storage: nil, **options) path = derivative_path(path) storage ||= derivative_storage(path) file.open if file.is_a?(Tempfile) # refresh file descriptor file.binmode if file.respond_to?(:binmode) # ensure binary mode upload(file, storage, derivative: path, delete: true, action: :derivatives, **options) end
Uploads given hash of files.
hash = attacher.upload_derivatives({ thumb: thumb }) hash[:thumb] #=> #<Shrine::UploadedFile>
# File lib/shrine/plugins/derivatives.rb, line 244 def upload_derivatives(files, **options) map_derivative(files) do |path, file| upload_derivative(path, file, **options) end end
Allows generating a URL to the derivative by passing the derivative name.
attacher.add_derivatives({ thumb: thumb }) attacher.url(:thumb) #=> "https://example.org/thumb.jpg"
# File lib/shrine/plugins/derivatives.rb, line 144 def url(*path, **options) return super if path.empty? path = derivative_path(path) url = derivatives.dig(*path)&.url(**options) url ||= default_url(**options, derivative: path) url end
Private Instance Methods
Calls the derivatives processor with the source file and options.
# File lib/shrine/plugins/derivatives.rb, line 490 def _process_derivatives(processor_name, source, **options) processor = self.class.derivatives_processor(processor_name) return {} unless processor result = instrument_derivatives(processor_name, source, options) do instance_exec(source, **options, &processor) end unless result.is_a?(Hash) fail Error, "expected derivatives processor #{processor_name.inspect} to return a Hash, got #{result.inspect}" end result end
Whether to automatically create derivatives on promotion
# File lib/shrine/plugins/derivatives.rb, line 544 def create_derivatives_on_promote? shrine_class.derivatives_options[:create_on_promote] end
Deep merge nested hashes/arrays.
# File lib/shrine/plugins/derivatives.rb, line 533 def deep_merge_derivatives(o1, o2) if o1.is_a?(Hash) && o2.is_a?(Hash) o1.merge(o2) { |_, v1, v2| deep_merge_derivatives(v1, v2) } elsif o1.is_a?(Array) && o2.is_a?(Array) o1 + o2 else o2 end end
Returns symbolized array or single key.
# File lib/shrine/plugins/derivatives.rb, line 519 def derivative_path(path) path = Array(path).map { |key| key.is_a?(String) ? key.to_sym : key } path = path.first if path.one? path end
Storage
to which derivatives will be uploaded to by default.
# File lib/shrine/plugins/derivatives.rb, line 526 def derivative_storage(path) storage = self.class.derivatives_storage storage = instance_exec(path, &storage) if storage.respond_to?(:call) storage end
# File lib/shrine/plugins/derivatives.rb, line 548 def derivatives_synchronize if @derivatives_mutex @derivatives_mutex.synchronize { yield } else yield end end
Sends a ‘derivatives.shrine` event for instrumentation plugin.
# File lib/shrine/plugins/derivatives.rb, line 507 def instrument_derivatives(processor_name, source, processor_options, &block) return yield unless shrine_class.respond_to?(:instrument) shrine_class.instrument(:derivatives, { processor: processor_name, processor_options: processor_options, io: source, attacher: self, }, &block) end