---
title: Shrine 2.14.0
---

## New features

* `Shrine::Storage::S3` now accepts a `:client` option for specifying an AWS
  SDK client object. This allows the user to configure client-side encryption by
  passing a `Aws::S3::Encryption::Client` object:

  ```rb
  client = Aws::S3::Encryption::Client.new(
    kms_key_id: "alias/my-key",
    **options
  )

  Shrine::Storage::S3.new(client: client, bucket: "my-bucket")
  ```

* The metadata blocks in the `add_metadata` plugin now have previous metadata
  available to them in `context[:metadata]` (thanks to @jrochkind).

  ```rb
  add_metadata :foo do |io, context|
    context[:metadata] #=>
    # {
    #   "filename"  => "nature.jpg",
    #   "size"      => 123,
    #   "mime_type" => "image/jpeg"
    # }

    "foo"
  end

  add_metadata :bar do |io, context|
    context[:metadata] #=>
    # {
    #   "filename"  => "nature.jpg",
    #   "size"      => 123,
    #   "mime_type" => "image/jpeg",
    #   "foo"       => "foo"
    # }

    "bar"
  end
  ```

* `UploadedFile#to_rack_response` from `rack_response` plugin now accepts
  `:type` and `:filename` options for overriding the values in metadata that
  would otherwise be used for the `Content-Type` and `Content-Disposition`
  response headers.

  ```rb
  status, headers, body = uploaded_file.to_rack_response(
    type:     "text/csv; charset=utf-8",
    filename: "export.csv",
  )

  headers["Content-Type"]        #=> "text/csv; charset=utf-8"
  headers["Content-Disposition"] #=> "inline; filename=\"export.csv\""
  ```

* `Shrine.data_uri` from `data_uri` plugin now accepts a `:filename` string,
  which will be returned in
  `Shrine::Plugins::DataUri::DataFile#original_filename`.

  ```rb
  io = Shrine.data_uri("data:,content", filename: "foo.txt")
  io.original_filename #=> "foo.txt"
  ```

* `UploadedFile#download_url` from `download_endpoint` plugin now accepts a
  `:host` option. Previously it was only possible to configure the URL host on
  the plugin level.

  ```rb
  uploaded_file.download_url(host: "https://example.com")
  ```

* `Attacher#cached?` and `Attacher#stored?` now accept an optional uploaded
  file parameter, and return whether the given uploaded file is uploaded to the
  temporary or permanent storage of the attacher (thanks to @jrochkind).

## Performance improvements

* `UploadedFile#download` doesn't call `Storage#download` anymore (only
  `Storage#open`). This improves performance when the uploaded file is
  already opened, e.g. when using `refresh_metadata` plugin and
  `Shrine.with_file` during metadata extraction. Instead of performing
  a new download request, `UploadedFile#download` will in this case reuse the
  already opened IO object.

* Added `tempfile` plugin that makes it easier to avoid copying the
  `UploadedFile` to disk multiple times during promotion. For example, in the
  following code the uploaded file will be downloaded to disk only once
  instead of three times.

  ```rb
  Shrine.plugin :tempfile
  ```
  ```rb
  class MyUploader < Shrine
    plugin :processing
    plugin :add_metadata
    plugin :refresh_metadata

    process(:store) do |io, context|
      io.open do
        io.refresh_metadata!

        processor = ImageProcessing::Vips.source(io.tempfile)
        # ... processing ...
      end
    end

    add_metadata :foo do |io, context|
      Shrine.with_file(io) { "..." } unless context[:action] == :store
    end

    add_metadata :bar do |io, context|
      Shrine.with_file(io) { "..." } unless context[:action] == :store
    end
  end
  ```

* `UploadedFile#refresh_metadata!` from `refresh_metadata` plugin now detects
  when the uploaded file is already opened and in that case doesn't re-open the
  file. This makes code like this faster:

  ```rb
  uploaded_file.open do
    uploaded_file.refresh_metadata! # doesn't re-open the file anymore
  end
  ```

* The `rack_response` plugin now integrates with `Rack::Sendfile` middleware
  when `Shrine::Storage::FileSystem` is used. It does so by making the response
  body respond to `#to_path`.

* `#<name>_attacher` doesn't look up the attachment class every time it's
  called with a new model instance anymore, making it slightly faster (thanks
  to @printercu).

## Other improvements

* The `lib/shrine.rb` file has been split into `lib/shrine.rb`,
  `lib/shrine/uploaded_file.rb`, `lib/shrine/attacher.rb`,
  `lib/shrine/attachment.rb`, and `lib/shrine/plugins.rb` (thanks to
  @printercu).

* `Shrine::Storage::S3` and `rack_response` plugin now use the
  [content_disposition] gem for generating correctly formatted
  `Content-Disposition` header values.

* The `S3#download` and `S3#open` methods now work correctly with server-side
  encryption (as long as the necessary `:sse_*` parameters are passed in).

* Fixed `FileSystem#clear!` not working with symlinks (at least on MRI).

* Fixed `add_metadata` plugin overriding previously defined metadata
  blocks when loaded again.

* Fixed `processing` plugin overriding previously defined processing
  blocks when loaded again.

