Module: Shrine::Plugins::Derivatives::AttacherMethods

Defined in:
lib/shrine/plugins/derivatives.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#derivativesObject

Returns the value of attribute derivatives.


105
106
107
# File 'lib/shrine/plugins/derivatives.rb', line 105

def derivatives
  @derivatives
end

Instance Method Details

#add_derivative(name, file, **options) ⇒ Object

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>,
# }

235
236
237
238
# File 'lib/shrine/plugins/derivatives.rb', line 235

def add_derivative(name, file, **options)
  add_derivatives({ name => file }, **options)
  derivatives[name]
end

#add_derivatives(files, **options) ⇒ Object

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>,
# }

217
218
219
220
221
# 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

#changeObject

Clears derivatives when attachment changes.

attacher.derivatives #=> { thumb: #<Shrine::UploadedFile> }
attacher.change(file)
attacher.derivatives #=> {}

462
463
464
465
466
# File 'lib/shrine/plugins/derivatives.rb', line 462

def change(*)
  result = super
  set_derivatives({})
  result
end

#create_derivatives(*args, storage: nil, **options) ⇒ Object

Calls processor and adds returned derivatives.

Attacher.derivatives_processor :my_processor do |original|
  # ...
end

attacher.create_derivatives(:my_processor)

199
200
201
202
# 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

#dataObject

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" => { ... },
#     }
#   }
# }

416
417
418
419
420
421
422
423
424
425
426
427
# 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

#delete_derivatives(derivatives = self.derivatives) ⇒ Object

Deletes given hash of uploaded files.

attacher.delete_derivatives({ thumb: uploaded_file })
uploaded_file.exists? #=> false

384
385
386
# File 'lib/shrine/plugins/derivatives.rb', line 384

def delete_derivatives(derivatives = self.derivatives)
  map_derivative(derivatives) { |_, derivative| derivative.delete }
end

#destroyObject

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

187
188
189
190
# File 'lib/shrine/plugins/derivatives.rb', line 187

def destroy
  super
  delete_derivatives
end

#get(*path) ⇒ Object

Convenience method for accessing derivatives.

photo.image_derivatives[:thumb] #=> #<Shrine::UploadedFile>
# can be shortened to
photo.image(:thumb) #=> #<Shrine::UploadedFile>

120
121
122
123
124
# File 'lib/shrine/plugins/derivatives.rb', line 120

def get(*path)
  return super if path.empty?

  get_derivatives(*path)
end

#get_derivatives(*path) ⇒ Object

Convenience method for accessing derivatives.

photo.image_derivatives.dig(:thumbnails, :large)
# can be shortened to
photo.image_derivatives(:thumbnails, :large)

131
132
133
134
135
136
137
# 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

#initialize(derivatives: {}, **options) ⇒ Object

Adds the ability to accept derivatives.


108
109
110
111
112
113
# File 'lib/shrine/plugins/derivatives.rb', line 108

def initialize(derivatives: {}, **options)
  super(**options)

  @derivatives       = derivatives
  @derivatives_mutex = Mutex.new
end

#load_data(data) ⇒ Object

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

445
446
447
448
449
450
451
452
453
454
455
# 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

#map_derivative(derivatives, **options, &block) ⇒ Object

Iterates through nested derivatives and maps results.

attacher.map_derivative(derivatives) { |path, file| ... }

483
484
485
# File 'lib/shrine/plugins/derivatives.rb', line 483

def map_derivative(derivatives, **options, &block)
  shrine_class.map_derivative(derivatives, **options, &block)
end

#merge_derivatives(new_derivatives) ⇒ Object

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

303
304
305
306
307
308
# File 'lib/shrine/plugins/derivatives.rb', line 303

def merge_derivatives(new_derivatives)
  @derivatives_mutex.synchronize do
    merged_derivatives = deep_merge_derivatives(derivatives, new_derivatives)
    set_derivatives(merged_derivatives)
  end
end

#process_derivatives(processor_name = :default, source = nil, **options) ⇒ Object

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

278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
# 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

#promote(**options) ⇒ Object

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

162
163
164
165
166
# File 'lib/shrine/plugins/derivatives.rb', line 162

def promote(**options)
  super
  promote_derivatives
  create_derivatives if create_derivatives_on_promote?
end

#promote_derivatives(**options) ⇒ Object

Uploads any cached derivatives to permanent storage.


169
170
171
172
173
174
175
176
177
178
179
# 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

#remove_derivative(path, **options) ⇒ Object

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

376
377
378
# File 'lib/shrine/plugins/derivatives.rb', line 376

def remove_derivative(path, **options)
  remove_derivatives(path, **options).first
end

#remove_derivatives(*paths, delete: false) ⇒ Object

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

341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
# 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

#set_derivatives(derivatives) ⇒ Object

Sets the given hash of uploaded files as derivatives.

attacher.set_derivatives({ thumb: uploaded_file })
attacher.derivatives #=> { thumb: #<Shrine::UploadedFile> }

392
393
394
395
396
# File 'lib/shrine/plugins/derivatives.rb', line 392

def set_derivatives(derivatives)
  self.derivatives = derivatives
  set file # trigger model write
  derivatives
end

#upload_derivative(path, file, storage: nil, **options) ⇒ Object

Uploads the given file and deletes it afterwards.

hash = attacher.upload_derivative(:thumb, thumb)
hash[:thumb] #=> #<Shrine::UploadedFile>

254
255
256
257
258
259
260
261
262
# 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

#upload_derivatives(files, **options) ⇒ Object

Uploads given hash of files.

hash = attacher.upload_derivatives({ thumb: thumb })
hash[:thumb] #=> #<Shrine::UploadedFile>

244
245
246
247
248
# 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

#url(*path, **options) ⇒ Object

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"

144
145
146
147
148
149
150
151
152
# 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