Rails - disable js&css //=require for tests - javascript

When running tests in a Rails (5) app, let's say controller rspecs for example, you need to have all external frontend dependencies available - anything that's included from application.js or application.css is needed to run these tests, otherwise you get a Sprockets::FileNotFound exception when the code gets to rendering the response.
That's not actually needed because we're not evaluating the resulting HTML or any such thing, and definitely not running the JS, but it makes those tests depend on npm/bower not failing, takes time downloading, etc. etc.
So, I'm hoping to find a way to not need js & css dependencies when running specs - essentially making application.js and application.css empty for the purpose of that spec run. (Except that doesn't work because other templates may require things too.)
Has anyone done such a thing? How? :)

You can work around this by just including the dependencies in the test environment, with Sprockets enabled it's just part of the Rails boot process, I don't believe there's an easy way around it.

Ended up solving this by doing.. in the test environment.
class << ActionController::Base.helpers
def image_path(path, options = {})
path
end
end
Turns out, not using image path (and possibly other related helpers, YMMV) doesn't trigger the lookups.

Related

Organizing javascript in Rails asset pipeline

Broadly: What are the best practices for organizing JS in the Rails pipeline?
Specifically: I have rapidly growing JS files that I'm fine with including in the overarching application.js manifest and letting Sprockets uglify. However, each individual JS file is starting to become unwieldy, and I'd love to organize them so that I don't have to dig through hundreds of lines of utility functions to get to the meat of the code. I recognize that the goal is some kind of namespacing/modularity, but I don't know what the best practices are for combining this with the asset pipeline, especially Sprockets manifests.
Things I've already considered, and read thoroughly:
Yes, I've read the entire Rails guide on its asset pipeline. I know how Sprockets directives like require and require_tree work; the issue is that I want to use those same directives like the equivalent of an ES6 import command so I can do something like
// in, say, controller.js
//= require 'utilities'
...
more code
...
// and in application.js, more confidently
//= require 'controller'
but I get the feeling that's not how manifests should be used, or at least that it'll needlessly recompile layers of assets every time I change a single line in utilities. I've also considered requiring every file individually from application.js but that doesn't really give the modularity that seems appropriate.
Gems like Paloma, CommonJS, or RequireJS. These seem like overkill, and seem meant to replace the pipeline rather than supplement it.
"Modern" JS like ES6, Babel, or Browserify. (I admit I don't really understand the overlap in these projects yet, but I think I get the gist of their purposes.) Maybe eventually, since JS seems to be moving in that direction, but also seems like overkill.
Gulp. In the same vein as previous, overkill and an unnecessary rewriting of the asset pipeline.
Rails 5 and Sprockets 4. We're on Rails 4 right now, and I'm aware that Sprockets 4 has some ES6 built in, but I don't plan on upgrading until at least a while after Rails 5 is publicly released.
So what should I do? I think I need to bite the bullet and go with one of these, but I just can't figure out which makes the most sense. The project isn't especially reliant on JS, but there's enough of it that I want to organize it now, rather than later.
I find when I have heavy amounts of page-specific js that it is best to leave the assets pipeline for only the js that is repeated more than once. Obviously all outside libraries should continue to go in your vendor/js folder. The most rails-esque way to add page-specific js to your app (thus avoiding loading js in every page when a library or script is only needed in one page) is to add to the very bottom of your layouts/application.html.erb file right before the closing body tag:
<%= yield :javascript %>
Then on the view that you're looking to run some snippets of javascript you should put this at the very bottom of it after all html tags have been closed. Make sure it's not within some divs or anything in that view but at the very bottom of it.
<%= content_for :javascript do %>
<script type="text/javascript">
$(document).on('page:load', function() {
all your page specific code here..
});
</script>
<% end %>
This code will now be yielded to the bottom of your layout view only loading on the specific views you need instead of being concatenated along with all other assets in your assets/js folder. I do this with all custom js that isn't application wide and it's very easy to maintain/debug.

Most efficient way to get a Three.js project to work in a ruby on rails app?

I'm trying -unsuccessfully- to get a Three.js project to work within the Ruby on Rails framework
I keep thinking that there must be an easier way to do this than the way I'm doing it at the moment
First I googled for a guide/tutorial on how to port working Three.js code to rails and found very little on the subject. Which I figure means: either people don't do it very often -or- that it's so obvious that no one has bothered to document it.
Next
I googled for a three.js gem and found one here - http://rubygems.org/gems/threejs-rails
(which I gave up trying to get to work - after much messing around with the manifest file)
Next
I installed the source of Three.js into the vendors folder and messed around with the manifest file again. This time I had more success - although there seems to be dependency issues within Three.js that require_tree (and myself) are oblivious to.
So theoretically, I could probably get it to work after a several hours of figuring out the Three.js loading sequence, but this doesn't feel right.
I feel like I've gone down a blind alley here and I'm missing something obvious.
is there a better way to do this?
Rails is, primarily, a back end server side framework. Three.js is a front end library for rendering WebGL in a browser. They have little to do with each other. I think you're trying too hard.
The process is the same as adding something like jQuery, or any other javascript library.
Drop the three.js file in app/assets/javascripts/three.js
Add #= require three to your application.js.coffee
Ensure that javascript_include_tag('application') is part of your application layout template
Now launch the server, load a page, and type THREE in the javascript console. If you get a return value, your good! Now write some custom javascript, save it in app/assets/javascripts and include it on the pages you want to do awesome 3D stuff.
You can also use the following gem threejs-rails that I created for my rails app today. It works right out of the box if you're on rails 4+. Fork and submit a PR if you need further support on it!
Most efficient would obviously be via CDN, I plan to add that support soon.
I found it the probably easiest way - though not the proper Rails way - to include all dependencies as written here https://guides.rubyonrails.org/working_with_javascript_in_rails.html
and then to simply create a partial with the JS code inside of a script type="module" tag. Though Alex Wayne's way is probably better, this one works, too.

Are there any reasons NOT to use angular-loader?

I've worked with Angular for a little bit, but I keep managing to learn something new - today, I installed the angular-seed project in order to give my development a little kick in the pants. I ran into the index-async file and learned about the angular-loader - which I hadn't used before.
I found this question, as well: What is angular-loader.js for?
It looks as though the index-async file is using a script loader in addition to the angular module loader, which makes sense. However, I've never used this method before. (In my company, we've used RequireJS to load angular modules before, and so I can understand why something like this would be easier and less cumbersome.) Yet, it also seems that I could use the loader without a third-party script loader - I could just include all of my app files, in any order, before the loader is called, without having to worry about the dependencies.
In short - when should I use angular-loader? More importantly, is there any reason NOT to use it all the time?
Well, you don't have to use it if you don't need it.
From the Angular Docs, you use it:
If you are loading multiple script files containing Angular modules, you can load them asynchronously and in any order as long as you load this file first. Often the contents of this file are copy&pasted into the index.html to avoid even the initial request to angular-loader.min.js. See angular-seed for an example of usage.
The reason behind is to optimize the loading time on the client-side - only load the currently needed module for the user, particularly if you have a reasonably huge app.

