What exactly "config.assets.debug" setting does? - ruby-on-rails

I have started development of simple rails application. After several hours work I have notices that somehow the deleted css is still applied to the web pages.
In order to fix the issue I executed the following actions several times:
stop/start server
use rails server
use torquebox server
delete browser cache
but nothing changes. It was very strange - the new css definitions were applied, but those that I have deleted were still there. So, I gave up and decided to create new project.
I have setup the new project (its scaffold is the same as the first one) and when I open one of the views, the styles from the old project were applied too. I have decided to look again into http://guides.rubyonrails.org/asset_pipeline.html and find out that setting
#Expands the lines which load the assets
config.assets.debug = false
solves the issue. But what is this option doing exactly? Why the old projects css were applied when this was true?

This option's effect is well described in this post, but I'll summarize it here as well. The value of changing config.assets.debug lies in a compromise between page load time in development and ease of debugging.
Basically:
config.assets.debug = true: assets are served individually, organized just as you see them in development. Preprocessed languages like SASS or CoffeeScript will still show up as their target languages (i.e., CSS and JS, respectively).
config.assets.debug = false: assets are bundled into files like application.css and application.js. Error stack traces will likely not have the correct line number any more and it is harder to map those back to your original code.

If you get to this web page, there is a possibility you are here because you are using the Rails Asset Pipeline and you made changes to one of the javascript files and reloaded the page and the change is not reflected when you search in the Sources tab in Chrome.
As stated above, config.assets.debug = false prompts the Sprockets gem to bundle all the individual javascript and css files into one application.js and application.css respectively. Also Sprockets runs the the SASS and CoffeeScript (if you did not use --skip-coffee) preprocessors on all associated files to generate css and javascript files that the browser can understand.
One important note is the following. Ruby Guides says this about debug = false:
Assets are cached on the first request after the server is started. If
any of the files in the manifest have changed between requests, the
server responds with a new compiled file.
This means if you do not change the css or javascript files between requests, then a cache will be used. As soon as you change a file, the cache is invalidated and a new cache is created for subsequent requests.
Consequently, if you made changes to a javascript file and the change is not reflected on page reload, it has nothing to do with this option config.assets.debug.
There is this other option called
config.action_controller.perform_caching.
But by default this option defaults to false in development. That is, by default, caching is only enabled in your production environment. And in current versions, Rails only ships with Fragment Caching by default. You have to install separate gems for Page and Action caching.
Fragment Caching allows a fragment of view logic to be wrapped in a cache block and served out of the cache store when the next request comes in. But then again, cache fragments will also be expired when the view fragment changes (e.g., the HTML in the view changes).
So the question remains why is the change of your javascript not reflected? The answer is Google Chrome, the browser itself, is caching the page despite your Rails settings. To remove the cache, close the current tab, open a new tab, and visit the site again.

Related

rails 4.2 assets loading

I'm playing with a rails 4.2 application and it seems like it loads all of my stylesheets for each and every page.
Is that true? How is it better than loading for each page only the relevant stylesheets?
On one hand, I know they'll all be minified when going to production, and it will reduce the total size and cache one CSS for the whole website.
On the other hand, page I might have some thin pages that will need no more than a few css lines to be rendered correctly that will get tones of files for no reason. It will also require me to be super strict and safe when choosing class names in order to have no collisions and unwanted overrides.
What about JS assets? It acts the same way?
(I guess the answer for image assets is "hell no!")
Yes, in development you'll see a lot of files (all of them) and in production they will be compiled and minified to a single file.
Once this file has been downloaded by the client it will be cached and wont need to make any further requests to load other stylesheet files on concurrent request (unless caching and turbolinks has been disabled). The downside is that the file size will be larger and make the initial load time slightly longer.
One problem as you point out is scoping. In my experience it's way better to always apply proper scopes when developing. And in rails using sass it's really easy to have nice and tidy css.
The same goes for javascript, but not for images as you pointed out.
That said. There are ways to work around this if desired, but more often than not I've realized that there are more pros than cons with a single file.
Edit:
Oh, and if you're new to rails, beware that turbolinks might cause you some headaches in the beginning messing with page ready in js-files before you get the hang of it. But it's worth it in the end.
The advantage and rationale for this is to reduce the amount of data that is sent to the client after the first page load. The first page load pulls down all the js/css (compiled into single files), but then this can be cached and reduces the amount on each subsequent page load.
The following is purely theoretical, if at all possible, try and work with the defaults of one css/jss file
If you absolutely do want to trim it down, then you can. Your application.css manifest file (or js) probably includes a line such as:
//= require_tree .
which says, require everything in this directory. you could then split this down so application.css only requires everything in the application folder (absolutely need to know):
//=require_tree ./application
Then add additional manifest files, for example for an admin section, so that the admin styles/JS can be loaded only when needed in admin.js
//=require_tree ./admin
Then in your layout file you can do
= stylesheet_link_tag "application", media: "all"
= yield :additional_stylesheets
And where you want those additional styles you can add them.
- content_for :additional_stylesheets do
= stylesheet_link_tag "admin", media: "all"
end
Note: this will cause issues if you're using turbolinks.
Note II: You may need to add these to the precompile list in application.rb:
config.assets.precompile += ['additional_manifests.css']
You could modify this workflow to not include any CSS at all to start with and only load what you need.

