Async JavaScript in production environment only? - ruby-on-rails

I'm trying to get my JavaScript (Angular app) to load asynchronously so that the page can render a loading image while the browser downloads the JavaScript later. In production, this works just fine, but not in development, because Sprockets hasn't concatinated all of the files yet.
I'm using the following in my HAML file:
= javascript_include_tag "mio", :async => true
which is working as intended in production:
<script async="async" src="/assets/mio.js"></script>
However, in development the files are all separate, and execute out of order. For example, my Angular Quote Form Controller is executing before Angular has finished loading:
<script async="async" src="/assets/angular.js?body=1"></script>
<script async="async" src="/assets/mio.js?body=1"></script>
<script async="async" src="/assets/mio-ng/controllers/quote_form.js?body=1"></script>
So the question is, can javascript_include_tag ignore the async flag when in development, but not in production?

I found a way to rig this up to work, but I feel it is not the best solution:
= javascript_include_tag "application", :async => Rails.env == "production"

Related

Why assets served as compiled?

I am using Rails 5.2, and I notice when serving a page, the javascript file being served is compiled, however, I am in development mode.
for example, I get this html:
<script src="/assets/auth0/auth0_dialog.self-73034e01a7da3c346f6c123d6890950c4f1aac7f5f35ca09ced526e9c550328d.js?body=1"></script>
<script src="/assets/auth0/lock_init.self-b564b1f75c18a2fbbe1d41be6af70aa3d1c36c31aeb50cf52017d2ec31ceba10.js?body=1"></script>
for:
= javascript_include_tag "auth0/auth0_dialog"
= javascript_include_tag "auth0/lock_init"
any idea?

How to configure Jasmine in Rails 6?

How do I configure Jasmine in the Rails 6 environment (where Webpack replaces the asset pipeline for Javascript) so I can test the Javascript modules I've written for my app?
I installed the jasmine gem, ran rails generate jasmine:install, and edited jasmine.yml to point to the location of my Javascript source and specs.
The problem is that I can't use import/export statements. (For example, attempting to load my first module to test results in this error in Chrome: Uncaught SyntaxError: Unexpected token 'export')
From what I can tell, I need to set up Jasmine to use babel; but, I'm not having any luck finding instructions on how to do this in the new Rails 6 layout.
Yes, you're right. The main problem of jasmine-gem is that it doesn't pipe the spec through babel. Let me post the quickest solution to your problem and after that, I will think of the possible implementation of a similar approach in jasmine-gem.
The main idea is to pipe the specs through the rails webpack as long as it has all the required babel configurations.
Install jasmine-core since we will not use jasmine-gem in this solution
yarn add jasmine-core -D
Now create two additional webpack packs.
One is for Jasmine and will contain only Jasmine and the test runner
// app/javascript/packs/jasmine.js
import 'jasmine-core/lib/jasmine-core/jasmine.css'
import 'jasmine-core/lib/jasmine-core/jasmine-html.js'
import 'jasmine-core/lib/jasmine-core/boot.js'
import 'jasmine-core/images/jasmine_favicon.png'
And the second one for your application code and the specs
// app/javascript/packs/specs.js
// First load your regular JavaScript (copy all the JavaScript imports from your main pack).
let webpackContext = require.context('../javascripts', true, /\.js(\.erb)?$/)
for(let key of webpackContext.keys()) { webpackContext(key) }
// Then load the specs
let specsContext = require.context('../spec', true, /\.js(\.erb)?$/)
for(let key of specsContext.keys()) { specsContext(key) }
Pay attention to your '../javascripts' and '../spec' paths. For me it looked like '../../assets/javascripts' and '../../../spec' respectevly.
Then add the Webpack ProvidePlugin for Jasmine (add this code to config/webpack/environment.js)
// config/webpack/environment.js
const webpack = require('webpack')
environment.plugins.prepend('Provide', new webpack.ProvidePlugin({
jasmineRequire: 'jasmine-core/lib/jasmine-core/jasmine.js',
}))
Add Jasmine ranner page to your application
# config/routes.rb
Rails.application.routes.draw do
# ...
if Rails.env.development? || Rails.env.test?
get 'jasmine', to: 'jasmine#index'
end
end
# app/controllers/jasmine_controller.rb
class JasmineController < ApplicationController
layout false
def index
end
end
# app/views/jasmine/index.html.haml
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<%= stylesheet_pack_tag 'jasmine', :media => 'all' %>
</head>
<body>
<%= javascript_pack_tag 'jasmine' %>
<%= javascript_pack_tag 'specs' %>
</body>
</html>
Now your Jasmine should work on /jasmine route
This answer is prepared on the basis of this post, however, I've rechecked the instructions on ruby 2.6.3, rails 6.0.2, added appropriate changes to the recommendations and prove that this works.
Please, let me know if my answer was helpful for you or you need some additional information. However, I'm going to work on a solution that will succeed with jasmine gem or similar implementation.

How should I reference conditional assets in a rails app?

