I have required CSS stylesheets many times in many of my RubyOnRails applications, and actually i need a few things demystified.
What is the difference between:
//=require mystyles
and
*=require mystyles
Both seem to work, so what is the difference?
And is this a part of the asset pipeline or a part of SASS or just plain CSS?
The two examples you gave are exactly the same for stylesheets, however the //= syntax is the only one that works for Javascript. You also need a standard CSS comment at the start (/*) and a close comment (*/) at the end of the require block to make it ignored to anything but the asset pipeline:
/* ...
*= require mystyles
*= require_self
*/
These require directives are only part of the asset pipeline, not CSS or SASS. To include a stylesheet for CSS or SASS, you would use a #import rule as Blieque stated in his comment.
Check out the manifest files and directives section of the Asset Pipeline guide on the Ruby on Rails guide site for a more detailed explanation of the difference. There is also a warning given there that might be of interest:
If you want to use multiple Sass files, you should generally use the
Sass #import rule instead of these Sprockets directives. Using
Sprockets directives all Sass files exist within their own scope,
making variables or mixins only available within the document they
were defined in.
There is no difference, as long as it's a valid comment line, it'll work :)
Related
I hope everyone is having a great day. In my pursuit of knowledge, I've ran into a huge stumbling block.
I'm following this tutorial. I'm using the most up to date everything, I think he is in rails 4 and I'm in rails 5.
https://youtu.be/GY7Ps8fqGdc?t=703
By no means is watching the tutorial necessary to understand my question. Right where I linked, the only thing he has done besides generate a simple rails app, is generate a controller like this:
rails generate controller welcome index
Then right where I linked into the video, he goes into the routes file and uncomments a line that isn't present in my rails app, so I just wrote it by hand
root 'welcome#index'
And it fails when I try to load localhost:3000, throwing a jsexception "TypeError: Object doesn't support this property or method"
Is there some functionality that has been deprecated or something? Why isn't this working? I've done the tutorial up to where I've linked at least 5 times. I have tried very hard to resolve this with no avail.
Oh, and before someone tells me you shouldn't route like that, I've read that before, but he's just trying to get the viewers to the point where they can execute ruby code on a web page, he covers routing in detail later, but I can't get there without beating this because the lesson relies on me getting this first.
Please, oh great forum, grace me with some answer and I will imbue you with an upvote and great thanks.
Also, on a side note, if anyone could recommend learning resources that have helped you get past beginner with this framework, I'd be happy to accept advice in addition to this question being answered.
EDIT: to include requested information
the routes.rb file
Rails.application.routes.draw do
get 'sausage/tuna'
root 'sausage#tuna'
end
One last piece of information, probably obvious, I call my things funny names so the generic "welcome" and "index" don't confuse me. I draw inspiration from the new boston and called mine 'sausage tuna' instead of 'welcome index'.
Thanks again for helping me out. I really want to learn this.
Ok the problem seems to come from ExecJS, which is basically the gem that deals with JS within Ruby.
The offending line is : <%= stylesheet_link_tag 'application', media: 'all' %>
(as you can see I am missing the turbolinks bit because I am not using turbolinks)
What this means? Basically this line is for a dedicated sheet of JS code attached to this page.
where is this code located ? In application.html.erb. application.html.erbis the default view layout. It means that every view triggered by your code inherits from this 'master' view.
So basicaly your routing is good if the error goes to this stage. Your Sausage#tuna action is triggered and then the `tuna.html.erb" view is rendered but with an error on this master view.
Basically because you have an error on application.html.erb, you will get the same error for every action of any controller and every view triggered.
Now what the problem ? Well I never encoutered this error but skipping coffee seems to solve your problem. Though in my case I have gem 'coffee-rails' and some of my JS files are actually "coffee.erb" files and I don't get the same error. So i guess it is kinda more complex than this..
But in order for you to understand Rails better , this line, as I said earlier, is compiling some JS and attaching it to the master view file (then to every view of your application)
Now if you want to know what JS will be compiled into this separate sheet, looks at the file app/assets/javascrips/application.js.
You should get something like :
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery3
//= require jquery_ujs
//= require bootstrap
//= require cable
//= require rails.validations
//= require footer.js
//= require serviceworker-companion
Everything looks like it is commented out, but actually the last lines with //= require are actually gathering some JS from different gems. (Jquery, ...)
In my case : Jquery, Bootstrap, actioncable .. which is the Js I need on every page of my app...
Regarding your question about a good tutorial, the one I prefer is the getting started tutorial on Rails website : https://guides.rubyonrails.org/getting_started.html
EDIT
My mistake. It is the stylesheet that creates a problem. Which I am even less sure what could cause this because ExecJS is dealing the Js code in your app, not the CSS.
The stylesheet work exactly the same as said above for application.js. It links some CSS stylesheets to every of your application pages.
Here is mine app/assets/stylesheets/application.css
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
* compiled file so the styles you add here take precedence over styles defined in any styles
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
* file per style scope.
*
*= require navbars.css.scss
*= require footer.css.scss
*= require cookiewarning.css.scss
*= require_self
*/
#import "bootstrap-sprockets";
#import "bootstrap";
As you can see, there is the navbar css and the footer css plus some other CSS that appends on everypage of my app.
(And thanks for accepting my answer as the answer but it is definitely not, just wanted to explain you what were things doing :) )
For anyone experiencing this issue, reinstalling node seemed to solve my problem.
Our application is a collection of 3 engines that make up our base gem. Other plugin gems, written specifically for our base gem, can be used to add functionality to the base.
Currently we have 2 custom sprockets directives and 1 custom Sass importer that attempts to pull in SCSS files from these plugins dynamically into the base manifest. A contrived example looks something like:
//= depend_on_gemfile # mimics default "depend_on" functionality
//= depend_on_stylesheets # mimics default "depend_on" functionality
...
#import 'engine/namespace/settings/global';
#import 'engine/namespace/settings/colors';
#import 'engine/namespace/settings/fonts';
#import '[engine-plugins]/namespace/settings'; # mimics Sass Filesystem importer
...
To be clear, this works. We're having problems with the way Sass is caching the files, that is a bit too complicated to get into and not really my goal at the moment.
The point is that we realized that removing the custom Sprockets directives and Sass importer, in favor of using a Rails helper, would also solve our problem, since the ERB would be compiled before the SCSS, we'd be able to find and format all of the paths needed in all included plugins into a string and dump it out into the manifest. Something like:
#import 'engine/namespace/settings/global';
#import 'engine/namespace/settings/colors';
#import 'engine/namespace/settings/fonts';
<%= load_plugin_stylesheets_for('settings'); %>
...
This seems like a much more simple solution, since all we are doing is finding each path and converting those paths to strings.
The problem seems to be that Rails helpers are outside of domain of Sprockets entirely.
I've found some resources, but they all seem to pertain to Rails 3.
Add custom methods to Rails 3.1 asset pipeline?
https://github.com/rails/rails/issues/3282
I'm curious if anyone has had this problem with Rails 4, if they solved it and how. Or if I'm just thinking about this all wrong and there is a better way to approach the problem without a similar over-complication like we had before.
TL;DR
I'd like to use regular Rails helpers inside of a Sprockets manifest file. The Rails version is 4.2.4. We're running engines. Is this possible?
EDIT:
This comment answered my question.
After implementing this solution in an initializer within our engine.rb file, all of the Rails ActionView Helpers were available, including my custom written helper.
We had a similar issue a while ago.
application.scss (within gem) looks like this:
/*
*= require_tree .
*= require_self
*/
#import "c3.min";
#import "analytics";
and gemspec contains line for including all the code from app folder.
Gem::Specification.new do |s|
s.files = Dir['{app,config,db,lib}/**/*', 'MIT-LICENSE', 'Rakefile', 'README.rdoc']
end
Be sure to check that you don't have any caching enabled in env files as well.
The documentation says:
Import Bootstrap styles in app/assets/stylesheets/application.scss:
// "bootstrap-sprockets" must be imported before "bootstrap" and "bootstrap/variables"
#import "bootstrap-sprockets";
#import "bootstrap";
bootstrap-sprockets must be imported before bootstrap for the icon fonts to work.
Make sure the file has .scss extension (or .sass for Sass syntax). If you have just generated a new Rails app, it may come with a .css file instead. If this file exists, it will be served instead of Sass, so rename it:
$ mv app/assets/stylesheets/application.css app/assets/stylesheets/application.scss
Then, remove all the //= require and //= require_tree statements from the file. Instead, use #import to import Sass files.
Do not use //= require in Sass or your other stylesheets will not be able to access the Bootstrap mixins or variables.
But if I do as they say, my other stylesheets won't be included automatically, as they did before that. Should I include every stylesheet explicitly in layout? AFAIU, that would also make me have separate stylesheets in production environment, instead of one as it would have been without bootstrap-sass.
There are several things here. First, by default each stylesheet is served in separate http request in development, and in one request in production (they are precompiled into one file). If we follow the docs, we'll end up having one request in development as well, which would negate performance benefit of compiling only the file that has changed. If we don't, we might end up having bootstrap several times in our stylesheets. In case we need some variables or mixins in several stylesheets.
So I suggest having it this way:
application.sass (do note that I put require_self before require_tree .):
/*
*= require_self
*= require_tree .
*/
// import bootstrap once
#import "bootstrap-sprockets"
#import "bootstrap"
.d1
text-align: center
welcome.sass:
// in other files import only some particular partials
#import "bootstrap/variables"
#import "bootstrap/mixins"
.d2
text-align: right
But if I do as they say, my other stylesheets won't be included automatically, as they did before that.
Yes, you a right.
1) You shouldn't remove your require directives from application.scss. They don't want to use require directives because in this case you don't have ability to use SASS variables and mixins inside of included files.
2) Just rename application.css to application.scss. They want it because in this case you will have ability to use #import directives inside application.scss file. This is mean that you will have ability to use SASS variables and mixins inside of included files.
Should I include every stylesheet explicitly from layout?
No, just leave them in application.scss
AFAIU, that would also make me have separate stylesheets in production environment, instead of one as it would have been without bootstrap-sass.
No, you will use one application.scss in different environments.
I want to use application.css on my email templates, but I can't find a way to make the stylesheet_link_tag helper to include the full url of the stylesheet so email clients can render properly.
By default, stylesheet_link_tag only adds the relative path, so, I want to know if there is a way to either tell stylesheet_link_tay to use the url, or a helper that returns the url to the stylesheet.
This is for Rails 3.1, by the way
Rails 3.1 uses asset pipeline. If you are using asset pipeline then you need to mention those assets in you application.css. For example in application.css
/*
* *application.css
*= require scaffold
*= require pagination
*= require_self
*= require_tree.
*/
In the above scaffold and pagination should be under rails_root/app/assets/stylesheets ditrectory. For more info check this out http://guides.rubyonrails.org/asset_pipeline.html
Or if asset pipeline is not your issue then check this railcast for HTML Email.
http://railscasts.com/episodes/312-sending-html-email?view=asciicast
You may consider that the <link rel=stylesheet> isn't supported by the mayor providers like Hotmail, Gmail and Yahoo: http://www.campaignmonitor.com/css/
The best option that you have is using <styles> tags in your body or header; or even uglier, using the inline styles, that is supported by most of the email clients today.
Google Mail, in particular, looks for STYLE anywhere in the email and (helpfully) deletes it. And don’t bother to use CSS LINK to a stylesheet. Google Mail, Hotmail, and other email software ignore, modify, or delete these external references to a stylesheet.
http://www.reachcustomersonline.com/2011/12/21/09.27.00/
you can specify the full path of your assets by using
config.action_controller.asset_host = "http://www.yourdomainFORassets.com"
in your environment config file
And you better be careful with the images URL inside CSS files, I have to move everything
to this way to use existing code, check http://guides.rubyonrails.org/asset_pipeline.html
specially with this part:
2.2.2 CSS and Sass
When using the asset pipeline, paths to assets must be re-written and sass-rails provides -url and -path helpers (hyphenated in Sass, underscored in Ruby) for the following asset classes: image, font, video, audio, JavaScript and stylesheet.
image-url("rails.png") becomes url(/assets/rails.png)
image-path("rails.png") becomes "/assets/rails.png".
The more generic form can also be used but the asset path and class must both be specified:
asset-url("rails.png", image) becomes url(/assets/rails.png)
asset-path("rails.png", image) becomes "/assets/rails.png"
http://guides.rubyonrails.org/asset_pipeline.html#in-production
Regards
Sprockets official documentation clearly says that:
Sprockets takes any number of source files and preprocesses them
line-by-line in order to build a `single` concatenation.
I'm a big fan of sprockets in Rails but here's the problem - my application has to support multiple layouts(desktop browsers) and mobile clients(iphone, ipad, android phones etc).
Both of this layouts require their own HTML reset CSS rules. Concatenated rules of desktop&mobile reset files would make a conflict because they override low level CSS directives.
How can I fix that?
You can get multiple top-level CSS files by making a Sprocket file for each. For example, say you want desktop.css to be comprised of reset.css, common.css, and ie.css and mobile.css to be comprised of common.css and ios.css. You would have the following files:
app/assets/stylesheets/desktop.css
app/assets/stylesheets/mobile.css
app/assets/stylesheets/reset.css
app/assets/stylesheets/common.css
app/assets/stylesheets/ie.css
app/assets/stylesheets/ios.css
In desktop.css, you would have the following:
/*
*= require reset.css
*= require common.css
*= require ie.css
*/
In mobile.css, you would have the following:
/*
*= require common.css
*= require ios.css
*/
Then, in app/views/layouts/desktop.html.erb, you would do
<%= stylesheet_link_tag :desktop, :debug => Rails.env.development? %>
and similarly for mobile.html.erb.
Lastly, you'll need to set the precompiled asset list in config/environments/production.rb:
config.assets.precompile = %w( desktop.css mobile.css )
I'm not really sure if sprockets supports this but I know that if you use the Jammit gem. You can setup different packages each with it's own cocktail of your JS or css files. e.g. have a :workspace package for desktop and and :mobile package for mobiles.
It is all defined in a config yaml file and it will concat them in the order you list them, which can help get plugin dependencies correct etc.
javascripts:
workspace:
- public/javascripts/vendor/jquery.js
- public/javascripts/lib/*.js
- public/javascripts/views/**/*.js
- app/views/workspace/*.jst
mobile:
- public/javascripts/vendor/jquery.js
- public/javascripts/lib/mobile.js
stylesheets:
common:
- public/stylesheets/reset.css
- public/stylesheets/widgets/*.css
workspace:
- public/stylesheets/pages/workspace.css
mobile:
- public/stylesheets/pages/mobile.css
Jammit might be worth a look for your needs
Hope this helps.
I'm assuming you already have different layouts for each device or device group. If so, just include a different top-level css file in each, then have different require statements in those top-level files. If you're using Rails 3.1, there's no reason you have to keep the built-in line that includes all css files.