Mod Pagespeed adding noscript when js enabled

I have the apache mod pagespeed installed, everything appears to be working ok.
I am using it on my ruby (1.9.3) and rails (3.2) app. I recently installed the gem 'turbolinks'.
Now the URLs on my site are getting ?ModPagespeed=noscriptadded after the trailing slash. This didn't happen before I added turbolinks.
Why does the page speed tool append ?ModPagespeed=noscript to the end of URLs and how can I prevent this?
If turning off a few of the mod pagespeed options were to help then I can maybe do that with no performance loss, eg, minifying js is something I do pre deploy in a precompile, likewise, the Rails asset pipeline does a good job of combining individual .js and .css files into compiled single files etc. Therefore, turning off combine javascript wouldn't be a problem. However, I don't know if this is what is causing it, as I implied I believe it is to do with the interaction of turbolinks, my app's http requests and the pagespeed module just not noticing that js is turned on.
I must admit I find it odd that I can't find even a remotely similar query to this online.
The redirect to ?ModPagespeed=noscript occurs in a meta-tag in a block. mod_pagepseed inserts this block when applying a filter that requires JavaScript to function. One example is lazyload_images, where the markup points to a blank image, and JavaScript is inserted into the page to point to the correct image.
The blocks are not needed for optimizations to JavaScript files; such optimizations will have no effect for browsers with JS disabled.

Cannot configure assets via pipeline on local production rails 3.1.3 server

This month, I upgraded from Rails 3.0 to Rails 3.1 - this week I tried to launch the server in production mode - today I have hit a wall !
I am unable to get my production environment server to serve up my public assets (JavaScript and CSS) via the asset pipeline, unless I set config.assets.compile = truein my environment.rb file, which for speed reasons I obviously don't want to do.
I have a large number of JS and CSS files, each of which tends to get used on one or two different pages. This means creating a single "manifest" file doesn't fit my usage, as each page wants something slightly different. I also expect some of the CSS won't sit together nicely. Therefore I have gone down the approach of "just get it working", looking to tidy up the large quantity of CSS / JS later.
Here is the production.rb file:
Implicit::Application.configure do
...
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
# I set this to true, as I am testing this locally, just running a local Thin server
config.serve_static_assets = true
config.assets.compress = true
# Setting this to false removes the issue - but is SLOW
config.assets.compile = true
config.assets.digest = true
# This is overkill - but does get EVERYTHING precompiled for now
config.assets.precompile += %w( *.css *.js )
config.action_dispatch.x_sendfile_header = nil
...
end
This is quite a new area for me, and so I've spent much of today toggling these booleans and stop/starting the local Thin / Rails server to try them out. But the only value that's made a solid difference is the compile flag.
My application.rb file is pretty much standard, and has config.assets.enabled = true and config.assets.initialize_on_precompile = false in it, the latter from a heroku post (and a guess, again).
I have a fully populated public/assets directory, created with the bundle exec rake assets:precompile command, that takes about 20 mins to run on my oldish laptop (5 years), probably something to do with that "catch all" precompile regex, although with that line commented it still takes over 10 mins (!)
With the compile flag set to true, I can see copies of these assets getting created in my /tmp/cache directory - this is clearly the application creating it's own "compiled copy" of the assets.
With the compile flag set to false, I am confronted with the error message (in the logs, unless I set requests to local, then I see it on the rendered error page) of jquery.reveal isn't precompiled. However, when I go to http://localhost:3000/assets/jquery.reveal.js the javascript file is served up.
The line of my application layout causing this is:
<%= javascript_include_tag "application", "jquery.reveal" %>
I have tried changing that jquery.reveal to jquery.reveal.js with no change. Removing it fixes the index page, except that the jquery.reveal functionality is gone of course ! So clearly the application.js is being served up correctly. I just can't figure out why jquery.reveal isn't, as I can see the precompiled files in the public/assets directory.
This is an interesting issue, and I think there may be two bugs - the one you've linked and another: the file is being being compiled to the wrong name. It might be worth trying to generate a minimal test case that you can submit with a bug report.
The workaround for this - and I suspect that this is why few people seem to have the problem - is to use a secondary manifest (linking libraries only via a manifest seems to be an evolving best-practice).
Create one called home.js and require just that one file to it.
This isn't a bad approach overall. These extra manifests have to be added to the precompile array (see the guide), and allow multiple libraries to be shared over several pages or sections without having to link them each time.
Answering my own question here, but looks like it might be a bug in parsing assets with "periods" such as jquery.reveal
An issue report (https://github.com/rails/rails/issues/3398) reports this and highlights a commit (https://github.com/sstephenson/sprockets/commit/4ba5b32764a9073671df5e77355df6ed2bb3d3c9) which occurs just after Sprockets 2.0.3 - the default version that rails 3.1.3 relies upon. Upgrading Sprocket would therefore involve moving onto 3.2-stable rails - a bit bleeding edge for this newbie !
But the bug report does say this only occurs when config.assets.compile = true - and I did see whilst that was the case in my code that jquery.reveal was being dynamically compiled to jquery-8fu...8g.reveal.js (instead of jquery.reveal-8fu...8g.js).
So perhaps this ISN'T the answer. At least I hope it isn't. But will certainly continue to look at this period issue, as the "non-period" containing CSS / JS files are being served up just fine, as far as I can tell.

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

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).

Resources