Rails 6 + Vue.js 2 Starter Kit GitHub Template.
NOTE: For Rails 5.2, please check https://github.com/jetthoughts/vuejs-rails-starterkit/tree/rails-5-latest
A quick and easy way to setup Rails + PWA + Turbolinks + Webpacker + Bootstrap with AdminLTE theme + Vue + Jest. If your team is considering or has already decided to use Vue, this is the right for you. As an additional review of how to setup PWA, Turbolinks, CSS frameworks, Storybook.
Things you may want to cover:
Features:
- Optimized for peformance Webpacker with Vue.js: Lazy Load, Split Chunks
- Turbolinks
- PWA
- Backend Unit and System Tests with coverage
- Vue.js Unit Tests with coverage
- Deployable to Heroku
- Pre-setup for services:
- GitHub
- Heroku (Heroku Reviews Apps)
- CircleCI
- Codecov and Simplecov
- Dependabot
- Static Code Analyzers:
- Pronto
- Rubocop
- ESLint
- EditorConfig
Dependencies:
- Ruby on Rails version 6
- Vue.js version 2 with Vue Test Utils
- Webpacker 5 with Webpack 4
- Bootstrap 4 with AdminLTE Template
- Babel 7
System Dependencies:
- Ruby 2.7
- Node.js 14
- Yarn
- PostgreSQL 12
- Heroku CLI
Table of Contents
- Generate Ruby on Rails Project with Vue.js
- Setup development environment
- Add sample page to host Vue.js component
- Use Webpacker assets in the application
- Install Jest for Component Unit Tests
- Setup Heroku and Deploy
- Setup basic PWA
- Setup Turbolinks
- Setup AdminLTE
- Configure continuous integration and other services for static code analysis
Generate Ruby on Rails Project with Vue.js (No Turbolinks included on this stage)
gem install rails
rails new vuejs-rails-starterkit --force --database=postgresql \
--skip-action-mailer --skip-action-cable --skip-sprockets --skip-turbolinks \
--webpack=vue
cd ./vuejs-rails-starterkit
bin/rails db:create db:migrate
This generates Rails project with:
- Vue component in
app/javascript/app.vue
- Example entry file
app/javascript/packs/hello_vue.js
Setup development environment:
-
Uncomment
system('bin/yarn')
inbin/setup
andbin/update
to install new node modules. -
Install dependencies:
bin/setup
- Enable content_security_policy in the
config/initializers/content_security_policy.rb
with the following configuration:
Rails.application.config.content_security_policy do |policy|
policy.script_src :self, :https
if Rails.env.development? || Rails.env.test?
policy.connect_src :self, :https, 'http://localhost:3035', 'ws://localhost:3035'
end
end
- Verify that we have not broken anything
bin/webpack
bin/rails runner "exit"
Add sample page to host Vue.js component
- Generate controller and view:
bin/rails generate controller Landing index --no-javascripts --no-stylesheets --no-helper --no-assets --no-fixture
- Update
app/views/landing/index.html.erb
to:
<h1>Landing#index</h1>
<p>Find me in app/views/landing/index.html.erb</p>
<div id='hello_vue_app'></div>
- Change
app/javascript/packs/hello_vue.js
to:
import Vue from 'vue'
import App from '../app.vue'
document.addEventListener('DOMContentLoaded', () => {
const app = new Vue({
render: h => h(App),
el: '#hello_vue_app'
}).$mount()
})
- Setup a sample page as a home page by updating
config/routes.rb
:
root 'landing#index'
Use Webpacker assets in the application
-
Enable Webpacker with
SplitChunks
:-
Enable
SplitChunks
with default config by adding toconfig/webpack/environment.js
:environment.splitChunks()
-
Enable Webpacker by updating
app/views/layout/application.html.erb
:Change:
<%= stylesheet_link_tag 'application', media: 'all' %> <%= javascript_pack_tag 'application' %>
to:
<%= stylesheet_packs_with_chunks_tag 'application', 'hello_vue', media: 'all' %> <%= javascript_packs_with_chunks_tag 'application', 'hello_vue' %>
-
-
Verify locally that vue.js is working and
SplitChunks
is enabled
bin/rails s
open "http://localhost:3000/"
The javascript_packs_with_chunks_tag
and stylesheet_packs_with_chunks_tag
helpers split assets
into small size chunks and create html tags for them:
<script src="/packs/js/runtime~hello_vue-818eba5af0151079cb6c.js"></script>
<script src="/packs/js/1-7b962b4481d6abff6c2b.chunk.js"></script>
<script src="/packs/js/hello_vue-bc0218ac204eff3ff742.chunk.js"></script>
Install Jest for Component Unit Tests
- Add Jest with required dependencies
yarn add --dev jest @vue/test-utils vue-jest babel-core@^7.0.0-bridge.0 babel-jest jest-serializer-vue
- Configure Jest in
package.json
(including the Coverage enabling):
"scripts": {
"test": "jest"
},
"jest": {
"verbose": true,
"testURL": "http://localhost/",
"roots": [
"test/javascript"
],
"moduleDirectories": [
"node_modules",
"app/javascript"
],
"moduleNameMapper": {
"^@/(.*)$": "<rootDir>/app/javascript/$1"
},
"moduleFileExtensions": [
"js",
"json",
"vue"
],
"transform": {
".+\\.js$": "babel-jest",
".+\\.vue$": "vue-jest"
},
"testPathIgnorePatterns": [
"<rootDir>/config/webpack/"
],
"snapshotSerializers": [
"jest-serializer-vue"
],
"collectCoverage": true,
"collectCoverageFrom": [
"**/*.{js,vue}",
"!**/node_modules/**"
]
},
- Add
test/javascript/test.test.js
:
test('there is no I in team', () => {
expect('team').not.toMatch(/I/);
});
- Verify installation
yarn test
- Add component test for App in
test/javascript/app.test.js
:
import { mount, shallowMount } from '@vue/test-utils'
import App from 'app';
describe('App', () => {
test('is a Vue instance', () => {
const wrapper = mount(App)
expect(wrapper.vm).toBeTruthy()
})
test('matches snapshot', () => {
const wrapper = shallowMount(App)
expect(wrapper.html()).toMatchSnapshot()
})
});
- Verify by
yarn test
You should see all tests passed
Setup Heroku and Deploy
- Confirm compilation is working:
RAILS_ENV=production \
NODE_ENV=production \
RAILS_SERVE_STATIC_FILES=true \
SECRET_KEY_BASE="7aa51097e982f34be02abe83528c3308768dff3837b405e0907028c750d22d067367fb79e2b223e3f223fea50ddf2d5dc9b3c933cf5bc8c7f2a3d3d75f73c4a7" \
bin/rails assets:precompile
- Create a Heroku App and provision it
Requirements: Heroku CLI.
NOTE: Do not forget to commit all your changes: git add . && git commit -m "Generates Ruby on Rails application with Vue.js onboard"
heroku create
heroku buildpacks:add heroku/ruby
heroku config:set RAILS_ENV=production NODE_ENV=production YARN_PRODUCTION=true MALLOC_ARENA_MAX=2
- Setup Node.js version 13.x for Heroku
heroku buildpacks:add --index 1 heroku/nodejs
Use the engines
section of the package.json
to specify the version of Node.js to use on Heroku.
Drop the ‘v’ to save only the version number:
{
"engines": {
"node": "13.x"
}
}
- Deploy and verify that vue.js is working on Heroku
git push heroku master
heroku apps:open
Setup basic PWA
- Install
serviceworker-rails
by adding intoGemfile
:
gem 'serviceworker-rails', github: 'rossta/serviceworker-rails'
-
Following the guide: https://github.com/rossta/serviceworker-rails you should get something like: https://gist.github.com/pftg/786b147eff85a6fc98bd8dc1c3c9778e
-
There'll be an issue with service worker registration on the page saying:
Uncaught ReferenceError: window is not defined
andFailed to register a ServiceWorker...
. To fix that add following line toconfig/webpack/environment.js
as suggested here:
environment.config.set('output.globalObject', 'this')
Setup Turbolinks
- Add node dependencies
yarn add vue-turbolinks turbolinks
yarn install
- Load Turbolinks by adding to
app/javascript/initializers/turbolinks.js
:
import Turbolinks from 'turbolinks'
Turbolinks.start()
- Add to
app/javascript/packs/application.js
:
import 'initializers/turbolinks.js'
- Change
app/javascript/packs/hello_vue.js
to:
import TurbolinksAdapter from 'vue-turbolinks'
import Vue from 'vue'
import App from '../app.vue'
Vue.use(TurbolinksAdapter)
document.addEventListener('turbolinks:load', () => {
const app = new Vue({
render: h => h(App),
el: '#hello_vue_app'
}).$mount()
})
- Update layout
app/views/layouts/application.html.erb
:
<%= javascript_packs_with_chunks_tag 'hello_vue', 'application', 'data-turbolinks-track': 'reload' %>
- Run tests and server to verify:
bin/rails t
bin/rails s
Setup AdminLTE
- Add node dependencies
yarn add admin-lte bootstrap jquery popover @fortawesome/fontawesome-free
- Add
app/javascript/initializers/adminlte.js
initializer:
import '../assets/adminlte.scss'
import('./plugins') // () needed for async loading
- Add
app/javascript/initializers/plugins.js
file with plugin importing:
import '@fortawesome/fontawesome-free'
import 'jquery/src/jquery.js'
import 'popper.js'
import 'bootstrap'
import 'admin-lte/build/js/AdminLTE'
- Import admin lte initializer in
app/javascript/packs/application.js
pack:
import 'initializers/adminlte'
- Next step is updating main layout. Code for layout you can find here.
Also don't forget to add
yield
in div withcontent
class:
<div class="content">
<%= yield %>
</div>
- Add styles to
app/javascript/assets/adminlte.scss
:
$fa-font-path: '~@fortawesome/fontawesome-free/webfonts';
@import '~@fortawesome/fontawesome-free/scss/fontawesome';
@import '~@fortawesome/fontawesome-free/scss/solid';
@import '~@fortawesome/fontawesome-free/scss/regular';
@import '~@fortawesome/fontawesome-free/scss/brands';
@import "~admin-lte/build/scss/AdminLTE";
- Run tests and server to verify:
yarn test
bin/rails test
bin/rails test:system
bin/rails s
Configure continuous integration and other services for static code analysis.
To be able to automatically analyze the quality of the code, let's install the jt_tools gem.
- Add this line to your application's Gemfile:
gem 'jt_tools', groups: [:development]
- Next step is running bundle install and generator:
bin/bundle
bin/rails jt_tools:install
- Run linters to verify
bin/lint-github-pr
You should see a list of the linters that were running.