Rails 4.2: Custom Helper for SCSS Manifest in Engines - ruby-on-rails

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.

Related

Learning Rails - Routes Not Working in My First Tutorial

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.

CSS require Syntax

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

How can I get Compass to automatically import all stylesheets in app/assets/stylesheets?

I'm trying to add Compass to my Rails 3.2 app, using compass-rails. How can I get it to automatically import all of the stylesheets in app/assets/stylesheets? At the moment I have to manually do #import 'filename'; in application.css.scss for each one.
Put all your scss files (except application.css.scss) in a different folder:
/application.css.scss
/all/hello.css.scss
/all/hi.css.scss
application.css.scss file like below will work.
#import "compass";
#import "all/*";
For bundling stylesheets, use the asset pipeline
If you're using the asset pipeline, this should happen automagically with:
/*
* In application.css
*= require_tree .
*/
Docs: http://guides.rubyonrails.org/asset_pipeline.html#manifest-files-and-directives
The important caveat is "Using Sprockets directives all Sass files exist within their own scope, making variables or mixins only available within the document they were defined in."
For mixins & vars, have your imports in one place, then import once
If you're heavy on the functions, try having a file like app/assets/stylesheets/base.css.scss that contains #import directives (wildcard or not) for all your mixin and var files. Then you only need to #import "base" once for every stylesheet and can still bundle your css using sprockets directives.

Evaluate SCSS (SASS) templates in ruby (Rails project)

I am working on a sort of CMS project where I am leveraging SCSS. I would like to allow the user to specify properties of a stylesheet in a simple way (enable a few color customization), and then generate a CSS file based on SCSS templates, and substitute some variables in the SCSS file using mustache or ERB evaluation.
Basically, I want an ERB file to be rendered as scss file, and then generate a css in my application, upload it to S3, and include in the user's layout.
If possible I would like to avoid using css.erb files :-)
I fact (I am answering my own question). What I am trying to do is really easy. I've made this really simple script:
#!/usr/bin/env ruby
#processs.rb file
require 'sass'
result = Sass.compile open(ARGV[0]).read
puts result
And it generates css out of a scss file she invoked like this:
ruby process.rb myfile.css.scss
And this works perfectly. The code documentation in sass source code helped me to find this out.

sprockets - multiple entry points?

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.

Resources