Skip to content

Commit

Permalink
Migrate to Inertia and Svelte.js
Browse files Browse the repository at this point in the history
  • Loading branch information
ledermann committed Jun 9, 2022
1 parent fe87e78 commit 62246f7
Show file tree
Hide file tree
Showing 117 changed files with 2,076 additions and 2,475 deletions.
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Hostname of the application
APP_HOST=templatus.test
APP_HOST=templatus-inertia.test

# Timezone
TIME_ZONE=Berlin
Expand Down
2 changes: 1 addition & 1 deletion .env.test
Original file line number Diff line number Diff line change
@@ -1 +1 @@
APP_HOST=templatus.test
APP_HOST=templatus-inertia.test
5 changes: 5 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@
/log
/db
/config
*.config.js
*.config.ts
.eslintrc.js
app/javascript/routes.js
app/javascript/routes.d.ts
42 changes: 1 addition & 41 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,4 @@
module.exports = {
root: true,
env: {
browser: true,
jest: true,
node: true,
},
plugins: [],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:jest/recommended',
'plugin:vue/base',
'plugin:vue/vue3-essential',
'plugin:vue/vue3-recommended',
'plugin:vue/vue3-strongly-recommended',
'prettier',
'plugin:prettier/recommended',
],
globals: {
defineProps: 'readonly',
defineEmits: 'readonly',
defineExpose: 'readonly',
withDefaults: 'readonly',
},
rules: {
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
},
],
'@typescript-eslint/no-var-requires': 'off',
'vue/no-v-html': 'off',
'vue/script-setup-uses-vars': 'error',
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
},
parserOptions: {
parser: '@typescript-eslint/parser', // the typescript-parser for eslint, instead of tslint
sourceType: 'module', // allow the use of imports statements
ecmaVersion: 2020, // allow the parsing of modern ecmascript
},
extends: '@feltcoop',
};
6 changes: 5 additions & 1 deletion .prettierrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@
"singleQuote": true,
"rubyPlugins": "plugin/single_quotes,plugin/trailing_comma",
"printWidth": 80,
"tabWidth": 2
"tabWidth": 2,
"svelteAllowShorthand": true,
"svelteSortOrder": "scripts-markup-styles",
"svelteStrictMode": false,
"plugins": ["prettier-plugin-svelte"]
}
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ Style/StringLiterals:
Style/StringLiteralsInInterpolation:
EnforcedStyle: single_quotes

Style/DisableCopsWithinSourceCodeDirective:
Enabled: false

### Metrics

Metrics/BlockLength:
Expand Down
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"**/node_modules": true
},

"eslint.validate": ["javascript", "vue"],
"eslint.validate": ["javascript"],

