Rails asset pipleline: compile to multiple stylesheets - ruby-on-rails

Due to a specific setup, I would like to split the compiled stylesheets in two files. This is because (a part of) the CSS is needed for a Java application which can parse the CSS, but it is a bit buggy and can't handle some css-(hack)-syntax. Because I am unable to modify this Java application, I want to feed it only the CSS which it needs and of which I can make sure it is correct.
So, normally the assets pipeline would produce just one '/assets/application-[..].css' file. It would to let it also generate '/assets/custom-[..].css', based on a file selection I make. This still can be pre-compiled.
Is there a way to do this? Although I understand this is not the ideal setup..

To tell rails about additional files you wish to have precompiled, you can add them to the config.assets.precompile setting.
config.assets.precompile += ["other_application.css"]
You only see application.css in your HTML because that's the only file you're including
<%= stylesheet_link_tag "application" %>
If you have some custom.css.scss in your apps/assets/stylesheets directory, it will be compiled just like application.css.
For example, I might have
- _common.css.scss
- application.css.erb.scss
- other_application.css.erb.scss
in app/assets/stylesheets. In the top of the non-partial files I will put
#import "common";
to include _common.css.scss. I can now reference either stylesheet independent of one another in a layout.
<%= stylesheet_link_tag "application" %>
# or
<%= stylesheet_link_tag "other_application" %>

Related

Multiple layouts in Ruby on Rails

