4

I've been struggling to figure out why I'm receiving this error on Heroku.

Shrine::Error (storage :cache isn't registered on PdfUploader)

initializer.rb

require "shrine"
require "shrine/storage/s3"

s3_options = {
  access_key_id:     ENV.fetch('AWS_ACCESS_KEY_ID'),
  secret_access_key: ENV.fetch('AWS_SECRET_ACCESS_KEY'),
  region:            ENV.fetch('AWS_REGION'),
  bucket:            ENV.fetch('AWS_BUCKET')
}

Shrine.storages = {
  cache: Shrine::Storage::S3.new(prefix: "cache", upload_options: {acl: "public-read"}, **s3_options),
  store: Shrine::Storage::S3.new(prefix: "store", upload_options: {acl: "public-read"}, **s3_options)
}

Shrine.plugin :activerecord
Shrine.plugin :direct_upload, presign: true

pdf_uploader.rb

class PdfUploader < Shrine
  plugin :determine_mime_type
  plugin :validation_helpers

  Attacher.validate do
    validate_mime_type_inclusion %w(image/jpeg image/png image/gif application/pdf), message: 'must be JPEG, PNG, GIF, or PDF'
  end
end

I'm able to upload to the cache folder in my AWS bucket directly from the DOM with jquery-file-upload and the route I've established for getting the presign URL:

mount PdfUploader::UploadEndpoint => '/pdfs/upload'

However, when trying to submit my form and create my object in the db, I get the following error:

Shrine::Error (storage :cache isn't registered on PdfUploader)

Any thoughts or suggestions would be greatly appreciated. Thanks!

PS - for what it's worth - everything works on my local ... this issue is just on Heroku

Adam Thede
  • 41
  • 2
  • For what it's worth, in the rails console on Heroku I can see that my PdfUploader.storages[:cache] returns nil, while the Shrine.storages[:cache] returns an instance of my AWS S3 storage cache. Still don't understand why the uploader class is not inheriting this from the initializer. Conversely, in my local, both PdfUploader.storages[:cache] and Shrine.storages[:cache] return the same object. – Adam Thede Apr 06 '17 at 20:01
  • The only way I can see this happening is if the initializer.rb was somehow loaded *after* your pdf_uploader.rb. This order wouldn't work because each Shrine subclass gets its own copy of storages, so modifying `Shrine.storages` after `PdfUploader` has already been instantiated wouldn't have had any effect, because `PdfUploader` already grabbed a copy of whichever value `Shrine.storages` had at that point. – Janko Apr 07 '17 at 01:27
  • Would appreciate more insight on how best to implement Shrine for direct to s3 uploads on Heroku. My uploader.rb file is within `app/models`. And it appears that with `config.eager_load = true` and `config.cache_classes = true` on Heroku my Classes within `app` are indeed loaded before the shrine initializer file is loaded (which is in `config/initializers`). And that is why my uploader never receives the storages. The only fix I've found is to include the following line in application.rb: `require_relative './initializers/shrine'` beneath `Bundler.require(*Rails.groups)` – Adam Thede Apr 07 '17 at 19:13
  • See the [Direct Uploads to S3](http://shrinerb.com/rdoc/files/doc/direct_s3_md.html) guide. – Janko Apr 08 '17 at 03:18
  • My concern is that the guide does not address my exact issue as stated above. When implementing the gem via the guide the issue above occurs due to the eager loading and caching of app assets on Heroku. As far as I can tell there are two ways of addressing this: set eager loading and caching to false (not ideal), or modify application.rb. Neither solution seems ideal and I'd be curious to hear of any other solutions (especially if there is a feature in the gem that specifically addresses said issue). – Adam Thede Apr 08 '17 at 13:16
  • 2
    I now think the problem you have isn't that `pdf_uploader.rb` loads before the initializer, but that `config/routes.rb` is the one that gets loaded before the initializer (which then in turn implicitly loads `pdf_uploader.rb` when referencing `PdfUploader`). However, I cannot reproduce this behaviour; [shrine-rails-example](https://github.com/erikdahlstrand/shrine-rails-example) also mounts the direct upload endpoint the same way, and there the initializer correct gets loaded before `config/routes.rb` in production env. Same with a new Rails 5 app. Perhaps it's a gem changing the order? – Janko Apr 09 '17 at 12:55
  • Why would Rails eager load anything in app/models before config/initializers? Today we added a simple model in a namespace, released to production and started getting Shrine::Error storage :store isn't registered. Renaming config/initializers/shrine.rb to 1_shrine.rb fixed the issue, but why? – vanboom Jul 07 '21 at 22:34

0 Answers0