Rails 4 assets - two different digests getting generated - ruby-on-rails

I clearly must be Doing Something Wrong here. I'm wrestling with the asset pipeline (again). I have a custom font, and it seems to me to get everything to compile properly I need to use asset_path() in multiple places, but it's having an unexpected effect.
I realize there are several ways to do this, but here's what I have currently:
In application.css.scss.erb:
#import "<%= asset_path("my-font.css") %>";
my-font.css's source file is app/assets/stylesheets/my-font.css.erb (it needs to be an .erb because I am also using asset_path() there as well).
In application.rb I am adding my-font.css to the precompile list.
config.assets.precompile << 'my-font.css'
When I clean out public/assets and run rake assets:precompile Everything's getting compiled, with digests, but the digest applied to the actual file is not the same as the digest calculated and put in to application.css.
The resulting file is
public/assets/my-font-2f25682a1ea904a866ef9f44101a5a2e.css but in public/assets/application-bba2edaee36771f4bdb5c89b8ec90aaf.css the reference to it is:
#import url(/assets/my-font-ed843d3b174ca427edf963e473ad9e9b.css);
I realize I'm probably using asset_path() more than I should, and also importing files via url() instead of requiring them, but this has gotten me the closest to having things working.
I suspect one of the digests is being calculated on my-font.css before it goes through ERB, and the other after, but I don't understand why nor how to fix it.
Suggestions?

I would guess that you're cleaning your assets just by emptying public/assets. That's not enough, you'll also need to empty your tmp/cache/assets, or just run rake assets:clobber to do both.

I've resolve this kind of dependency by injecting the raw contents of the asset you want to bundle into a composite asset like application.scss.erb. You need to explicitly declare the dependency (so that changes in my-fonts.css cause a regenerations of application.css) and then inject the contents:
application.scss.erb:
// Should be at Top of File
//= depend_on_asset my-fonts.css
//... wherever in the file you want the contents injected:
<%= environment['my-fonts.css'] %>
How does this work? During asset pipeline compilation, the environment here has a hash of all pre-compiled asset contents. This allows you to inject the compiled contents from my-fonts.css into your composite asset. Because we're manually injecting the value, we need to explicitly create a dependency to track this relationship via depend_on_asset.
One thing to keep in mind is that multiple asset pre-processors (SCSS, ERB, etc) are processed from the "outside in", so the contents of the my-fonts.css asset will be compiled/injected during the ERB processing as CSS output. They will be included within the asset before SCSS processing. This shouldn't pose a problem, because if this is an SCSS asset, any SCSS references will be compiled before injection into the parent asset.

Related

Rails.application.assets.find_assets(file).digest_path doesn't work in production

I need to iterate through multiple images via a JS file, so I am making an http request to the server to match the filename to its client-side fingerprinted equivalent. I'm getting the fingerprinted file by passing the original file through Rails.application.assets.find_asset(file).digest_path
Example, in the rails console:
Rails.application.assets.find_asset("scene1.jpg").digest_path
returns
"scene1-b691b411ad644bcf2c84ef9e30f52db9ffdf57c18fadf99872dff3ebb81fa548.jpg"
However, when I run on my local server the output is simply nil.
Using the Rails asset pipeline, assets are referenced with their logical path. In this case, you can use embedded ruby (erb) in your script by adding the .erb extension to the file, e.g.
var logo = <%= asset_path('logo.png') %>;
When this script is compiled, the ruby code will produce a reference to the asset with the current digest. The digest path is a "fingerprint" and will change as often as the assets themselves change.
To learn more about the asset pipeline, check out the ROR guide and the Sprockets documentation.

Rails application returns 404 for images even after rake assets:precompile

