rails 4.2 assets loading - ruby-on-rails

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.

Related

What exactly "config.assets.debug" setting does?

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.

When I am given multiple CSS files, should I just dump all the styles in my main application.css?

I work with front-end designers that give me a bunch of HTML & CSS files. There are some things that are very straight forward - like CSS for Twitter Bootstrap or any other framework.
But when the designer has created a bunch of other stylesheets, should I just include them in the stylesheets directory or should I just copy all the styles to the main application.css file?
Is this just a matter of style or does my approach really (or not) matter with the asset pipeline?
Thanks.
The approach doesn't usually matter, but it can matter if you do not want to include everything in one stylesheet link on the final page. I'd strongly recommend keeping the stylesheets separated for the same reason you keep code separated. Easier maintenance, less conceptual overhead per file, and easier to find where to make the change you need to make.
Even though they get combined by the asset pipeline, I tend to do one stylesheet per controller, or component, with certain shared styles (navigation, forms, common UI forms) in their own file. I haven't found the perfect solution yet (I still struggle with wanting "DRYer" stylesheets), but this works pretty well.
application.css is ment to organize css file, i remember there are comments on the top of the application.css telling not to directly write css file inside it. You should create a new css file for those style-sheet content your front-end guy gave you. And as application.css will include all the files under the stylestheet folder automatically. It will work.

How to manage CSS Stylesheet Assets in Rails 3.1?

I'm just learning the new asset pipeline in Rails 3.1. One particular problem I'm having is with the way Sprockets just mashes all the found CSS stylesheets into one massive stylesheet. I understand why this is advantageous over manually merging stylesheets and minifying for production. But I want to be able to selectively cascade stylesheets instead of having all rules all mashed together. For instance, I want:
master.css
to be loaded by all pages in the Rails app, but I want
admin.css only to be loaded by pages/views within the admin section/namespace.
How can I take advantage of the great way that Rails 3.1 combines stylesheets and minifies them for production, but also have the former flexibility of being able to load only certain stylesheet combinations per layout?
Or should this be done by adding a class to body tags in layouts-
body class="admin"
And then target style rules as appropriate. Using SASS scoped selectors this might be a reasonable solution.
This is how i solved the styling issue: (excuse the Haml)
%div{:id => "#{params[:controller].parameterize} #{params[:view]}"}
= yield
This way i start all the page specific .css.sass files with:
#post
/* Controller specific code here */
&#index
/* View specific code here */
&#new
&#edit
&#show
This way you can easily avoid any clashes.
Hope this helped some.
I have a post about this on my website:
Leveraging Rails 3.1, SCSS, and the assets pipeline to differentiate your stylesheets
And check out this answer to another question: Using Rails 3.1 assets pipeline to conditionally use certain css
Hope this helps.
Best regards,
Lasse
#nathanvda: sure...
We make use of multiple layout files. So in our app/views/layouts, instead of having just application.html.haml (we use HAML), we actually ignore the application layout and use 3 custom layouts:
admin.html.haml (admin section views only)
registered.html.haml (registered/signed in users views only)
unregistered.html.haml (unregistered/unsigned in users views only)
So at the top of my admin.html.haml file I will have my stylesheet link tags to a separate admin.scss (we use SCSS) manifest. That manifest will load any necessary sub-stylesheets just for the admin section. This allows us to specify rules just for the admin section while also making use of common styles. For instance, we use jquery-ui throughout the site, so the styles associated with jquery-ui sit in their own stylesheet and we include them in the manifests for all 3 css manifest files.
This solution doesn't give you a single CSS file that can be cached, but it ends up giving you 3 CSS files, each of which can be cached. This allows a tradeoff between performance and some flexibility in organizing CSS rules so we don't have to worry about CSS rule collisions.
The way I've been doing it so far is to have two seperate folders a/ and u/ where a/ is for the admin view and u/ is for the user view. Then in the layout I point to the appropriate application.css with assets/u/application.css(js). Bit of a pain having to move the auto generated files each time but a lot less than having to require each file individually in the manifest.
I use something like
application.html.erb
">
show.html.erb
content_for :body_id do
page_specific_body_id
end

Can Rails' javascript_include_tag ignore previously-loaded scripts?

