Getting 404 for Rails Static Assets - ruby-on-rails

I'm using Carrierwave to handle file uploads. Files get stored under public/uploads/. Project uses Ember.js templates, and the img tags point to the proper src. (I've verified that the files are present at those paths.) However, the server returns a 404 for each.
It looks like this is a common problem, and the common solution is to:
config.serve_static_assets = true
However, this isn't working. I'm still getting 404s. Any other ideas about how to deal with this?
It should be noted that I'm not using Apache or nginx.

Rails no longer compiles the assets without digests. In order for this to work properly, you need to use a rails helper to include the proper asset name (with the digest), or use a gem like https://github.com/alexspeller/non-stupid-digest-assets as a workaround.

Related

Rails 4 Heroku Assets not loading, though in heroku asset pipeline

I have a problem with certain assets on heroku. (local environment is working fine)
The assets are in the pipeline. If I execute in the heroku rails console:
helper.asset_path("typicons.woff")
helper.asset_path("backgrounds/1.jpg")
I get the following response:
/assets/typicons-c2430aad2b6a33948dc064cfaee8ad65ff9e3ca439834f3aaa84abec3d10dea8.woff
/assets/backgrounds/1-c2098ff7e7fbb89b2d18e9cd9089f712f2b837265d1d2e4182c36c23392760c6.jpg
So I assume that the assets are in the heroku asset pipeline. As well by opening the url directly with the digest in it, I receive the file.
However if I try to reference the files in css or javascript like this:
$('.top-content').backstretch("/assets/backgrounds/1.jpg");
The file does not load. As well opening /assets/backgrounds/1.jpg directly does not work. Referencing assets from .rb or .erb files works.
Please can someone tell me, what kind of config I have to change, so the URLs for assets work as well without the digest?
Thank you!
Assuming you are using a fairly standard asset pipeline setup, this passage from the Rails Guides should help:
If you add an erb extension to a JavaScript asset, making it something such as application.js.erb, you can then use the asset_path helper in your JavaScript code:
-- http://guides.rubyonrails.org/asset_pipeline.html (section 2.3.3)
In your example, add the erb extension to your JS file and then change the line to
$('.top-content').backstretch(<%= asset_path("backgrounds/1.jpg") %>);
The problem is that Rails 4 does not support non-digested assets at all.(For whatever reason)
Here is a more thorough explanation on the issue: Non Digested Asset Names in Rails 4
My personal workaround was to add a public asset folder to my app:
public/assets/static/
and upload the assets required there. Since it was only about fonts and background images, which do not change often, it does not seem to be a problem. In the link above there are several other solutions proposed.

Am I handling the 'public' directory the right way?

I am using Ruby on Rails 4.1.1 and I want to add a linked image - my app's logo - in email messages but I had a doubt since my previous question.
By using files located in the public directory it seems that to link to an image in your Asset Pipeline you can use
link_to(LINK_TEXT_OR_IMAGE_TAG_HELPER, image_path(IMAGE_NAME))
but for those located in the app/assets it seems do not work the same, at least in rendered email messages in production mode. All assets that are compiled in Production have a fingerprint ID... is the fingerprint causing the load of static assets to do not render images in email messages?
I doubt since I would like to access images from both browser and email. I tried to solve the issue by moving the image from app/assets/images/logo.png to public/images/logo.png and by changing statements in my application.css.scss file from image-url("logo.png") to url("/images/logos.png"). However I do not know if I am following "the Rails way" or a "best practice". Do I? Should I add to the public directory all assets that I plan to use outside my application and in the app/assets directory all assets that I plan to use internally to my application?
For emails, it almostisn't any different compared to standard Rails views.
You can link to an image in your mailer using the image_tag helper you'd normally use in regular views as well:
<%= image_tag('some_image.jpg') %>
You also need to tell Action Mailer where it can find the assets, because it will use absolute URLs to refer to your images:
config.action_mailer.asset_host = 'http://www.example.com/'

Rails 4 Asset Pipeline: Asset missing fingerprint in asset_path from js