To install bootstrap and have it work correctly on legacy versions of IE, you need to place this your head:
<!--[if lt IE 9]>
<script src="js/html5shiv.js"></script>
<script src="js/respond.min.js"></script>
<![endif]-->
That's fine on a static site, but how would this work in a Rails app? Do I use sprockets to do this?
I have html5shiv.js located at
root/vendor/assets/javascripts/html5shiv.js
and respond.min.js located at
root/vendor/assets/javascripts/respond.js
How should I go about including these conditional assets?
In application.rb:
config.assets.precompile += %w(html5shiv.js respond.js)
Then add them in your layout:
/[if LT IE 9]
= javascript_include_tag 'html5shiv'
= javascript_include_tag 'respond'
By the way, what about a CDN for those resources?

how to use dust partial template files (.tl files)

I have few questions on partials and overriding templates.
For that i used the following folder structure.
projectRoot
dust-core-0.6.0.min.js
jquery.js
test.html
partial.tl
main_without_override.tl
The content of partial.tl:
{+greeting} Hola {/greeting}
{+world} World {/world}
The content of main_without_override.tl:
{>partial/}
The content of test.html:
<!DOCTYPE html>
<html>
<head>
<script src="dust-core-0.6.0.min.js" type="text/javascript"></script>
<script src="jq.js" type="text/javascript"></script>
</head>
<body>
</body>
<script>
$.get('main_without_override.tl', function(){
console.log(arguments);
})
</script>
</html>
In the index.html when i try to get the main_without_override.tl its saying 404. But im sure that the file is there. The path that firebug is showing is correct.But browser says 404.
I want to know
How to get this main_without_override.tl
Apply templating for main_without_override.tl and render in the browser.
I searched in google most of the examples give only the syntax. Can somebody help me in rendering the main_without_override.tl template.
In order to compile templates on the client (which is probably not a really good idea), you need to include dust-full instead of dust-core. This is because dust-core does not include the Dust compiler.
The reason that compiling templates on the client is probably not a good idea is that Dust compiles to JavaScript and as #monshi mentioned, you can compile the templates and then serve them as JavaScript. It is possible to get .tl files through AJAX if you include dust-full, but it is a better idea to compile that template beforehand and then make a dynamic request for that .js file when you need.
You can include your dust template as a JavaScript file by using <script> tag, but you need to compile it first, which is explained here
Then add following templates (scripts) to test.html:
<script type="text/javascript" src="partial.js"></script>
<script type="text/javascript" src="main_without_override.js"></script>
And in you JavaScript render the template by dust.render method:
dust.render('main_without_override', your_json_object, function(err, out){
your_dom_element.innerHTML = out;
});
Related question:
how to use dustjs-linkedin as client side templating?

Why is resource bundling being redirected in my ASP MVC4 app?

I'm using the RC and I've checked everything is up to date via NuGet.
In my global.asax.cs ive got:
BundleTable.Bundles.AddDefaultFileExtensionReplacements();
BundleTable.Bundles.AddDefaultIgnorePatterns();
BundleTable.Bundles.AddDefaultFileOrderings();
Bundle scripts = new Bundle("~/Scripts");
scripts.IncludeDirectory("~/Scripts", "*.js");
BundleTable.Bundles.Add(scripts);
Bundle css = new Bundle("~/Content/css");
css.IncludeDirectory("~/Content/css", "*.css", false);
BundleTable.Bundles.Add(css);
I've tried a few different configurations of this with no improvement.
Then in my layout ive got:
<link href="#BundleTable.Bundles.ResolveBundleUrl("~/Content/css")" rel="stylesheet" type="text/css" />
<script src="#BundleTable.Bundles.ResolveBundleUrl("~/Scripts")"> </script>
When the page loads its got decent looking urls:
<link href="/Content/css?v=QAsFYXHCbnaU70oGVxpgi9py9iKQrT9C4BVNdHa7xoI1" rel="stylesheet" type="text/css" />
But that url redirects to:
/Content/css/
Which returns a 404 not found error...
Anybody got any ideas?
The ~/Scripts and ~/Content/css virtual-path already exists on disk, so you need to make them some virtual-url, lets say ~/Scripts/js, and ~/Content/styles that's it, it's fine now.
Bundle scripts = new Bundle("~/Scripts/js");
scripts.IncludeDirectory("~/Scripts", "*.js");
BundleTable.Bundles.Add(scripts);
Bundle css = new Bundle("~/Content/styles");
css.IncludeDirectory("~/Content/css", "*.css", false);
BundleTable.Bundles.Add(css);
Also in MVC4 the Routing, Bundles, and Filters configuration has been moved to the
~/App_Start/(RouteConfig, BundleConfig, FilterConfig).cs
so check that you have those, if so then write your configurations there.
The bundle module logic that decides whether or not to handle a request, will not takeover requests to existing files or directories. So that's why your bundle requests don't work when they live at the same virtual path as an existing directory (or file).

Resources