👋 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).
Table of contents:
- Editor and VS Code setup
- Webpacker usage
- Automatic testing and desktop notifications
- Reproducible environments
- Heroku and double Yarn installs
- Configuration management
- Best resources for learning Ruby and Rails
Editor and VS Code setup
I extracted this section in its own article, read it here:
👩💻 The three extensions you need for Rails development in VS Code
Vincent Voyer ・ Dec 2 '19 ・ 5 min read
Webpacker usage
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:
app/views/layout/application.html.erb
<%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track": "reload" %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
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:
app/views/layout/application.html.erb
<%= stylesheet_pack_tag "application", media: "all", "data-turbolinks-track": "reload" %>
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.
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.
Turns out the easiest way to have notifications is to use terminal-notifier along with terminal-notifier-guard.
All you have to do is to add to your Gemfile, in the development
and test
groups:
gem 'terminal-notifier', '2.0.0'
gem 'terminal-notifier-guard', '1.7.0'
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 - Exact versions for dependencies known as dependencies pining
1., install rbenv.
2., create a .ruby-version file:
2.6.5
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:
Pinning dependencies can be a touchy subject. Luckily there's a very good article summarizing the pro and cons of various Gemfile and bundler strategies when coming to specifying versions: https://thoughtbot.com/blog/a-healthy-bundle.
Reproducible environments: Node.js and Yarn
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) - Exact versions for dependencies
To do so, 1. install nvm.
And create an .nvmrc file:
12.13.1
Use the latest Node.js version available when reading this guide.
2., set the Yarn policy in a terminal:
yarn policies set-version 1.19.1
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.
You can use the latest LTS available.
3., update your package.json:
{
// [...],
"engines": {
"node": "12.13.1",
"yarn": "1.19.1"
}
}
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.
Heroku and double Yarn installs
As for deploying on Heroku, their Getting started with Rails 6.x guide is well written, you can follow 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.
Let's do that, in a terminal:
heroku buildpacks:clear
heroku buildpacks:add heroku/nodejs
heroku buildpacks:add heroku/ruby
Now we have to disable any Yarn install ran by Webpacker, add to your Rakefile:
Rake::Task['yarn:install'].clear
namespace :yarn do
desc "Disabling internal yarn install from Rails"
task :install => [:environment] do
puts "Disabling internal yarn install from Rails"
end
end
Rake::Task['webpacker:yarn_install'].clear
namespace :webpacker do
desc "Disabling internal yarn install from Rails"
task :yarn_install => [:environment] do
puts "Disabling internal yarn install from Rails"
end
end
Thanks to Rebecca Lynn Cremona for digging the issue.
Configuration management
I created a dedicated article to speak about credentials, configuration files and environment variables, read it:
🤫 Secrets, environment variables & config files: the Ruby On Rails case
Vincent Voyer ・ Dec 16 '19 ・ 7 min read
Best resources for learning Ruby and Rails
I extracted this section in its own article, read it here:
Best modern resources for learning Rails 6 and Ruby
Vincent Voyer ・ Nov 19 '19 ・ 3 min read
🔚 That's it!
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:
Vincent Voyer@vvoyer📚 Modern resources for learning Rails 6 and Ruby, 2019 edition: dev.to/vvo/modern-res…22:12 PM - 19 Nov 2019
Thanks for reading.
Top comments (27)
Nice article, thanks. In my notes I also have:
lefthook or any other precommit config would be nice to have
I'd like to translate the article dev.to/vvo/a-rails-6-setup-guide-f... into Japanese and publish on our tech blog techracho.bpsinc.jp/ for sharing it. Is it OK for you?
I make sure to indicate the link to original, title, author name in the case.
Best regards,
Yes, no problem
Published the JP translation techracho.bpsinc.jp/hachi8833/2019...
Thank you very much for your kindness! 😂
Awesome! 🙏
My solution for notifications in my scripts is to use the bell sound and apple script.
Ohh do we need this kind of best practices build-ups! Thx for sharing!
You might consider overmind as a sort of "icing on the cake" though ;)
Read about it here: evilmartians.com/chronicles/introd...
Indeed I saw overmind but since I yet did not know why I would need it vs the simpler version of hivemind then I went for the simpler version. Maybe later of if you already have killer features we would need maybe? Thanks!
Nice article. The renovate tip along with hivemind and the pointer to learn more about what webpack can do with rails are huge.
A very small typo in the "Reproducible environments: Node.js and Yarn" section. Step 1 says "12.13.1" while 3 is "12.13.0" I know it doesn't break anything but just to avoid minor confusions to the reader.
Thanks a lot, updated!
I found in order to get jQuery $ to be defined properly, I had to follow step (3) here: botreetechnologies.com/blog/introd...
Otherwise, everything here worked.
But I prefer to require it whenever I need it rather than relying on a global variable. Still, thanks for heads up!
Hey, super interesting stuff.
Thanks a lot, especially for the webpack and overmind part.
I had an extra issue with webpack handling stylesheets :
delivering images from the asset pipeline in [s]css needs a tiny bit of extra configuration : stackoverflow.com/a/57175231/8131629
Cheers from Vigneux de Bretagne :)
Nice, I yet have to use that but once I do Ill make sure to edit this post or create a new one focused on webpacker + Rails.
Thanks for reading and adding your comment! Cheers from Rezé, France :D
Updated on Nov 26, 2019:
Great work Vincent, really helpful.
Thanks for your encouragement towards the Webpacker docs too... I'm pleasantly surprised.
Fabulous guide
Thanks! I made some minor edits right now.
First experience contributing to dev.to. It was 11am today and I thought "I really really need to publish/write about Rails setup I have". Two seconds later I was on dev.to, congrats :)