I have Rails 4 application deployed on Digital Ocean. Everything went smoothly but the app returns 404 for images:
I, [2014-07-20T22:28:00.693171 #7751] INFO -- : Started GET "/assets/left.png" for 128.73.51.11 at 2014-07-20 22:28:00 +0000
F, [2014-07-20T22:28:00.695271 #7751] FATAL -- :
ActionController::RoutingError (No route matches [GET] "/assets/left.png"):
vendor/bundle/ruby/2.0.0/gems/actionpack-4.1.0/lib/action_dispatch/middleware/debug_exceptions.rb:21:in `call'
After some googling I found this this and it is said that I can use either config.serve_static_assets = true or rake assets:precompile. I tried both but still have 404 for fonts and images (CSS and JS) are OK.
Can anyone help with this?
This might not solve your issue directly - it's more a clarification on what may be going wrong :)
Fingerprinting
Without knowing your app setup properly, this is just something to consider, but the main cause of this particular issue is the idea of asset fingerprinting
Fingerprinting is basically where Rails will take your asset files, compile them into single files (typically application.css / application.js), and allow you to reference those files as you wish.
The problem here is that if you're serving static assets, which have been fingerprinted, you won't be able to reference them as "static" assets from your CSS / JS any more. You'll have to use dynamic references:
Paths
Dynamic references are basically where you use something like asset_path - which allows you to reference the paths as you require regardless of their filename
Typically, you'll have CSS set up like this:
#app/stylesheets/application.css
body {
background: url("your/image.png");
}
The problem here is that this will reference the image directly, and won't be able to use the dynamic fingerprinted path which is required. To fix this, you'll need to use one of the Rails CSS pre-processors (SASS & SCSS):
#app/assets/stylesheets/application.css.scss
body {
background: asset_url("your/image.png"); /* Notice the path helper? */
}
The issue of dynamically referenced filenames / paths will be one of the issues you'll likely be facing with your application. Without seeing how you're referencing the images, I can only speculate - but I'd recommend taking a look at which path / asset helpers you're using

Rails 4 image-path, image-url and asset-url no longer work in SCSS files

Are we supposed to use something else aside from image-url and others in Rails 4? They return different values that don't seem to make sense. If I have logo.png in /app/assets/images/logo.png and I do the following, this is what I get:
image-url("logo.png") -> url("/images/logo.png") #obviously doesn't work
image-path("logo.png") -> "/images/logo.png"
asset-url("logo.png") -> url("/logo.png")
Of course none of these work because they need at least /assets in front.
UPDATE: Actually, I just noticed, how do I access images in Rails 4? I have an image at /app/assets/images/logo.png. But if I go to any of the following URLs, I still don't see my image:
http://localhost:3000/assets/logo.png
http://localhost:3000/assets/images/logo.png
http://localhost:3000/logo.png
http://localhost:3000/images/logo.png
UPDATE 2: The only way I can bring up my logo.png is by moving it into the /app/assets/stylesheets directory and then pulling up:
http://localhost:3000/assets/logo.png
I just had this issue myself.
3 points that will hopefully help:
If you place images in your app/assets/images directory, then you should be able to call the image directly with no prefix in the path. ie. image_url('logo.png')
Depending on where you use the asset will depend on the method. If you are using it as a background-image: property, then your line of code should be background-image: image-url('logo.png'). This works for both less and sass stylesheets. If you are using it inline in the view, then you will need to use the built in image_tag helper in rails to output your image. Once again, no prefixing <%= image_tag 'logo.png' %>
Lastly, if you are precompiling your assets, run rake assets:precompile to generate your assets, or rake assets:precompile RAILS_ENV=production for production, otherwise, your production environment will not have the fingerprinted assets when loading the page.
Also for those commands in point 3 you will need to prefix them with bundle exec if you are running bundler.
Your first formulation, image_url('logo.png'), is correct. If the image is found, it will generate the path /assets/logo.png (plus a hash in production). However, if Rails cannot find the image that you named, it will fall back to /images/logo.png.
The next question is: why isn't Rails finding your image? If you put it in app/assets/images/logo.png, then you should be able to access it by going to http://localhost:3000/assets/logo.png.
If that works, but your CSS isn't updating, you may need to clear the cache. Delete tmp/cache/assets from your project directory and restart the server (webrick, etc.).
If that fails, you can also try just using background-image: url(logo.png); That will cause your CSS to look for files with the same relative path (which in this case is /assets).
I just found out, that by using asset_url helper you solve that problem.
asset_url("backgrounds/pattern.png", image)
I had a similar problem, trying to add a background image with inline css. No need to specify the images folder due to the way asset sync works.
This worked for me:
background-image: url('/assets/image.jpg');
Rails 4.0.0 will look image defined with image-url in same directory structure with your css file.
For example, if your css in assets/stylesheets/main.css.scss, image-url('logo.png') becomes url(/assets/logo.png).
If you move your css file to assets/stylesheets/cpanel/main.css.scss, image-url('logo.png') becomes /assets/cpanel/logo.png.
If you want to use image directly under assets/images directory, you can use asset-url('logo.png')
for stylesheets:
url(asset_path('image.jpg'))
In case anyone arrives looking for how to generate a relative path from the rails console
ActionView::Helpers::AssetTagHelper
image_path('my_image.png')
=> "/images/my_image.png"
Or the controller
include ActionView::Helpers::AssetTagHelper
image_path('my_image.png')
=> "/images/my_image.png"

Rails: How to access images in Javascript?

i'm pretty new to Rails and have a little question:
How can i access images in Javascript? SCSS, ERB, ... have methods like "image_path", but i didn't find something similar for Javascript.
I need it to specify the image URLs for the firefly plugin:
$.firefly({images : ['???/1.jpg', '???/2.jpg'],total : 40});
if your image in /app/assets/images/ you can simply use
/assets/1.jpg
Similarly in css, you can use
url(/assets/1.jpg)
You can follow same thing when using in javascript.
$.firefly({images : ['/assets/1.jpg', '/assets/2.jpg'],total : 40});
Note: The above methods will cause problem when your rails app is in sub-directory. In that case use relative path. Asset pre-compilation will compile all assets in public/assets directory. so your structure may be like:
public
-assets
--images
---1.png
--javascripts
--stylesheets
---style.css
so from style.css, you can use relative path like ../images/1.png
When I needed to do this, I inserted the whatever image was required on the page under a div#class and set that class as hidden in my css. Then, in javascript, I could access the image from that div.
May not be ideal solution, but couldn't think of anything else because of asset pipeline.
Also, try accessing image from ./assets/image_name.jpg

Why are Rails assets not isolated between models?

I have these a coffeescript file for a model entree that just instanciates a class in another coffeescript file:
jQuery(document).ready ->
ch = new CepageHandling
ch.handleKeyPress()
The handlePress function captures keyup events on a control.
I have another model vin where I want to enable the same feature. I'm surprised to see I don't need to do anything (it already has the same html), it's already working, even though the coffeescript for the vin model is completely empty. I assume that the created javascript for entree gets called even when I'm not on this page.
I have seen the same behavior with scss files, where style defined for one model gets applied to others if the descriptors match. Can someone explain (or point to some article) if this is normal behavior that assets are not isolated in rails? I really have a hard time grasping how it works.
The default manifest files (application.js, etc) do require_tree . which will load all files on all pages, concat them all together in production, etc. If you want things to be isolated you'll need to put a test in your ready handler to skip this code in some cases, or you'll need more manifests (and not use application.js for example) to silo your code per page. I suggest you read every word of the asset pipeline Rails Guide very carefully... required reading!
It happens because of the manifest file application.js, more precisely in this line of code:
//= require_tree .
What this means is that all .js files contained inside the /assets/javascripts/ folder would be loaded to your views.
As your entree.js is already loaded and using the JQuery document ready function, it searches for the rules of your file inside all of your views.
This means that all of your pages that contains this ch element will get the same behavior.

Resources