---
title: Shrine 2.19.0
---

## New features

* A new `instrumentation` plugin has been added. It sends and logs events for
  various Shrine operations, and provides an API for other plugins to send and
  log their own events.

  ```rb
  Shrine.plugin :instrumentation

  uploaded_file = Shrine.upload(io, :store)
  uploaded_file.exists?
  uploaded_file.download
  uploaded_file.delete
  ```
  ```
  Metadata (32ms) – {:storage=>:store, :io=>StringIO, :uploader=>Shrine}
  Upload (1523ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :io=>StringIO, :upload_options=>{}, :uploader=>Shrine}
  Exists (755ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :uploader=>Shrine}
  Download (1002ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :download_options=>{}, :uploader=>Shrine}
  Delete (700ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :uploader=>Shrine}
  ```

  It supports [ActiveSupport::Notifications] (default) and [dry-monitor]
  notification backends out of the box.

  ```rb
  require "dry-monitor"

  Shrine.plugin :instrumentation, notifications: Dry::Monitor::Notifications.new(:test)
  ```

* Metadata extraction can now be skipped during the upload by passing
  `metadata: false` to the uploader. This is useful in tests, where you might
  want to skip any potentially expensive metadata extraction while creating
  test data.

  ```rb
  uploaded_file = uploader.upload(io, metadata: false) # skips metadata extraction
  uploaded_file.metadata #=> {}
  ```

* Metadata extraction can now be forced during the upload of a
  `Shrine::UploadedFile` object by passing `metadata: true` to the uploader.

  ```rb
  uploaded_file = uploader.upload(another_uploaded_file, metadata: true) # forces metadata extraction
  uploaded_file.metadata #=> re-extracted metadata
  ```

* The `pretty_location` plugin now supports specifying a different identifier.

  ```rb
  plugin :pretty_location, identifier: "uuid"
  # "user/aa357797-5845-451b-8662-08eecdc9f762/profile_picture/493g82jf23.jpg"

  plugin :pretty_location, identifier: :email
  # "user/foo@bar.com/profile_picture/493g82jf23.jpg"
  ```

* The `store_dimensions` plugin has gained an `:on_error` option for specifying
  how to react when an error occurrs while extracting dimensions.

  ```rb
  plugin :store_dimensions, on_error: :warn        # prints a warning message (default)
  plugin :store_dimensions, on_error: :fail        # raises the exception
  plugin :store_dimensions, on_error: :ignore      # ignores the exception
  plugin :store_dimensions, on_error: -> (error) { # custom handler
    # report the exception to your exception handler
  }
  ```

* The `FileSystem#upload` method now accepts a `:move` option for moving the
  file instead of copying it. This is now preferred over the `moving` plugin.

  ```rb
  uploader.upload(file, move: true)
  File.exist?(file.path) #=> false
  ```

* The `FileSystem#clear!` method can now take a block for more control over
  which files should be deleted.

  ```rb
  file_system.clear! { |path| path.mtime < Time.now - 7*24*60*60 } # delete files older than 1 week
  ```

## Other improvements

* Registering storage objects under string keys now works again (this got
  broken in version 2.18.0).

* Several plugins now add their own instrumentation when the `instrumentation`
  plugin has been loaded:

  | Plugin                | Instrumentation                         |
  | :-----                | :--------------                         |
  | `derivation_endpoint` | instruments file processing             |
  | `determine_mime_type` | instruments analyzing MIME type         |
  | `store_dimensions`    | instruments extracting image dimensions |
  | `signature`           | instruments calculating signature       |
  | `infer_extension`     | instruments inferring extension         |
  | `remote_url`          | instruments remote URL downloading      |
  | `data_uri`            | instruments data URI parsing            |

* `Shrine.logger` has been added, and any warnings or other messages (such as
  from the `instrumentation` plugin) now go through it. This way you can change
  the logging destination:

  ```rb
  # log messages into the Rails logger
  Shrine.logger = Rails.logger
  ```

* The `store_dimensions` plugin now prints warnings by default when extracting
  dimensions failed.

