I wrote this to help a new dev at Fire Hazard get up to speed, but it might be useful for anyone looking to add advanced js to a rails application. This is one potential implementation of the stack - there are plenty of other ways to to it.
The 'player' sections of Citydash are a single-page javascript app, served by a Rails application. The actual javascript we write is several layers removed from what executes, because:
We want to use lots of external libraries (which themselves have dependencies).
Javascript's syntax is evolving all the time, and we want to use more recent language features now even though they may not be supported in all browsers yet.
We want to compress the js before it goes down the wire, and use cache-busting to ensure we have the latest version.
Here's how we go from frontend/*.js in the repo to a running js app on the client:
* deploy
* before_assets_precompile
* yarn install
* package.json
* yarn run
* package.json
* cross-env
* browserify
* envify
* babelify
* .babelrc
* assets:precompile
* web request
* ...
task :before_assets_precompile do
system('yarn install && yarn run build_players')
end
Rake::Task['assets:precompile'].enhance ['before_assets_precompile']
yarn will create yarn.lock, the equivalent of Gemfile.lock. This means we can safely specify versions as "^1.2.3" (meaning "compatible with 1.2.3") - we'll only get new versions if we do 'yarn upgrade', rather than 'yarn install'.
{
"name": "app",
"version": "1.0.0",
"private": true,
"dependencies": {
"axios": "0.15.2",
"vue": "2.4.4",
"vue-router": "2.7.0",
...
},
}
{
"name": "frontend",
"version": "1.0.0",
"private": true,
"scripts": {
"build_players": "yarn install && cross-env NODE_ENV=production browserify -g envify frontend/app.js > app/assets/javascripts/app.js",
},
This translates calls like
import VueRouter from 'vue-router'
into something the browser can execute, since 'import' isn't implemented by browsers.
It also does a couple of additional transformations:
...
"browserify": {
"transform": [
"babelify"
]
}
...
Babel is a tool that translates javascript to javascript. Here we're using it to enable using newer syntax (eg arrow functions) than browsers support. In package.json we require the dependency 'babel-preset-env' in order to get a preset.
{
"presets": ["env"]
}
This could be optimised away, or nearly away, since browserify has already done most of this work.
Once everything's built, the actual request is very simple:
<%= javascript_include_tag "app" %>