"ruby.lint": {
"rubocop": {
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ FROM ghcr.io/ledermann/rails-base-builder:3.1.2-alpine AS Builder

# Remove some files not needed in resulting image.
# Because they are required for building the image, they can't be added to .dockerignore
RUN rm -r package.json yarn.lock tsconfig.json tailwind.config.js postcss.config.js vite.config.ts
RUN rm -r package.json yarn.lock tsconfig.json tailwind.config.js postcss.config.js vite.config.ts svelte.config.js cypress.config.ts

FROM ghcr.io/ledermann/rails-base-final:3.1.2-alpine
LABEL maintainer="[email protected]"
Expand Down
6 changes: 6 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ gem 'rails', '~> 7.0.3'
# Use Vite in Rails and bring joy to your JavaScript experience (https://github.com/ElMassimo/vite_ruby)
gem 'vite_rails'

# Inertia adapter for Rails (https://github.com/inertiajs/inertia-rails)
gem 'inertia_rails'

# Pg is the Ruby interface to the PostgreSQL RDBMS (https://github.com/ged/ruby-pg)
gem 'pg', '~> 1.1'

Expand Down Expand Up @@ -39,6 +42,9 @@ gem 'rack-brotli'
# Simple, efficient background processing for Ruby (https://sidekiq.org)
gem 'sidekiq'

# Brings Rails named routes to javascript (http://github.com/railsware/js-routes)
gem 'js-routes'

group :development, :test do
# Debugging functionality for Ruby (https://github.com/ruby/debug)
gem 'debug', platforms: %i[mri mingw x64_mingw]
Expand Down
6 changes: 6 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,13 @@ GEM
honeybadger (4.12.1)
i18n (1.10.0)
concurrent-ruby (~> 1.0)
inertia_rails (1.12.1)
rails (>= 5)
io-console (0.5.11)
irb (1.4.1)
reline (>= 0.3.0)
js-routes (2.2.4)
railties (>= 4)
listen (3.7.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
Expand Down Expand Up @@ -323,6 +327,8 @@ DEPENDENCIES
dotenv-rails
guard-rspec
honeybadger
inertia_rails
js-routes
lograge
pg (~> 1.1)
prettier_print
Expand Down
81 changes: 38 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
[![Build Status](https://github.com/ledermann/templatus/workflows/CI/badge.svg)](https://github.com/ledermann/templatus/actions)
[![Build Status](https://github.com/ledermann/templatus-inertia/workflows/CI/badge.svg)](https://github.com/ledermann/templatus-inertia/actions)
[![Cypress](https://img.shields.io/endpoint?url=https://dashboard.cypress.io/badge/simple/5d6bqs&style=flat-square&logo=cypress)](https://dashboard.cypress.io/projects/5d6bqs/runs)

# Templatus (Vue edition)
# Templatus (Inertia edition)

Templatus is an opinionated template to build web applications with Ruby on Rails and Vue.js 3. It simplifies the process of setting up a new application while following best practices.
Templatus is an opinionated template to build web applications with Ruby on Rails and Inertia (using Svelte.js). It simplifies the process of setting up a new application while following best practices.

Live demo available at https://templatus.ledermann.dev
Live demo available at https://templatus-inertia.ledermann.dev

There is a sister repository that uses Hotwire instead of Vue.js:
There are two sister repositories:

Using Hotwire instead of Inertia (the "DHH way"):
https://github.com/ledermann/templatus-hotwire/

Using API instead of Inertia and Svelte.js instead of Vue.js;
https://github.com/ledermann/templatus/

## Features / Technology stack

### Backend
Expand All @@ -23,13 +28,10 @@ https://github.com/ledermann/templatus-hotwire/

### Frontend

- [Vite](https://vitejs.dev/) for bundling JavaScript and CSS with Hot Module Replacement (HMR) in development
- [Vue 3](https://v3.vuejs.org/) as frontend framework
- [Vue Router 4](https://next.router.vuejs.org/) for frontend routing
- [Pinia](https://pinia.esm.dev/) for frontend state management
- [Inertia](https://inertiajs.com/) as a protocol for frontend/backend communication
- [Tailwind CSS 3](https://tailwindcss.com/) to not have to write CSS at all
- [Heroicons](https://heroicons.com/) for SVG icons as Vue components
- [Rails Request.JS](https://github.com/rails/request.js) for AJAX requests with default headers
- [Tabler Icons](https://tablericons.com/) for free SVG icons
- [Vite](https://vitejs.dev/) for bundling JavaScript and CSS with Hot Module Replacement (HMR) in development

### Development

Expand All @@ -44,7 +46,6 @@ https://github.com/ledermann/templatus-hotwire/
- [RuboCop](https://rubocop.org/) for Ruby static code analysis
- [RSpec](https://rspec.info/) for Ruby testing
- [ESLint](https://eslint.org/) for TypeScript static code analysis
- [Jest](https://jestjs.io/) for TypeScript unit testing
- [Cypress](https://www.cypress.io/) for E2E testing

### Deployment
Expand All @@ -62,7 +63,6 @@ https://github.com/ledermann/templatus-hotwire/
- [Lograge](https://github.com/roidrage/lograge) for single-line logging
- Gzip and Brotli compression of dynamic responses (HTML, JSON) using [Rack::Deflater](https://github.com/rack/rack/blob/master/lib/rack/deflater.rb), [Rack::Brotli](https://github.com/marcotc/rack-brotli)
- Fine-tuned Content Security Policy (CSP)
- Ready for PWA (manifest, service-worker)

## Metrics

Expand All @@ -76,35 +76,30 @@ This template is developed with optimized performance and security in mind. The

### Security headers

[![Security headers](docs/security-headers.png)](https://securityheaders.com/?q=templatus.ledermann.dev&followRedirects=on)
[![Security headers](docs/security-headers.png)](https://securityheaders.com/?q=templatus-inertia.ledermann.dev&followRedirects=on)

What's the red _Permissions-Policy_ badge? This seems to be fixed with one of the next Rails update:
https://github.com/rails/rails/pull/41994

### Mozilla Observatory

[![Mozilla Observatory](docs/mozilla-observatory.png)](https://observatory.mozilla.org/analyze/templatus.ledermann.dev)
[![Mozilla Observatory](docs/mozilla-observatory.png)](https://observatory.mozilla.org/analyze/templatus-inertia.ledermann.dev)

### WebPageTest

[![WebPageTest](docs/web-page-test.png)](https://www.webpagetest.org/result/220603_AiDcD6_63C/)
[![WebPageTest](docs/web-page-test.png)](https://www.webpagetest.org/result/220530_BiDcQQ_83b754931114d99469b521e72731eb4d/)

### GTmetrix

[![GTmetrix](docs/GTmetrix.png)](https://gtmetrix.com/reports/templatus.ledermann.dev/OsUlXu9V/)
[![GTmetrix](docs/GTmetrix.png)](https://gtmetrix.com/reports/templatus-inertia.ledermann.dev/OsUlXu9V/)

### Check-your-website

[![Check-your-website](docs/check-your-website.png)](https://check-your-website.server-daten.de/?q=templatus.ledermann.dev)
[![Check-your-website](docs/check-your-website.png)](https://check-your-website.server-daten.de/?q=templatus-inertia.ledermann.dev)

### JavaScript size

147 KB of compiled JavaScript (after tree-shaking, minified & uncompressed). The largest parts are:

- Vue.js + Vue Router (75 KB)
- Honeybadger (23 KB)
- ActionCable (9 KB)
- Pinia (6 KB)
165 KB of compiled JavaScript (after tree-shaking, minified & uncompressed).

```
$ RAILS_ENV=production bin/rails assets:precompile
Expand All @@ -115,41 +110,41 @@ success Already up-to-date.
Building with Vite ⚡️
vite v2.9.10 building for production...
transforming...
298 modules transformed.
155 modules transformed.
rendering chunks...
../../public/vite/assets/logo.44ced38d.svg 0.48 KiB
../../public/vite/assets/logo.10f03a2b.svg 0.48 KiB
../../public/vite/manifest-assets.json 0.22 KiB
../../public/vite/manifest.json 0.32 KiB
../../public/vite/assets/application.46f860cd.js 16.18 KiB / gzip: 6.58 KiB
../../public/vite/assets/application.46f860cd.js.map 25.15 KiB
../../public/vite/assets/application.97255384.css 19.66 KiB / gzip: 4.69 KiB
../../public/vite/assets/vendor.bc768583.js 130.42 KiB / gzip: 48.90 KiB
../../public/vite/assets/vendor.bc768583.js.map 979.48 KiB
Build with Vite complete: /Users/ledermann/Projects/templatus/public/vite
../../public/vite/assets/application.5e23928d.js 27.28 KiB / gzip: 10.26 KiB
../../public/vite/assets/application.5e23928d.js.map 49.74 KiB
../../public/vite/assets/application.a344bc09.css 18.45 KiB / gzip: 4.47 KiB
../../public/vite/assets/vendor.b59622b9.js 137.75 KiB / gzip: 48.35 KiB
../../public/vite/assets/vendor.b59622b9.js.map 555.30 KiB
Build with Vite complete: /Users/ledermann/Projects/templatus-inertia/public/vite
```

### Network transfer

Small footprint: The demo application transfers only **62 KB** of data on the first visit.
Small footprint: The demo application transfers only **72 KB** of data on the first visit.

![Network](docs/network.png)

### Docker build time

With multi-stage building and using [DockerRailsBase](https://github.com/ledermann/docker-rails-base) the build of the Docker image takes very little time. Currently, the build job requires about 1,5 minutes on GitHub Actions (see https://github.com/ledermann/templatus/actions)
With multi-stage building and using [DockerRailsBase](https://github.com/ledermann/docker-rails-base) the build of the Docker image takes very little time. Currently, the `deploy` job requires about 1,5 minutes on GitHub Actions (see https://github.com/ledermann/templatus-inertia/actions)

### Docker image size

The Docker image is based on Alpine Linux and is optimized for minimal size (currently **113 MB** uncompressed disk size). It includes just the bare minimum - no build tools like Node.js, no JS sources (just the compiled assets), no tests.
The Docker image is based on Alpine Linux and is optimized for minimal size (currently **115 MB** uncompressed disk size). It includes just the bare minimum - no build tools like Node.js, no JS sources (just the compiled assets), no tests.

```
$ container-diff analyze ghcr.io/ledermann/templatus -n
$ container-diff analyze ghcr.io/ledermann/templatus-inertia -n
-----Size-----
Analysis for ghcr.io/ledermann/templatus:
IMAGE DIGEST SIZE
ghcr.io/ledermann/templatus sha256:... 114.9M
Analysis for ghcr.io/ledermann/templatus-inertia:
IMAGE DIGEST SIZE
ghcr.io/ledermann/templatus-inertia sha256:... 114.7M
```

## Getting started
Expand All @@ -159,8 +154,8 @@ ghcr.io/ledermann/templatus sha256:... 114.9M
1. Clone the repo locally:

```bash
git clone [email protected]:ledermann/templatus.git
cd templatus
git clone [email protected]:ledermann/templatus-inertia.git
cd templatus-inertia
```

2. Install PostgreSQL, Redis, and puma-dev (if not already present). On a Mac with Homebrew, run this to install from the `Brewfile`:
Expand All @@ -178,7 +173,7 @@ puma-dev link

# Use Vite via puma-dev proxy
# Adopted from https://github.com/puma/puma-dev#webpack-dev-server
echo 3036 > ~/.puma-dev/vite.templatus
echo 3036 > ~/.puma-dev/vite.templatus-inertia
```

4. Setup the application to install gems and NPM packages and create the database:
Expand All @@ -193,7 +188,7 @@ bin/setup
bin/dev
```

Then open https://templatus.test in your browser.
Then open https://templatus-inertia.test in your browser.

### Running linters

Expand Down
5 changes: 5 additions & 0 deletions app/controllers/about_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AboutController < ApplicationController
def show
render inertia: 'About/Show', props: { features: Features.new.data }
end
end
1 change: 1 addition & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
class ApplicationController < ActionController::Base
include InertiaErrors
end
9 changes: 3 additions & 6 deletions app/controllers/clicks_controller.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
class ClicksController < ApplicationController
def index
clicks = Click.order(created_at: :desc).limit(5).to_a
return unless stale?(clicks, template: false, public: true)

expires_in 0, must_revalidate: true

respond_to do |format|
format.json { render json: { total: Click.count, items: clicks } }
end
render inertia: 'Clicks/Index', props: { total: Click.count, items: clicks }
end

def create
click =
Click.create! user_agent: request.user_agent,
ip: anonymize(request.remote_ip)
ActionCable.server.broadcast 'clicks_channel', click

render inertia: 'Clicks/Index'
end

private
Expand Down
11 changes: 11 additions & 0 deletions app/controllers/concerns/inertia_errors.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require 'active_support/concern'

module InertiaErrors
extend ActiveSupport::Concern

included do
rescue_from ActiveRecord::RecordNotFound do
render inertia: 'Error/Show', props: { status: 404 }, status: :not_found
end
end
end
14 changes: 14 additions & 0 deletions app/controllers/exceptions_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class ExceptionsController < ActionController::Base
# Don't raise InvalidCrossOriginRequest for requesting not existing JavaScript file
skip_after_action :verify_same_origin_request

def show
status = request.path_info.delete_prefix('/').to_i

if request.format.html? || request.inertia?
render inertia: 'Error/Show', props: { status: }, status: status
else
head status
end
end
end
4 changes: 0 additions & 4 deletions app/controllers/vue_controller.rb

This file was deleted.

Loading

0 comments on commit 62246f7

Please sign in to comment.