Rails Engine asset images are not compiled - ruby-on-rails

I have a strange behavior with my engine gem https://github.com/antpaw/bhf on the production environment. The bhf/application.js and bhf/application.css is compiled the way you would expect it and also linked the right way in the template. But none of the assets/bhf/image files are compiled and can be found in shared/assets/bhf/ on production server, unless i change
config.serve_static_assets = false
to true in production.rb?
How bad is it to use this setting? And is there a way for my engine to work out-of-the-box?

Per your gemspec https://github.com/antpaw/bhf/blob/master/bhf.gemspec
It has a dependency on rails v4 s.add_dependency(%q<rails>, [">= 4.0.0"])
I suspect its related to asset digest. Where the assets are getting compiled as expected with digest but are referred from respective CSS without digest.
Its a possible issue with sprockets-rails discussed here issue#49
non-stupid-digest-assets - not so good but preferred solution
It will copy non-digested assets to /public

Are you using Webrick in production? If so, you will need to set config.serve_static_assets = true since it's no good at serving static assets. Other Ruby 'app servers' aren't also ideal for serving static assets so you'll need to have Rails do that for the meantime. It's not an ideal set-up though since page caching won't work and all requests will hit your app.
Once you use a proper server for serving static assets like Nginx or Apache, you will need to set it to config.serve_static_assets = false so that Rails will leave it to Nginx/Apache to handle serving static assets. That way, not all requests will have to hit your Rails app since caching will work.
Since you're building a Rails engine, you don't need to worry about that since that is the responsibility of the one who is deploying the Rails app. You won't have control over their config.

Related

Rails Application - Using Cloudfront for asset delivery with Heroku

The situation
I couldn't get my vendor assets to precompile in heroku without specifying each individual file to precompile in config/initializers/assets so resorted to setting
config.assets.compile = true
Note: I didn't require vendor assets in application.js because I'm calling them on a per page basis when they are needed.
Anyhow I setup a Cloudfront account and now everything is working as it does in development. But on deploy to Heroku, there is a warning and a link that leads to a StackOverflow post, warning against setting config.assets.compile to true.
Compile Set to True in Production If you have enabled your application
to config.assets.compile = true in production, your application might
be very slow. This was best described in a stack overflow post:
When you have compile on, this is what happens: Every request for a
file in /assets is passed to Sprockets. On the first request for each
and every asset it is compiled and cached in whatever Rails is using
for cache (usually the filesystem). On subsequent requests Sprockets
receives the request and has to look up the fingerprinted filename,
check that the file (image) or files(css and js) that make up the
asset were not modified, and then if there is a cached version serve
that.
This setting is also known to cause other run-time instabilities and
is generally not recommended. Instead we recommend either precompiling
all of your assets on deploy (which is the default) or if that is not
possible compiling assets locally.
My question is, Since I'm now using Cloudfront, does that cover me from what they are warning about, slowness etc.?
Thanks in advance for any advice :)
Yes, you're likely covered. The request for an asset will hit CloudFront first. After the first request, it will be cached and shouldn't hit your server for compilation. Every time your assets change, however, they'll have to recompile, which is of course very slow.

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

Precompile assets for a rails engine

In a standard app, I have this line in my production.rb, which creates endpoints for non-default precompiled assets:
config.assets.precompile += %w( mobile.css )
My rails engine is a standard Sinatra app. It has its own assets.
When on development, these assets are served fine, presumably the web requests are handled by rails and sprockets. On production I'm getting 404s on the assets, and think I have to manually tell sprockets to provide the files. How can this be done without tightly linking?
It isin't evident how to set up env-specific initializers for engines. Is this done? Not only, for example, is config/development.rb within the engine not loaded, but there's no way to get the application class itself without knowing its name, in order to modify configuration.
And even if there was, it seems that having any engine able to reconfigure the main app would be very bad idea.
So maybe its better to let assets handling be done by sinatra itself? Or another instance of sprockets for the engine? How do other engines handle this?

config.assets.compile=true in Rails production, why not?

