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

Created on 6 Apr 2017  路  11Comments  路  Source: shrinerb/shrine

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

Most helpful comment

Yes, just call self.storages = { ... } inside the uploader definition.

All 11 comments

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.

I will close this issue over the StackOverflow question, since as I commented there it's most likely that your pdf_uploader.rb somehow gets loaded before initializer.rb on Heroku.

Thanks for the quick reply! I really appreciate your level of attention. Sorry to duplicate my comments (as I've already posted this on StackOverflow) but I wanted to make sure I somehow relayed the following (as I'm still curious as to what you believe an ideal solution would be):

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)

@adamthede It's very unusual that Rails loads models before initializers, even when with config.eager_load = true and config.cache_classes = true, because I'm sure most of other Shrine users that use Rails have the same configuration for production, and this is the first time this problem got reported. I'm thinking there is likely some code that gets executed before your shrine.rb initializer which references the PdfUploader class, thus forcing it to load before the shrine.rb initializer 馃

This issue deserves a better solution. What about creating a Config class?

@prem-prakash did you run into the problem too? It's a bit of mystery why it's happening, I wouldn't predict it. It would be useful to figure out exactly what's going on, if you have a reproduction and time to dig into it to try to figure it out.

Without knowing exactly what's going on, it's hard to say what would solve it, what kind of "config class" (If the problem is the uploader being loaded before the initializer... what if it's loaded before the "config class"?)

If the problem is really load order (for mysterious reasons), you could put your config in a file in your local ./lib (so Rails won't auto-load it), then have an initializer that requires it, and _also_ require it at the top of your Uploader class(es). That would ensure it's always loaded when needed. But I personally wouldn't do something like that in my app without debugging to diagnose exactly what's going on... it may be just be a symptom of some other underlying problem that will cause other problems too (and not just with shrine), if your Rails app isn't loading expected initialization code.

(I wonder if it's related to "pre-forking", if you're using puma or passenger with such an option... but this is me just kind of guessing)

I tried to find out who is causing this problem, but I was not able, it is kind of hard debugging.
Is there a way to config my storages directly on the class that inherits from Shrine?

Yes, just call self.storages = { ... } inside the uploader definition.

Hi @janko sorry for the issue, I discoreved my local problem. It was a environment variable APP_ENV that overrides RAILS_ENV.
My initializer had a case...when for each environment normally used in Rails, but not this one set on APP_ENV, and thus my config never got loaded.

I ran into a similar problem working on a Rails engine, where config.cache_classes = true was causing any references to Shrine attachment code to fail. Also sounded potentially similar to https://github.com/shrinerb/shrine/issues/173.

I was able to determine that the initializers in the engine were loading after the rest of the classes in /app/models and /app/uploaders. I worked around this by moving the shrine configuration out the initializers folder and into /lib, then requiring the file in engine.rb.

module MyEngine
  class Engine < ::Rails::Engine
    config.before_configuration do
      require 'shrine'
    end
  end
end
Was this page helpful?
0 / 5 - 0 ratings