Unable to autoload constant ActiveStorage::Blob::Analyzable Error with Rails 5.2, AWS S3, and ActiveStorage
Asked Answered
P

7

13

I've been battling this guy for a while and have done all the Googlies on it (here, here, and many equally-unhelpful others) but to no avail.

The official error is this, called on the first line of my create method:

Unable to autoload constant ActiveStorage::Blob::Analyzable, expected /Users/lizbayardelle/.rvm/gems/ruby-2.5.0/gems/activestorage-5.2.1/app/models/active_storage/blob/analyzable.rb to define it

I'm creating a blog model, which has_one_attached :pdf and one :image, both through ActiveStorage. These relationships are all in the blog model:

class Blog < ApplicationRecord
  belongs_to :user
  has_one_attached :image
  has_one_attached :pdf
end

My controller blogs#create model is here:

  def create
    @blog = Blog.new(blog_params)
    @blog.user_id = current_user.id
    if @blog.published
      @blog.published_on = DateTime.current
    end

    respond_to do |format|
      if @blog.save
        @blog.image.attach(params[:image])
        @blog.pdf.attach(params[:pdf])
        format.html { redirect_to @blog, notice: 'Blog was successfully created.' }
        format.json { render :show, status: :created, location: @blog }
      else
        format.html { render :new }
        format.json { render json: @blog.errors, status: :unprocessable_entity }
      end
    end
  end

With this as my params:

def blog_params
  params.require(:blog).permit(:title, :teaser, :body, :cta, :category, :linked_module, :published, :published_on, :user_id, :image, :pdf)
end

I followed this tutorial to set up ActiveStorage with S3 and this one to set up ActiveStorage in general.

My storage.yml looks like this:

    test:   service: Disk   root: <%= Rails.root.join("tmp/storage") %>

    local:   service: Disk   root: <%= Rails.root.join("storage") %>

    # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) amazon:   service: S3   access_key_id: <%= ENV["AWS_ACCESS_KEY_ID"] %>   secret_access_key: <%= ENV["AWS_SECRET_ACCESS_KEY"] %>   region: us-west-1   bucket: *bucket name*

With this in my `secrets.yml` (which is GitIgnored, if it makes a difference):
development:
  secret_key_base: actual key here
  AWS_ACCESS_KEY_ID: actual key here
  AWS_SECRET_ACCESS_KEY: actual key here
test:
  secret_key_base: actual key here

# Do not keep production secrets in the repository,
# instead read values from the environment.
production:
  AWS_ACCESS_KEY_ID: <%= ENV["AWS_ACCESS_KEY_ID"] %>
  AWS_SECRET_ACCESS_KEY: <%= ENV["AWS_SECRET_ACCESS_KEY"] %>
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
  recaptcha_site_key: <%= ENV["RECAPTCHA_SITE_KEY"] %>
  recaptcha_secret_key: <%= ENV["RECAPTCHA_SECRET_KEY"] %>

My new blog form looks like this:

<%= simple_form_for(@blog) do |f| %>
  <%= f.error_notification %>
  <%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>

  <div class="form-inputs">
    <div class="form-group">
      <%= f.label :category %>
      <%= f.select :category, options_for_select(['General', 'House', 'Spouse', 'Kids', 'Other'], { class: "form-control" }) %>
    </div>
    <div class="form-group">
      <%= f.label :title %>
      <%= f.text_field :title, class: "form-control" %>
    </div>
    <div class="form-group">
      <%= f.label :teaser %>
      <%= f.text_area :teaser, class: "form-control" %>
    </div>
    <div class="form-group">
      <%= f.label :body %>
      <%= f.trix_editor :body %>
    </div>
      <div class="form-group">
      <%= f.label :cta %>
      <%= f.text_field :cta, class: "form-control" %>
    </div>
    <div class="form-group">
      <%= f.label :linked_module %>
      <%= f.text_field :linked_module, class: "form-control" %>
    </div>
    <div class="form-group">
      <%= f.label "Blog Image" %><br />
      <%= f.file_field :image %>
    </div>
    <div class="form-group">
      <%= f.label "Linked PDF" %><br />
      <%= f.file_field :pdf %>
    </div>
    <div class="form-group text-center">
      <%= f.input :published %>
    </div>
  </div>

  <div class="form-actions text-center">
    <%= f.button :submit %>
  </div>