* Fixed `backgrounding` plugin erroring when temorary/permanent storage is
  declared in `Shrine::Attachment.new` and there are no `:cache` or `:store`
  storages defined.

* Fixed `UploadedFile#extension` (and thus the tempfile in
  `UploadedFile#download`) including URL query parameters in the file extension
  when `Shrine::Storage::Url` is used (`shrine-url` gem) with URLs that have
  query parameters (thanks to @jrochkind).

* Fixed `backgrounding` plugin aborting promotion when cached file metadata was
  refreshed in the processing block and Active Record JSON column was used.
    - `refresh_metadata` plugin now avoids mutating the uploaded file data hash
    - `backgrounding` plugin now continues promotion if cached file metadata has changed

* The `Shrine.data_uri` method from the `data_uri` plugin now picks up media
  type parameters from the data URI and includes them in
  `Shrine::Plugins::DataUri::DataFile#content_type`.

  ```rb
  io = Shrine.data_uri("data:text/plain;charset=utf-8,content")
  io.content_type #=>
  # BEFORE: "text/plain"
  # AFTER:  "text/plain;charset=utf-8"
  ```

* When fetching `mime_type` metadata value from `io.content_type`, any media
  type parameters are now stripped. This means that if the user uploads a text
  file with a `Content-Type` of `text/plain; charset=utf-8`, the `mime_type`
  will now correctly be stored as `text/plain`. This fixes any MIME type
  validations that might not have been passing due to additional media type
  parameters.

* When `determine_mime_type` plugin is loaded with the `:default` analyzer,
  a warning is not printed anymore.

* When `Shrine::Storage::S3` is initialized with `bucket: nil`, an appropriate
  error message is now raised.

* The `:content_type` MIME type analyzer has been added to the
  `Shrine.mime_type_analyzers` hash in `determine_mime_type` plugin (previously
  known as `:default`).

## Documentation

* The [Retrieving Uploads] guide has been added.

## Backwards compatibility

* Support for MRI 2.1 and MRI 2.2 has been dropped.

* `Shrine::Plugins::Base` module has been removed and contained modules have
  been moved to `InstanceMethods` and `ClassMethods` modules inside the
  corresponding core classes. This should not break anything unless you were
  referencing those modules directly.

  ```rb
  Shrine::Plugins::Base::InstanceMethods        => Shrine::InstanceMethods
  Shrine::Plugins::Base::ClassMethods           => Shrine::ClassMethods
  Shrine::Plugins::Base::FileMethods            => Shrine::UploadedFile::InstanceMethods
  Shrine::Plugins::Base::FileClassMethods       => Shrine::UploadedFile::ClassMethods
  Shrine::Plugins::Base::AttacherMethods        => Shrine::Attacher::InstanceMethods
  Shrine::Plugins::Base::AttacherClassMethods   => Shrine::Attacher::ClassMethods
  Shrine::Plugins::Base::AttachmentMethods      => Shrine::Attachment::InstanceMethods
  Shrine::Plugins::Base::AttachmentClassMethods => Shrine::Attachment::ClassMethods
  ```

* `UploadedFile#download` doesn't call `Storage#download` anymore, it now
  always calls `Storage#open`. Since `Storage#download` is not used anywhere
  else in Shrine, storages can remove the `#download` method.

* `Shrine::Storage::S3#download` has been deprecated and will be removed in
  Shrine 3.

* In `Shrine::Storage::S3#upload`, `#open`, and `#presign` it's now deprecated
  to pass the `Content-Disposition` header value with non-ASCII characters.
  Starting with Shrine 3 these characters won't be escaped anymore. You should
  now use the [content_disposition] gem to properly format the
  `Content-Disposition` header value.

  ```rb
  # BAD:
  plugin :default_url_options, store: -> (io, **options) do
    { response_content_disposition: "attachment; filename=\"#{io.original_filename}\"" }
  end

  # GOOD:
  plugin :default_url_options, store: -> (io, **options) do
    { response_content_disposition: ContentDisposition.attachment(io.original_filename) }
  end
  ```

* The `mime_type` metadata value will now strip any additional media type
  parameters such as `charset` from `io.content_type`. If you were relying on
  this behaviour, you will need to update your code.

* In `determine_mime_type` plugin, the `:default` MIME type analyzer has been
  renamed to `:content_type`. The `:default` alias will stop being supported in
  Shrine 3.

  ```rb
  plugin :determine_mime_type, analyzer: :default
  # should be changed to
  plugin :determine_mime_type, analyzer: :content_type
  ```

* The `UploadedFile` private methods that were added by the `rack_response`
  plugin have now been moved to an internal class (leaving only
  `#to_rack_response`). If you've are currently overriding any of these private
  methods, you'll need to update your code.

* The `UploadedFile` private methods that were added by the `download_endpoint`
  plugin have now been moved to an internal class (leaving only
  `#download_url`). If you've are currently overriding any of these private
  methods, you'll need to update your code.

[Retrieving Uploads]: https://shrinerb.com/docs/retrieving-uploads
[content_disposition]: https://github.com/shrinerb/content_disposition
