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.
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:
app/javascript/app.vue
app/javascript/packs/hello_vue.js
Uncomment system('bin/yarn')
in bin/setup
and bin/update
to install new node modules.
Install dependencies:
bin/setup
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
bin/webpack
bin/rails runner "exit"
bin/rails generate controller Landing index --no-javascripts --no-stylesheets --no-helper --no-assets --no-fixture
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>
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()
})
config/routes.rb
: root 'landing#index'
Enable Webpacker with SplitChunks
:
Enable SplitChunks
with default config by adding to config/webpack/environment.js
:
environment.splitChunks()
Enable Webpacker by updating app/views/layouts/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>
yarn add --dev jest @vue/test-utils vue-jest babel-core@^7.0.0-bridge.0 babel-jest jest-serializer-vue
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/**"
]
},
test/javascript/test.test.js
:test('there is no I in team', () => {
expect('team').not.toMatch(/I/);
});
yarn test
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()
})
});
yarn test
You should see all tests passed
RAILS_ENV=production \
NODE_ENV=production \
RAILS_SERVE_STATIC_FILES=true \
SECRET_KEY_BASE="7aa51097e982f34be02abe83528c3308768dff3837b405e0907028c750d22d067367fb79e2b223e3f223fea50ddf2d5dc9b3c933cf5bc8c7f2a3d3d75f73c4a7" \
bin/rails assets:precompile
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
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": ">= 12.x"
}
}
git push heroku master
heroku apps:open
serviceworker-rails
by adding into Gemfile
: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
and Failed to register a ServiceWorker...
. To fix that add following line to config/webpack/environment.js
as suggested here:
environment.config.set('output.globalObject', 'this')
yarn add vue-turbolinks turbolinks
app/javascript/initializers/turbolinks.js
:import Turbolinks from 'turbolinks'
Turbolinks.start()
app/javascript/packs/application.js
:import 'initializers/turbolinks.js'
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()
})
app/views/layouts/application.html.erb
:<%= javascript_packs_with_chunks_tag 'hello_vue', 'application', 'data-turbolinks-track': 'reload' %>
bin/rails t
bin/rails s
yarn add admin-lte bootstrap jquery popover @fortawesome/fontawesome-free
app/javascript/initializers/adminlte.js
initializer:import '../assets/adminlte.scss'
import('./plugins') // () needed for async loading
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'
app/javascript/packs/application.js
pack:import 'initializers/adminlte'
app/views/layouts/application.html.erb
. Code for layout you can find here. Also don’t forget to add yield
in div with content
class:<div class="content">
<%= yield %>
</div>
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";
yarn test
bin/rails test
bin/rails test:system
bin/rails s
To be able to automatically analyze the quality of the code, let’s install the jt_tools gem.
gem 'jt_tools', groups: [:development]
bin/bundle
bin/rails jt_tools:install
bin/lint-pr
You should see a list of the linters that were running.
Author: jetthoughts
Demo: https://vuejs-rails-starterkit.herokuapp.com/
Source Code: https://github.com/jetthoughts/vuejs-rails-starterkit
#vuejs #vue #javascript #rails