<% end %>

I did input the config keys to Heroku, but since this error is on localhost I can't see that making a difference. I also made sure minimagick is installed.

Can anyone see what's going wrong here? I'm really growing to hate ActiveStorage after all the issues it's giving me...

ADDITIONAL INFORMATION

Here's my Gemfile:

source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.5.0'

gem 'rails', '~> 5.2.0'
gem 'puma', '~> 3.11'
gem 'sass-rails', '~> 5.0'
gem 'uglifier', '>= 1.3.0'
gem 'coffee-rails', '~> 4.2'
gem 'jbuilder', '~> 2.5'
gem 'mini_magick'
gem 'jquery-rails'
gem 'devise'
gem 'bootsnap'
gem 'bootstrap', '~> 4.1.3'
gem 'sprockets-rails'
gem 'bootstrap-sass'
gem 'bcrypt', '~> 3.1.7'
gem 'friendly_id', '~> 5.1.0'
gem 'stripe'
gem 'figaro'
gem 'magnific-popup-rails', '~> 1.1.0'
gem 'simple_form'
gem 'acts-as-taggable-on'
gem 'aws-sdk' , '~> 3'
gem 'aws-sdk-s3', require: false
gem 'simple_form_extension'
gem 'recaptcha', require: "recaptcha/rails"
gem 'font-awesome-rails'
gem 'trix', git: 'https://github.com/bcoia/trix.git'

group :production do
  gem 'pg', '~> 0.20.0'
  gem 'rails_12factor'
end

group :development, :test do
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  gem 'sqlite3'
end

group :development do
  gem 'sqlite3'
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '>= 3.0.5', '< 3.2'
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
end

group :test do
  gem 'sqlite3'
  gem 'capybara', '>= 2.15', '< 4.0'
  gem 'selenium-webdriver'
  gem 'chromedriver-helper'
end

gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]


  [1]: https://medium.com/alturasoluciones/setting-up-rails-5-active-storage-with-amazon-s3-3d158cf021ff
  [2]: https://edgeguides.rubyonrails.org/active_storage_overview.html
Pentomic answered 16/9, 2018 at 21:14 Comment(6)
Can you share the Gemfile ?Unpaidfor
@KedarnagMukanahallipatna I added it to the OP.Pentomic
Did you run rails active_storage:install and rake db:migrate ?Unpaidfor
@KedarnagMukanahallipatna I did not, which is embarrassing. However, this addition only moved me to a ActiveRecord::RecordNotSaved in BlogsController#create Failed to save the new associated image_attachment error on the @blog.image.attach(params[:image]) line of the controller method. Thoughts?Pentomic
Ok seems like the issue with the blob is resolved. The error you're seeing is caused by has_one association. The problem is when you're attaching a new image, it is trying to replace the old one, which would cause the foreign_key to fail. You'll have to purge the existing one and then attach a new one @blog.image.purge @blog.image.attach(params[:image])Unpaidfor
@KedarnagMukanahallipatna Thank you! If you want to write it up as an answer I'll happily choose it!Pentomic
U
7

Fix for this is to run the following in the order.

rails active_storage:install
rake db:migrate

And if you get the error, Failed to save the new associated image_attachment, it's mostly because of has_one association. To fix it, you should do the following

@blog.image.purge
@blog.image.attach(params[:image])
Unpaidfor answered 18/9, 2018 at 14:44 Comment(3)
I already ran the migrations and copied over the S3 paths to the new table for all the model attachments and I'm still getting this error when I try to preview an image.Introjection
which leads me to think I'm not including something explicitly perhaps. hmmIntrojection
Why does this work? Just giving some commands for people to blindly plug in is not helpful.Nonnah
C
8

In my case the problem was with Autoloading and Reloading Constants (Zeitwerk Mode) (Rails 6.1)

I solved changing the file: /config/environments/development.rb

config.autoloader = :classic