I am deploying a Rails 4.0 application which includes HTML partial templates as assets for our front-end javascript framework. Although these templates are part of the asset pipeline and are properly precompiled, when I call asset_path from embedded ruby in our js files, it returns the path to our templates without the fingerprint.
I am quite certain that this is purely a Asset Pipeline question, but to give you a complete sense of our tech stack: We use Rails 4.0, Ruby 2.1, AngularJS for our front-end MVC framework, and AssetSync to synchronize our assets between Rails and our CDN.
An example of where this occurs (in a file included in app/assets/application.js.erb:
$routeProvider
.when('/', {
templateUrl: "<%= asset_path 'home.html' %>",
controller: "HomeController"
});
This works great locally, but as soon as config.assets.digest = true in production, the call to asset_path does not properly factor in the fingerprint. The templates are in the app/assets directory within a new subdirectory templates. So in the above example, the home.html asset is at app/assets/templates/home.html. Our javascript has itself been precompiled at that point, so I'm thinking that it might be an issue of which order the assets are precompiled in.
I've noticed a few issues on the Rails Github (1, 2, 3) and a couple of SO posts about fingerprints not being set properly (1, 2), but can't find anything about them not being included at all...
Any help or ideas that you can provide would be much appreciated.
Edit 4/15: forgot to include that the extensions on my application javascript file DOES include .erb (app/assets/application.js.erb). Thanks Alex for catching that. I've updated it above.
Also, following instructions in this article on Heroku, I confirmed that running puts helper.asset_path("home.html") from within a Rails console running in production prints a properly fingerprinted URL for that asset.
This appears to be an issue with the AssetSync gem. I removed it, reconfigured the app so that Rails serves the assets, and the fingerprinting works fine.
If anyone else finds this question and is running into the same issue, I would recommend against using AssetSync. According to Heroku:
Many developers make use of Amazon’s S3 service for serving static assets that
have been uploaded previously, either manually or by some form of build process.
Whilst this works, this is not recommended as S3 was designed as a file storage
service and not for optimal delivery of files under load. Therefore, serving
static assets from S3 is not recommended.
Amazon CloudFront is the preferred method of serving assets through a CDN, and is very easy to configure with a Rails app that serves its own static assets, accomplishing the same goals as AssetSync.
I'm pretty new to this stuff, but to get the asset_path to work, don't you need a .erb on the end of that file?
Check out the bottom of this article for more info:
https://devcenter.heroku.com/articles/rails-4-asset-pipeline
If it works in development, that may not help. There is a helpful section on debugging at the bottom of the article though.
Update
Here's another article that could help:
https://medium.com/self-directed-learning/9ba1f595102a
Flipping on this configuration in Heroku made some of my asset pipeline problems go away:
heroku labs:enable user-env-compile -a yourapp
Hope this helps!
Alex

Static asset caching on Heroku with Jammit by changing ActionController::Base#page_cache_directory

I'm attempting to use Jammit for packaging CSS and JS for a Rails app deployed on Heroku, which doesn't work out of the box due to Heroku's read only file system. Every example I've seen of how to do this recommends building all the packaged asset files in advance. Because of Heroku's Git-based deployment, this means you need to make a separate commit to your repository every time these files change, which is not an acceptable solution to me. Instead, I want to change the path that Jammit uses to write the cached packages to #{Rails.root}/tmp/assets (by changing ActionController::Base#page_cache_directory), which is writable on Heroku.
What I don't understand is how the cached files will be used without hitting the Rails stack every time, even using the default path for cached packages. Let me explain what I mean:
When you include a package using Jammit's helper, it looks something like this:
<%= include_javascripts :application %>
which generates this script tag:
<script src="/assets/application.js" type="text/javascript"></script>
When the browser requests this URL, what actually happens is that it gets routed to Jammit::Controller#package, which renders the contents of the package to the browser and then writes a cached copy to #{page_cache_directory}/assets/application.js. The idea is that this cached file is built on the first request, and subsequent requests should serve the cached file directly without hitting the Rails stack. I looked through the Jammit code and I don't see how this is supposed to happen. What prevents subsequent requests to /assets/application.js from simply routing to Jammit::Controller again and never using the cached file?
My guess is that there's a Rack middleware somewhere I'm not seeing that serves the file if it exists and forwards the request on to the controller if it doesn't. If that's the case, where is that code? And how would it work when changing ActionController::Base#page_cache_directory (effectively changing where Jammit writes cached packages)? Since #{Rails.root}/tmp is above the public document root, there's no URL that maps to that path.
Great question! I haven't set this up myself, but it's something I've been meaning to look into, so you've prompted me to do so. Here's what I would try (I'll give a shot myself soon, but you are probably going to beat me to it).
config.action_controller.page_cache_directory = "#{Rails.root}/tmp/page_cache"
Now change your config.ru to:
require ::File.expand_path('../config/environment', __FILE__)
run Rack::URLMap.new(
"/" => Your::App.new,
"/assets" => Rack::Directory.new("tmp/page_cache/assets"))
Just make sure not to have anything in public/assets, since that won't ever be picked up.
Notes:
This is for Rails 3. Not sure of the solution under Rails 2.
It looks like Rack::Directory sets cache control headers to 12 hours so Heroku will cache your assets to Varnish. Not sure if Jammit sets this in its controller, but even if it doesn't, it will be cached quite quickly.
Heroku also sets ENV['TMPDIR'] now as well, so you can use that instead of Rails.root + '/tmp' if you wish.
This might be of use, it's for a different gem but the idea is similar and I'm trying to get it working with the plain asset helpers.
http://devcenter.heroku.com/articles/using-compass
Unfortunately it seems to be quite difficult to get rails to do this without patching/rewriting the asset helpers module (which resembles coupled spaghetti).

Should I disable mod_rails for images and stylesheets directories?

I had a rare error in my Rails application. A CSS file was referring to non existing image files. And missing PNG file was somehow mapped to a controller action. Fortunately the action wasn't changing DB. This seems to be not OK that missing PNG can trigger controller action.
So should I disable mod_rails for static asset directories? However I've never heard this is required for Rails apps.
It is definitely a good idea, since if you allow any kind of image upload the target destination is usually the asset directory. Normally the user can quite easily upload a php or ruby file instead, so disabling all mod_evil_script for these directories is a good idea in general.
You should be serving static assets directly via Apache anyway, because it's faster. Let Rails do what it's designed to do which is handle dynamic requests.

Resources