Requirejs - versioning

Quick note - by versioning I mean for the purposes of cache busting. The common practice of adding query params to the end of the script request does not work on all browsers. The easiest and the most messiest way that I have found to date is to version my entire deploy folder name.
-- scripts.v1
-- scripts.v2
But this is incredibly messy and mucks up the deploy times too (I use S3 as my cdn). Does anyone know of an alternate method to this?
EDIT
It seems, I have not been very clear. Let me be a bit more explicit.
I use requirejs on my site. It is quite a JavaScript heavy application with frequent updates and iterations. With requirejs in place now, the only way I can reliably make sure that browsers are serving the latest version, is to version my whole deploy folder name (javascript) and upload the whole lot of files to S3 again. I then use the data-main method to set the base path of the project.
For many reason, this is quite cumbersome. Even if the code change is just a few lines, the whole process has to be repeated. Is there some other decent method to let requirejs know that files have versions? As in, if I call
require(["superImportantJSFile"], function(){})
it will know that the current version is superImportantJSFile.v4.js or something along those lines.
I hope I have been more clear now. Any suggestions as to how the community in general does this? I'm pretty sure this has to be a common scenario, but I haven't been able to find a good solution to this yet
I like to use a post-build step that puts static resources into a folder with a path that includes the version control version number. For example source control revision number 1234 would lead to the creation of a path: /1234/scripts/*. These directories are also created in the CDN with the correct version of the assets within.
In our require.js config in a template, we replace the baseURL with the appropriate revision, which is controlled via a config file, eg:
var require = {
baseUrl: "%%resDir%%",
...
};
This makes it easy to change the asset versions between a few different releases, which can all stay on the CDN without causing any conflicts. It also solves the browser cache busting issue.
The HTML5 Boilerplate offers one of the most graceful solutions I have seen. They have configs available for Apache and nginx. From there you can just add a timestamp to the filename within your script tags, like so:
<script src="scripts/app.20130728.js"></script>
Which the web server would rewrite to scripts/app.js.
You can add aliases to your RequireJS configuration by using map (see http://requirejs.org/docs/api.html#config-map) for example:
require.config({ /* ... other config.... */
map: { '*': {'superImportantJSFile': 'superImportantJSFile.v4'} }
})
So you only have one place to update :)
You mentioned the use of a CDN which is a good use case to not put those files in your minimized r.js bundle (in case that you are using that tool). But if those files are updated frequently, maybe it makes sense to pack your modules with r.js and update the whole code.