(source: https://github.com/rails/rails/issues/38681)

Caylor answered 7/1, 2021 at 3:17 Comment(0)
U
7

Fix for this is to run the following in the order.

rails active_storage:install
rake db:migrate

And if you get the error, Failed to save the new associated image_attachment, it's mostly because of has_one association. To fix it, you should do the following

@blog.image.purge
@blog.image.attach(params[:image])
Unpaidfor answered 18/9, 2018 at 14:44 Comment(3)
I already ran the migrations and copied over the S3 paths to the new table for all the model attachments and I'm still getting this error when I try to preview an image.Introjection
which leads me to think I'm not including something explicitly perhaps. hmmIntrojection
Why does this work? Just giving some commands for people to blindly plug in is not helpful.Nonnah
P
4

I know that this already has an answer, but I stumbled across this same situation where re-installing active_storage and migrating my database didn't work.

I stumbled across this forum: https://groups.google.com/forum/#!topic/rubyonrails-talk/RaFBG6wi2K0 and found an answer that pushed me in the right direction.

My problem was that my storage.yml file was incorrect, and wasn't even valid yml. Try running your storage.yml file through an online yml validator: http://www.yamllint.com/

It failed to validate, and I was able to figure out my problem from there.

Precursory answered 28/3, 2019 at 18:55 Comment(0)
C
4

I ran into this after having done:

rails active_storage:install
rake db:migrate

the issue was that I didn't have the correct "config/master.key" file for the current "config/credentials.yml.enc" file.

Celanese answered 23/8, 2019 at 13:19 Comment(1)
Perfect! I changed laptops and when resetting my development environment hit this issue when running rails db:seed. Hadn't even turned the server on yet to see that I hadn't copied my master.key. Simple fix and back on the road again!Chromolithography
P
3

Thanks to nudges from Thomas and user6374022, I found that my YAML was not parsing correctly in development.

storage.yml:

local:
  service: Disk
  root: <%= Rails.root.join("tmp/storage") %>

google:
  service: GCS
  project: app
  bucket: <%= Rails.application.credentials.dig(Rails.env.to_sym, :google, :bucket) %>
  credentials:
    type: <%= Rails.application.credentials.dig(Rails.env.to_sym, :google, :type) %>
    project_id: <%= Rails.application.credentials.dig(Rails.env.to_sym, :google, :project_id) %>
    private_key_id: <%= Rails.application.credentials.dig(Rails.env.to_sym, :google, :private_key_id) %>
    private_key: <%= Rails.application.credentials.dig(Rails.env.to_sym, :google, :private_key).dump %>
    client_email: <%= Rails.application.credentials.dig(Rails.env.to_sym, :google, :client_email) %>
    client_id: <%= Rails.application.credentials.dig(Rails.env.to_sym, :google, :client_id) %>
    auth_uri: <%= Rails.application.credentials.dig(Rails.env.to_sym, :google, :auth_uri) %>
    token_uri: <%= Rails.application.credentials.dig(Rails.env.to_sym, :google, :token_uri) %>
    auth_provider_x509_cert_url: <%= Rails.application.credentials.dig(Rails.env.to_sym, :google, :auth_provider_x509_cert_url) %>
    client_x509_cert_url: <%= Rails.application.credentials.dig(Rails.env.to_sym, :google, :client_x509_cert_url) %>

In my case the private_key line wasn't parsing correctly in development because that dig function returns null, and the dump function doesn't exist for nil.

I corrected it with the following:

private_key: <%= (Rails.application.credentials.dig(Rails.env.to_sym, :google, :private_key) || '').dump %>
Pennell answered 23/8, 2020 at 20:30 Comment(0)
W
3

For some reason the config.autoloader = :classic option broke loading for some other classes in my app; however, adding an initializer with the following worked (Rails 6.0.4.7):

Rails.application.reloader.to_prepare do
  ActiveStorage::Blob
end
Wetzell answered 25/4, 2022 at 20:32 Comment(0)
F
0

Don't forget you can debug storage.yml with Rails.logger as well. Always restart the server after a change.

# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
<% Rails.logger.info "STORAGE.YML" %>
<% Rails.logger.info Rails.application.credentials.dig(:s3, :key) %>
<% Rails.logger.info Rails.application.credentials.dig(:s3, :secret) %>
s3:
  service: S3
  access_key_id: <%= Rails.application.credentials.dig(:s3, :key) %>
  secret_access_key: <%= Rails.application.credentials.dig(:s3, :secret) %>
Fussbudget answered 19/5, 2023 at 13:48 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.