* The `pretty_location` plugin now comes with a `#pretty_location` method which
  you can call for customization.

  ```rb
  def generate_location(io, record: nil, **context)
    identifier = record.email if record.is_a?(User)
    pretty_location(io, record: record, identifier: identifier, **context)
  end
  ```

* The `Shrine::UploadedFile#[]` operator has been added for easier metadata
  retrieving.

  ```rb
  uploaded_file.metadata["duration"]
  # can now be just
  uploaded_file["duration"]
  ```

* The `Shrine.mime_type`, `Shrine.dimensions`, and `Shrine.signature` aliases
  have been added to `determine_mime_type`, `store_dimensions`, and `signature`
  plugins.

  ```rb
  Shrine.determine_mime_type(io)
  # can now be just
  Shrine.mime_type(io)
  ```
  ```rb
  Shrine.extract_dimensions(io)
  # can now be just
  Shrine.dimensions(io)
  ```
  ```rb
  Shrine.calculate_signature(io)
  # can now be just
  Shrine.signature(io)
  ```

* The `#validate_{max,min}_dimensions` validators have been added to the
  `validation_helpers` plugin.

  ```rb
  validate_min_width  10
  validate_min_height 10
  validate_max_width  5000
  validate_max_height 5000

  # can now be written as

  validate_min_dimensions [10, 10]
  validate_max_dimensions [5000, 5000]

  # which can be additionally shortened to

  validate_dimensions [10..5000, 10..5000]
  ```

* The `#validate_size`, `#validate_width`, and `#validate_height` shorthands
  have been added to the `validation_helpers` plugin.

  ```rb
  validate_min_size 1024
  validate_max_size 10*1024*1024

  # can now be shortned to

  validate_size 1024..10*1024*1024
  ```
  ```rb
  validate_min_width 10
  validate_max_width 5000

  # can now be shortened to

  validate_width 10..5000
  ```
  ```rb
  validate_min_height 10
  validate_max_height 5000

  # can now be shortened to

  validate_height 10..5000
  ```

* The `#validate_mime_type` and `#validate_extension` shorthands have been
  added to the `validation_helpers` plugin.

  ```rb
  validate_mime_type_inclusion %w[image/jpeg image/png image/webp]
  # can now be just
  validate_mime_type %w[image/jpeg image/png image/webp]
  ```
  ```rb
  validate_extension_inclusion %w[jpeg png webp]
  # can now be just
  validate_extension %w[jpeg png webp]
  ```

* Default error messages in `validation_helpers` plugin have been simplified.

* You can now call `super` when overriding `Shrine::UploadedFile` methods
  defined by the `add_metadata` plugin.

## Backwards compatibility

* The `logging` plugin has been deprecated in favour of the `instrumentation`
  plugin.

* The `moving` pluing has been deprecated in favour of the `:move` option to
  `FileSystem#upload`. This means that the `#move` && `#movable?` methods are
  not part of the storage abstraction anymore.

* The `backup` plugin has been deprecated (a new `mirroring` plugin will get
  added in 3.0).

* The `copy` plugin has been deprecated.

* The `Shrine::Plugins::DataUri::DataFile` constant from the `data_uri` plugin
  has been renamed to `Shrine::DataFile`.

* The `Shrine::Plugins::RackFile::UploadedFile` constant from the `data_uri` plugin
  has been renamed to `Shrine::RackFile`.

* The `:older_than` option for `FileSystem#clear!` has been deprecated in
  favour of passing a block.

  ```rb
  file_system.clear!(older_than: Time.now - 7*24*60*60)
  # should now be replaced with
  file_system.clear! { |path| path.mtime < Time.now - 7*24*60*60 }
  ```

* The `FileSystem#upload` method deprecates ignoring unrecognized upload
  options.

* The `FileSystem#upload` method doesn't backfill `size` metadata anymore if an
  IO with unknown size is being uploaded via `Shrine#upload`.

* Several plugins have changed how they store configuration options internally.
  If you were accessing these options directly, you will need to update your
  code.

[ActiveSupport::Notifications]: https://api.rubyonrails.org/classes/ActiveSupport/Notifications.html
[dry-monitor]: https://github.com/dry-rb/dry-monitor