How do I configure paths to my javascript files in the Jasmine / Maven autogenerated ManualSpecRunner.html?

I think the question says most of it. I have an autogenerated ManualSpecRunner.html file as created by maven / jasmine plug-in and I've got it to put itself into the deployable .war by using:
<jasmineTargetDir>${basedir}/pathForMyWebapp</jasmineTargetDir>
However, all the links to js files within the ManualSpecRunner.html are hard coded file:/// references - this is a bit mental, I want them to just be the relative paths to the files that are also in the webapp i.e.
Currently it gives me this path:
file:///home/username/code/HEAD/pathForMyWebapp/js/yui.js
whereas I need it to have the far more simple
/pathForMyWebapp/js/yui.js
I have tried changing two other variables in the maven script, but neither seems to have the desired effect, neither of these configuration options do what I need, the second having seemingly no effect:
<jsSrcDir>/pathForMyWebapp</jsSrcDir>
nor
<jsTestSrcDir>/pathForMyWebapp</jsTestSrcDir>
I've looked through the documentation but think I must be missing something (also, more notes on various config params listed in https://github.com/searls/jasmine-maven-plugin/blob/master/src/main/java/com/github/searls/jasmine/AbstractJasmineMojo.java are meant to do would be helpful so I can work out if I'm doing it wrong or if it's not possible!)
Any suggestions?
[p.s. I've changed some of the path names as they've got sensitive info in them, so please ignore their oddness!]
I think I understand the source of your confusion. It looks like you're trying to direct the target of the jasmine-maven-plugin to a directory inside your project's packaged *.war file so that you can run your specs against the code after it's deployed to a server, is that correct?
Unfortunately, the plugin wasn't designed with that use in mind. The jasmineTargetDir directory is usually left at its default value of target/jasmine and wasn't intended to be bundled with your application (it's analogous to the target/surefire-reports generated by maven-surefire-plugin for Java unit tests). So the reason that the script tags in ManualSpecRunner.html point to invalid locations is because that file is generated in order to be run from the local filesystem in a browser from the workstation that's building the project (to facilitate TDD).
All of that to say, if I'm reading your intention right, I think it'd be a cool feature to build a third spec runner that could be deployed with the app and executed remotely. (Especially if the project's Jasmine specs are functional/integration as opposed to isolated unit tests.) Unfortunately that's not something the project does yet.
I'm afraid that for now, if you needed to bundle the jasmine tests and execute them on the deployed server, you would need to copy ManualSpecRunner.html and jasmine into your src/main/webapp, fix the script tag references, and then manually maintain it as files are added and removed.
Make sense?

Categories

Resources