I have a project where each page needs to import an extra css or JS file for only that page. Is it a good practise to use a different layout file for each view? (I don't want my view files to contain references to css/JS files).
I mean i can have a different layout for each view but i don't know if it is the optimal way to do it.
Best strategy:
Don't worry about it and just let your app handle this for you. Minified CSS and JS file delivery isn't really that slow/big and Rails will compile your assets into single files in production.
If you're determined, it is possible (your mileage may vary depending on your Rails version, and usage of Sprockets or Webpacker)
And you'll really want to understand the Rails asset pipeline (for your version of Rails) before you go nerfing these settings.
CSS inclusion/exclusion
You can ignore the asset pipeline and manually choose which CSS files to include, but you'll need to be diligent about doing this everywhere.
In your app/views/layouts/application.html file, you have a line like this inside the <head> tag:
<%= stylesheet_link_tag 'application', media: 'all' ... %>
This is loading application.css, which is a compiled version of app/assets/stylesheets/application.css and will include all CSS from all other files inside app/assets/stylesheets
Depending on your version of Rails, you'll need to turn off asset pre-compilation in either config/initializers/production.rb or config/initializers/assets.rb:
Rails.application.config.assets.precompile = []
Warning: this will turn off ALL asset compilation, including JS. Be sure you have a strategy to re-include JS files (based upon your asset pipeline)
You will now need to implement some logic to handle what CSS is included when. This could be in app/helpers/application_helper.rb. It could use the view helper [controller_name], (https://apidock.com/rails/ActionController/Metal/controller_name/class), for example, to determine which page you are on and fetch the correct CSS file.
Really rough example:
# app/helpers/application_helper.rb
module ApplicationHelper
def css_file_chooser
"/app/assets/stylesheets/#{controller_name}.css"
end
end
Then in your application.html file, you can do something like this:
<head>
<%= stylesheet_link_tag css_file_chooser, media: 'all' ... %>
...
</head>
The catch here is you'd need to have your specific CSS files named according to your controllers. E.g. if controller_name returns 'posts', you'd need to have a file: 'app/assets/stylesheets/posts.css'
If you do have some site-wide CSS that needs to be applied, you'd need to include a second stylesheet_link_tag:
In this case, if you put all your global styles into app/assets/stylesheets/global.css:
<head>
<%= stylesheet_link_tag 'global', media: 'all' ... %>
<%= stylesheet_link_tag css_file_chooser, media: 'all' ... %>
...
</head>
JS inclusion/exclusion
It's a bit dusty, but this article outlines a strategy that only loads a JS function or file if the <body> has certain class tags. I used to use it on my Rails 4 and 5 projects.

Rails CSS stylesheets overriding each other

I have a clients.css and jobs.css in the assets/stylesheets location.
Each has a respective controller. Jobs was created with a scaffold after clients. The scaffolds.scss file is blank.
application.css is blank
When I code a change such as body{color:black} in the jobs.css, it changes the clients/index.html.erb view and the jobs/index.html.erb view.
What could be the reason for this? I would like to have separate .css files for jobs and clients..
From the documentation:
Sprockets concatenates all JavaScript files into one master .js file
and all CSS files into one master .css file.
What this means, of course, is that when you make a change to jobs.css, you'll see the same css being applied to every matching element throughout your application. All of those separate .css files are there to help you keep things organized from a human perspective, rather than from the perspective of your application.
You might want to just come up with different IDs and classes depending on your page (like #body_client and #body_job) to differentiate them, but you can see how this naming convention could get unwieldy as your app grows.
Having separate assets is possible, but not without some pain.
In application.js, remove:
//= require_tree
In application.css, remove:
*= require_tree
In application.html.erb, add the following:
<%= stylesheet_link_tag "application", params[:controller], :media => "all" %>
<%= javascript_include_tag "application", params[:controller] %>
Create a new initializer file at config/initializers/assets.rb and add the following code:
%w( clients_controller jobs_controller ).each do |controller|
Rails.application.config.assets.precompile += ["#{controller}.js.coffee", "#{controller}.css"]
end
That should get you set up with separate per page assets. Check the original blog post for more details.

Adding page-specific CSS to Rails Asset Pipeline

This is a question that several people have asked before, but none of the questions were quite asked or answered in a way that I found helpful, so I'm writing the question and answer that I would have found helpful.
I have a Rails 3.1+ app using the asset pipeline. There's one specific action that I want to have use different CSS. (In my specific case, I have a page that is intended to be printed, so it truly needs completely different CSS and does not need any Javascript.) Currently, I have only one application-specific CSS file. How do I add the new CSS file and direct the asset pipeline to use my file?
For example, right now, my app/assets looks like
app/assets
/javascript
application.js
custom.js.coffee
/css
application.css
custom.css.scss
I want to add a print.css file that is used by the view of a specific action. This view will not use the application.css file. How do I add print.css?
I found this blog post to be very helpful: http://blog.seancarpenter.net/2012/11/05/page-specific-javascript-with-the-asset-pipeline/. My answer paraphrases what this blogger already wrote and fills in a few missing details.
First, it's important that you've read and understood the Rails Guide to the Asset Pipeline. Unfortunately, this guide doesn't clearly explain how to add action-specific assets, but it does cover some concepts you need to know. Made sure you understand these ideas:
That the asset pipeline compiles Javascript, CSS, and other assets so that Rails servers can cache assets for better performance.
That manifest files use commands like require, require_tree, and require_self to indicate which files are compiled together.
That in order for the asset pipeline to work properly in production, you need to manually run rake assets:precompile to produce the compiled, minified assets in the public directory.
These ideas are the minimum "need-to-know" pieces of information about the asset pipeline. If you don't already understand these ideas, you don't have an "expert or enthusiast" level of knowledge about the pipeline, and unfortunately, SO isn't the right place to learn this stuff. Fortunately, the the Rails Guide to the Asset Pipeline is a short 15-minute read and can get you up to speed quickly if you need it.
Second, these are the changes you need to make in order to ensure that the asset pipeline correctly sees and handles your new print.css file.
Follow these steps:
Add your print.css file to app/assets/css.
You'll need to create a manifest file that will show Rails where to find print.css. You need to do this, even though you only have a single CSS file you're adding. This is an easy step:
Add a file called print.js to app/assets/javascript.
Add this line to print.js:
//= require print
This will be the only line in the entire print.js file. If I understand correctly, Rails expects manifest files to have the file extension .js, which is why we aren't using print.css as the manifest file.
We now need to instruct Rails to find and use the print.js manifest. Add the following line in your config/application.rb file:
config.assets.precompile += %w( print.js )
We're almost finished! However, the already-present application.js manifest includes the line //= require_tree . which means that it will include your print.css file. This will cause your print.css styling to affect your entire site, not just the single view. There are two ways of dealing with this:
If application.js and print.js do not share any assets, you can use the stub command in your application.js to exclude the assets used in print.js. What this does is instruct application.js to remove any of the assets that print.js references from its own list of referenced files. Our modified application.js looks like:
(snip...)
require_tree .
stub print
See this answer for more information.
If your print.js and application.js files share some assets, you'll need to move all of the assets used by application.js into subdirectories. I didn't do this myself, so I'm not the most help in this area. Look at this answer for instructions.
Now we have included print.css in the asset pipeline. We now need to direct Rails to use print.css in your specific view.
Let's say your action is in the reports controller, and that the action is named print_reports. This means we have a reports_controller.rb file and a print_reports.html.erb (or .haml) file. We need to make several changes to these files.
To start, add a new layout in app/views/layouts. Perhaps call it print.html.erb. We'll use this new layout for your print_reports.html.erb file. Set it up as you desire. For a page intended to be printed, this will likely be very simple, such as
<html>
<head>
<title="Print">
</head>
<body>
<%= yield %>
</body>
</html>
Using a separate layout the disadvantage that it's difficult to keep this layout and the layout used by the rest of the application in sync, but if you are using separate CSS files for the action, it's unlikely that you want the layout to be the same anyway.
Add a stylesheet_link_tag in the layout's header pointing to your print.css:
<html>
<head>
<title="Print"/>
<%= stylesheet_link_tag "print" %>
</head>
<body>
<%= yield %>
</body>
</html>
In the controller, we'll tell Rails to use our new layout for the action. Add the line layout 'print', only: [:print_reports] to your controller:
class reports_controller < ApplicationController
layout 'print', only: [:print_reports]
#snip
See this question for more information and a few different approaches.
At this point, when you run the app, your print_reports action should be using print.css correctly!
Remember to run rake assets:precompile before deploying on the server.
Official Solution
It is documented in the official Rails Guides here: http://guides.rubyonrails.org/asset_pipeline.html#controller-specific-assets
Actually you can leave out the require_tree directive (located in application.css and application.js) then use this in your template:
For controller specific JavaScript:
<%= javascript_include_tag params[:controller] %>
For controller specific CSS:
<%= stylesheet_link_tag params[:controller] %>
All of you are putting very complicated answers.
1 Go to app/assets/stylesheets
2.Make a file with the extension .css
3.Go to config/initializers/assets.rb
4.Put this line of of code Rails.application.config.assets.precompile += %w( file.css )
5.Replace file.css with the file you created
6.Go to your html.erb file
7.Type this in the <head>, <%= stylesheet_link_tag "file" %>
8.Replace file with the filename(no extension in the name)
Good Job you linked the file!
There's one specific action that I want to have use different CSS.
Here's an alternative way to accomplish what you're looking for:
Add the controller name and action name to the app body in your /views/layouts/application.html.rb:
<body class="<%= controller_name %>-<%= action_name %>">
<%= yield %>
</body>
Then in your .scss file:
.controller_name-action_name {
// your css goes here
}
So if your controller was static_pages and your action was home:
.static_pages-home {
// your css goes here
}
Tada! Your css only appears for that specific action.
In your layout
<head>
// ...
<%= yield :stylesheets %>
</head>
In your view
<%= provide :stylesheets do %>
// your page-specific css
<% end %>

where to put external assets in ruby on rails project?

If I have Javascrips/CSS files from an ASP .NET Project and I want to put them in my Rails project. Where's the best place to put them? Do I need to change every url into <%= asset_path "img" %>?
Put it in assets folder and add below line in views:
To add CSS files:
<%= stylesheet_link_tag "test.css" %>
To add JavaScript files:
<%= javascript_include_tag 'test.js'%>
Building on #Unknown's answer:
Yes, you do need to use asset_path, or one of the sprocket helpers, to refer to your assets in your CSS file so that they will properly include the MD5 fingerprint. Plus, this way you get the right asset between development and production (since they don't live in public while in development). Here's the relevant guide: http://guides.rubyonrails.org/asset_pipeline.html#coding-links-to-assets
According to http://railsapps.github.io/rails-javascript-include-external.html
The best practise for DRY and speed (according to article above) is to download all files to your projects assets folders, put them in the assets pipe for each application.js/.css and let rails compile them into one application.js and application.css in production mode. There are several ways to do this in detail (see link) The preferred way according to article for speed and DRY-practise is to call all js and css from application.css/.js and not put stylesheet_link_tag, calling css or js from view-files. This even though you may have one specific user.js which you only want to use on users page. Though there are ways to load specific files into specific views (see article for details)
Ecxept from having to call them in once in head of application.html(.haml/.erb):
= stylesheet_link_tag "application", :media => "all"
= javascript_include_tag "application"
If you are calling files in css eg. an image, as long as you have the image in "assets/images" you only need to refer to the images as 'image.jpg' in your css/js-files.

Javascript and CSS specific files, how to include them by convention ? Rails 3.1

How can I include specific JS or CSS files (by convention ?) with Ruby Rails 3.1 ?
I have a view :
views/project/index.html.erb
And I want to include a specific javascript file for this page. I put it in
assets/javascripts/project/index.js
Same for another view :
home/index.html
Thanks
In the application.css and application.js file, be sure to remove the line \\= require tree.
Then, manually list all the css/js files you want included in each manifest file, for example:
// application.js
//= global.js
//= everywhere.js
Then, I would setup a yield in your header or your closing body tag for your application layout file, for instance (in haml)
%head
%title Some Page
= stylesheet_link_tag 'application'
= yield :stylesheets
Then in your particular view, say _example_partial.html.haml, do this:
- content_for :stylesheets do
= stylesheet_link_tag 'example_partial'
-# the rest of your view goes here
You do the exact same thing with Javascript files, just using javascript_include_tag instead of stylesheet_link_tag.
This will let you quickly and easily assemble view-specific javascript / css payloads. There may be a more sophisticated way to handle this using the asset pipeline, but I would suggest that if the asset pipeline is already minifying and merging you major stylesheets that this kind of +1 css / js file per view is not going to cause a major performance hit. Just try to make sure you don't overdo it with dozens of separate files loading into a single view.

Resources