sprockets-rails: Rails.application.assets is nil in production

Now in sprockets-rails 3 app.assets is set only in case when assets.compile option is enabled (https://github.com/rails/sprockets-rails/blob/master/lib/sprockets/railtie.rb#L142).

By default in Rails this option is set to false in production environment: https://github.com/rails/rails/blob/master/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt#L27

This leads us to the bug when some gem calls Rails.application.assets.resolve("asset.js") in production environment and the NoMethodError is raised.

@josh does it sounds like a bug?

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Comments: 21 (7 by maintainers)

Commits related to this issue

Most upvoted comments

There is a method to that in sprocket-rails. Rails.application.assets_manifest.find_sources

Just as the reference for people trying to fix it in the local codebase after the update. The easy fix is to replace Rails.application.assets.find_asset("#{asset_name}") with Rails.application.assets_manifest.files.values.map{|v| v['logical_path']}.include?('#{asset_name}')

We needed to access a compiled file within another file during precompilation, so Rails.application.assets_manifest didn’t work the first time you run assets:precompile.

We solved this by doing this:

Sprockets::Railtie.build_environment(Rails.application, true)[file_name].to_s

In case anyone is in the same situation that we are.

Thanks @rafaelfranca. Rails.application.assets_manifest.find_sources(‘asset.js’).first seems to return exactly what Rails.application.assets[‘asset.js’].to_s used to produce and it works both in dev and prod 😃

@nasa42 Rails.application.assets_manifest.find(name).first gives the error:

“Sprockets::Error: manifest requires environment for compilation” which makes it sound like it’s trying to recompile…

If you are looking for an exact replacement for asset = Rails.application.assets.find_asset(name), (i.e., a method that returns a Sprockets::Asset object, so that you can have access to methods like content_type), use asset = Rails.application.assets_manifest.find(name).first.

My workaround suggestion is to make your own helper in ApplicationController, which will use application.assets or application.assets_manifest as needed. Because the manifest will be empty when using the debug asset pipeline, whereas the debug .assets hash will be nil on production. And remember to use File.binread to ensure images don’t get broken.

def self.get_asset(name)
  if Rails.application.assets
    asse = Rails.application.assets[name]
    return asse.to_s if asse 
  end
  asse = Rails.application.assets_manifest.assets[name]
  return nil unless asse
  return File.binread(File.join(Rails.application.assets_manifest.dir, asse))
end

I found ways to check for assets in both development and production (heroku) in Rails 5.2, but neither works in both environments, so I added an application helper like this:

def image_exists?(path)
    if Rails.env == "development" && Rails.application.assets.find_asset(path)
      return true
    elsif Rails.env == "production" && Rails.application.assets_manifest.assets[path]
      return true
    else
      return false
    end
 end

Now I can check if an image_exists in both production and development before calling image_tag. The root of the problem is image_tag throws an exception if the image doesn’t exist…why not just respond nil???

ISTM the ideal would be for us to provide an application.assets that knows how to look things up from the manifest, without doing any new compilation.

That way we could help people ignore implementation details (that, IMO, they shouldn’t need to care about) like where on the filesystem the assets actually live – basically providing a more-suitably-shaped API around just using File.read.