Note: Webpacker update
Although this tutorial will still work it has been superseded by the addition of Elm to the webpacker gem. See this commit and the entry in the README. The end result is nearly the same.
Adding Elm to a Rails 5.1 application
The release of Rails 5.1 (http://weblog.rubyonrails.org/2017/4/27/Rails-5-1-final/)has brought some interesting changes for frontend development. Most notably is that it comes with yarn
, a tool to manage your javascript dependencies from NPM. It resembles Bundler so should be familiar to any Rails developer. It has made my life a lot easier at least.
Secondly, it has been made easier to integrate Webpack into your Rails development workflow using the webpacker
gem. There have been several gems to provide similar functionality but this one can be automatically configured when you create a new Rails project. Support for VueJs, React and Angular is available, but Elm is lacking right now. I’ll show how you can integrate Elm with Rails 5.1 and Webpack.
We’re gonna start from scratch and create a new Rails project with Webpack support.
$ rails new rails51-elm-example --webpack
This will generate the new project and run yarn to install all npm dependencies.
Next we’ll create a simple home controller to render a page.
$ rails g controller home index
And, we will modify routes.rb
to point to the new controller action
Rails.application.routes.draw do
root to: "home#index"
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
Running rails s
and visiting http://localhost:3000
should render a simple page with the following text:
Home#index
Find me in app/views/home/index.html.erb
We now have a simple rails app setup. Next we will install the elm npm dependencies using yarn, as well as add the webpack loaders for elm.
$ yarn add elm elm-hot-loader elm-webpack-loader
Elm comes with its own package manager elm-package
and to proceed we need to install the default Elm packages. Because this package manager is not in our path, but resides under ./node_modules/.bin/
we can use yarn run
to execute it.
$ yarn run elm-package install
You will be asked whether to execute the plan of dependencies to be installed. Press Y
to continue. The command will create elm-package.json
to list your elm-dependencies and an elm-stuff
directory. This directory can be added to .gitignore
so we don’t track it in our version control.
echo "/elm-stuff" >> .gitignore
Integrating Elm with Rails
So, we have a working Rails installation and we have installed Elm, all that is left is make them work together. Remember we installed the webpacker
when we created the new rails application. This created a whole bunch of files in the config/webpack
directory. This stuff is new in Rails. If you are familiar with Webpack you’ll know how most webpack.config.js
files look, it’s a big object with loaders, entry points, outputs, plugins etc. and you can chain and hook up all kinds of asset-loaders. We are working with basically the same config file here only it looks a little different.
Much like the usual Rails configuration files there is configuration for each kind of environment (production.js/development.js/testing.js), as well as a shared configuration file shared.js
that has configuration common for all environments. If you know Webpack you’ll recognize the configuration in these files.
There is also a directory loaders
in the directory that comes with several preconfigured loaders. Usually the loaders are in an array in the webpack configuration object, here each loader gets its own file in this directory. There are loaders for coffeescript/babel and erb for example. In a moment we will add a loader for elm
-files.
But first, apart from the loaders there is a paths.yml
that has several configuration options available, and we’ll need to adjust this so files with the .elm
extension are matched by Webpack. Add .elm
to the extensions
array in paths.yml
...
extensions:
- .coffee
- .elm
- .js
- .jsx
- .ts
...
Now we need to add a loader for the .elm
files. Add the file elm.js
to config/webpack/loaders
.
// elm.js
module.exports = {
test: /.elm$/,
exclude: [/elm-stuff/, /node_modules/],
loader: 'elm-hot-loader!elm-webpack-loader?verbose=true&warn=true&pathToMake=node_modules/.bin/elm-make'
}
This loader does several things, we ensure it matches .elm
files but exclude the elm-stuff
and node_modules
directory. Then we chain two loaders, the elm-hot-loader
handles hot module replacements, and the elm-webpack-loader
handles the loading of Elm. Note that we pass in the argument pathToMake=node_modules/.bin/elm-make
to point the loader to the elm-make
program.
We are now nearly done.
You may be used to Rails placing the javascript assets in the app/assets/javascripts
directory. This is different when you use the webpacker
gem, as the assets are now in app/javascripts/packs
. In fact, during installation a file named application.js
has been placed there already. You can link to these in your html using the javascript_pack_tag
method in your views.
We will add link to the application.js
from your layout file in app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>Rails51ElmExample</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all' %>
<%= javascript_include_tag 'application' %>
</head>
<body>
<%= yield %>
</body>
<%= javascript_pack_tag 'application' %>
</html>
We have now linked to the webpack compiled application.js file. However, since webpack serves the compiled files on a different server we will need to start it to access it. The webpack dev server is binstubbed in your bin folder as ./bin/webpack-dev-server
. Remember, this server runs concurrent to your rails server. So, you’ll need to run rails server
as well.
If you open http://localhost:3000/
now you will see Hello World from Webpacker
in your console.
Last steps
However, we want to see a basic Elm app in our application. Add a file app/javascripts/packs/elm/Main.elm
in your app with the following contents
import Html exposing (text)
main : Html.Html msg
main =
text "Hello from Elm!"
This is a simple elm app. Our last step is to ensure the Elm app is required and mounted onto our html in the application. Change the app/javascripts/application.js
by adding these lines.
var Elm = require('./elm/Main.elm');
window.addEventListener('DOMContentLoaded', function(){
Elm.Main.embed( document.getElementById( 'main' ) );
})
And add the mounting point to the app/views/home/index.html.erb
file.
<div id="main"></div>
If you run the webpack dev server and the rails server now you should be able to see Hello from Elm!
if you go to http:localhost:3000
, this means Elm is succesfully working for this Rails app.
The end result and all the steps can be seen on (https://github.com/maartenvanvliet/rails5.1-elm-example)
A lot of help for this post came from (https://blog.abevoelker.com/2016-09-05/adding-elm-to-a-rails-application/), though the release of Rails 5.1 with the webpacker gem made some alterations necessary.
Photo by Johannes Plenio on Unsplash