I'm using this line:
= javascript_include_tag :all, :recursive => true, :cache => true
in the footer of a Rails app to do the following:
Load all the scripts under public/javascripts recursively
In production, lump them all into one file (the :cache part)
However, I want to load one of these scripts in the head, because some inline JS depends on it.
Is there an option for javascript_include_tag to exclude files that have already been loaded?
I know it doesn't do this by default, because right now I see a script referenced twice in the source HTML.
javascript_include_tag is a simple html helper and doesn't track weather or not something has already been included on the page, so no, there isn't an option, sucks.
In place of this, you can solve your problem in a few ways:
Use something like Jammit, which I use right now and is an absolute joy. It packages all your javascripts up in production into one file, allows you to compress the shit out of them, and does the same for CSS. You configure it using a .yml file in your config directory, making it portable and easy to recursively include scripts from directories while still controlling the order in which they are included on the page or appended to the package. An alternative (less awesome IMHO) to Jammit is Asset Packager.
Use javascript_include_tag in the head. Not really the end of the world. Optimize later.
Not really Rails specific, but use head.js. head.js is pretty new but allows you to load many scripts quickly, in parallel, while preserving the execution order and taking callbacks to fire once your scripts are loaded but before the dom is ready. It includes a bunch of other goodies like HTML5 friendliness in older browsers. This option is interesting because you keep your scripts separate, or at least in different packages for maximum parallel download speed. I'm not sure weather or not downloading one huge compressed package serially in the <head> or even right at the footer, or loading things in parallel using head.js will be faster, but it sure makes managing these things easier.
I've used both head.js and Jammit recently and I must say, for heavy Javascript apps, they make things wonderfully easy.
In config/application.rb you can set the meaning of :defaults in the javascript_include_tag :
# JavaScript files you want as :defaults (application.js is always included).
config.action_view.javascript_expansions[:defaults] = %w(rails, jquery)
You can then use the :defaults parameter to use the tag in application.erb.html, or wherever you like:
<%= javascript_include_tag :defaults %>
Once you've done this at the bottom of your view template, you can of course include single scripts before it.
Now, I realise that you wanted to use the :all parameter, and load recursively, but this solution does give you more control, which I think you are going to need here.

Including large JS library with :cache => true into a rails app

I am thinking about the best way of including a JS library into rails app supporting :cache => true option for both JS and CSS.
Let me take an example to describe the question: jQueryUI (that's just an example). It usually has the following structure when downloaded:
+jq.ui
+css
+skin1
+images
all_the_images.png
jq-ui.css
+skin2
+images
all_the_images.png
jq-ui.css
+js
jquery.js
jq.ui.js
Now in order to use it I have to include this structure into rails app (2 js files + 1-2 css).
I need to be able to use :cache => true option (so that the jquery, jquery ui, application.js etc would be all in one file; also the jq.ui/skin2/jq-ui.css and application.css would be in a single file too).
The problem with :cache => true is that the single (combined) CSS file will not reference the correct images as it will be moved to the stylesheets path instead of stylesheets/jq.ui.css/skin2/jq-ui.css. Thus broken links to images from the CSS.
The question is:
Where the library like this should go in to the rails app? Should I reshuffle the structure to the default rails convention (and thus manually modify jquery ui css to fix image references) or use it as it is and combine all the files some other way?
Thanks,
Dmitriy.
If I really wanted to keep the directory structure of the jquery ui skins together, I would just exclude those css files from being cached into 1 file. Say you had 2 other css files outside of jquery ui:
stylesheet_link_tag "css-file1", "css-file2", :cache => "main"
I would just cache those and keep skins packaged with jquery ui, even if they don't get combined into a single file. I would probably do this because if a skin or a skin image got updated I would just want to be able to drop in and replace it to update it, rather than deal with restructuring directories or modifying css/js files that came with it. It's too error prone and not worth the benefit of saving 2 extra http requests.
Is there a specific reason why you need to combine them? I understand that it means more requests to your webserver, but the results will be cached. You might also want to consider using a version of jquery that's served off of the google CDN.
Alternatively if you must combine them into one file then I suggest flattening the directory structure and/or using absolute paths to your resources. The problem with this is that you'll need to be careful when updating to new version of your third party libraries as it will involve editing the libraries.
I personally use sprokets for managing my JS dependencies (instead of :cache => true), but it also won't help you with this use case. Even so, you might want to consider taking a look if you're going to have a lot of javascript.

Resources