The default Rails app installed by rails new has config.assets.compile = false in production.
And the ordinary way to do things is to run rake assets:precompile before deploying your app, to make sure all asset pipeline assets are compiled.
So what happens if I set config.assets.compile = true in production?
I wont' need to run precompile anymore. What I believe will happen is the first time an asset is requested, it will be compiled. This will be a performance hit that first time (and it means you generally need a js runtime in production to do it). But other than these downsides, after the asset was lazily compiled, I think all subsequent access to that asset will have no performance hit, the app's performance will be exactly the same as with precompiled assets after this initial first-hit lazy compilation. is this true?
Is there anything I'm missing? Any other reasons not to set config.assets.compile = true in production? If I've got a JS runtime in production, and am willing to take the tradeoff of degraded performance for the first access of an asset, in return for not having to run precompile, does this make sense?
I wrote that bit of the guide.
You definitely do not want to live compile in production.
When you have compile on, this is what happens:
Every request for a file in /assets is passed to Sprockets. On the first request for each and every asset it is compiled and cached in whatever Rails is using for cache (usually the filesystem).
On subsequent requests Sprockets receives the request and has to look up the fingerprinted filename, check that the file (image) or files(css and js) that make up the asset were not modified, and then if there is a cached version serve that.
That is everything in the assets folder and in any vendor/assets folders used by plugins.
That is a lot of overhead as, to be honest, the code is not optimized for speed.
This will have an impact on how fast asset go over the wire to the client, and will negatively impact the page load times of your site.
Compare with the default:
When assets are precompiled and compile is off, assets are compiled and fingerprinted to the public/assets. Sprockets returns a mapping table of the plain to fingerprinted filenames to Rails, and Rails writes this to the filesystem. The manifest file (YML in Rails 3 or JSON with a randomised name in Rails 4) is loaded into Memory by Rails at startup and cached for use by the asset helper methods.
This makes the generation of pages with the correct fingerprinted assets very fast, and the serving of the files themselves are web-server-from-the-filesystem fast. Both dramatically faster than live compiling.
To get the maximum advantage of the pipeline and fingerprinting, you need to set far-future headers on your web server, and enable gzip compression for js and css files. Sprockets writes gzipped versions of assets which you can set your server to use, removing the need for it to do so for each request.
This get assets out to the client as fast as possible, and in the smallest size possible, speeding up client-side display of the pages, and reducing (with far-future header) requests.
So if you are live compiling it is:
Very slow
Lacks compression
Will impact render time of pages
Versus
As fast as possible
Compressed
Remove compression overheard from server (optionally).
Minimize render time of pages.
Edit: (Answer to follow up comment)
The pipeline could be changed to precompile on the first request but there are some major roadblocks to doing so. The first is that there has to be a lookup table for fingerprinted names or the helper methods are too slow. Under a compile-on-demand senario there would need to be some way to append to the lookup table as each new asset is compiled or requested.
Also, someone would have to pay the price of slow asset delivery for an unknown period of time until all the assets are compiled and in place.
The default, where the price of compiling everything is paid off-line at one time, does not impact public visitors and ensures that everything works before things go live.
The deal-breaker is that it adds a lot of complexity to production systems.
[Edit, June 2015] If you are reading this because you are looking for a solution for slow compile times during a deploy, then you could consider precompiling the assets locally. Information on this is in the asset pipeline guide. This allows you to precompile locally only when there is a change, commit that, and then have a fast deploy with no precompile stage.
To have less overhead with Pre-compiling thing.
Precompile everything initially with these settings in production.rb
# Precompile *all* assets, except those that start with underscore
config.assets.precompile << /(^[^_\/]|\/[^_])[^\/]*$/
you can then simply use images and stylesheets as as "/assets/stylesheet.css" in *.html.erb
or "/assets/web.png"
For anyone using Heroku:
If you deploy to Herkou, it will do the precompile for you automatically during the deploy if compiled assets are not included (i.e. public/assets not committed) so no need for config.assets.compile = true, or to commit the precompiled assets.
Heroku's docs are here. A CDN is recommended to remove the load on the dyno resource.
It won't be the same as precompiling, even after that first hit: because the files aren't written to the filesystem they can't be served directly by the web server. Some ruby code will always be involved, even if it just reads a cache entry.
Set config.asset.compile = false
Add to your Gemfile
group :assets do
gem 'turbo-sprockets-rails3'
end
Install the bundle
Run rake assets:precompile
Then Start your server
From the official guide:
On the first request the assets are compiled and cached as outlined in development above, and the manifest names used in the helpers are altered to include the MD5 hash.
Sprockets also sets the Cache-Control HTTP header to max-age=31536000. This signals all caches between your server and the client browser that this content (the file served) can be cached for 1 year. The effect of this is to reduce the number of requests for this asset from your server; the asset has a good chance of being in the local browser cache or some intermediate cache.
This mode uses more memory, performs poorer than the default and is not recommended.
Also, precompile step is not trouble at all if you use Capistrano for your deploys. It takes care of it for you. You just run
cap deploy
or (depending on your setup)
cap production deploy
and you're all set. If you still don't use it, I highly recommend checking it out.
Because it is opening a directory traversal vulnerability - https://blog.heroku.com/rails-asset-pipeline-vulnerability

Rails 3 automatic asset deployment to Amazon CloudFront?

Is there a gem or method available in Rails 3.1 that can upload assets to amazon cloud front automatically and use those instead of serving locally hosted ones? I guess it's easy to upload compiled assets manually and then change the rails app config to use that asset host, but when an asset is modified, the uploads to cloud front would need to be done manually again. Any good ways out there for this?
Definitely check out asset_sync on github. Or our Heroku dev centre article on Using a CDN asset Host with Rails 3.1 on Heroku.
There is quite a big performance improvement in using asset_sync vs a CDN custom origin, letting your application lazily compile assets in production or serving them precompiled directly off your app servers. However I would say that. I wrote it.
With asset_sync and S3 you can precompile assets meaning all the assets are there ready to be served on the asset host / CDN immediately
You can only require the :assets bundle in application.rb on precompile, saving memory in production
Your app servers are NEVER hit for asset requests. You can spend expensive compute time on, you know. Computing.
Best practice HTTP cache headers are all set by default
You can enable automatic gzip compression with one extra config
If you use Cloudfronts “Custom origin” option you do not need to upload anything, Cloudfront will fetch the assets from your server when needed. For details of setting this up see:
http://blog.ertesvag.no/post/10720082458
Take a look at https://github.com/rumblelabs/asset_sync - we're using it just to S3 but I guess the CloudFront part is pretty easy once the assets are on S3.
It's ends up being a rake task that you just add to execute in your deployment process.
another option would be https://github.com/moocode/asset_id, the readme has an example to use it with cloudfront.
It should work with rails 3.1 but I have only used it on 3.0.x.
Ss John said all solutions would end up being a rake task + a bit of logic to change the asset path in rails.

Resources