π Welcome! This post contains everything I learned and did that was not documented as part of my Ruby and Ruby on Rails 6 learning. I am new to the ecosystem and really looking forward to feedback, if you have any, drop me a comment here!
Most of the Ruby on Rails tutorials are focused, for good reasons, on using the API. For example the official Getting Started.
But what happens after a few rails new blog, controllers editing and git push heroku? Well I wanted to go further, I wanted the best practices as for:
developer setup
code editor configuration for Ruby and Ruby on Rails
assets configuration and auto-reloading (Webpacker)
reproducible builds and safe environments
a few books and resources to learn more
Those subjects are the ones I struggled the most with because they are the least discussed online: those are usually learned through experience and usage of Ruby on Rails.
While learning and deploying Rails applications, I started a README file where I would put all the small traps and bits of code I had to add to solve all the previous subjects.
At some point the list of notes grew and I felt I needed to rewrite it in a clean way so that next time I have to start a Rails application, I already have a reference I can use.
Making this reference public is a way for me to share my knowledge and help others (you hopefully).
In this part, I speak about [S]CSS because all of this applies to SCSS or CSS files. Webpacker supports multiple formats.
Webpacker, at first, was confusing for me and here's why:
As of Rails 6, Rails started to bundle and wrap Webpack inside Rails applications. This is done through Webpacker. What Webpacker provides is a preconfigured Webpack along with view helpers to easily get corresponding generated assets like JavaScript and [S]CSS files.
The state as of November 2019 is that the default Rails application erb template file contains:
The second line gets from Webpacker the compiled JavaScript file path generated from the entry point app/javascript/packs/application.js.
But the first line, ask the regular Rails asset pipeline (whatever that is today) to generate such a file.
I was confused as to why there is a dual system running on, there might be reasons for that but let's fix this situation by only using Webpacker to generate our assets.
Using Webpacker for [S]CSS files
To use Webpacker for [S]CSS files, for example to use Bootstrap 4 with Rails 6, you need to change the line including your CSS to this:
So that it tells Webpacker to provide us with the right stylesheet asset url for the pack named "application". The name application actually refers to the filename of the main current pack: app/javascript/packs/application.js.
You'll notice the change from stylesheet_link_tag to stylesheet_pack_tag.
Then create the main SCSS file (the name and location of the file actually do not matters):
app/javascript/src/style.scss
@import"~bootstrap/scss/bootstrap";// if you plan on using bootstrap.
And import it from JavaScript:
app/javascript/packs/application.js:
// [...]import"../src/style.scss";
The stylesheet won't actually be loaded by the JavaScript file, it just tells Webpack to compile it so that we can then require its path in our application template.
Using Bootstrap 4 with Rail 6
For the @import "~bootstrap/scss/bootstrap"; bit from the last part to work, you need to install Bootstrap. But not via a Gemfile, via Yarn. Since ~bootstrap means to Webpack: find this in node_modules/bootstrap/.... Add it now:
in a terminal:
yarn add bootstrap
For the JavaScript parts of Bootstrap to work, you would have to install the JavaScript dependencies too:
in a terminal:
yarn add jquery popper.js
Then require those files in your JavaScript application.
app/javascript/packs/application.js:
require("jquery");require("bootstrap");
Auto-reloading of [S]CSS and JavaScript files
One of the main advantages of Webpack to me is the auto-reloading feature, but it's not activated in Rails 6 by default. Which means that every page load in development will take forever (really). For the auto-reloading to work, you need to use webpack-dev-server. Fortunately it's already shipped with your new Rails 6 application, all you need to do is launch it alongside your Rails server:
in a terminal:
./bin/webpack-dev-server
I like to have a single command to run as for development servers, ideally both rails server and ./bin/webpack-dev-server would be launched in parallel. It seems the easiest and modern way to do so nowadays is to use a Procfile.dev and overmind.
Process manager for Procfile-based applications and tmux
Overmind
Overmind is a process manager for Procfile-based applications and tmux. With Overmind, you can easily run several processes from your Procfile in a single terminal.
Procfile is a simple format to specify types of processes your application provides (such as web application server, background queue process, front-end builder) and commands to run those processes. It can significantly simplify process management for developers and is used by popular hosting platforms, such as Heroku and Deis. You can learn more about the Procfile format here.
There are some good Procfile-based process management tools, including foreman by David Dollar, which started it all. The problem with most of those tools is that processes you want to manage start to think they are logging their output into a file, and that can lead to all sorts of problems: severe lagging, losing or breaking colored output. Tools can also add vanity informationβ¦
overmind is basically foreman but done "well". Since I just trust stuff from the internet, I am using it now. Here's what you need to do:
Create Procfile.dev:
web: rails server
webpacker: ./bin/webpack-dev-server
Create or update .env:
OVERMIND_PROCFILE=Procfile.dev
Now instead of using rails server or rails s locally, just use overmind:
in a terminal:
brew install overmind
overmind start
# You can also use overmind s
Congrats, your web server now runs at http://localhost:5000 and has fast auto reloading built in.
If you want to debug your rails server, since multiple processes are running via overmind, you need to do this:
overmind connect web
To detach the tmux session opened by overmind and go back to your terminal, hit CTRL+b then d (for detach).
Initially I recommended to use the simpler hivemind but as soon as you need to debug your rails server then hivemind cannot help you there.
One issue I had was about Webpacker requesting Yarn integrity checks too often, resulting in slow development environment. Since I know how to ensure my Yarn dependencies are up to date and always the same (see next parts about Reproducible environments), I disabled this feature: https://github.com/rails/webpacker#yarn-integrity.
You can do a lot more with Webpacker, but you won't learn that from the main Rails documentation. To learn more about Webpacker, I recommend to check out their docs. I learned everything I needed from them. Especially the folder structure, webpack-dev-server and CSS.
Automatic testing and desktop notifications
Coming from the JavaScript world, I was used to exceptional JavaScript testing tooling in the name of Jest, which is today the standard for testing JavaScript applications (you might be using something else, that's fine). The Ruby and Ruby on Rails testing world is more various. Mostly my needs are: automatic test running, desktop notifications when tests are passing or failing.
As for automatic test running, you can already find a lot of material online, for example Automating Minitest in Rails 6 is a good starting point.
But then, I want need those notifications:
So that I don't have to leave my editor to check the status of my tests.
And then run bundle. Guard will automatically detect the gems and then send you nice notifications.
In the first version of this post I advocated for the use of Growl because I could not have terminal-notifier working well but a commenter on lobste.rs recommended to try it again and it works.
Growl will also work of course if you're already using it.
Reproducible environments
Coming from the Node.js and JavaScript world, I developed good habits as for reproducible environments.
What is a reproducible environment? No matter the machine running: Travis CI, Heroku or your macbook it should always run using the same dependencies and platforms/languages versions. At no moment you want Heroku to install dependencies using Yarn 1.1, Ruby 2.4 and Node.js 12.1.0 while your macbook is using Yarn 1.9, Ruby 2.6 and Node.js 12.3.4. And ideally, this should be easy to install, maintain and upgrade all those versions.
This applies to: bundler, Rails, Ruby, Node.js and Yarn. Here's how to do it:
Reproducible environments: Ruby
We will be using:
rbenv to easily install and run different Ruby versions
.ruby-version to enforce the Ruby version on all platforms
Gemfile changes to enforce the Ruby version when running Bundler
3., indicate in your Gemfile the Ruby version for Bundler to check against:
ruby '2.6.5'
4., make sure to use exact versions as for your Ruby dependencies in your Gemfile. This means changing lines like:
gem 'webpacker', '~> 4.0'
To:
gem 'webpacker', '4.2.0'
Luckily we installed a VS Code extension to easily know what is the latest version available of any Ruby dependency, just hover a line in your Gemfile and you'll get this:
Now that Rails has a dependency on Webpack, it then has a dependency on Node.js and Yarn, surprise! Which means we also need to ensure the versions used for those binaries are specific.
We will be using:
An .nvmrc file will specify which Node.js version to install and use. Both Heroku and Travis CI are using it to infer the Node.js version to use.
A Yarn policy will allow us to specify which Yarn version to install and use. This will work on any machine that has Yarn >= 1.13.0 which is one year old, that's safe.
The engines field in your package.json will verify the Yarn and Node.js versions on any Node.js script running (like Webpack from Webpacker)
This will download Yarn 1.19.1 and create a file .yarnrc that will tell any Yarn binary to use Yarn version 1.19.1. Neat. Again, use the latest Yarn version available when reading this guide.
4. Pin all your package.json dependencies to use exact versions. To do so, change any line where the version starts like:
"@rails/webpacker": "^4.2.0",
To:
"@rails/webpacker": "4.2.0",
4. As for using exact versions of your dependencies in your package.json. This all boils down to updating in your package.json all lines like:
"@rails/webpacker": "^4.2.0",
To:
"@rails/webpacker": "4.2.0",
In the next part we will actually see a tool that can pin dependencies for you.
That's it! Now you have a Node.js and Yarn environment you master.
Automatic dependencies updates
As we have seen, I always use specific versions for dependencies, this is part of having reproducible environments. You never want to end up in a case where for some reasons an environment or platform is using different versions of your dependencies. This can be a huge waste of time.
Sure lockfiles like Gemfile.lock and yarn.lock do solve a lot of issues, but that's not enough. Not only there are cases where those files might fail. But I also want to know exactly which versions of my direct dependencies I am using at any moment. Both to know which features I have available and which bugs are still here without having to dig into obscure commands to understand what's hidden behind that "~2.x" notation in my package.json or Gemfile.
But then, I still want to upgrade my dependencies from time to time, in a painless way. I recommend checking Renovate which handles JavaScript and Ruby dependencies auto updating. Use it.
Now I had a weird behaviour where deploying on Heroku would trigger multiple Yarn installs: one from Heroku when they detect a package.json file and one from Webpacker while bundling the assets. This was annoying and I wanted to separate those tasks into:
Install Yarn dependencies
Install Ruby dependencies
Compile assets
Tasks 1. and 2. are either automatically detected by Heroku or you can also specify them yourself, and their order. To do so you have to define yourself the buildpacks and buildpacks order to use first the Node.js one and then the Ruby one.
Overall diving into the Rails world feels great from a Node.js background. Hopefully some of the issues mentioned will disappear over time, I will make sure to update this post when that's the case. If you happen to have more information on any of the subjects here, please add a comment or write me at vincent@codeagain.com.
If this guide helped you in any way, share it for others to read it: