I'm using rails31 & coffeescript and have defined some classes that extend from a parent class. Coffeescript adds an __extends helper method in the generated output to make this happen, problem is when using sprockets a copy of the method is added for every subclass, creating a lot of duplication.
My classes are defined in separate files, so it looks like sprockets is getting coffee to process them individually. I know there is a —join option with coffee but not sure how to use that with sprockets.
Does anyone know if it's possible to generate coffee helpers only once when using sprockets?
Nope. Whether you're using Sprockets or not, helpers are a one-per-file kind of deal. There's no compiler option for avoiding this duplication (--join won't do it; it just concatenates the compiled JS), and no existing JS minifier is smart enough to identify and remove the duplicates (which would require moving one instance to a global scope).
My advice would be to not worry about it. CoffeeScript uses few helpers, and they're unlikely to account for more than a tiny fraction of your code.
Related
Image showing my rails structure for react
I have a lot of react components and wish to simply keep them better organized. So I'll cut to the chase and just ask. Is it possible to put my react components in directories and if so- how do I select them in a view and other react components?
I've already tried a few variatians like the one below without success and fear that it's simply not possible? Can't find any documentation about this either.
<%= react_component('OutfitCreate.Images') %>
Edit: To clarify, I want to make it clear that I'm using a typical rails project structure, and for react I am making use of the react-rails gem to handle my components as well as generating them.
Edit2: To clarify further, what I'm seeking is a way to target a react component by directory as such:
<%= react_component('<directory>/<classname>') %>
where each directory will be sub directories of "components".
I know this is an old question but maybe it helps anyone. You can do it like this:
<%= react_component "ComponentFolder/ComponentClassName" %>
It works just fine with rails-react and webpacker.
I recently used the react-rails gem and had a good experience. It takes care of accessing your components in Rails templates and does a few other nice things.
To answer your question specifically, you can put your components inside as many directories as you like as long as you follow the gem docs. The gem provides a view helper that allows you to render a specific component, and to pass data to it as props (the helper takes a hash which becomes the component's props).
Edit -- I'm not sure if you're already using the gem, the helper method you are using looks like it, but you don't specifically mention it. Perhaps there is a problem with the gem installation and/or setup. You need to edit application.js and also to run the generator task: rails g react:install
Edit -- To make sure you can access components in the components subdirectory, you should do the following things:
*run the generator: rails g react:install
*make sure the generated components.js file contains: //= require_tree ./components
*make sure your application.js file contains:
//= require react
//= require react_ujs
//= require components
React components will be considered global, because all .js will be minified in one single big .js file. So, you will have to take some considerations.
First, in your sprockets file (Normally application.js) you need to tell Rails to serve the React Components you want. The easy way would be to require all folders and files in components folder, because all your components will be there, but organized in subfolders. This is made adding the next line:
// File: app/assets/javascripts/application.js
//= require_tree ./components
Second, if you want to use your React component named for example Images, which could be defined in ../components/folder/images.js, you just could use in your Rails View the helper react_component('Images') like usual.
React will find the Images component if it match with the Images component you defined inside some served .js (But a sane human will expect you defined the Image Component inside some images.js) because this name Images is global (Remember? One big .js file will be served).
With this approach, the client will download only one big .js file with all your React Components. Bad for download speed, but could be cached.
So, here, you would have to take care to name all components with a different name each one.
Now, if you have for example a component named Foo inside ../components/folder/doesnt_matter.js and another component with the same name Foo, but inside ../components/another_folder/this_neither.js, you will have to tell Rails which file do you want to serve for the specific View you want to render to evade collision name problems.
This means, you could make the client to download only the needed js code (and the needed React Components) for the rendered page. It will be downloading .js files for each page, losing the cache possibility.
One intermediate way to do this, would be to serve a middle sized .js file for all the views in the same controller.
To do this, you could organize your js (or jsx, or ...) in a way you could have a place for common Components (which are used in two or more controllers), and a specific folder for each controller with specific React Components in there.
First, you will have to replace the previously added //require_tree ./components in application.js with //require ./components/common/ for serving only the common React components to All Views.
Second, in your layout, you could serve two js files; an application.js and a #{controller_name}.js (This means, one specific .js file per controller).
Third, for each controller, you will have a .js file with the same name. So, supose you have a controller called home_controller, then, you will need to have a home.js (controller_name helper in layout view will make rails to look for this file) and in it, using sprockets again, you will serve specific React Components with //require ./components/home/
In this way, you will have to take care of your organization, and the React Components Names need to differ with the ones inside .../components/common/.
If you want to be more specific yet, you could use the same technique in layout in combination with controller_name and action_name helpers. Or you could use content_for asking for the specific .js files that have the React Components the View needs.
However, you need to be carefull because i don't know how crazy the Assets Pipeline could turn against you. Specially using content_for.
I'd like to add source map support on a rails 3.2 application I am working on. As far as I know, generating source maps is not supported by Sprockets and from its github page it looks like the feature is planned for 4.0. I am working with Sprockets 2.2 and I think monkey patching is the only way to go. The module Processing under the main Sprockets module gives access to the js_compressor function which can be patched to generate source map for a single file. But, I don't know how to add this when the JS files combine. I am using Uglifier 2.4 as the compressor.
The project has a mixture of CoffeeScript, JS and EJS files. So, I think this is how sprockets would be compiling them together. First, it would convert Coffeescript and EJS to JS, then use the js_compressor to compress individual files and later concatenate them in groups. Now, as the source map for multiple files combined to the same file is a single file. So, I will need to change the compilation process somewhat and make the js_compressor run over the files, once the concatenation is finished. So, can anyone help out with this? Even explaining the sprockets compilation process and the modules used and functions involved would be of great help. I don't care about making source map files for the CoffeeScript code at present. Even mapping to their converted JS files would do.
Also, would like to add if there is some gem which can help with this it would be most welcome.
Rails 4 does not have source maps either.
As far as I know, and as of today, this will only be part of rails 5.
A really nice approach to solve this right now is implemented in discourse by #SamSaffron and explained here:
https://github.com/discourse/discourse/blob/master/lib/tasks/assets.rake
The gist, add a "before" task to the sprockets precompile process, and hack into the compilation process to generate the sourcemapped files and directives.
The nice thing in this approach is that you don't lose stuff from files that are both js and erb (*.js.erb) which is something quite common in rails.
I think that patching the whole sprockets pipeline is a bit of an abuse and risky.
This is what I want my gem to do...once included in a Rails gem file, it will allow the developer to specify which stylesheet is the main, and which is a 'secondary'. The idea being, that all the styles in the first stylesheet take precedence over all the others - but when a style does not exist in the main, it uses the ones in secondary.
For instance, if there is body in main.css and in secondary.css, it always uses the style from main.css. But if there is alert-message in secondary.css, but not in main, the developer can use alert-message, without any fear of conflicts.
The idea basically is to allow you to leverage existing frameworks or minified stylesheets that might have classes that clash with your main.css without having to worry that your entire site/app will be messed up.
It would be awesome if I could get a general overview of the entire Rails/Ruby gem creating process - as this would be my first. Then maybe some discussions about how I might go about solving this particular problem (from a high-level is fine).
I imagine that it would require a lot of work. You'd have to extend (or write your own) CSS parser. Or come up with another way of specifying which classes are "main".
I think, the solution is much less complicated. Simply include your "main" stylesheet after all "secondaries". That'll overwrite the classes, if there are conflicts.
Note: This question refers to a Rails project with Sass & Compass.
Using the Rails Asset Pipeline? Then have a look at this question.
We are developing a big application with many use cases and many individually styled pages, partly for multiple contexts. Which simply means a lot of style information.
With the newest section of our application, we have broken Internet Explorer's limit of 4095 selectors per stylesheet. (Want a proof of this limitation? http://marc.baffl.co.uk/browser_bugs/css-selector-limit/)
Okay. So, why do we not simply split the application style sheet into multiple ones by design?
Well, mixins and selector inheritance will not work across multiple Sass files (not partials), right?
I'd say the quality of the stylesheets is rather good, we cannot optimize away the exceeding amount of selectors. (There is rather more to come.)
I also believe that minimizing the amount of selectors should not be our primary optimization goal. The Sass core team advises to use selector inheritance instead of mixins where applicable in order to save CSS file size. By doing so, the number of selectors tends to grow though.
So what should I do?
I am thinking about writing a script that generates additional css files, partitioning my big application.css file. These would only be loaded in IE then (so that I don't have multiple requests in modern browsers).
I would need a simple css parser for that in order to cut the application.css file after max. 4095 selectors at a valid position.
And I would need an compass compile - after hook so that developers don't need to generate the IE files by hand in order to test it.
Please, tell me, that you got a better idea!
Best,
Christian
Mixins are usable across multiple files. However, it is logically not possible that #extend may work with multiple files. It is the purpose of this directive to result in a single rule
(which should not be duplicated across multiple files). Therefore, I cannot split up files.
Thus, I implemented a splitter: https://gist.github.com/1131536
After these two commits have found their way into Sass and Compass, you can use the following hook in your Rails config/compass.rb in order to automatically create the additional stylesheets for IE:
on_stylesheet_saved do |filename|
if File.exists?(filename)
CssSplitter.split(filename)
end
end
Update:
The CssSplitter mentionend above has been release as a gem: https://github.com/zweilove/css_splitter
If you can't reduce the number of selectors, there is no choice other than to split the CSS file.
Your proposed solution for doing so already sounds optimal, if a little complicated to implement.
An easy way to do it is http://blesscss.com/.
Simply :
install node.js
Execute npm install bless -g
blessc source.css output.css
Where's the recommended location for libraries in Rails 3? Is it as simple as 'lib'?
I'm not sure because 'lib' seems more like a Rails 2 remnant, especially considering that it's no longer auto-loaded (and there was a lot of discussion about that, apparently).
Initializers are more for (obviously) initialization tasks such as overrides.
Specifically I have a small module for attachment handling (Paperclip doesn't fit here) that's too large and distinct to include in my model, but not generic or worthwhile enough to implement as a gem.
From a functionality standpoint it lives somewhere in the middle among the model, view, and controller. This makes it sound like a helper, but in Rails helpers are intended for views AFAIK.
Should I just put it in 'lib' and autoload it in application.rb? Or maybe I could create a custom form builder to handle the presentation (or both).
I know how to make it work, but I'm hoping to learn something new. :)
lib is still the right place to put these kind of things.
Autoloading lib was removed in Rails 3 because of the way engines work, but mainly because it's easy to just add it to the autoload_paths if you do want it automatically loaded and if not, you can require as needed. lib is still in the load path, so you don't need to specify where the module or class you're requiring is.
You're correct, helpers are intended for the view, and would not be the place to put any model-related logic.
I'd put the module in lib, and require and include it in your model as needed.