├── src
├── api
│ └── .keep
├── esbuild_tailwind
│ └── .keep
├── importmap_tailwind
│ └── .keep
├── shared
│ ├── ci.rb
│ ├── packages.rb
│ ├── run_rubocop.rb
│ ├── gems_active_decorator.rb
│ ├── gems_active_interaction.rb
│ ├── docs.rb
│ ├── gems_pagy.rb
│ ├── gems_i18n_tasks.rb
│ ├── gems_lockbox.rb
│ ├── migrations_uuid.rb
│ ├── init_db_cli.rb
│ ├── git_init.rb
│ ├── init_i18n.rb
│ ├── init_generators.rb
│ ├── env_rubocop.rb
│ ├── yarnconfig.rb
│ ├── solid_queue_setup.rb
│ ├── general.rb
│ ├── gems_rspec.rb
│ ├── gems_anyway_config.rb
│ ├── staging_env.rb
│ └── gems_shrine.rb
├── classic_shared
│ ├── gems_erblint.rb
│ ├── helpers.rb
│ ├── routes.rb
│ ├── gems_better_html.rb
│ ├── app_static_pages.rb
│ ├── gems_view_component.rb
│ └── custom_error_pages.rb
└── inertia_shared
│ ├── init_generators.rb
│ ├── rspec_inertia.rb
│ ├── general.rb
│ ├── custom_error_pages.rb
│ ├── cleanup.rb
│ ├── gems_inertia.rb
│ └── finalize_layouts.rb
├── variants
├── shared
│ ├── .rubocop_todo.yml
│ ├── app
│ │ ├── decorators
│ │ │ └── .keep
│ │ ├── interactions
│ │ │ └── .keep
│ │ ├── uploaders
│ │ │ └── base_uploader.rb
│ │ ├── controllers
│ │ │ └── errors_controller.rb
│ │ ├── helpers
│ │ │ ├── view_component_helper.rb
│ │ │ └── active_link_to_helper.rb
│ │ └── views
│ │ │ └── errors
│ │ │ ├── unprocessable.html.erb
│ │ │ ├── not_found.html.erb
│ │ │ ├── unacceptable.html.erb
│ │ │ └── internal_server_error.html.erb
│ ├── config
│ │ ├── locales
│ │ │ ├── en.yml
│ │ │ └── ru.yml
│ │ ├── settings
│ │ │ ├── aws_config.rb
│ │ │ └── lockbox_config.rb
│ │ ├── aws.yml
│ │ ├── routes
│ │ │ └── errors.rb
│ │ ├── ci.rb
│ │ └── database.yml.tt
│ ├── .erdconfig
│ ├── .rspec
│ ├── spec
│ │ ├── support
│ │ │ ├── factory_bot.rb
│ │ │ ├── shoulda.rb
│ │ │ ├── time_helpers.rb
│ │ │ └── database_cleaner.rb
│ │ ├── spec_helper.rb
│ │ ├── requests
│ │ │ └── health_spec.rb
│ │ └── rails_helper.rb
│ ├── migrations
│ │ └── uuid.rb
│ ├── .rubocop_rails.yml
│ ├── Brewfile
│ ├── .rubocop_rspec.yml
│ ├── lib
│ │ └── tasks
│ │ │ └── factory_bot.rake
│ ├── .rubocop_strict.yml
│ ├── .editorconfig
│ └── .erb_lint.yml
├── esbuild_tailwind
│ ├── spec
│ │ ├── components
│ │ │ └── previews
│ │ │ │ └── .keep
│ │ └── better_html_spec.rb
│ ├── .yarnrc.yml
│ ├── config
│ │ ├── routes
│ │ │ ├── landing.rb
│ │ │ ├── pages.rb
│ │ │ ├── dev.rb
│ │ │ └── support.rb
│ │ └── routes.rb
│ ├── app
│ │ ├── helpers
│ │ │ └── application_helper.rb
│ │ └── views
│ │ │ ├── landing
│ │ │ ├── about.html.erb
│ │ │ └── home.html.erb
│ │ │ ├── pages
│ │ │ ├── privacy.html.erb
│ │ │ └── terms.html.erb
│ │ │ └── layouts
│ │ │ ├── component_preview.html.erb
│ │ │ ├── plain.html.erb
│ │ │ ├── landing.html.erb
│ │ │ └── application.html.erb
│ ├── README.md.tt
│ ├── .rubocop.yml
│ ├── lefthook.yml
│ ├── .gitignore
│ ├── mise.toml
│ └── Gemfile.tt
├── importmap_tailwind
│ ├── spec
│ │ ├── components
│ │ │ └── previews
│ │ │ │ └── .keep
│ │ └── better_html_spec.rb
│ ├── config
│ │ ├── routes
│ │ │ ├── landing.rb
│ │ │ ├── pages.rb
│ │ │ ├── dev.rb
│ │ │ └── support.rb
│ │ └── routes.rb
│ ├── app
│ │ ├── helpers
│ │ │ └── application_helper.rb
│ │ └── views
│ │ │ ├── landing
│ │ │ ├── about.html.erb
│ │ │ └── home.html.erb
│ │ │ ├── pages
│ │ │ ├── privacy.html.erb
│ │ │ └── terms.html.erb
│ │ │ └── layouts
│ │ │ ├── component_preview.html.erb
│ │ │ ├── plain.html.erb
│ │ │ ├── landing.html.erb
│ │ │ └── application.html.erb
│ ├── README.md.tt
│ ├── .rubocop.yml
│ ├── lefthook.yml
│ ├── .gitignore
│ ├── mise.toml
│ └── Gemfile.tt
├── inertia_react
│ ├── .yarnrc.yml
│ ├── config
│ │ ├── routes
│ │ │ ├── landing.rb
│ │ │ ├── pages.rb
│ │ │ └── support.rb
│ │ └── routes.rb
│ ├── app
│ │ ├── helpers
│ │ │ └── application_helper.rb
│ │ ├── frontend
│ │ │ ├── layouts
│ │ │ │ └── Layout.tsx
│ │ │ ├── pages
│ │ │ │ ├── About.tsx
│ │ │ │ ├── Home.tsx
│ │ │ │ ├── Terms.tsx
│ │ │ │ └── Privacy.tsx
│ │ │ └── lib
│ │ │ │ └── components
│ │ │ │ └── Flash.tsx
│ │ └── views
│ │ │ └── layouts
│ │ │ └── application.html.erb
│ ├── README.md.tt
│ ├── lefthook.yml
│ ├── .rubocop.yml
│ ├── .gitignore
│ ├── mise.toml
│ └── Gemfile.tt
├── inertia_vue
│ ├── .yarnrc.yml
│ ├── config
│ │ ├── routes
│ │ │ ├── landing.rb
│ │ │ ├── pages.rb
│ │ │ └── support.rb
│ │ └── routes.rb
│ ├── app
│ │ ├── helpers
│ │ │ └── application_helper.rb
│ │ ├── frontend
│ │ │ ├── layouts
│ │ │ │ └── Layout.vue
│ │ │ ├── pages
│ │ │ │ ├── About.vue
│ │ │ │ ├── Home.vue
│ │ │ │ ├── Terms.vue
│ │ │ │ └── Privacy.vue
│ │ │ └── lib
│ │ │ │ └── components
│ │ │ │ └── Flash.vue
│ │ └── views
│ │ │ └── layouts
│ │ │ └── application.html.erb
│ ├── README.md.tt
│ ├── lefthook.yml
│ ├── .rubocop.yml
│ ├── .gitignore
│ ├── mise.toml
│ └── Gemfile.tt
├── inertia_svelte
│ ├── .yarnrc.yml
│ ├── config
│ │ ├── routes
│ │ │ ├── landing.rb
│ │ │ ├── pages.rb
│ │ │ └── support.rb
│ │ └── routes.rb
│ ├── app
│ │ ├── helpers
│ │ │ └── application_helper.rb
│ │ ├── frontend
│ │ │ ├── layouts
│ │ │ │ └── Layout.svelte
│ │ │ ├── pages
│ │ │ │ ├── About.svelte
│ │ │ │ ├── Home.svelte
│ │ │ │ ├── Terms.svelte
│ │ │ │ └── Privacy.svelte
│ │ │ └── lib
│ │ │ │ └── components
│ │ │ │ └── Flash.svelte
│ │ └── views
│ │ │ └── layouts
│ │ │ └── application.html.erb
│ ├── README.md.tt
│ ├── lefthook.yml
│ ├── .rubocop.yml
│ ├── .gitignore
│ ├── mise.toml
│ └── Gemfile.tt
├── classic_shared
│ ├── config
│ │ └── routes
│ │ │ ├── landing.rb
│ │ │ ├── pages.rb
│ │ │ ├── dev.rb
│ │ │ └── support.rb
│ ├── app
│ │ ├── helpers
│ │ │ └── application_helper.rb
│ │ ├── controllers
│ │ │ ├── pages_controller.rb
│ │ │ └── landing_controller.rb
│ │ └── components
│ │ │ └── ui
│ │ │ ├── alert_component
│ │ │ └── alert_component.html.erb
│ │ │ └── alert_component.rb
│ └── spec
│ │ ├── support
│ │ ├── capybara.rb
│ │ └── view_component.rb
│ │ ├── components
│ │ ├── previews
│ │ │ └── ui
│ │ │ │ └── alert_component_preview.rb
│ │ └── ui
│ │ │ └── alert_component_spec.rb
│ │ └── requests
│ │ ├── landing_spec.rb
│ │ └── pages_spec.rb
├── api
│ ├── README.md.tt
│ ├── lefthook.yml
│ ├── .rubocop.yml
│ ├── .gitignore
│ ├── mise.toml
│ └── Gemfile.tt
└── inertia_shared
│ ├── config
│ ├── routes
│ │ ├── dev.rb
│ │ └── errors.rb
│ └── vite.json
│ ├── app
│ ├── controllers
│ │ └── errors_controller.rb
│ └── views
│ │ └── errors
│ │ ├── unprocessable.html.erb
│ │ ├── not_found.html.erb
│ │ ├── unacceptable.html.erb
│ │ └── internal_server_error.html.erb
│ └── spec
│ └── requests
│ ├── landing_spec.rb
│ └── pages_spec.rb
├── .rubocop.yml
├── .gitignore
├── LICENSE.md
├── api.rb
├── inertia_vue.rb
├── inertia_react.rb
├── inertia_svelte.rb
├── importmap_tailwind.rb
├── esbuild_tailwind.rb
└── TEMPLATES.md
/src/api/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/esbuild_tailwind/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/importmap_tailwind/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/variants/shared/.rubocop_todo.yml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/variants/shared/app/decorators/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/variants/shared/app/interactions/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/variants/shared/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | en:
2 |
--------------------------------------------------------------------------------
/variants/shared/config/locales/ru.yml:
--------------------------------------------------------------------------------
1 | ru:
2 |
--------------------------------------------------------------------------------
/variants/shared/.erdconfig:
--------------------------------------------------------------------------------
1 | filename: docs/erd
2 |
--------------------------------------------------------------------------------
/src/shared/ci.rb:
--------------------------------------------------------------------------------
1 | copy_file "config/ci.rb", force: true
2 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/spec/components/previews/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/variants/importmap_tailwind/spec/components/previews/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/shared/packages.rb:
--------------------------------------------------------------------------------
1 | template "Gemfile.tt", force: true
2 |
--------------------------------------------------------------------------------
/src/shared/run_rubocop.rb:
--------------------------------------------------------------------------------
1 | system("bundle exec rubocop -A")
2 |
--------------------------------------------------------------------------------
/variants/inertia_react/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | nodeLinker: node-modules
2 |
--------------------------------------------------------------------------------
/variants/inertia_vue/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | nodeLinker: node-modules
2 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | nodeLinker: node-modules
2 |
--------------------------------------------------------------------------------
/variants/inertia_svelte/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | nodeLinker: node-modules
2 |
--------------------------------------------------------------------------------
/src/classic_shared/gems_erblint.rb:
--------------------------------------------------------------------------------
1 | copy_file ".erb_lint.yml", force: true
2 |
--------------------------------------------------------------------------------
/src/shared/gems_active_decorator.rb:
--------------------------------------------------------------------------------
1 | directory "app/decorators", force: true
2 |
--------------------------------------------------------------------------------
/src/shared/gems_active_interaction.rb:
--------------------------------------------------------------------------------
1 | directory "app/interactions", force: true
2 |
--------------------------------------------------------------------------------
/variants/inertia_vue/config/routes/landing.rb:
--------------------------------------------------------------------------------
1 | get "/about", to: "landing#about"
2 |
--------------------------------------------------------------------------------
/variants/classic_shared/config/routes/landing.rb:
--------------------------------------------------------------------------------
1 | get "/about", to: "landing#about"
2 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/config/routes/landing.rb:
--------------------------------------------------------------------------------
1 | get "/about", to: "landing#about"
2 |
--------------------------------------------------------------------------------
/variants/inertia_react/config/routes/landing.rb:
--------------------------------------------------------------------------------
1 | get "/about", to: "landing#about"
2 |
--------------------------------------------------------------------------------
/variants/inertia_svelte/config/routes/landing.rb:
--------------------------------------------------------------------------------
1 | get "/about", to: "landing#about"
2 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | Style/StringLiterals:
2 | Enabled: true
3 | EnforcedStyle: double_quotes
4 |
--------------------------------------------------------------------------------
/variants/importmap_tailwind/config/routes/landing.rb:
--------------------------------------------------------------------------------
1 | get "/about", to: "landing#about"
2 |
--------------------------------------------------------------------------------
/src/classic_shared/helpers.rb:
--------------------------------------------------------------------------------
1 | copy_file "app/helpers/active_link_to_helper.rb", force: true
2 |
--------------------------------------------------------------------------------
/src/shared/docs.rb:
--------------------------------------------------------------------------------
1 | empty_directory "docs", force: true
2 | copy_file ".erdconfig", force: true
3 |
--------------------------------------------------------------------------------
/variants/shared/.rspec:
--------------------------------------------------------------------------------
1 | --require spec_helper
2 | --color
3 | --format documentation
4 | --fail-fast
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
3 | # IDE specific files
4 | .idea
5 | .ruby-lsp
6 | *.swp
7 | .vscode/settings.json
8 |
--------------------------------------------------------------------------------
/variants/classic_shared/config/routes/pages.rb:
--------------------------------------------------------------------------------
1 | get "/terms", to: "pages#terms"
2 | get "/privacy", to: "pages#privacy"
3 |
--------------------------------------------------------------------------------
/variants/inertia_react/config/routes/pages.rb:
--------------------------------------------------------------------------------
1 | get "/terms", to: "pages#terms"
2 | get "/privacy", to: "pages#privacy"
3 |
--------------------------------------------------------------------------------
/variants/inertia_svelte/config/routes/pages.rb:
--------------------------------------------------------------------------------
1 | get "/terms", to: "pages#terms"
2 | get "/privacy", to: "pages#privacy"
3 |
--------------------------------------------------------------------------------
/variants/inertia_vue/config/routes/pages.rb:
--------------------------------------------------------------------------------
1 | get "/terms", to: "pages#terms"
2 | get "/privacy", to: "pages#privacy"
3 |
--------------------------------------------------------------------------------
/variants/classic_shared/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | include BetterHtml::Helpers
3 | end
4 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/config/routes/pages.rb:
--------------------------------------------------------------------------------
1 | get "/terms", to: "pages#terms"
2 | get "/privacy", to: "pages#privacy"
3 |
--------------------------------------------------------------------------------
/variants/importmap_tailwind/config/routes/pages.rb:
--------------------------------------------------------------------------------
1 | get "/terms", to: "pages#terms"
2 | get "/privacy", to: "pages#privacy"
3 |
--------------------------------------------------------------------------------
/variants/inertia_react/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | include BetterHtml::Helpers
3 | end
4 |
--------------------------------------------------------------------------------
/variants/inertia_svelte/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | include BetterHtml::Helpers
3 | end
4 |
--------------------------------------------------------------------------------
/variants/inertia_vue/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | include BetterHtml::Helpers
3 | end
4 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | include BetterHtml::Helpers
3 | end
4 |
--------------------------------------------------------------------------------
/variants/importmap_tailwind/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | include BetterHtml::Helpers
3 | end
4 |
--------------------------------------------------------------------------------
/variants/shared/spec/support/factory_bot.rb:
--------------------------------------------------------------------------------
1 | RSpec.configure do |config|
2 | config.include FactoryBot::Syntax::Methods
3 | end
4 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/app/views/landing/about.html.erb:
--------------------------------------------------------------------------------
1 |
About us
2 | Find me in app/views/landing/about.html.erb
3 |
--------------------------------------------------------------------------------
/variants/importmap_tailwind/app/views/landing/about.html.erb:
--------------------------------------------------------------------------------
1 | About us
2 | Find me in app/views/landing/about.html.erb
3 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/app/views/pages/privacy.html.erb:
--------------------------------------------------------------------------------
1 | Privacy policy
2 | Find me in app/views/pages/privacy.html.erb
3 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/app/views/pages/terms.html.erb:
--------------------------------------------------------------------------------
1 | Terms of service
2 | Find me in app/views/pages/terms.html.erb
3 |
--------------------------------------------------------------------------------
/variants/importmap_tailwind/app/views/pages/privacy.html.erb:
--------------------------------------------------------------------------------
1 | Privacy policy
2 | Find me in app/views/pages/privacy.html.erb
3 |
--------------------------------------------------------------------------------
/variants/importmap_tailwind/app/views/pages/terms.html.erb:
--------------------------------------------------------------------------------
1 | Terms of service
2 | Find me in app/views/pages/terms.html.erb
3 |
--------------------------------------------------------------------------------
/src/shared/gems_pagy.rb:
--------------------------------------------------------------------------------
1 | # Download and install Pagy initializer
2 | get "https://ddnexus.github.io/pagy/gem/config/pagy.rb", "config/initializers/pagy.rb"
3 |
--------------------------------------------------------------------------------
/src/shared/gems_i18n_tasks.rb:
--------------------------------------------------------------------------------
1 | system("cp $(i18n-tasks gem-path)/templates/config/i18n-tasks.yml config/")
2 | system("cp $(i18n-tasks gem-path)/templates/rspec/i18n_spec.rb spec/")
3 |
--------------------------------------------------------------------------------
/variants/classic_shared/spec/support/capybara.rb:
--------------------------------------------------------------------------------
1 | require "capybara/rspec"
2 |
3 | RSpec.configure do |config|
4 | config.include Capybara::RSpecMatchers, type: :component
5 | end
6 |
--------------------------------------------------------------------------------
/src/shared/gems_lockbox.rb:
--------------------------------------------------------------------------------
1 | copy_file "config/settings/lockbox_config.rb", force: true
2 |
3 | initializer "lockbox.rb", <<-CODE
4 | Lockbox.master_key = LockboxConfig.master_key
5 | CODE
6 |
--------------------------------------------------------------------------------
/variants/shared/spec/support/shoulda.rb:
--------------------------------------------------------------------------------
1 | Shoulda::Matchers.configure do |config|
2 | config.integrate do |with|
3 | with.test_framework :rspec
4 | with.library :rails
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/variants/shared/migrations/uuid.rb:
--------------------------------------------------------------------------------
1 | class EnableUuidPsqlExtension < ActiveRecord::Migration[8.1]
2 | def change
3 | enable_extension "pgcrypto"
4 | enable_extension "uuid-ossp"
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/variants/classic_shared/app/controllers/pages_controller.rb:
--------------------------------------------------------------------------------
1 | class PagesController < ApplicationController
2 | layout "plain"
3 |
4 | def terms
5 | end
6 |
7 | def privacy
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/variants/shared/config/settings/aws_config.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class AwsConfig < ApplicationConfig
4 | attr_config :access_key_id, :secret_access_key, :s3_region, :s3_bucket
5 | end
6 |
--------------------------------------------------------------------------------
/variants/classic_shared/app/controllers/landing_controller.rb:
--------------------------------------------------------------------------------
1 | class LandingController < ApplicationController
2 | layout "landing"
3 |
4 | def home
5 | end
6 |
7 | def about
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/variants/inertia_react/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | draw :pages
3 | draw :landing
4 | draw :errors
5 | draw :dev
6 | draw :support
7 |
8 | root "landing#home"
9 | end
10 |
--------------------------------------------------------------------------------
/variants/inertia_vue/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | draw :pages
3 | draw :landing
4 | draw :errors
5 | draw :dev
6 | draw :support
7 |
8 | root "landing#home"
9 | end
10 |
--------------------------------------------------------------------------------
/src/classic_shared/routes.rb:
--------------------------------------------------------------------------------
1 | empty_directory "config/routes"
2 |
3 | copy_file "config/routes/dev.rb", force: true
4 | copy_file "config/routes/support.rb", force: true
5 | copy_file "config/routes.rb", force: true
6 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | draw :pages
3 | draw :landing
4 | draw :errors
5 | draw :dev
6 | draw :support
7 |
8 | root "landing#home"
9 | end
10 |
--------------------------------------------------------------------------------
/variants/importmap_tailwind/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | draw :pages
3 | draw :landing
4 | draw :errors
5 | draw :dev
6 | draw :support
7 |
8 | root "landing#home"
9 | end
10 |
--------------------------------------------------------------------------------
/variants/inertia_svelte/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | draw :pages
3 | draw :landing
4 | draw :errors
5 | draw :dev
6 | draw :support
7 |
8 | root "landing#home"
9 | end
10 |
--------------------------------------------------------------------------------
/variants/api/README.md.tt:
--------------------------------------------------------------------------------
1 | # <%= app_name %>
2 |
3 | ## Description
4 |
5 | To be defined
6 |
7 | ## Setup
8 |
9 | 1. Install [homebrew](https://brew.sh/) and install all required packages using `brew bundle`
10 |
--------------------------------------------------------------------------------
/variants/shared/spec/support/time_helpers.rb:
--------------------------------------------------------------------------------
1 | RSpec.configure do |config|
2 | # https://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html
3 | config.include ActiveSupport::Testing::TimeHelpers
4 | end
5 |
--------------------------------------------------------------------------------
/src/shared/migrations_uuid.rb:
--------------------------------------------------------------------------------
1 | generate "migration EnableUuidPsqlExtension"
2 | uuid_migration_file = (Dir["db/migrate/*_enable_uuid_psql_extension.rb"]).first
3 | copy_file "migrations/uuid.rb", uuid_migration_file, force: true
4 |
--------------------------------------------------------------------------------
/variants/inertia_shared/config/routes/dev.rb:
--------------------------------------------------------------------------------
1 | if Rails.env.development?
2 | namespace :dev do
3 | mount LetterOpenerWeb::Engine, at: "/letter_opener"
4 | mount MissionControl::Jobs::Engine, at: "/jobs"
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/variants/inertia_vue/README.md.tt:
--------------------------------------------------------------------------------
1 | # <%= app_name %>
2 |
3 | ## Description
4 |
5 | To be defined
6 |
7 | ## Setup
8 |
9 | 1. Install [homebrew](https://brew.sh/) and install all required packages using `brew bundle`
10 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/README.md.tt:
--------------------------------------------------------------------------------
1 | # <%= app_name %>
2 |
3 | ## Description
4 |
5 | To be defined
6 |
7 | ## Setup
8 |
9 | 1. Install [homebrew](https://brew.sh/) and install all required packages using `brew bundle`
10 |
--------------------------------------------------------------------------------
/variants/inertia_react/README.md.tt:
--------------------------------------------------------------------------------
1 | # <%= app_name %>
2 |
3 | ## Description
4 |
5 | To be defined
6 |
7 | ## Setup
8 |
9 | 1. Install [homebrew](https://brew.sh/) and install all required packages using `brew bundle`
10 |
--------------------------------------------------------------------------------
/variants/inertia_svelte/README.md.tt:
--------------------------------------------------------------------------------
1 | # <%= app_name %>
2 |
3 | ## Description
4 |
5 | To be defined
6 |
7 | ## Setup
8 |
9 | 1. Install [homebrew](https://brew.sh/) and install all required packages using `brew bundle`
10 |
--------------------------------------------------------------------------------
/variants/shared/.rubocop_rails.yml:
--------------------------------------------------------------------------------
1 | Rails/LexicallyScopedActionFilter:
2 | Enabled: false # we use BaseController
3 | Rails/UnknownEnv:
4 | Environments:
5 | - production
6 | - development
7 | - test
8 | - staging
9 |
--------------------------------------------------------------------------------
/variants/importmap_tailwind/README.md.tt:
--------------------------------------------------------------------------------
1 | # <%= app_name %>
2 |
3 | ## Description
4 |
5 | To be defined
6 |
7 | ## Setup
8 |
9 | 1. Install [homebrew](https://brew.sh/) and install all required packages using `brew bundle`
10 |
--------------------------------------------------------------------------------
/src/shared/init_db_cli.rb:
--------------------------------------------------------------------------------
1 | initializer "database_cli.rb", <<-CODE
2 | Rails.application.configure do
3 | if Rails.env.local?
4 | config.active_record.database_cli = {postgresql: "pgcli"}
5 | end
6 | end
7 | CODE
8 |
--------------------------------------------------------------------------------
/src/shared/git_init.rb:
--------------------------------------------------------------------------------
1 | return if ARGV.include?("--skip-git")
2 |
3 | unless system("git init")
4 | puts "Failed to initialize git repository"
5 | exit 1
6 | end
7 |
8 | system("git add .")
9 | system('git commit -m "Initial commit"')
10 |
--------------------------------------------------------------------------------
/src/shared/init_i18n.rb:
--------------------------------------------------------------------------------
1 | initializer "i18n.rb", <<-CODE
2 | I18n.load_path += Rails.root.glob("config/locales/**/*.{rb,yml}")
3 | I18n.default_locale = :en
4 | I18n.available_locales = %i[en ru]
5 | CODE
6 |
7 | directory "config/locales", force: true
8 |
--------------------------------------------------------------------------------
/src/shared/init_generators.rb:
--------------------------------------------------------------------------------
1 | initializer "generators.rb", <<-CODE
2 | Rails.application.config.generators do |g|
3 | g.orm :active_record, primary_key_type: :uuid
4 | g.helper false
5 | g.test_framework :rspec, view_specs: false
6 | end
7 | CODE
8 |
--------------------------------------------------------------------------------
/variants/classic_shared/spec/components/previews/ui/alert_component_preview.rb:
--------------------------------------------------------------------------------
1 | class UI::AlertComponentPreview < Lookbook::Preview
2 | def alert
3 | flash = {notice: "Notification message!"}
4 | render UI::AlertComponent.new(flash:)
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/variants/classic_shared/config/routes/dev.rb:
--------------------------------------------------------------------------------
1 | if Rails.env.development?
2 | namespace :dev do
3 | mount LetterOpenerWeb::Engine, at: "/letter_opener"
4 | mount MissionControl::Jobs::Engine, at: "/jobs"
5 | mount Lookbook::Engine, at: "/lookbook"
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/variants/classic_shared/spec/support/view_component.rb:
--------------------------------------------------------------------------------
1 | require "view_component/test_helpers"
2 |
3 | RSpec.configure do |config|
4 | config.include ViewComponent::TestHelpers, type: :component
5 | config.include ViewComponent::SystemTestHelpers, type: :component
6 | end
7 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/config/routes/dev.rb:
--------------------------------------------------------------------------------
1 | if Rails.env.development?
2 | namespace :dev do
3 | mount LetterOpenerWeb::Engine, at: "/letter_opener"
4 | mount MissionControl::Jobs::Engine, at: "/jobs"
5 | mount Lookbook::Engine, at: "/lookbook"
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/variants/importmap_tailwind/config/routes/dev.rb:
--------------------------------------------------------------------------------
1 | if Rails.env.development?
2 | namespace :dev do
3 | mount LetterOpenerWeb::Engine, at: "/letter_opener"
4 | mount MissionControl::Jobs::Engine, at: "/jobs"
5 | mount Lookbook::Engine, at: "/lookbook"
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/variants/inertia_vue/app/frontend/layouts/Layout.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/variants/shared/config/aws.yml:
--------------------------------------------------------------------------------
1 | default: &default
2 | access_key_id: "000"
3 | secret_access_key: "000"
4 | s3_region: "local"
5 | s3_bucket: "uploads"
6 |
7 | development:
8 | <<: *default
9 |
10 | test:
11 | <<: *default
12 |
13 | production:
14 | <<: *default
15 |
--------------------------------------------------------------------------------
/src/shared/env_rubocop.rb:
--------------------------------------------------------------------------------
1 | # Uncomment the RuboCop autocorrection line in development.rb
2 | gsub_file "config/environments/development.rb",
3 | "# config.generators.apply_rubocop_autocorrect_after_generate!",
4 | "config.generators.apply_rubocop_autocorrect_after_generate!"
5 |
--------------------------------------------------------------------------------
/variants/inertia_react/config/routes/support.rb:
--------------------------------------------------------------------------------
1 | get "up", to: "rails/health#show", as: :rails_health_check
2 |
3 | # Render dynamic PWA files from app/views/pwa/*
4 | get "service-worker", to: "rails/pwa#service_worker", as: :pwa_service_worker
5 | get "manifest", to: "rails/pwa#manifest", as: :pwa_manifest
6 |
--------------------------------------------------------------------------------
/variants/inertia_vue/config/routes/support.rb:
--------------------------------------------------------------------------------
1 | get "up", to: "rails/health#show", as: :rails_health_check
2 |
3 | # Render dynamic PWA files from app/views/pwa/*
4 | get "service-worker", to: "rails/pwa#service_worker", as: :pwa_service_worker
5 | get "manifest", to: "rails/pwa#manifest", as: :pwa_manifest
6 |
--------------------------------------------------------------------------------
/variants/classic_shared/config/routes/support.rb:
--------------------------------------------------------------------------------
1 | get "up", to: "rails/health#show", as: :rails_health_check
2 |
3 | # Render dynamic PWA files from app/views/pwa/*
4 | get "service-worker", to: "rails/pwa#service_worker", as: :pwa_service_worker
5 | get "manifest", to: "rails/pwa#manifest", as: :pwa_manifest
6 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/config/routes/support.rb:
--------------------------------------------------------------------------------
1 | get "up", to: "rails/health#show", as: :rails_health_check
2 |
3 | # Render dynamic PWA files from app/views/pwa/*
4 | get "service-worker", to: "rails/pwa#service_worker", as: :pwa_service_worker
5 | get "manifest", to: "rails/pwa#manifest", as: :pwa_manifest
6 |
--------------------------------------------------------------------------------
/variants/importmap_tailwind/config/routes/support.rb:
--------------------------------------------------------------------------------
1 | get "up", to: "rails/health#show", as: :rails_health_check
2 |
3 | # Render dynamic PWA files from app/views/pwa/*
4 | get "service-worker", to: "rails/pwa#service_worker", as: :pwa_service_worker
5 | get "manifest", to: "rails/pwa#manifest", as: :pwa_manifest
6 |
--------------------------------------------------------------------------------
/variants/inertia_svelte/config/routes/support.rb:
--------------------------------------------------------------------------------
1 | get "up", to: "rails/health#show", as: :rails_health_check
2 |
3 | # Render dynamic PWA files from app/views/pwa/*
4 | get "service-worker", to: "rails/pwa#service_worker", as: :pwa_service_worker
5 | get "manifest", to: "rails/pwa#manifest", as: :pwa_manifest
6 |
--------------------------------------------------------------------------------
/variants/inertia_react/app/frontend/layouts/Layout.tsx:
--------------------------------------------------------------------------------
1 | import Flash from '$lib/components/Flash';
2 |
3 | export default function Layout({ children }: { children: React.ReactNode }) {
4 | return (
5 |
6 |
7 | {children}
8 |
9 | );
10 | }
11 |
--------------------------------------------------------------------------------
/variants/shared/config/routes/errors.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Custom error pages routes
4 | match "/404", to: "errors#not_found", via: :all
5 | match "/406", to: "errors#unacceptable", via: :all
6 | match "/422", to: "errors#unprocessable", via: :all
7 | match "/500", to: "errors#internal_server_error", via: :all
8 |
--------------------------------------------------------------------------------
/variants/inertia_shared/config/routes/errors.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Custom error pages routes
4 | match "/404", to: "errors#not_found", via: :all
5 | match "/406", to: "errors#unacceptable", via: :all
6 | match "/422", to: "errors#unprocessable", via: :all
7 | match "/500", to: "errors#internal_server_error", via: :all
8 |
--------------------------------------------------------------------------------
/variants/shared/Brewfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | brew "act"
4 | brew "gh"
5 | brew "imagemagick"
6 | brew "jq"
7 | brew "lefthook"
8 | brew "mise"
9 | brew "neovim"
10 | brew "osv-scanner"
11 | brew "pgcli"
12 | brew "vips"
13 | brew "xh"
14 | brew "zellij"
15 |
16 | cask "ghostty"
17 | cask "postgres-unofficial"
18 |
--------------------------------------------------------------------------------
/variants/shared/spec/support/database_cleaner.rb:
--------------------------------------------------------------------------------
1 | RSpec.configure do |config|
2 | config.before(:suite) do
3 | DatabaseCleaner.strategy = :transaction
4 | DatabaseCleaner.clean_with(:truncation)
5 | end
6 |
7 | config.around do |example|
8 | DatabaseCleaner.cleaning do
9 | example.run
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/variants/shared/.rubocop_rspec.yml:
--------------------------------------------------------------------------------
1 | RSpec/ImplicitExpect:
2 | Enabled: false
3 | RSpec/ContextWording:
4 | Enabled: false
5 | RSpec/ExampleWording:
6 | Enabled: false
7 | RSpec/NestedGroups:
8 | AllowedGroups: [get, post, patch, delete, path, response]
9 | RSpec/ExampleLength:
10 | Max: 20
11 | RSpec/MultipleExpectations:
12 | Max: 5
13 |
--------------------------------------------------------------------------------
/variants/inertia_svelte/app/frontend/layouts/Layout.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 | {@render children()}
14 |
15 |
--------------------------------------------------------------------------------
/variants/shared/app/uploaders/base_uploader.rb:
--------------------------------------------------------------------------------
1 | class BaseUploader < Shrine
2 | def generate_location(io, record: nil, derivative: nil, **)
3 | return super unless record
4 |
5 | table = record.class.table_name
6 | id = record.id
7 | prefix = derivative || "original"
8 |
9 | "uploads/#{table}/#{id}/#{prefix}-#{super}"
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/variants/inertia_shared/config/vite.json:
--------------------------------------------------------------------------------
1 | {
2 | "all": {
3 | "sourceCodeDir": "app/frontend",
4 | "watchAdditionalPaths": []
5 | },
6 | "development": {
7 | "autoBuild": true,
8 | "publicOutputDir": "vite-dev",
9 | "port": 3036
10 | },
11 | "test": {
12 | "autoBuild": true,
13 | "publicOutputDir": "vite-test",
14 | "port": 3037
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/variants/shared/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | RSpec.configure do |config|
2 | config.expect_with :rspec do |expectations|
3 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true
4 | end
5 |
6 | config.mock_with :rspec do |mocks|
7 | mocks.verify_partial_doubles = true
8 | end
9 |
10 | config.shared_context_metadata_behavior = :apply_to_host_groups
11 | end
12 |
--------------------------------------------------------------------------------
/variants/shared/config/settings/lockbox_config.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class LockboxConfig < ApplicationConfig
4 | attr_config :master_key
5 |
6 | def master_key
7 | if Rails.env.production?
8 | Rails.application.credentials.lockbox[:master_key]
9 | else
10 | "0000000000000000000000000000000000000000000000000000000000000000"
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/variants/classic_shared/spec/components/ui/alert_component_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe UI::AlertComponent, type: :component do
4 | before do
5 | flash = {notice: "Notification message!"}
6 | render_inline(described_class.new(flash:))
7 | end
8 |
9 | it "renders flash notification" do
10 | expect(page).to have_text("Notification message!")
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/src/classic_shared/gems_better_html.rb:
--------------------------------------------------------------------------------
1 | copy_file "spec/better_html_spec.rb", force: true
2 | copy_file "app/helpers/application_helper.rb", force: true
3 |
4 | initializer "better_html.rb", <<-CODE
5 | BetterHtml.configure do |config|
6 | config.template_exclusion_filter = proc { |filename| !filename.start_with?(Rails.root.to_s) }
7 | config.partial_tag_name_pattern = /\\A[a-zA-Z0-9\\-:]+\\z/
8 | end
9 | CODE
10 |
--------------------------------------------------------------------------------
/variants/shared/lib/tasks/factory_bot.rake:
--------------------------------------------------------------------------------
1 | namespace :factory_bot do
2 | desc "Verify that all FactoryBot factories are valid"
3 | task lint: :environment do
4 | if Rails.env.test?
5 | DatabaseCleaner.cleaning do
6 | FactoryBot.lint
7 | end
8 | else
9 | system("bundle exec rake factory_bot:lint RAILS_ENV='test'")
10 | raise if $?.exitstatus.nonzero?
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/src/inertia_shared/init_generators.rb:
--------------------------------------------------------------------------------
1 | # Configure generators for Inertia - skip JavaScript, assets, and helpers
2 | application do
3 | <<-RUBY
4 | config.generators do |g|
5 | g.orm :active_record, primary_key_type: :uuid
6 | g.test_framework :rspec, view_specs: false
7 | g.assets false
8 | g.helper false
9 | g.javascripts false
10 | g.stylesheets false
11 | end
12 | RUBY
13 | end
14 |
--------------------------------------------------------------------------------
/variants/classic_shared/app/components/ui/alert_component/alert_component.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <% unless @flash.empty? %>
3 | <% @flash.each do |key, value| %>
4 | <% next unless value.is_a? String %>
5 |
6 | <%= sanitize(value) %>
7 |
8 | <% end %>
9 | <% end %>
10 |
11 |
--------------------------------------------------------------------------------
/variants/shared/.rubocop_strict.yml:
--------------------------------------------------------------------------------
1 | Lint/Debugger: # don't leave binding.pry or debugger
2 | Enabled: true
3 | Exclude: []
4 |
5 | Rails/Output: # Don't leave puts-debugging
6 | Enabled: true
7 | Exclude: []
8 |
9 | Rails/FindEach: # each could severely affect the performance, use find_each
10 | Enabled: true
11 | Exclude: []
12 |
13 | Rails/UniqBeforePluck: # uniq.pluck and not pluck.uniq
14 | Enabled: true
15 | Exclude: []
16 |
--------------------------------------------------------------------------------
/variants/shared/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
14 | [*.{rb,rake}]
15 | indent_size = 2
16 |
17 | [*.{js,ts,jsx,tsx,vue,svelte}]
18 | indent_size = 2
19 |
20 | [*.{css,scss,sass,less}]
21 | indent_size = 2
22 |
23 | [*.{json,yml,yaml}]
24 | indent_size = 2
--------------------------------------------------------------------------------
/variants/shared/spec/requests/health_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe "Health", type: :request do
4 | describe "GET /up" do
5 | it "returns http success" do
6 | get "/up"
7 | expect(response).to have_http_status(:success)
8 | end
9 |
10 | it "returns ok status" do
11 | get "/up"
12 | expect(response.body).to eq('')
13 | end
14 | end
15 | end
--------------------------------------------------------------------------------
/variants/classic_shared/spec/requests/landing_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe "Landing", type: :request do
4 | describe "GET /home" do
5 | it "returns http success" do
6 | get "/"
7 | expect(response).to have_http_status(:success)
8 | end
9 | end
10 |
11 | describe "GET /about" do
12 | it "returns http success" do
13 | get "/about"
14 | expect(response).to have_http_status(:success)
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/src/classic_shared/app_static_pages.rb:
--------------------------------------------------------------------------------
1 | copy_file "app/controllers/pages_controller.rb", force: true
2 | copy_file "app/controllers/landing_controller.rb", force: true
3 |
4 | copy_file "config/routes/pages.rb", force: true
5 | copy_file "config/routes/landing.rb", force: true
6 | copy_file "spec/requests/pages_spec.rb", force: true
7 | copy_file "spec/requests/landing_spec.rb", force: true
8 | directory "app/views/pages", force: true
9 | directory "app/views/landing", force: true
10 |
--------------------------------------------------------------------------------
/variants/classic_shared/spec/requests/pages_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe "Pages", type: :request do
4 | describe "GET /terms" do
5 | it "returns http success" do
6 | get "/terms"
7 | expect(response).to have_http_status(:success)
8 | end
9 | end
10 |
11 | describe "GET /privacy" do
12 | it "returns http success" do
13 | get "/privacy"
14 | expect(response).to have_http_status(:success)
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/variants/shared/.erb_lint.yml:
--------------------------------------------------------------------------------
1 | ---
2 | EnableDefaultLinters: true
3 | linters:
4 | TrailingWhitespace:
5 | enabled: false
6 | SelfClosingTag:
7 | enabled: false
8 | ParserErrors:
9 | enabled: true
10 | SpaceInHtmlTag:
11 | enabled: true
12 | AllowedScriptType:
13 | enabled: true
14 | allowed_types:
15 | - "text/javascript"
16 | - "text/x-tmpl"
17 | - "application/ld+json"
18 | allow_blank: true
19 | disallow_inline_scripts: false
20 |
--------------------------------------------------------------------------------
/variants/shared/app/controllers/errors_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ErrorsController < ApplicationController
4 | layout "application"
5 |
6 | def not_found
7 | render status: :not_found
8 | end
9 |
10 | def unprocessable
11 | render status: :unprocessable_entity
12 | end
13 |
14 | def unacceptable
15 | render status: :not_acceptable
16 | end
17 |
18 | def internal_server_error
19 | render status: :internal_server_error
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/variants/inertia_shared/app/controllers/errors_controller.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class ErrorsController < ApplicationController
4 | layout "application"
5 |
6 | def not_found
7 | render status: :not_found
8 | end
9 |
10 | def unprocessable
11 | render status: :unprocessable_entity
12 | end
13 |
14 | def unacceptable
15 | render status: :not_acceptable
16 | end
17 |
18 | def internal_server_error
19 | render status: :internal_server_error
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/variants/importmap_tailwind/app/views/layouts/component_preview.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= csrf_meta_tags %>
7 | <%= csp_meta_tag %>
8 |
9 | <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
10 | <%= javascript_importmap_tags %>
11 |
12 |
13 |
14 | <%= yield %>
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/shared/yarnconfig.rb:
--------------------------------------------------------------------------------
1 | # Configure Yarn with corepack
2 | copy_file ".yarnrc.yml", force: true
3 | system("yarn set version stable")
4 |
5 | # Configure devcontainer to use corepack instead of APT for Yarn installation
6 | dc_file = ".devcontainer/devcontainer.json"
7 | if File.exist?(dc_file)
8 | gsub_file dc_file,
9 | '"ghcr.io/devcontainers/features/node:1": {},',
10 | <<-CODE
11 | "ghcr.io/devcontainers/features/node:1": {
12 | "installYarnUsingApt": false
13 | },
14 | CODE
15 | end
16 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/app/views/layouts/component_preview.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= csrf_meta_tags %>
7 | <%= csp_meta_tag %>
8 |
9 | <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
10 | <%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module" %>
11 |
12 |
13 |
14 | <%= yield %>
15 |
16 |
17 |
--------------------------------------------------------------------------------
/variants/classic_shared/app/components/ui/alert_component.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | class UI::AlertComponent < ViewComponent::Base
4 | def initialize(flash:)
5 | @flash = flash
6 | @flash_class = {
7 | notice: "text-blue-800 bg-blue-50 dark:bg-gray-800 dark:text-blue-400",
8 | success: "text-green-800 bg-green-50 dark:bg-gray-800 dark:text-green-400",
9 | error: "text-red-800 bg-red-50 dark:bg-gray-800 dark:text-red-400",
10 | alert: "text-yellow-800 bg-yellow-50 dark:bg-gray-800 dark:text-yellow-300"
11 | }
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/variants/api/lefthook.yml:
--------------------------------------------------------------------------------
1 | pre-push:
2 | parallel: true
3 | commands:
4 | gems-audit:
5 | tags: backend security
6 | run: bundle-audit check --update
7 |
8 | pre-commit:
9 | parallel: true
10 | commands:
11 | rspec:
12 | tags: backend frontend test
13 | run: bundle exec rspec
14 | rubocop:
15 | tags: backend codestyle
16 | run: bundle exec rubocop -A
17 | brakeman:
18 | tags: backend security
19 | run: bundle exec brakeman --no-pager
20 | zeitwerk:
21 | tags: backend
22 | run: bundle exec rails zeitwerk:check
23 |
--------------------------------------------------------------------------------
/variants/shared/app/helpers/view_component_helper.rb:
--------------------------------------------------------------------------------
1 | module ViewComponentHelper
2 | # Helper for view_component gem
3 | # can be used with
4 | # <%= component "example", title: "Hello World!" %>
5 | # or for namespaced components
6 | # <%= component "way_down/we_go/example", title: "Hello World!" %>
7 | # taken with small improvements from
8 | # https://evilmartians.com/chronicles/viewcomponent-in-the-wild-supercharging-your-components
9 | def component(name, *, **, &)
10 | component = "#{name.to_s.camelize}Component".constantize
11 | render(component.new(*, **), &)
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/src/inertia_shared/rspec_inertia.rb:
--------------------------------------------------------------------------------
1 | # Configure RSpec for Inertia Rails testing
2 | # Inject the inertia_rails/rspec require into rails_helper.rb
3 |
4 | inject_into_file "spec/rails_helper.rb", after: "require 'rspec/rails'\n" do
5 | <<-RUBY
6 | require 'inertia_rails/rspec'
7 | # Load Vite Ruby helpers for test environment (needed for vite_stylesheet_tag, etc.)
8 | require 'vite_ruby'
9 | RUBY
10 | end
11 |
12 | # Copy Inertia request specs
13 | copy_file "spec/requests/landing_spec.rb", force: true
14 | copy_file "spec/requests/pages_spec.rb", force: true
15 |
16 | say "✓ Inertia RSpec configuration added", :green
17 |
--------------------------------------------------------------------------------
/variants/inertia_vue/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%= MainConfig.app_name %>
5 |
6 |
7 | <%= csrf_meta_tags %>
8 | <%= csp_meta_tag %>
9 |
10 | <%= yield :head %>
11 | <%= vite_stylesheet_tag "application" %>
12 | <%= vite_client_tag %>
13 | <%= vite_typescript_tag "inertia.ts" %>
14 | <%= inertia_ssr_head %>
15 |
16 |
17 |
18 | <%= yield %>
19 |
20 |
21 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/spec/better_html_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "rails_helper"
4 |
5 | RSpec.describe "BetterHtml" do
6 | it "does assert that all .html.erb templates are parseable" do # rubocop:disable RSpec/ExampleLength
7 | erb_glob = Rails.root.join(
8 | "app/views/**/{*.htm,*.html,*.htm.erb,*.html.erb,*.html+*.erb}"
9 | )
10 |
11 | Dir[erb_glob].each do |filename|
12 | data = File.read(filename)
13 | expect {
14 | BetterHtml::BetterErb::ErubiImplementation.new(data, filename:).validate!
15 | }.not_to raise_exception
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/variants/importmap_tailwind/spec/better_html_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "rails_helper"
4 |
5 | RSpec.describe "BetterHtml" do
6 | it "does assert that all .html.erb templates are parseable" do # rubocop:disable RSpec/ExampleLength
7 | erb_glob = Rails.root.join(
8 | "app/views/**/{*.htm,*.html,*.htm.erb,*.html.erb,*.html+*.erb}"
9 | )
10 |
11 | Dir[erb_glob].each do |filename|
12 | data = File.read(filename)
13 | expect {
14 | BetterHtml::BetterErb::ErubiImplementation.new(data, filename:).validate!
15 | }.not_to raise_exception
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/variants/inertia_svelte/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%= MainConfig.app_name %>
5 |
6 |
7 | <%= csrf_meta_tags %>
8 | <%= csp_meta_tag %>
9 |
10 | <%= yield :head %>
11 | <%= vite_stylesheet_tag "application" %>
12 | <%= vite_client_tag %>
13 | <%= vite_typescript_tag "inertia.ts" %>
14 | <%= inertia_ssr_head %>
15 |
16 |
17 |
18 | <%= yield %>
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/shared/solid_queue_setup.rb:
--------------------------------------------------------------------------------
1 | # Replace default database.yml with multi-database configuration
2 | template "config/database.yml.tt", "config/database.yml", force: true
3 |
4 | # Add solid_queue configuration to development.rb
5 | inject_into_file "config/environments/development.rb", before: /^end\s*$/ do
6 | <<-RUBY
7 |
8 | config.active_job.queue_adapter = :solid_queue
9 | config.solid_queue.connects_to = {database: {writing: :queue}}
10 | RUBY
11 | end
12 |
13 | # Add jobs process to Procfile.dev if it exists
14 | if File.exist?("Procfile.dev")
15 | append_to_file "Procfile.dev" do
16 | "jobs: bin/jobs start\n"
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/variants/shared/spec/rails_helper.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | ENV['RAILS_ENV'] ||= 'test'
3 | require_relative '../config/environment'
4 |
5 | abort("The Rails environment is running in production mode!") if Rails.env.production?
6 |
7 | require 'rspec/rails'
8 |
9 | Rails.root.glob('spec/support/**/*.rb').sort_by(&:to_s).each { |f| require f }
10 |
11 | begin
12 | ActiveRecord::Migration.maintain_test_schema!
13 | rescue ActiveRecord::PendingMigrationError => e
14 | abort e.to_s.strip
15 | end
16 |
17 | RSpec.configure do |config|
18 | config.filter_run_excluding skip: true # filter pending specs output
19 | config.filter_rails_from_backtrace!
20 | end
21 |
--------------------------------------------------------------------------------
/variants/inertia_react/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%= MainConfig.app_name %>
5 |
6 |
7 | <%= csrf_meta_tags %>
8 | <%= csp_meta_tag %>
9 |
10 | <%= yield :head %>
11 | <%= vite_stylesheet_tag "application" %>
12 | <%= vite_react_refresh_tag %>
13 | <%= vite_client_tag %>
14 | <%= vite_typescript_tag "inertia.tsx" %>
15 | <%= inertia_ssr_head %>
16 |
17 |
18 |
19 | <%= yield %>
20 |
21 |
22 |
--------------------------------------------------------------------------------
/variants/api/.rubocop.yml:
--------------------------------------------------------------------------------
1 | require:
2 | - standard
3 |
4 | plugins:
5 | - rubocop-rails
6 | - rubocop-rspec
7 | - rubocop-performance
8 | - rubocop-thread_safety
9 |
10 | inherit_from:
11 | - .rubocop_todo.yml
12 | - .rubocop_strict.yml
13 | - .rubocop_rspec.yml
14 | - .rubocop_rails.yml
15 |
16 | inherit_gem:
17 | standard: config/base.yml
18 |
19 | AllCops:
20 | NewCops: enable
21 | SuggestExtensions: false
22 | Exclude:
23 | - node_modules/**/*
24 | - public/**/*
25 | - vendor/**/*
26 | - bin/**/*
27 | - db/schema.rb
28 |
29 | Rails:
30 | Enabled: true
31 | RSpec:
32 | Enabled: true
33 | Performance:
34 | Enabled: true
35 |
--------------------------------------------------------------------------------
/variants/inertia_react/lefthook.yml:
--------------------------------------------------------------------------------
1 | pre-push:
2 | parallel: true
3 | commands:
4 | gems-audit:
5 | tags: backend security
6 | run: bundle-audit check --update
7 | node-audit:
8 | tags: frontend security
9 | run: yarn npm audit
10 |
11 | pre-commit:
12 | parallel: true
13 | commands:
14 | rspec:
15 | tags: backend frontend test
16 | run: bundle exec rspec
17 | rubocop:
18 | tags: backend codestyle
19 | run: bundle exec rubocop -A
20 | brakeman:
21 | tags: backend security
22 | run: bundle exec brakeman --no-pager
23 | zeitwerk:
24 | tags: backend
25 | run: bundle exec rails zeitwerk:check
26 |
--------------------------------------------------------------------------------
/variants/inertia_vue/lefthook.yml:
--------------------------------------------------------------------------------
1 | pre-push:
2 | parallel: true
3 | commands:
4 | gems-audit:
5 | tags: backend security
6 | run: bundle-audit check --update
7 | node-audit:
8 | tags: frontend security
9 | run: yarn npm audit
10 |
11 | pre-commit:
12 | parallel: true
13 | commands:
14 | rspec:
15 | tags: backend frontend test
16 | run: bundle exec rspec
17 | rubocop:
18 | tags: backend codestyle
19 | run: bundle exec rubocop -A
20 | brakeman:
21 | tags: backend security
22 | run: bundle exec brakeman --no-pager
23 | zeitwerk:
24 | tags: backend
25 | run: bundle exec rails zeitwerk:check
26 |
--------------------------------------------------------------------------------
/variants/inertia_svelte/lefthook.yml:
--------------------------------------------------------------------------------
1 | pre-push:
2 | parallel: true
3 | commands:
4 | gems-audit:
5 | tags: backend security
6 | run: bundle-audit check --update
7 | node-audit:
8 | tags: frontend security
9 | run: yarn npm audit
10 |
11 | pre-commit:
12 | parallel: true
13 | commands:
14 | rspec:
15 | tags: backend frontend test
16 | run: bundle exec rspec
17 | rubocop:
18 | tags: backend codestyle
19 | run: bundle exec rubocop -A
20 | brakeman:
21 | tags: backend security
22 | run: bundle exec brakeman --no-pager
23 | zeitwerk:
24 | tags: backend
25 | run: bundle exec rails zeitwerk:check
26 |
--------------------------------------------------------------------------------
/src/inertia_shared/general.rb:
--------------------------------------------------------------------------------
1 | copy_file ".editorconfig", force: true
2 | template "README.md", force: true
3 | copy_file ".gitignore", force: true
4 | copy_file "mise.toml", force: true
5 | copy_file "lefthook.yml", force: true
6 |
7 | copy_file ".rubocop.yml", force: true
8 | copy_file ".rubocop_rails.yml", force: true
9 | copy_file ".rubocop_rspec.yml", force: true
10 | copy_file ".rubocop_strict.yml", force: true
11 | copy_file ".rubocop_todo.yml", force: true
12 |
13 | # Copy vite.json early to prevent "file not found" errors during bundle install
14 | copy_file "config/vite.json", force: true
15 |
16 | # For Inertia templates, layouts will be copied at the end after Inertia generator runs
17 |
--------------------------------------------------------------------------------
/variants/inertia_vue/.rubocop.yml:
--------------------------------------------------------------------------------
1 | require:
2 | - standard
3 |
4 | plugins:
5 | - rubocop-rails
6 | - rubocop-rspec
7 | - rubocop-performance
8 | - rubocop-thread_safety
9 | - rubocop-capybara
10 |
11 | inherit_from:
12 | - .rubocop_todo.yml
13 | - .rubocop_strict.yml
14 | - .rubocop_rspec.yml
15 | - .rubocop_rails.yml
16 |
17 | inherit_gem:
18 | standard: config/base.yml
19 |
20 | AllCops:
21 | NewCops: enable
22 | SuggestExtensions: false
23 | Exclude:
24 | - node_modules/**/*
25 | - public/**/*
26 | - vendor/**/*
27 | - bin/**/*
28 | - db/schema.rb
29 |
30 | Rails:
31 | Enabled: true
32 | RSpec:
33 | Enabled: true
34 | Performance:
35 | Enabled: true
36 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/.rubocop.yml:
--------------------------------------------------------------------------------
1 | require:
2 | - standard
3 |
4 | plugins:
5 | - rubocop-rails
6 | - rubocop-rspec
7 | - rubocop-performance
8 | - rubocop-thread_safety
9 | - rubocop-capybara
10 |
11 | inherit_from:
12 | - .rubocop_todo.yml
13 | - .rubocop_strict.yml
14 | - .rubocop_rspec.yml
15 | - .rubocop_rails.yml
16 |
17 | inherit_gem:
18 | standard: config/base.yml
19 |
20 | AllCops:
21 | NewCops: enable
22 | SuggestExtensions: false
23 | Exclude:
24 | - node_modules/**/*
25 | - public/**/*
26 | - vendor/**/*
27 | - bin/**/*
28 | - db/schema.rb
29 |
30 | Rails:
31 | Enabled: true
32 | RSpec:
33 | Enabled: true
34 | Performance:
35 | Enabled: true
36 |
--------------------------------------------------------------------------------
/variants/inertia_react/.rubocop.yml:
--------------------------------------------------------------------------------
1 | require:
2 | - standard
3 |
4 | plugins:
5 | - rubocop-rails
6 | - rubocop-rspec
7 | - rubocop-performance
8 | - rubocop-thread_safety
9 | - rubocop-capybara
10 |
11 | inherit_from:
12 | - .rubocop_todo.yml
13 | - .rubocop_strict.yml
14 | - .rubocop_rspec.yml
15 | - .rubocop_rails.yml
16 |
17 | inherit_gem:
18 | standard: config/base.yml
19 |
20 | AllCops:
21 | NewCops: enable
22 | SuggestExtensions: false
23 | Exclude:
24 | - node_modules/**/*
25 | - public/**/*
26 | - vendor/**/*
27 | - bin/**/*
28 | - db/schema.rb
29 |
30 | Rails:
31 | Enabled: true
32 | RSpec:
33 | Enabled: true
34 | Performance:
35 | Enabled: true
36 |
--------------------------------------------------------------------------------
/variants/inertia_svelte/.rubocop.yml:
--------------------------------------------------------------------------------
1 | require:
2 | - standard
3 |
4 | plugins:
5 | - rubocop-rails
6 | - rubocop-rspec
7 | - rubocop-performance
8 | - rubocop-thread_safety
9 | - rubocop-capybara
10 |
11 | inherit_from:
12 | - .rubocop_todo.yml
13 | - .rubocop_strict.yml
14 | - .rubocop_rspec.yml
15 | - .rubocop_rails.yml
16 |
17 | inherit_gem:
18 | standard: config/base.yml
19 |
20 | AllCops:
21 | NewCops: enable
22 | SuggestExtensions: false
23 | Exclude:
24 | - node_modules/**/*
25 | - public/**/*
26 | - vendor/**/*
27 | - bin/**/*
28 | - db/schema.rb
29 |
30 | Rails:
31 | Enabled: true
32 | RSpec:
33 | Enabled: true
34 | Performance:
35 | Enabled: true
36 |
--------------------------------------------------------------------------------
/variants/importmap_tailwind/.rubocop.yml:
--------------------------------------------------------------------------------
1 | require:
2 | - standard
3 |
4 | plugins:
5 | - rubocop-rails
6 | - rubocop-rspec
7 | - rubocop-performance
8 | - rubocop-thread_safety
9 | - rubocop-capybara
10 |
11 | inherit_from:
12 | - .rubocop_todo.yml
13 | - .rubocop_strict.yml
14 | - .rubocop_rspec.yml
15 | - .rubocop_rails.yml
16 |
17 | inherit_gem:
18 | standard: config/base.yml
19 |
20 | AllCops:
21 | NewCops: enable
22 | SuggestExtensions: false
23 | Exclude:
24 | - node_modules/**/*
25 | - public/**/*
26 | - vendor/**/*
27 | - bin/**/*
28 | - db/schema.rb
29 |
30 | Rails:
31 | Enabled: true
32 | RSpec:
33 | Enabled: true
34 | Performance:
35 | Enabled: true
36 |
--------------------------------------------------------------------------------
/src/shared/general.rb:
--------------------------------------------------------------------------------
1 | copy_file ".editorconfig", force: true
2 | template "README.md", force: true
3 | copy_file ".gitignore", force: true
4 | copy_file "mise.toml", force: true
5 | copy_file "lefthook.yml", force: true
6 |
7 | copy_file ".rubocop.yml", force: true
8 | copy_file ".rubocop_rails.yml", force: true
9 | copy_file ".rubocop_rspec.yml", force: true
10 | copy_file ".rubocop_strict.yml", force: true
11 | copy_file ".rubocop_todo.yml", force: true
12 |
13 | if File.exist?("app/views/layouts/application.html.erb")
14 | copy_file "app/views/layouts/application.html.erb", force: true
15 | copy_file "app/views/layouts/landing.html.erb", force: true
16 | copy_file "app/views/layouts/plain.html.erb", force: true
17 | end
18 |
--------------------------------------------------------------------------------
/variants/inertia_shared/spec/requests/landing_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe "Landing", type: :request, inertia: true do
4 | describe "GET /" do
5 | it "returns http success" do
6 | get "/"
7 | expect(response).to have_http_status(:success)
8 | end
9 |
10 | it "renders the Home component" do
11 | get "/"
12 | expect(inertia.component).to eq("Home")
13 | end
14 | end
15 |
16 | describe "GET /about" do
17 | it "returns http success" do
18 | get "/about"
19 | expect(response).to have_http_status(:success)
20 | end
21 |
22 | it "renders the About component" do
23 | get "/about"
24 | expect(inertia.component).to eq("About")
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/variants/inertia_shared/spec/requests/pages_spec.rb:
--------------------------------------------------------------------------------
1 | require "rails_helper"
2 |
3 | RSpec.describe "Pages", type: :request, inertia: true do
4 | describe "GET /terms" do
5 | it "returns http success" do
6 | get "/terms"
7 | expect(response).to have_http_status(:success)
8 | end
9 |
10 | it "renders the Terms component" do
11 | get "/terms"
12 | expect(inertia.component).to eq("Terms")
13 | end
14 | end
15 |
16 | describe "GET /privacy" do
17 | it "returns http success" do
18 | get "/privacy"
19 | expect(response).to have_http_status(:success)
20 | end
21 |
22 | it "renders the Privacy component" do
23 | get "/privacy"
24 | expect(inertia.component).to eq("Privacy")
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/lefthook.yml:
--------------------------------------------------------------------------------
1 | pre-push:
2 | parallel: true
3 | commands:
4 | gems-audit:
5 | tags: backend security
6 | run: bundle-audit check --update
7 | node-audit:
8 | tags: frontend security
9 | run: yarn npm audit
10 |
11 | pre-commit:
12 | parallel: true
13 | commands:
14 | rspec:
15 | tags: backend frontend test
16 | run: bundle exec rspec
17 | rubocop:
18 | tags: backend codestyle
19 | run: bundle exec rubocop -A
20 | brakeman:
21 | tags: backend security
22 | run: bundle exec brakeman --no-pager
23 | zeitwerk:
24 | tags: backend
25 | run: bundle exec rails zeitwerk:check
26 | erb-lint:
27 | tags: erb-lint frontend
28 | run: bundle exec erb_lint --lint-all
29 |
--------------------------------------------------------------------------------
/variants/importmap_tailwind/lefthook.yml:
--------------------------------------------------------------------------------
1 | pre-push:
2 | parallel: true
3 | commands:
4 | gems-audit:
5 | tags: backend security
6 | run: bundle-audit check --update
7 | node-audit:
8 | tags: frontend security
9 | run: yarn npm audit
10 |
11 | pre-commit:
12 | parallel: true
13 | commands:
14 | rspec:
15 | tags: backend frontend test
16 | run: bundle exec rspec
17 | rubocop:
18 | tags: backend codestyle
19 | run: bundle exec rubocop -A
20 | brakeman:
21 | tags: backend security
22 | run: bundle exec brakeman --no-pager
23 | zeitwerk:
24 | tags: backend
25 | run: bundle exec rails zeitwerk:check
26 | erb-lint:
27 | tags: erb-lint frontend
28 | run: bundle exec erb_lint --lint-all
29 |
--------------------------------------------------------------------------------
/src/shared/gems_rspec.rb:
--------------------------------------------------------------------------------
1 | generate "rspec:install"
2 |
3 | # Copy only common spec files (not spec/components and spec/requests)
4 | copy_file ".rspec", force: true
5 | copy_file "spec/rails_helper.rb", force: true
6 | copy_file "spec/spec_helper.rb", force: true
7 | copy_file "spec/support/database_cleaner.rb", force: true
8 | copy_file "spec/support/factory_bot.rb", force: true
9 | copy_file "spec/support/shoulda.rb", force: true
10 | copy_file "spec/support/time_helpers.rb", force: true
11 | copy_file "spec/requests/health_spec.rb", force: true
12 | copy_file "lib/tasks/factory_bot.rake", force: true
13 |
14 | # Enable parallel_tests support in database.yml
15 | gsub_file "config/database.yml",
16 | /(test:.*?database: \w+)(_test)/m,
17 | "\\1\\2<%= ENV['TEST_ENV_NUMBER'] %>"
18 |
--------------------------------------------------------------------------------
/variants/api/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore bundler config.
2 | /.bundle
3 |
4 | # Ignore all environment files.
5 | /.env*
6 |
7 | # Ignore all logfiles and tempfiles.
8 | /log/*
9 | /tmp/*
10 | !/log/.keep
11 | !/tmp/.keep
12 |
13 | # Ignore pidfiles, but keep the directory.
14 | /tmp/pids/*
15 | !/tmp/pids/
16 | !/tmp/pids/.keep
17 |
18 | # Ignore storage (uploaded files in development and any SQLite databases).
19 | /storage/*
20 | !/storage/.keep
21 | /tmp/storage/*
22 | !/tmp/storage/
23 | !/tmp/storage/.keep
24 |
25 | /public/uploads
26 |
27 | # Ignore keys for decrypting credentials and more.
28 | /config/master.key
29 | /config/credentials/development.key
30 | /config/credentials/test.key
31 |
32 | .DS_Store
33 |
34 | # IDE specific files
35 | .idea
36 | .ruby-lsp
37 | *.swp
38 | .vscode/settings.json
39 | .vscode/rdbg_autoattach.json
40 |
--------------------------------------------------------------------------------
/variants/shared/config/ci.rb:
--------------------------------------------------------------------------------
1 | # Run using bin/ci
2 |
3 | CI.run do
4 | step "Setup", "bin/setup --skip-server"
5 |
6 | step "Style: Ruby", "bin/rubocop"
7 |
8 | step "Security: Gem audit", "bin/bundler-audit"
9 | step "Security: Brakeman code analysis", "bin/brakeman --quiet --no-pager --exit-on-warn --exit-on-error"
10 |
11 | step "Test: Run rspec tests", "bin/rails spec"
12 | step "Test: Test code loader", "bin/rails zeitwerk:check"
13 |
14 | # Optional: set a green GitHub commit status to unblock PR merge.
15 | # Requires the `gh` CLI and `gh extension install basecamp/gh-signoff`.
16 | # if success?
17 | # step "Signoff: All systems go. Ready for merge and deploy.", "gh signoff"
18 | # else
19 | # failure "Signoff: CI failed. Do not merge or deploy.", "Fix the issues and try again."
20 | # end
21 | end
22 |
--------------------------------------------------------------------------------
/variants/shared/app/views/errors/unprocessable.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Unprocessable entity
7 |
8 |
9 | The change you wanted was rejected.
10 |
11 |
12 |
13 | <%= link_to "Go back home", root_path, class: "inline-flex items-center px-4 py-2 border border-transparent text-base font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" %>
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/variants/shared/app/views/errors/not_found.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Page not found
7 |
8 |
9 | Sorry, we couldn't find the page you're looking for.
10 |
11 |
12 |
13 | <%= link_to "Go back home", root_path, class: "inline-flex items-center px-4 py-2 border border-transparent text-base font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" %>
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/variants/inertia_shared/app/views/errors/unprocessable.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Unprocessable entity
7 |
8 |
9 | The change you wanted was rejected.
10 |
11 |
12 |
13 | <%= link_to "Go back home", root_path, class: "inline-flex items-center px-4 py-2 border border-transparent text-base font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" %>
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/variants/inertia_shared/app/views/errors/not_found.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Page not found
7 |
8 |
9 | Sorry, we couldn't find the page you're looking for.
10 |
11 |
12 |
13 | <%= link_to "Go back home", root_path, class: "inline-flex items-center px-4 py-2 border border-transparent text-base font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" %>
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/variants/shared/app/views/errors/unacceptable.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Not acceptable
7 |
8 |
9 | The requested resource is not available in the requested format.
10 |
11 |
12 |
13 | <%= link_to "Go back home", root_path, class: "inline-flex items-center px-4 py-2 border border-transparent text-base font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" %>
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/variants/inertia_shared/app/views/errors/unacceptable.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Not acceptable
7 |
8 |
9 | The requested resource is not available in the requested format.
10 |
11 |
12 |
13 | <%= link_to "Go back home", root_path, class: "inline-flex items-center px-4 py-2 border border-transparent text-base font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" %>
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/variants/shared/app/views/errors/internal_server_error.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Internal server error
7 |
8 |
9 | Something went wrong on our end. We're working on fixing it.
10 |
11 |
12 |
13 | <%= link_to "Go back home", root_path, class: "inline-flex items-center px-4 py-2 border border-transparent text-base font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" %>
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/variants/inertia_shared/app/views/errors/internal_server_error.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Internal server error
7 |
8 |
9 | Something went wrong on our end. We're working on fixing it.
10 |
11 |
12 |
13 | <%= link_to "Go back home", root_path, class: "inline-flex items-center px-4 py-2 border border-transparent text-base font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" %>
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/shared/gems_anyway_config.rb:
--------------------------------------------------------------------------------
1 | generate "anyway:install --configs-path=config/settings", force: true
2 | generate "anyway:config main app_name base_url --yml", force: true
3 | generate "anyway:config mailer email_from --yml", force: true
4 |
5 | gsub_file "config/settings/application_config.rb",
6 | /(@instance \|\|= new)/,
7 | '\1 # rubocop:disable ThreadSafety/ClassInstanceVariable'
8 |
9 | gsub_file "config/main.yml",
10 | /# app_name: ".*"/,
11 | " app_name: \"#{app_name}\""
12 |
13 | gsub_file "config/main.yml",
14 | /# base_url: ".*"/,
15 | ' base_url: "http://localhost:3000"'
16 |
17 | gsub_file "config/mailer.yml",
18 | /# email_from: ".*"/,
19 | ' email_from: "no-reply@example.com"'
20 |
21 | gsub_file "app/mailers/application_mailer.rb",
22 | /default from: ".*"/,
23 | 'default from: MailerConfig.email_from'
24 |
--------------------------------------------------------------------------------
/variants/inertia_vue/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore bundler config.
2 | /.bundle
3 |
4 | # Ignore all environment files.
5 | /.env*
6 |
7 | # Ignore all logfiles and tempfiles.
8 | /log/*
9 | /tmp/*
10 | !/log/.keep
11 | !/tmp/.keep
12 |
13 | # Ignore pidfiles, but keep the directory.
14 | /tmp/pids/*
15 | !/tmp/pids/
16 | !/tmp/pids/.keep
17 |
18 | # Ignore storage (uploaded files in development and any SQLite databases).
19 | /storage/*
20 | !/storage/.keep
21 | /tmp/storage/*
22 | !/tmp/storage/
23 | !/tmp/storage/.keep
24 |
25 | /public/assets
26 | /public/uploads
27 |
28 | # Ignore keys for decrypting credentials and more.
29 | /config/master.key
30 | /config/credentials/development.key
31 | /config/credentials/test.key
32 |
33 | /node_modules
34 | .pnp.*
35 | .yarn/*
36 | !.yarn/patches
37 | !.yarn/plugins
38 | !.yarn/releases
39 | !.yarn/sdks
40 | !.yarn/versions
41 |
42 | .DS_Store
43 |
44 | # IDE specific files
45 | .idea
46 | .ruby-lsp
47 | *.swp
48 | .vscode/settings.json
49 | .vscode/rdbg_autoattach.json
50 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore bundler config.
2 | /.bundle
3 |
4 | # Ignore all environment files.
5 | /.env*
6 |
7 | # Ignore all logfiles and tempfiles.
8 | /log/*
9 | /tmp/*
10 | !/log/.keep
11 | !/tmp/.keep
12 |
13 | # Ignore pidfiles, but keep the directory.
14 | /tmp/pids/*
15 | !/tmp/pids/
16 | !/tmp/pids/.keep
17 |
18 | # Ignore storage (uploaded files in development and any SQLite databases).
19 | /storage/*
20 | !/storage/.keep
21 | /tmp/storage/*
22 | !/tmp/storage/
23 | !/tmp/storage/.keep
24 |
25 | /public/assets
26 | /public/uploads
27 |
28 | # Ignore keys for decrypting credentials and more.
29 | /config/master.key
30 | /config/credentials/development.key
31 | /config/credentials/test.key
32 |
33 | /node_modules
34 | .pnp.*
35 | .yarn/*
36 | !.yarn/patches
37 | !.yarn/plugins
38 | !.yarn/releases
39 | !.yarn/sdks
40 | !.yarn/versions
41 |
42 | .DS_Store
43 |
44 | # IDE specific files
45 | .idea
46 | .ruby-lsp
47 | *.swp
48 | .vscode/settings.json
49 | .vscode/rdbg_autoattach.json
50 |
--------------------------------------------------------------------------------
/variants/inertia_react/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore bundler config.
2 | /.bundle
3 |
4 | # Ignore all environment files.
5 | /.env*
6 |
7 | # Ignore all logfiles and tempfiles.
8 | /log/*
9 | /tmp/*
10 | !/log/.keep
11 | !/tmp/.keep
12 |
13 | # Ignore pidfiles, but keep the directory.
14 | /tmp/pids/*
15 | !/tmp/pids/
16 | !/tmp/pids/.keep
17 |
18 | # Ignore storage (uploaded files in development and any SQLite databases).
19 | /storage/*
20 | !/storage/.keep
21 | /tmp/storage/*
22 | !/tmp/storage/
23 | !/tmp/storage/.keep
24 |
25 | /public/assets
26 | /public/uploads
27 |
28 | # Ignore keys for decrypting credentials and more.
29 | /config/master.key
30 | /config/credentials/development.key
31 | /config/credentials/test.key
32 |
33 | /node_modules
34 | .pnp.*
35 | .yarn/*
36 | !.yarn/patches
37 | !.yarn/plugins
38 | !.yarn/releases
39 | !.yarn/sdks
40 | !.yarn/versions
41 |
42 | .DS_Store
43 |
44 | # IDE specific files
45 | .idea
46 | .ruby-lsp
47 | *.swp
48 | .vscode/settings.json
49 | .vscode/rdbg_autoattach.json
50 |
--------------------------------------------------------------------------------
/variants/inertia_svelte/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore bundler config.
2 | /.bundle
3 |
4 | # Ignore all environment files.
5 | /.env*
6 |
7 | # Ignore all logfiles and tempfiles.
8 | /log/*
9 | /tmp/*
10 | !/log/.keep
11 | !/tmp/.keep
12 |
13 | # Ignore pidfiles, but keep the directory.
14 | /tmp/pids/*
15 | !/tmp/pids/
16 | !/tmp/pids/.keep
17 |
18 | # Ignore storage (uploaded files in development and any SQLite databases).
19 | /storage/*
20 | !/storage/.keep
21 | /tmp/storage/*
22 | !/tmp/storage/
23 | !/tmp/storage/.keep
24 |
25 | /public/assets
26 | /public/uploads
27 |
28 | # Ignore keys for decrypting credentials and more.
29 | /config/master.key
30 | /config/credentials/development.key
31 | /config/credentials/test.key
32 |
33 | /node_modules
34 | .pnp.*
35 | .yarn/*
36 | !.yarn/patches
37 | !.yarn/plugins
38 | !.yarn/releases
39 | !.yarn/sdks
40 | !.yarn/versions
41 |
42 | .DS_Store
43 |
44 | # IDE specific files
45 | .idea
46 | .ruby-lsp
47 | *.swp
48 | .vscode/settings.json
49 | .vscode/rdbg_autoattach.json
50 |
--------------------------------------------------------------------------------
/variants/importmap_tailwind/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore bundler config.
2 | /.bundle
3 |
4 | # Ignore all environment files.
5 | /.env*
6 |
7 | # Ignore all logfiles and tempfiles.
8 | /log/*
9 | /tmp/*
10 | !/log/.keep
11 | !/tmp/.keep
12 |
13 | # Ignore pidfiles, but keep the directory.
14 | /tmp/pids/*
15 | !/tmp/pids/
16 | !/tmp/pids/.keep
17 |
18 | # Ignore storage (uploaded files in development and any SQLite databases).
19 | /storage/*
20 | !/storage/.keep
21 | /tmp/storage/*
22 | !/tmp/storage/
23 | !/tmp/storage/.keep
24 |
25 | /public/assets
26 | /public/uploads
27 |
28 | # Ignore keys for decrypting credentials and more.
29 | /config/master.key
30 | /config/credentials/development.key
31 | /config/credentials/test.key
32 |
33 | /node_modules
34 | .pnp.*
35 | .yarn/*
36 | !.yarn/patches
37 | !.yarn/plugins
38 | !.yarn/releases
39 | !.yarn/sdks
40 | !.yarn/versions
41 |
42 | .DS_Store
43 |
44 | # IDE specific files
45 | .idea
46 | .ruby-lsp
47 | *.swp
48 | .vscode/settings.json
49 | .vscode/rdbg_autoattach.json
50 |
--------------------------------------------------------------------------------
/src/classic_shared/gems_view_component.rb:
--------------------------------------------------------------------------------
1 | initializer "view_component.rb", <<-CODE
2 | Rails.application.config.view_component.previews.paths << Rails.root.join("spec/components/previews").to_s
3 | Rails.application.config.view_component.default_preview_layout = "component_preview"
4 | Rails.application.config.view_component.generate.sidecar = true
5 | CODE
6 |
7 | # Create or overwrite inflections.rb initializer
8 | inflections_file = "config/initializers/inflections.rb"
9 | remove_file inflections_file if File.exist?(inflections_file)
10 | initializer "inflections.rb", <<-CODE
11 | ActiveSupport::Inflector.inflections(:en) do |inflect|
12 | inflect.acronym "UI"
13 | end
14 | CODE
15 |
16 | copy_file "app/helpers/view_component_helper.rb", force: true
17 | copy_file "app/views/layouts/component_preview.html.erb", force: true
18 |
19 | directory "app/components", force: true
20 | directory "spec/components", force: true
21 |
22 | # Copy view component support files for RSpec
23 | copy_file "spec/support/view_component.rb", force: true
24 | copy_file "spec/support/capybara.rb", force: true
25 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Alexey Poimtsev
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/variants/importmap_tailwind/app/views/layouts/plain.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= content_for(:title) || MainConfig.app_name %>
6 |
7 |
8 |
9 |
10 | <%= csrf_meta_tags %>
11 | <%= csp_meta_tag %>
12 |
13 | <%= yield :head %>
14 |
15 | <%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %>
16 | <%= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %>
17 |
18 |
19 |
20 |
21 |
22 | <%# Includes all stylesheet files in app/assets/stylesheets %>
23 | <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
24 | <%= javascript_importmap_tags %>
25 |
26 |
27 |
28 | <%= yield %>
29 |
30 |
31 |
--------------------------------------------------------------------------------
/variants/inertia_svelte/app/frontend/pages/About.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 | About - {$page.props.app_name}
8 |
9 |
10 |
11 |
12 |
13 |
14 | About {$page.props.app_name}
15 |
16 |
17 | This is a modern web application built with Rails, Inertia.js, and Svelte 5.
18 | It combines the power of server-side rendering with the flexibility of a
19 | single-page application.
20 |
21 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/variants/importmap_tailwind/app/views/layouts/landing.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= content_for(:title) || MainConfig.app_name %>
6 |
7 |
8 |
9 |
10 | <%= csrf_meta_tags %>
11 | <%= csp_meta_tag %>
12 |
13 | <%= yield :head %>
14 |
15 | <%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %>
16 | <%= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %>
17 |
18 |
19 |
20 |
21 |
22 | <%# Includes all stylesheet files in app/assets/stylesheets %>
23 | <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
24 | <%= javascript_importmap_tags %>
25 |
26 |
27 |
28 | <%= yield %>
29 |
30 |
31 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/app/views/landing/home.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= MainConfig.app_name %>
6 |
7 |
8 | Built with Rails, ESBuild, and Tailwind CSS
9 |
10 |
11 | <%= link_to "Rails Guides", "https://guides.rubyonrails.org",
12 | target: "_blank",
13 | rel: "noopener noreferrer",
14 | class: "rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" %>
15 | <%= link_to "https://tailwindcss.com",
16 | target: "_blank",
17 | rel: "noopener noreferrer",
18 | class: "text-sm font-semibold leading-6 text-gray-900" do %>
19 | Tailwind CSS Docs →
20 | <% end %>
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/variants/importmap_tailwind/app/views/landing/home.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= MainConfig.app_name %>
6 |
7 |
8 | Built with Rails, Importmap, and Tailwind CSS
9 |
10 |
11 | <%= link_to "Rails Guides", "https://guides.rubyonrails.org",
12 | target: "_blank",
13 | rel: "noopener noreferrer",
14 | class: "rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" %>
15 | <%= link_to "https://tailwindcss.com",
16 | target: "_blank",
17 | rel: "noopener noreferrer",
18 | class: "text-sm font-semibold leading-6 text-gray-900" do %>
19 | Tailwind CSS Docs →
20 | <% end %>
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/app/views/layouts/plain.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= content_for(:title) || MainConfig.app_name %>
6 |
7 |
8 |
9 |
10 | <%= csrf_meta_tags %>
11 | <%= csp_meta_tag %>
12 |
13 | <%= yield :head %>
14 |
15 | <%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %>
16 | <%= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %>
17 |
18 |
19 |
20 |
21 |
22 | <%# Includes all stylesheet files in app/assets/stylesheets %>
23 | <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
24 | <%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module" %>
25 |
26 |
27 |
28 | <%= yield %>
29 |
30 |
31 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/app/views/layouts/landing.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= content_for(:title) || MainConfig.app_name %>
6 |
7 |
8 |
9 |
10 | <%= csrf_meta_tags %>
11 | <%= csp_meta_tag %>
12 |
13 | <%= yield :head %>
14 |
15 | <%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %>
16 | <%= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %>
17 |
18 |
19 |
20 |
21 |
22 | <%# Includes all stylesheet files in app/assets/stylesheets %>
23 | <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
24 | <%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module" %>
25 |
26 |
27 |
28 | <%= yield %>
29 |
30 |
31 |
--------------------------------------------------------------------------------
/variants/importmap_tailwind/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= content_for(:title) || MainConfig.app_name %>
6 |
7 |
8 |
9 |
10 | <%= csrf_meta_tags %>
11 | <%= csp_meta_tag %>
12 |
13 | <%= yield :head %>
14 |
15 | <%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %>
16 | <%= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %>
17 |
18 |
19 |
20 |
21 |
22 | <%# Includes all stylesheet files in app/assets/stylesheets %>
23 | <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
24 | <%= javascript_importmap_tags %>
25 |
26 |
27 |
28 | <%= component "ui/alert", flash: flash %>
29 | <%= yield %>
30 |
31 |
32 |
--------------------------------------------------------------------------------
/variants/inertia_vue/app/frontend/pages/About.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | About {{ appName }}
16 |
17 |
18 | This is a modern web application built with Rails, Inertia.js, and Vue 3.
19 | It combines the power of server-side rendering with the flexibility of a
20 | single-page application.
21 |
22 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/classic_shared/custom_error_pages.rb:
--------------------------------------------------------------------------------
1 | copy_file "app/controllers/errors_controller.rb", force: true
2 | directory "app/views/errors", force: true
3 | copy_file "config/routes/errors.rb", force: true
4 |
5 | inject_into_file "config/application.rb", after: "config.generators.system_tests = nil\n" do
6 | <<-RUBY
7 |
8 | # Use custom error pages
9 | config.exceptions_app = routes
10 | RUBY
11 | end
12 |
13 | # Configure development environment to optionally use custom error pages
14 | gsub_file "config/environments/development.rb",
15 | /config\.consider_all_requests_local\s*=\s*.+$/,
16 | 'config.consider_all_requests_local = !Rails.root.join("tmp/errors-dev.txt").exist? # Toggle with: rails dev:errors'
17 |
18 | # Create rake task to toggle error pages in development
19 | create_file "lib/tasks/dev.rake", <<~RUBY
20 | # frozen_string_literal: true
21 |
22 | namespace :dev do
23 | desc "Toggle custom error pages in development"
24 | task :errors do
25 | error_file = Rails.root.join("tmp/errors-dev.txt")
26 |
27 | if error_file.exist?
28 | error_file.delete
29 | puts "Custom error pages disabled. Restart the server."
30 | else
31 | error_file.write("")
32 | puts "Custom error pages enabled. Restart the server."
33 | end
34 | end
35 | end
36 | RUBY
37 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= content_for(:title) || MainConfig.app_name %>
6 |
7 |
8 |
9 |
10 | <%= csrf_meta_tags %>
11 | <%= csp_meta_tag %>
12 |
13 | <%= yield :head %>
14 |
15 | <%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %>
16 | <%= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %>
17 |
18 |
19 |
20 |
21 |
22 | <%# Includes all stylesheet files in app/assets/stylesheets %>
23 | <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
24 | <%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module" %>
25 |
26 |
27 |
28 | <%= component "ui/alert", flash: flash %>
29 | <%= yield %>
30 |
31 |
32 |
--------------------------------------------------------------------------------
/variants/inertia_react/app/frontend/pages/About.tsx:
--------------------------------------------------------------------------------
1 | import { usePage, Head } from '@inertiajs/react';
2 | import Layout from '../layouts/Layout';
3 |
4 | interface PageProps {
5 | app_name: string;
6 | [key: string]: any;
7 | }
8 |
9 | export default function About() {
10 | const { app_name } = usePage().props;
11 |
12 | return (
13 |
14 |
15 |
16 |
17 |
18 | About {app_name}
19 |
20 |
21 | This is a modern web application built with Rails, Inertia.js, and React.
22 | It combines the power of server-side rendering with the flexibility of a
23 | single-page application.
24 |
25 |
33 |
34 |
35 |
36 | );
37 | }
38 |
--------------------------------------------------------------------------------
/variants/inertia_svelte/app/frontend/pages/Home.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 | Home - {$page.props.app_name}
8 |
9 |
10 |
11 |
12 |
13 |
14 | {$page.props.app_name}
15 |
16 |
17 | Built with Rails, Inertia.js, and Svelte 5
18 |
19 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/inertia_shared/custom_error_pages.rb:
--------------------------------------------------------------------------------
1 | # Configure custom error pages for Inertia templates
2 | # Note: Files from inertia_shared need to be copied explicitly due to source_paths priority
3 |
4 | # Copy errors controller from inertia_shared
5 | copy_file "app/controllers/errors_controller.rb", force: true
6 |
7 | # Copy error views from inertia_shared
8 | directory "app/views/errors", force: true
9 |
10 | inject_into_file "config/application.rb", after: "config.generators.system_tests = nil\n" do
11 | <<-RUBY
12 |
13 | # Use custom error pages
14 | config.exceptions_app = routes
15 | RUBY
16 | end
17 |
18 | # Configure development environment to optionally use custom error pages
19 | gsub_file "config/environments/development.rb",
20 | /config\.consider_all_requests_local\s*=\s*.+$/,
21 | 'config.consider_all_requests_local = !Rails.root.join("tmp/errors-dev.txt").exist? # Toggle with: rails dev:errors'
22 |
23 | # Create rake task to toggle error pages in development
24 | create_file "lib/tasks/dev.rake", <<~RUBY
25 | # frozen_string_literal: true
26 |
27 | namespace :dev do
28 | desc "Toggle custom error pages in development"
29 | task :errors do
30 | error_file = Rails.root.join("tmp/errors-dev.txt")
31 |
32 | if error_file.exist?
33 | error_file.delete
34 | puts "Custom error pages disabled. Restart the server."
35 | else
36 | error_file.write("")
37 | puts "Custom error pages enabled. Restart the server."
38 | end
39 | end
40 | end
41 | RUBY
42 |
--------------------------------------------------------------------------------
/variants/inertia_vue/app/frontend/pages/Home.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | {{ appName }}
16 |
17 |
18 | Built with Rails, Inertia.js, and Vue 3
19 |
20 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/variants/api/mise.toml:
--------------------------------------------------------------------------------
1 | [tools]
2 | ruby = "latest"
3 |
4 | [tasks.update]
5 | description = "Update packages"
6 | run = ["bundle update", "touch tmp/restart.txt"]
7 |
8 | [tasks.install]
9 | description = "Install packages"
10 | run = ["bundle install"]
11 |
12 | [tasks.lint]
13 | description = "Run linters"
14 | run = ["bundle exec rubocop -A", "bundle exec i18n-tasks normalize"]
15 |
16 | [tasks.test]
17 | description = "Run tests in parallel (fast)"
18 | run = ["bundle exec parallel_rspec spec/"]
19 |
20 | [tasks."test:serial"]
21 | description = "Run tests serially (for debugging)"
22 | run = ["bundle exec rspec"]
23 |
24 | [tasks.dev]
25 | description = "Run development server"
26 | run = ["./bin/dev"]
27 |
28 | [tasks.debug]
29 | description = "Run debug server"
30 | run = ["rdbg -n --open=vscode -c -- bin/rails s "]
31 |
32 | [tasks.audit]
33 | description = "Run security audit"
34 | run = ["bundle audit", "osv-scanner scan source -r ."]
35 |
36 | [tasks.db-create]
37 | description = "Prepare development database"
38 | run = [
39 | "bundle exec rails db:create",
40 | "bundle exec rails db:prepare",
41 | "bin/rails db:schema:load:cache",
42 | "bin/rails db:schema:load:queue",
43 | "bin/rails db:schema:load:cable",
44 | "bundle exec rails parallel:create",
45 | ]
46 |
47 | [tasks.db-drop]
48 | description = "Destroy development database"
49 | run = ["bundle exec rails db:drop", "bundle exec rails parallel:drop"]
50 |
51 | [tasks.db-reset]
52 | description = "Reset development database"
53 | run = ["mise run db-drop", "mise run db-create"]
54 |
55 | [tasks.restart]
56 | description = "Restart server"
57 | run = "touch tmp/restart.txt"
58 |
--------------------------------------------------------------------------------
/variants/inertia_react/app/frontend/pages/Home.tsx:
--------------------------------------------------------------------------------
1 | import { usePage, Head } from '@inertiajs/react';
2 | import Layout from '../layouts/Layout';
3 |
4 | interface PageProps {
5 | app_name: string;
6 | [key: string]: any;
7 | }
8 |
9 | export default function Home() {
10 | const { app_name } = usePage().props;
11 |
12 | return (
13 |
14 |
15 |
16 |
17 |
18 | {app_name}
19 |
20 |
21 | Built with Rails, Inertia.js, and React
22 |
23 |
41 |
42 |
43 |
44 | );
45 | }
46 |
--------------------------------------------------------------------------------
/variants/inertia_svelte/app/frontend/pages/Terms.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 | Terms of Service - {$page.props.app_name}
8 |
9 |
10 |
11 |
12 |
13 |
14 | Terms of Service
15 |
16 |
17 |
18 | Welcome to {$page.props.app_name}. By using our service, you agree to these terms.
19 |
20 |
1. Use of Service
21 |
22 | You may use our service in accordance with these terms and applicable laws.
23 |
24 |
2. User Accounts
25 |
26 | You are responsible for maintaining the security of your account.
27 |
28 |
3. Content
29 |
30 | You retain ownership of content you submit, but grant us a license to use it.
31 |
32 |
33 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/variants/importmap_tailwind/mise.toml:
--------------------------------------------------------------------------------
1 | [tools]
2 | ruby = "latest"
3 | node = "latest"
4 |
5 | [env]
6 | COREPACK_ENABLE_STRICT = "0"
7 |
8 | [tasks.update]
9 | description = "Update packages"
10 | run = ["bundle update", "touch tmp/restart.txt"]
11 |
12 | [tasks.install]
13 | description = "Install packages"
14 | run = ["bundle install"]
15 |
16 | [tasks.lint]
17 | description = "Run linters"
18 | run = [
19 | "bundle exec rubocop -A",
20 | "bundle exec i18n-tasks normalize",
21 | "bundle exec erb_lint --lint-all --autocorrect",
22 | ]
23 |
24 | [tasks.test]
25 | description = "Run tests in parallel (fast)"
26 | run = ["bundle exec parallel_rspec spec/"]
27 |
28 | [tasks."test:serial"]
29 | description = "Run tests serially (for debugging)"
30 | run = ["bundle exec rspec"]
31 |
32 | [tasks.dev]
33 | description = "Run development server"
34 | run = ["./bin/dev"]
35 |
36 | [tasks.debug]
37 | description = "Run debug server"
38 | run = ["rdbg -n --open=vscode -c -- bin/rails s "]
39 |
40 | [tasks.audit]
41 | description = "Run security audit"
42 | run = ["bundle audit", "osv-scanner scan source -r ."]
43 |
44 | [tasks.db-create]
45 | description = "Prepare development database"
46 | run = [
47 | "bundle exec rails db:create",
48 | "bundle exec rails db:prepare",
49 | "bin/rails db:schema:load:cache",
50 | "bin/rails db:schema:load:queue",
51 | "bin/rails db:schema:load:cable",
52 | "bundle exec rails parallel:create",
53 | ]
54 |
55 | [tasks.db-drop]
56 | description = "Destroy development database"
57 | run = ["bundle exec rails db:drop", "bundle exec rails parallel:drop"]
58 |
59 | [tasks.db-reset]
60 | description = "Reset development database"
61 | run = ["mise run db-drop", "mise run db-create"]
62 |
63 | [tasks.restart]
64 | description = "Restart server"
65 | run = "touch tmp/restart.txt"
66 |
--------------------------------------------------------------------------------
/variants/api/Gemfile.tt:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | ### Application
4 | gem "rails", "~> <%= Rails.version %>"
5 | gem "puma"<%= gemfile_requirement("puma") %>
6 |
7 | ### Database
8 | gem "pg"<%= gemfile_requirement("pg") %>
9 |
10 | ### Solid Stack
11 | gem "solid_cache"
12 | gem "solid_queue"
13 | gem "solid_cable"
14 |
15 | ### Encryption and Security
16 | gem "blind_index"
17 | gem "lockbox"
18 |
19 | ### Deployment
20 | gem "kamal", require: false
21 | gem "thruster", require: false
22 |
23 | ### I18n
24 | gem "i18n-tasks"
25 | gem "rails-i18n"
26 |
27 | ### File uploads
28 | gem "shrine", "~> 3.0"
29 | gem "aws-sdk-s3"
30 |
31 | ### Image processing
32 | gem "image_processing", "~> 1.2"
33 | gem "fastimage"
34 | gem "marcel", "~> 1.0", ">= 1.0.2"
35 |
36 | ### Code structure
37 | gem "anyway_config", "~> 2.0"
38 | gem "active_interaction", "~> 5.5"
39 |
40 | ### Tools
41 | gem "tzinfo-data", platforms: %i[ windows jruby ]
42 | gem "bootsnap", require: false
43 | gem "jbuilder"
44 | gem "rack-cors"
45 |
46 | group :development, :test do
47 | gem "debug", platforms: %i[ mri windows ], require: "debug/prelude"
48 |
49 | # security
50 | gem "brakeman", require: false
51 | gem "bundler-audit"
52 |
53 | # linters
54 | gem "standard", require: false, github: "standardrb/standard"
55 | gem "rubocop-rails", require: false
56 | gem "rubocop-rspec", require: false
57 | gem "rubocop-performance", require: false
58 | gem "rubocop-thread_safety", require: false
59 |
60 | # testing
61 | gem "rspec-rails"
62 | gem "parallel_tests"
63 | gem "ffaker"
64 | gem "database_cleaner-active_record"
65 | gem "factory_bot_rails"
66 | gem "shoulda-context", "~> 3.0.0.rc1"
67 | gem "shoulda-matchers", "~> 6.5.0"
68 | end
69 |
70 | group :development do
71 | ## dev tools
72 | gem "amazing_print"
73 | gem "rails-erd"
74 | end
75 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/mise.toml:
--------------------------------------------------------------------------------
1 | [tools]
2 | ruby = "latest"
3 | node = "latest"
4 |
5 | [env]
6 | COREPACK_ENABLE_STRICT = "0"
7 |
8 | [tasks.update]
9 | description = "Update packages"
10 | run = [
11 | "bundle update",
12 | "yarn up",
13 | "touch tmp/restart.txt",
14 | ]
15 |
16 | [tasks.install]
17 | description = "Install packages"
18 | run = ["corepack enable", "bundle install", "yarn install"]
19 |
20 | [tasks.lint]
21 | description = "Run linters"
22 | run = [
23 | "bundle exec rubocop -A",
24 | "bundle exec i18n-tasks normalize",
25 | "bundle exec erb_lint --lint-all --autocorrect",
26 | ]
27 |
28 | [tasks.test]
29 | description = "Run tests in parallel (fast)"
30 | run = ["bundle exec parallel_rspec spec/"]
31 |
32 | [tasks."test:serial"]
33 | description = "Run tests serially (for debugging)"
34 | run = ["bundle exec rspec"]
35 |
36 | [tasks.dev]
37 | description = "Run development server"
38 | run = ["./bin/dev"]
39 |
40 | [tasks.debug]
41 | description = "Run debug server"
42 | run = ["rdbg -n --open=vscode -c -- bin/rails s "]
43 |
44 | [tasks.audit]
45 | description = "Run security audit"
46 | run = ["bundle audit", "yarn audit", "osv-scanner scan source -r ."]
47 |
48 | [tasks.db-create]
49 | description = "Prepare development database"
50 | run = [
51 | "bundle exec rails db:create",
52 | "bundle exec rails db:prepare",
53 | "bin/rails db:schema:load:cache",
54 | "bin/rails db:schema:load:queue",
55 | "bin/rails db:schema:load:cable",
56 | "bundle exec rails parallel:create",
57 | ]
58 |
59 | [tasks.db-drop]
60 | description = "Destroy development database"
61 | run = ["bundle exec rails db:drop", "bundle exec rails parallel:drop"]
62 |
63 | [tasks.db-reset]
64 | description = "Reset development database"
65 | run = ["mise run db-drop", "mise run db-create"]
66 |
67 | [tasks.restart]
68 | description = "Restart server"
69 | run = "touch tmp/restart.txt"
70 |
--------------------------------------------------------------------------------
/variants/inertia_vue/app/frontend/pages/Terms.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | Terms of Service
16 |
17 |
18 |
19 | Welcome to {{ appName }}. By using our service, you agree to these terms.
20 |
21 |
1. Use of Service
22 |
23 | You may use our service in accordance with these terms and applicable laws.
24 |
25 |
2. User Accounts
26 |
27 | You are responsible for maintaining the security of your account.
28 |
29 |
3. Content
30 |
31 | You retain ownership of content you submit, but grant us a license to use it.
32 |
33 |
34 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/variants/inertia_react/mise.toml:
--------------------------------------------------------------------------------
1 | [tools]
2 | ruby = "latest"
3 | node = "latest"
4 |
5 | [env]
6 | COREPACK_ENABLE_STRICT = "0"
7 |
8 | [tasks.update]
9 | description = "Update packages"
10 | run = [
11 | "bundle update",
12 | "yarn up",
13 | "touch tmp/restart.txt",
14 | ]
15 |
16 | [tasks.install]
17 | description = "Install packages"
18 | run = ["corepack enable", "bundle install", "yarn install"]
19 |
20 | [tasks.lint]
21 | description = "Run linters"
22 | run = [
23 | "bundle exec rubocop -A",
24 | "bundle exec i18n-tasks normalize",
25 | "yarn run check"
26 | ]
27 |
28 | [tasks.test]
29 | description = "Run tests in parallel (fast)"
30 | run = [
31 | "bundle exec parallel_rspec spec/",
32 | ]
33 |
34 | [tasks."test:serial"]
35 | description = "Run tests serially (for debugging)"
36 | run = [
37 | "bundle exec rspec",
38 | ]
39 |
40 | [tasks.dev]
41 | description = "Run development server"
42 | run = ["./bin/dev"]
43 |
44 | [tasks.debug]
45 | description = "Run debug server"
46 | run = ["rdbg -n --open=vscode -c -- bin/rails s "]
47 |
48 | [tasks.audit]
49 | description = "Run security audit"
50 | run = ["bundle audit", "yarn audit", "osv-scanner scan source -r ."]
51 |
52 | [tasks.db-create]
53 | description = "Prepare development database"
54 | run = [
55 | "bundle exec rails db:create",
56 | "bundle exec rails db:prepare",
57 | "bin/rails db:schema:load:cache",
58 | "bin/rails db:schema:load:queue",
59 | "bin/rails db:schema:load:cable",
60 | "bundle exec rails parallel:create"
61 | ]
62 |
63 | [tasks.db-drop]
64 | description = "Destroy development database"
65 | run = [
66 | "bundle exec rails db:drop",
67 | "bundle exec rails parallel:drop"
68 | ]
69 |
70 | [tasks.db-reset]
71 | description = "Reset development database"
72 | run = [
73 | "mise run db-drop",
74 | "mise run db-create"
75 | ]
76 |
77 | [tasks.restart]
78 | description = "Restart server"
79 | run = "touch tmp/restart.txt"
80 |
--------------------------------------------------------------------------------
/variants/inertia_vue/mise.toml:
--------------------------------------------------------------------------------
1 | [tools]
2 | ruby = "latest"
3 | node = "latest"
4 |
5 | [env]
6 | COREPACK_ENABLE_STRICT = "0"
7 |
8 | [tasks.update]
9 | description = "Update packages"
10 | run = [
11 | "bundle update",
12 | "yarn up",
13 | "touch tmp/restart.txt",
14 | ]
15 |
16 | [tasks.install]
17 | description = "Install packages"
18 | run = ["corepack enable", "bundle install", "yarn install"]
19 |
20 | [tasks.lint]
21 | description = "Run linters"
22 | run = [
23 | "bundle exec rubocop -A",
24 | "bundle exec i18n-tasks normalize",
25 | "yarn run check"
26 | ]
27 |
28 | [tasks.test]
29 | description = "Run tests in parallel (fast)"
30 | run = [
31 | "bundle exec parallel_rspec spec/",
32 | ]
33 |
34 | [tasks."test:serial"]
35 | description = "Run tests serially (for debugging)"
36 | run = [
37 | "bundle exec rspec",
38 | ]
39 |
40 | [tasks.dev]
41 | description = "Run development server"
42 | run = ["./bin/dev"]
43 |
44 | [tasks.debug]
45 | description = "Run debug server"
46 | run = ["rdbg -n --open=vscode -c -- bin/rails s "]
47 |
48 | [tasks.audit]
49 | description = "Run security audit"
50 | run = ["bundle audit", "yarn audit", "osv-scanner scan source -r ."]
51 |
52 | [tasks.db-create]
53 | description = "Prepare development database"
54 | run = [
55 | "bundle exec rails db:create",
56 | "bundle exec rails db:prepare",
57 | "bin/rails db:schema:load:cache",
58 | "bin/rails db:schema:load:queue",
59 | "bin/rails db:schema:load:cable",
60 | "bundle exec rails parallel:create"
61 | ]
62 |
63 | [tasks.db-drop]
64 | description = "Destroy development database"
65 | run = [
66 | "bundle exec rails db:drop",
67 | "bundle exec rails parallel:drop"
68 | ]
69 |
70 | [tasks.db-reset]
71 | description = "Reset development database"
72 | run = [
73 | "mise run db-drop",
74 | "mise run db-create"
75 | ]
76 |
77 | [tasks.restart]
78 | description = "Restart server"
79 | run = "touch tmp/restart.txt"
80 |
--------------------------------------------------------------------------------
/variants/inertia_svelte/mise.toml:
--------------------------------------------------------------------------------
1 | [tools]
2 | ruby = "latest"
3 | node = "latest"
4 |
5 | [env]
6 | COREPACK_ENABLE_STRICT = "0"
7 |
8 | [tasks.update]
9 | description = "Update packages"
10 | run = [
11 | "bundle update",
12 | "yarn up",
13 | "touch tmp/restart.txt",
14 | ]
15 |
16 | [tasks.install]
17 | description = "Install packages"
18 | run = ["corepack enable", "bundle install", "yarn install"]
19 |
20 | [tasks.lint]
21 | description = "Run linters"
22 | run = [
23 | "bundle exec rubocop -A",
24 | "bundle exec i18n-tasks normalize",
25 | "yarn run check"
26 | ]
27 |
28 | [tasks.test]
29 | description = "Run tests in parallel (fast)"
30 | run = [
31 | "bundle exec parallel_rspec spec/",
32 | ]
33 |
34 | [tasks."test:serial"]
35 | description = "Run tests serially (for debugging)"
36 | run = [
37 | "bundle exec rspec",
38 | ]
39 |
40 | [tasks.dev]
41 | description = "Run development server"
42 | run = ["./bin/dev"]
43 |
44 | [tasks.debug]
45 | description = "Run debug server"
46 | run = ["rdbg -n --open=vscode -c -- bin/rails s "]
47 |
48 | [tasks.audit]
49 | description = "Run security audit"
50 | run = [
51 | "bundle audit",
52 | "yarn audit",
53 | "osv-scanner scan source -r ."
54 | ]
55 |
56 | [tasks.db-create]
57 | description = "Prepare development database"
58 | run = [
59 | "bundle exec rails db:create",
60 | "bundle exec rails db:prepare",
61 | "bin/rails db:schema:load:cache",
62 | "bin/rails db:schema:load:queue",
63 | "bin/rails db:schema:load:cable",
64 | "bundle exec rails parallel:create"
65 | ]
66 |
67 | [tasks.db-drop]
68 | description = "Destroy development database"
69 | run = [
70 | "bundle exec rails db:drop",
71 | "bundle exec rails parallel:drop"
72 | ]
73 |
74 | [tasks.db-reset]
75 | description = "Reset development database"
76 | run = [
77 | "mise run db-drop",
78 | "mise run db-create"
79 | ]
80 |
81 | [tasks.restart]
82 | description = "Restart server"
83 | run = "touch tmp/restart.txt"
84 |
--------------------------------------------------------------------------------
/src/shared/staging_env.rb:
--------------------------------------------------------------------------------
1 | # Create staging environment based on production
2 | run "cp config/environments/production.rb config/environments/staging.rb"
3 |
4 | # Update staging environment configuration
5 | gsub_file "config/environments/staging.rb",
6 | /Rails\.application\.configure do/,
7 | "# Staging environment configuration (based on production)\nRails.application.configure do"
8 |
9 | gsub_file "config/environments/staging.rb",
10 | /config\.log_level = :info/,
11 | "config.log_level = :debug"
12 |
13 | # Add staging to database.yml
14 | append_to_file "config/database.yml" do
15 | <<-YAML
16 |
17 | staging:
18 | primary: &primary_staging
19 | <<: *default
20 | database: <%= ENV.fetch("DATABASE_NAME") { "#{app_name}_staging" } %>
21 | username: <%= ENV.fetch("DATABASE_USERNAME") { "#{app_name}" } %>
22 | password: <%= ENV["DATABASE_PASSWORD"] %>
23 | cache:
24 | <<: *primary_staging
25 | database: <%= ENV.fetch("DATABASE_NAME") { "#{app_name}_staging_cache" } %>
26 | migrations_paths: db/cache_migrate
27 | queue:
28 | <<: *primary_staging
29 | database: <%= ENV.fetch("DATABASE_NAME") { "#{app_name}_staging_queue" } %>
30 | migrations_paths: db/queue_migrate
31 | cable:
32 | <<: *primary_staging
33 | database: <%= ENV.fetch("DATABASE_NAME") { "#{app_name}_staging_cable" } %>
34 | migrations_paths: db/cable_migrate
35 | YAML
36 | end
37 |
38 | # Add staging to cable.yml if it exists
39 | if File.exist?("config/cable.yml")
40 | append_to_file "config/cable.yml" do
41 | <<-YAML
42 |
43 | staging:
44 | adapter: solid_cable
45 | connects_to:
46 | database:
47 | writing: cable
48 | polling_interval: 0.1.seconds
49 | message_retention: 1.day
50 | YAML
51 | end
52 | end
53 |
54 | # Add staging to aws.yml
55 | gsub_file "config/aws.yml",
56 | /production:\n <<: \*default/,
57 | "staging:\n <<: *default\n\nproduction:\n <<: *default"
58 |
--------------------------------------------------------------------------------
/variants/inertia_react/app/frontend/pages/Terms.tsx:
--------------------------------------------------------------------------------
1 | import { usePage, Head } from '@inertiajs/react';
2 | import Layout from '../layouts/Layout';
3 |
4 | interface PageProps {
5 | app_name: string;
6 | [key: string]: any;
7 | }
8 |
9 | export default function Terms() {
10 | const { app_name } = usePage().props;
11 |
12 | return (
13 |
14 |
15 |
16 |
17 |
18 | Terms of Service
19 |
20 |
21 |
22 | Welcome to {app_name}. By using our service, you agree to these terms.
23 |
24 |
1. Use of Service
25 |
26 | You may use our service in accordance with these terms and applicable laws.
27 |
28 |
2. User Accounts
29 |
30 | You are responsible for maintaining the security of your account.
31 |
32 |
3. Content
33 |
34 | You retain ownership of content you submit, but grant us a license to use it.
35 |
36 |
37 |
45 |
46 |
47 |
48 | );
49 | }
50 |
--------------------------------------------------------------------------------
/variants/inertia_svelte/app/frontend/pages/Privacy.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 | Privacy Policy - {$page.props.app_name}
8 |
9 |
10 |
11 |
12 |
13 |
14 | Privacy Policy
15 |
16 |
17 |
18 | At {$page.props.app_name}, we take your privacy seriously. This policy describes how we collect,
19 | use, and protect your personal information.
20 |
21 |
1. Information We Collect
22 |
23 | We collect information you provide directly to us, such as when you create an account.
24 |
25 |
2. How We Use Your Information
26 |
27 | We use your information to provide, maintain, and improve our services.
28 |
29 |
3. Information Sharing
30 |
31 | We do not share your personal information with third parties except as described in this policy.
32 |
33 |
4. Data Security
34 |
35 | We implement appropriate security measures to protect your information.
36 |
37 |
38 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/variants/inertia_vue/Gemfile.tt:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | ### Application
4 | gem "rails", "~> <%= Rails.version %>"
5 | gem "puma"<%= gemfile_requirement("puma") %>
6 |
7 | ### Database
8 | gem "pg"<%= gemfile_requirement("pg") %>
9 |
10 | ### Solid Stack
11 | gem "solid_cache"
12 | gem "solid_queue"
13 | gem "solid_cable"
14 | gem "mission_control-jobs"
15 |
16 | ### Frontend
17 | gem "propshaft"
18 | gem "inertia_rails", "~> 3.0"
19 | gem "vite_rails", "~> 3.0"
20 |
21 | ### Encryption and Security
22 | gem "blind_index"
23 | gem "lockbox"
24 |
25 | ### Deployment
26 | gem "kamal", require: false
27 | gem "thruster", require: false
28 |
29 | ### I18n
30 | gem "i18n-tasks"
31 | gem "rails-i18n"
32 |
33 | ### File uploads
34 | gem "shrine", "~> 3.0"
35 | gem "aws-sdk-s3"
36 |
37 | ### Image processing
38 | gem "image_processing", "~> 1.2"
39 | gem "fastimage"
40 | gem "marcel", "~> 1.0", ">= 1.0.2"
41 |
42 | ### Code structure
43 | gem "anyway_config", "~> 2.0"
44 | gem "active_interaction", "~> 5.5"
45 | gem "active_decorator"
46 | gem "madmin"
47 | gem "pagy", "~> 9.0"
48 |
49 | ### Tools
50 | gem "jbuilder"
51 | gem "tzinfo-data", platforms: %i[ windows jruby ]
52 | gem "bootsnap", require: false
53 |
54 | group :development, :test do
55 | gem "debug", platforms: %i[ mri windows ], require: "debug/prelude"
56 |
57 | # security
58 | gem "brakeman", require: false
59 | gem "bundler-audit"
60 |
61 | # linters
62 | gem "standard", require: false, github: "standardrb/standard"
63 | gem "rubocop-rails", require: false
64 | gem "rubocop-rspec", require: false
65 | gem "rubocop-performance", require: false
66 | gem "rubocop-thread_safety", require: false
67 | gem "rubocop-capybara", require: false
68 |
69 | # testing
70 | gem "rspec-rails"
71 | gem "parallel_tests"
72 | gem "capybara"
73 | gem "email_spec"
74 | gem "ffaker"
75 | gem "database_cleaner-active_record"
76 | gem "factory_bot_rails"
77 | gem "shoulda-context", "~> 3.0.0.rc1"
78 | gem "shoulda-matchers", "~> 6.5.0"
79 | end
80 |
81 | group :development do
82 | ## dev tools
83 | gem "web-console"
84 | gem "amazing_print"
85 | gem "letter_opener_web", "~> 3.0"
86 | gem "rails-erd"
87 | end
88 |
--------------------------------------------------------------------------------
/variants/inertia_react/Gemfile.tt:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | ### Application
4 | gem "rails", "~> <%= Rails.version %>"
5 | gem "puma"<%= gemfile_requirement("puma") %>
6 |
7 | ### Database
8 | gem "pg"<%= gemfile_requirement("pg") %>
9 |
10 | ### Solid Stack
11 | gem "solid_cache"
12 | gem "solid_queue"
13 | gem "solid_cable"
14 | gem "mission_control-jobs"
15 |
16 | ### Frontend
17 | gem "propshaft"
18 | gem "inertia_rails", "~> 3.0"
19 | gem "vite_rails", "~> 3.0"
20 |
21 | ### Encryption and Security
22 | gem "blind_index"
23 | gem "lockbox"
24 |
25 | ### Deployment
26 | gem "kamal", require: false
27 | gem "thruster", require: false
28 |
29 | ### I18n
30 | gem "i18n-tasks"
31 | gem "rails-i18n"
32 |
33 | ### File uploads
34 | gem "shrine", "~> 3.0"
35 | gem "aws-sdk-s3"
36 |
37 | ### Image processing
38 | gem "image_processing", "~> 1.2"
39 | gem "fastimage"
40 | gem "marcel", "~> 1.0", ">= 1.0.2"
41 |
42 | ### Code structure
43 | gem "anyway_config", "~> 2.0"
44 | gem "active_interaction", "~> 5.5"
45 | gem "active_decorator"
46 | gem "madmin"
47 | gem "pagy", "~> 9.0"
48 |
49 | ### Tools
50 | gem "jbuilder"
51 | gem "tzinfo-data", platforms: %i[ windows jruby ]
52 | gem "bootsnap", require: false
53 |
54 | group :development, :test do
55 | gem "debug", platforms: %i[ mri windows ], require: "debug/prelude"
56 |
57 | # security
58 | gem "brakeman", require: false
59 | gem "bundler-audit"
60 |
61 | # linters
62 | gem "standard", require: false, github: "standardrb/standard"
63 | gem "rubocop-rails", require: false
64 | gem "rubocop-rspec", require: false
65 | gem "rubocop-performance", require: false
66 | gem "rubocop-thread_safety", require: false
67 | gem "rubocop-capybara", require: false
68 |
69 | # testing
70 | gem "rspec-rails"
71 | gem "parallel_tests"
72 | gem "capybara"
73 | gem "email_spec"
74 | gem "ffaker"
75 | gem "database_cleaner-active_record"
76 | gem "factory_bot_rails"
77 | gem "shoulda-context", "~> 3.0.0.rc1"
78 | gem "shoulda-matchers", "~> 6.5.0"
79 | end
80 |
81 | group :development do
82 | ## dev tools
83 | gem "web-console"
84 | gem "amazing_print"
85 | gem "letter_opener_web", "~> 3.0"
86 | gem "rails-erd"
87 | end
88 |
--------------------------------------------------------------------------------
/variants/inertia_svelte/Gemfile.tt:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | ### Application
4 | gem "rails", "~> <%= Rails.version %>"
5 | gem "puma"<%= gemfile_requirement("puma") %>
6 |
7 | ### Database
8 | gem "pg"<%= gemfile_requirement("pg") %>
9 |
10 | ### Solid Stack
11 | gem "solid_cache"
12 | gem "solid_queue"
13 | gem "solid_cable"
14 | gem "mission_control-jobs"
15 |
16 | ### Frontend
17 | gem "propshaft"
18 | gem "inertia_rails", "~> 3.0"
19 | gem "vite_rails", "~> 3.0"
20 |
21 | ### Encryption and Security
22 | gem "blind_index"
23 | gem "lockbox"
24 |
25 | ### Deployment
26 | gem "kamal", require: false
27 | gem "thruster", require: false
28 |
29 | ### I18n
30 | gem "i18n-tasks"
31 | gem "rails-i18n"
32 |
33 | ### File uploads
34 | gem "shrine", "~> 3.0"
35 | gem "aws-sdk-s3"
36 |
37 | ### Image processing
38 | gem "image_processing", "~> 1.2"
39 | gem "fastimage"
40 | gem "marcel", "~> 1.0", ">= 1.0.2"
41 |
42 | ### Code structure
43 | gem "anyway_config", "~> 2.0"
44 | gem "active_interaction", "~> 5.5"
45 | gem "active_decorator"
46 | gem "madmin"
47 | gem "pagy", "~> 9.0"
48 |
49 | ### Tools
50 | gem "jbuilder"
51 | gem "tzinfo-data", platforms: %i[ windows jruby ]
52 | gem "bootsnap", require: false
53 |
54 | group :development, :test do
55 | gem "debug", platforms: %i[ mri windows ], require: "debug/prelude"
56 |
57 | # security
58 | gem "brakeman", require: false
59 | gem "bundler-audit"
60 |
61 | # linters
62 | gem "standard", require: false, github: "standardrb/standard"
63 | gem "rubocop-rails", require: false
64 | gem "rubocop-rspec", require: false
65 | gem "rubocop-performance", require: false
66 | gem "rubocop-thread_safety", require: false
67 | gem "rubocop-capybara", require: false
68 |
69 | # testing
70 | gem "rspec-rails"
71 | gem "parallel_tests"
72 | gem "capybara"
73 | gem "email_spec"
74 | gem "ffaker"
75 | gem "database_cleaner-active_record"
76 | gem "factory_bot_rails"
77 | gem "shoulda-context", "~> 3.0.0.rc1"
78 | gem "shoulda-matchers", "~> 6.5.0"
79 | end
80 |
81 | group :development do
82 | ## dev tools
83 | gem "web-console"
84 | gem "amazing_print"
85 | gem "letter_opener_web", "~> 3.0"
86 | gem "rails-erd"
87 | end
88 |
--------------------------------------------------------------------------------
/variants/inertia_vue/app/frontend/pages/Privacy.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | Privacy Policy
16 |
17 |
18 |
19 | At {{ appName }}, we take your privacy seriously. This policy describes how we collect,
20 | use, and protect your personal information.
21 |
22 |
1. Information We Collect
23 |
24 | We collect information you provide directly to us, such as when you create an account.
25 |
26 |
2. How We Use Your Information
27 |
28 | We use your information to provide, maintain, and improve our services.
29 |
30 |
3. Information Sharing
31 |
32 | We do not share your personal information with third parties except as described in this policy.
33 |
34 |
4. Data Security
35 |
36 | We implement appropriate security measures to protect your information.
37 |
38 |
39 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/variants/esbuild_tailwind/Gemfile.tt:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | ### Application
4 | gem "rails", "~> <%= Rails.version %>"
5 | gem "puma"<%= gemfile_requirement("puma") %>
6 |
7 | ### Database
8 | gem "pg"<%= gemfile_requirement("pg") %>
9 |
10 | ### Solid Stack
11 | gem "solid_cache"
12 | gem "solid_queue"
13 | gem "solid_cable"
14 | gem "mission_control-jobs"
15 |
16 | ### Frontend
17 | gem "propshaft"
18 | gem "jsbundling-rails"
19 | gem "cssbundling-rails"
20 | gem "turbo-rails"
21 | gem "stimulus-rails"
22 | gem "better_html"
23 | gem "inline_svg"
24 | gem "lookbook"
25 | gem "view_component"
26 |
27 | ### Encryption and Security
28 | gem "blind_index"
29 | gem "lockbox"
30 |
31 | ### Deployment
32 | gem "kamal", require: false
33 | gem "thruster", require: false
34 |
35 | ### I18n
36 | gem "i18n-tasks"
37 | gem "rails-i18n"
38 |
39 | ### File uploads
40 | gem "shrine", "~> 3.0"
41 | gem "aws-sdk-s3"
42 |
43 | ### Image processing
44 | gem "image_processing", "~> 1.2"
45 | gem "fastimage"
46 | gem "marcel", "~> 1.0", ">= 1.0.2"
47 |
48 | ### Code structure
49 | gem "anyway_config", "~> 2.0"
50 | gem "active_interaction", "~> 5.5"
51 | gem "active_decorator"
52 | gem "madmin"
53 | gem "pagy", "~> 9.0"
54 |
55 | ### Tools
56 | gem "jbuilder"
57 | gem "tzinfo-data", platforms: %i[ windows jruby ]
58 | gem "bootsnap", require: false
59 |
60 | group :development, :test do
61 | gem "debug", platforms: %i[ mri windows ], require: "debug/prelude"
62 |
63 | # security
64 | gem "brakeman", require: false
65 | gem "bundler-audit"
66 |
67 | # linters
68 | gem "standard", require: false, github: "standardrb/standard"
69 | gem "rubocop-rails", require: false
70 | gem "rubocop-rspec", require: false
71 | gem "rubocop-performance", require: false
72 | gem "rubocop-thread_safety", require: false
73 | gem "rubocop-capybara", require: false
74 | gem "erb_lint", require: false
75 |
76 | # testing
77 | gem "rspec-rails"
78 | gem "parallel_tests"
79 | gem "capybara"
80 | gem "email_spec"
81 | gem "ffaker"
82 | gem "database_cleaner-active_record"
83 | gem "factory_bot_rails"
84 | gem "shoulda-context", "~> 3.0.0.rc1"
85 | gem "shoulda-matchers", "~> 6.5.0"
86 | end
87 |
88 | group :development do
89 | ## dev tools
90 | gem "web-console"
91 | gem "amazing_print"
92 | gem "letter_opener_web", "~> 3.0"
93 | gem "rails-erd"
94 | end
95 |
--------------------------------------------------------------------------------
/variants/importmap_tailwind/Gemfile.tt:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | ### Application
4 | gem "rails", "~> <%= Rails.version %>"
5 | gem "puma"<%= gemfile_requirement("puma") %>
6 |
7 | ### Database
8 | gem "pg"<%= gemfile_requirement("pg") %>
9 |
10 | ### Solid Stack
11 | gem "solid_cache"
12 | gem "solid_queue"
13 | gem "solid_cable"
14 | gem "mission_control-jobs"
15 |
16 | ### Frontend
17 | gem "propshaft"
18 | gem "importmap-rails"
19 | gem "tailwindcss-rails"
20 | gem "turbo-rails"
21 | gem "stimulus-rails"
22 | gem "better_html"
23 | gem "inline_svg"
24 | gem "lookbook"
25 | gem "view_component"
26 |
27 | ### Encryption and Security
28 | gem "blind_index"
29 | gem "lockbox"
30 |
31 | ### Deployment
32 | gem "kamal", require: false
33 | gem "thruster", require: false
34 |
35 | ### I18n
36 | gem "i18n-tasks"
37 | gem "rails-i18n"
38 |
39 | ### File uploads
40 | gem "shrine", "~> 3.0"
41 | gem "aws-sdk-s3"
42 |
43 | ### Image processing
44 | gem "image_processing", "~> 1.2"
45 | gem "fastimage"
46 | gem "marcel", "~> 1.0", ">= 1.0.2"
47 |
48 | ### Code structure
49 | gem "anyway_config", "~> 2.0"
50 | gem "active_interaction", "~> 5.5"
51 | gem "active_decorator"
52 | gem "madmin"
53 | gem "pagy", "~> 9.0"
54 |
55 | ### Tools
56 | gem "jbuilder"
57 | gem "tzinfo-data", platforms: %i[ windows jruby ]
58 | gem "bootsnap", require: false
59 |
60 | group :development, :test do
61 | gem "debug", platforms: %i[ mri windows ], require: "debug/prelude"
62 |
63 | # security
64 | gem "brakeman", require: false
65 | gem "bundler-audit"
66 |
67 | # linters
68 | gem "standard", require: false, github: "standardrb/standard"
69 | gem "rubocop-rails", require: false
70 | gem "rubocop-rspec", require: false
71 | gem "rubocop-performance", require: false
72 | gem "rubocop-thread_safety", require: false
73 | gem "rubocop-capybara", require: false
74 | gem "erb_lint", require: false
75 |
76 | # testing
77 | gem "rspec-rails"
78 | gem "parallel_tests"
79 | gem "capybara"
80 | gem "email_spec"
81 | gem "ffaker"
82 | gem "database_cleaner-active_record"
83 | gem "factory_bot_rails"
84 | gem "shoulda-context", "~> 3.0.0.rc1"
85 | gem "shoulda-matchers", "~> 6.5.0"
86 | end
87 |
88 | group :development do
89 | ## dev tools
90 | gem "web-console"
91 | gem "amazing_print"
92 | gem "letter_opener_web", "~> 3.0"
93 | gem "rails-erd"
94 | end
95 |
--------------------------------------------------------------------------------
/variants/inertia_svelte/app/frontend/lib/components/Flash.svelte:
--------------------------------------------------------------------------------
1 |
49 |
50 | {#if visible && getFlashType()}
51 |
52 |
53 |
54 |
{getFlashMessage()}
55 |
(visible = false)}
57 | class="ml-4 text-current opacity-70 hover:opacity-100"
58 | aria-label="Close notification"
59 | >
60 |
61 |
66 |
67 |
68 |
69 |
70 |
71 | {/if}
72 |
--------------------------------------------------------------------------------
/variants/inertia_react/app/frontend/pages/Privacy.tsx:
--------------------------------------------------------------------------------
1 | import { usePage, Head } from '@inertiajs/react';
2 | import Layout from '../layouts/Layout';
3 |
4 | interface PageProps {
5 | app_name: string;
6 | [key: string]: any;
7 | }
8 |
9 | export default function Privacy() {
10 | const { app_name } = usePage().props;
11 |
12 | return (
13 |
14 |
15 |
16 |
17 |
18 | Privacy Policy
19 |
20 |
21 |
22 | At {app_name}, we take your privacy seriously. This policy describes how we collect,
23 | use, and protect your personal information.
24 |
25 |
1. Information We Collect
26 |
27 | We collect information you provide directly to us, such as when you create an account.
28 |
29 |
2. How We Use Your Information
30 |
31 | We use your information to provide, maintain, and improve our services.
32 |
33 |
3. Information Sharing
34 |
35 | We do not share your personal information with third parties except as described in this policy.
36 |
37 |
4. Data Security
38 |
39 | We implement appropriate security measures to protect your information.
40 |
41 |
42 |
50 |
51 |
52 |
53 | );
54 | }
55 |
--------------------------------------------------------------------------------
/src/shared/gems_shrine.rb:
--------------------------------------------------------------------------------
1 | copy_file "config/settings/aws_config.rb", force: true
2 | copy_file "config/aws.yml", force: true
3 |
4 | initializer "shrine.rb", <<-CODE
5 | require "shrine"
6 | require "shrine/storage/file_system"
7 | require "shrine/storage/memory"
8 | require "shrine/storage/s3"
9 |
10 | Shrine.plugin :activerecord
11 | Shrine.plugin :backgrounding
12 | Shrine.plugin :cached_attachment_data
13 | Shrine.plugin :derivatives
14 | Shrine.plugin :determine_mime_type, analyzer: :marcel
15 | Shrine.plugin :download_endpoint, prefix: "downloads"
16 | Shrine.plugin :instrumentation, notifications: ActiveSupport::Notifications unless Rails.env.test?
17 | Shrine.plugin :pretty_location
18 | Shrine.plugin :remove_attachment
19 | Shrine.plugin :restore_cached_data
20 | Shrine.plugin :store_dimensions, analyzer: :fastimage
21 | Shrine.plugin :validation
22 | Shrine.plugin :validation_helpers
23 |
24 | ### Storages
25 | s3_options = {
26 | access_key_id: AwsConfig.access_key_id,
27 | secret_access_key: AwsConfig.secret_access_key,
28 | region: AwsConfig.s3_region,
29 | bucket: AwsConfig.s3_bucket
30 | }
31 |
32 | Shrine.storages =
33 | if Rails.env.production?
34 | {
35 | cache: Shrine::Storage::S3.new(prefix: "cache", **s3_options),
36 | store: Shrine::Storage::S3.new(prefix: "store", **s3_options)
37 | }
38 | elsif Rails.env.test?
39 | {
40 | cache: Shrine::Storage::Memory.new,
41 | store: Shrine::Storage::Memory.new
42 | }
43 | else # development and staging
44 | {
45 | cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"),
46 | store: Shrine::Storage::FileSystem.new("public", prefix: "uploads")
47 | }
48 | end
49 |
50 | ### Backgrounding
51 | Shrine::Attacher.promote_block do
52 | job_class = Shrine::PromoteAttachmentJob
53 |
54 | if Rails.env.production?
55 | job_class.perform_later(self.class.name, record.class.name, record.id, name, file_data)
56 | else
57 | job_class.perform_now(self.class.name, record.class.name, record.id, name, file_data)
58 | end
59 | end
60 |
61 | Shrine::Attacher.destroy_block do
62 | job_class = Shrine::DestroyAttachmentJob
63 |
64 | if Rails.env.production?
65 | job_class.perform_later(self.class.name, data)
66 | else
67 | job_class.perform_now(self.class.name, data)
68 | end
69 | end
70 | CODE
71 |
72 | directory "app/uploaders", force: true
73 |
--------------------------------------------------------------------------------
/variants/inertia_vue/app/frontend/lib/components/Flash.vue:
--------------------------------------------------------------------------------
1 |
54 |
55 |
56 |
57 |
58 |
59 |
{{ getFlashMessage() }}
60 |
65 |
66 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/src/inertia_shared/cleanup.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Cleanup unnecessary files created by the Inertia Rails generator
4 | #
5 | # The inertia:install generator creates some files that are not needed
6 | # for our Kickstart configuration:
7 | # 1. package-lock.json - we use Yarn, not npm
8 | # 2. app/frontend/entrypoints/application.js - not used with our Vite setup
9 | # 3. app/assets/stylesheets/ - we use Tailwind via Vite, not the asset pipeline
10 |
11 | say "Cleaning up unnecessary files created by Inertia generator..."
12 |
13 | # Remove package-lock.json if it exists (we use Yarn)
14 | if File.exist?("package-lock.json")
15 | remove_file "package-lock.json"
16 | say " ✓ Removed package-lock.json (using Yarn)", :green
17 | end
18 |
19 | # Remove app/frontend/entrypoints/application.js if it exists
20 | # Inertia uses app/frontend/entrypoints/inertia.{js,ts,tsx} instead
21 | application_js_path = "app/frontend/entrypoints/application.js"
22 | if File.exist?(application_js_path)
23 | remove_file application_js_path
24 | say " ✓ Removed #{application_js_path} (not used)", :green
25 | end
26 |
27 | # Remove app/assets/stylesheets directory if it exists and is empty or only contains application.css
28 | stylesheets_dir = "app/assets/stylesheets"
29 | if Dir.exist?(stylesheets_dir)
30 | # Check if directory only contains application.css or is empty
31 | files = Dir.glob("#{stylesheets_dir}/**/*").select { |f| File.file?(f) }
32 |
33 | if files.empty?
34 | FileUtils.rm_rf(stylesheets_dir)
35 | say " ✓ Removed empty #{stylesheets_dir} directory", :green
36 | elsif files.size == 1 && files.first.end_with?("application.css")
37 | # Remove application.css and then the directory
38 | remove_file files.first
39 | FileUtils.rm_rf(stylesheets_dir)
40 | say " ✓ Removed #{stylesheets_dir} (using Tailwind via Vite)", :green
41 | end
42 | end
43 |
44 | # Fix tsconfig.node.json to reference vite.config.mts instead of vite.config.ts
45 | # The inertia:install generator creates tsconfig.node.json with "vite.config.ts"
46 | # but the actual file created is "vite.config.mts"
47 | tsconfig_node_path = "tsconfig.node.json"
48 | if File.exist?(tsconfig_node_path)
49 | tsconfig_content = File.read(tsconfig_node_path)
50 | if tsconfig_content.include?('"include": ["vite.config.ts"]')
51 | gsub_file tsconfig_node_path, '"include": ["vite.config.ts"]', '"include": ["vite.config.mts"]'
52 | say " ✓ Fixed #{tsconfig_node_path} to reference vite.config.mts", :green
53 | end
54 | end
55 |
56 | say "Cleanup complete!", :green
57 |
--------------------------------------------------------------------------------
/variants/inertia_react/app/frontend/lib/components/Flash.tsx:
--------------------------------------------------------------------------------
1 | import { usePage } from '@inertiajs/react';
2 | import { useEffect, useState } from 'react';
3 |
4 | interface PageProps {
5 | flash: {
6 | notice?: string;
7 | alert?: string;
8 | error?: string;
9 | };
10 | [key: string]: any;
11 | }
12 |
13 | export default function Flash() {
14 | const { flash } = usePage().props;
15 | const [visible, setVisible] = useState(false);
16 |
17 | useEffect(() => {
18 | if (flash?.notice || flash?.alert || flash?.error) {
19 | setVisible(true);
20 | const timer = setTimeout(() => {
21 | setVisible(false);
22 | }, 5000);
23 | return () => clearTimeout(timer);
24 | }
25 | }, [flash]);
26 |
27 | const getFlashType = (): 'notice' | 'alert' | 'error' | null => {
28 | if (flash?.notice) return 'notice';
29 | if (flash?.alert) return 'alert';
30 | if (flash?.error) return 'error';
31 | return null;
32 | };
33 |
34 | const getFlashMessage = (): string => {
35 | const type = getFlashType();
36 | if (!type) return '';
37 | return flash[type] as string;
38 | };
39 |
40 | const getFlashStyles = (): string => {
41 | const type = getFlashType();
42 | switch (type) {
43 | case 'notice':
44 | return 'bg-green-50 text-green-800 border-green-200';
45 | case 'error':
46 | return 'bg-red-50 text-red-800 border-red-200';
47 | case 'alert':
48 | return 'bg-yellow-50 text-yellow-800 border-yellow-200';
49 | default:
50 | return '';
51 | }
52 | };
53 |
54 | if (!visible || !getFlashType()) {
55 | return null;
56 | }
57 |
58 | return (
59 |
60 |
61 |
62 |
{getFlashMessage()}
63 |
setVisible(false)}
65 | className="ml-4 text-current opacity-70 hover:opacity-100"
66 | aria-label="Close notification"
67 | >
68 |
69 |
74 |
75 |
76 |
77 |
78 |
79 | );
80 | }
81 |
--------------------------------------------------------------------------------
/variants/shared/app/helpers/active_link_to_helper.rb:
--------------------------------------------------------------------------------
1 | module ActiveLinkToHelper
2 | # Creates a link with 'active' class when current page matches the target
3 | # Simplified version of active_link_to gem with only essential functionality
4 | #
5 | # Examples:
6 | # active_link_to "Home", root_path
7 | # active_link_to "Users", users_path, match: :exact
8 | # active_link_to "Dashboard", dashboard_path, active_class: "current"
9 | #
10 | # @param name [String] Link text
11 | # @param target [String, Hash] Path or URL for the link
12 | # @param options [Hash] Additional options
13 | # @option options [Symbol] :match (:inclusive) Match type - :exact, :inclusive
14 | # @option options [String] :active_class ("active") CSS class for active state
15 | # @option options [Hash] :html_options ({}) Standard link_to HTML options
16 | def active_link_to(name, target = nil, options = {}, &block)
17 | # Handle block syntax like standard link_to
18 | if block_given?
19 | options = target || {}
20 | target = name
21 | name = capture(&block)
22 | end
23 |
24 | # Set default options
25 | match_type = options.delete(:match) || :inclusive
26 | active_class = options.delete(:active_class) || "active"
27 |
28 | # Support legacy html_options parameter
29 | if options[:html_options]
30 | html_options = options.delete(:html_options)
31 | options = options.merge(html_options)
32 | end
33 |
34 | # Determine if link should be active
35 | is_active = link_active?(target, match_type)
36 |
37 | # Add active class if needed
38 | if is_active
39 | current_classes = options[:class].to_s
40 | css_classes = [current_classes, active_class].compact.reject(&:empty?).join(" ")
41 | options[:class] = css_classes unless css_classes.empty?
42 | end
43 |
44 | # Generate the link
45 | link_to(name, target, options)
46 | end
47 |
48 | private
49 |
50 | # Check if the link should be considered active
51 | # @param target [String, Hash] The link target
52 | # @param match_type [Symbol] How to match - :exact or :inclusive
53 | # @return [Boolean] Whether the link is active
54 | def link_active?(target, match_type)
55 | target_path = normalize_path(target)
56 | current_path = request.path
57 |
58 | case match_type
59 | when :exact
60 | current_path == target_path
61 | when :inclusive
62 | return true if current_path == target_path
63 | return false if target_path == "/"
64 | current_path.start_with?(target_path + "/") || current_path.start_with?(target_path + "?") || current_path.start_with?(target_path + "#")
65 | else
66 | false
67 | end
68 | rescue
69 | # Return false if path generation fails
70 | false
71 | end
72 |
73 | # Convert target to normalized path string
74 | # @param target [String, Hash] Link target
75 | # @return [String] Normalized path
76 | def normalize_path(target)
77 | case target
78 | when String
79 | target
80 | when Hash
81 | url_for(target)
82 | else
83 | polymorphic_path(target)
84 | end
85 | rescue
86 | # Return empty string if path generation fails
87 | ""
88 | end
89 | end
90 |
--------------------------------------------------------------------------------
/src/inertia_shared/gems_inertia.rb:
--------------------------------------------------------------------------------
1 | # Determine framework from template name
2 | framework = case TEMPLATE_NAME
3 | when "inertia_react" then "react"
4 | when "inertia_vue" then "vue"
5 | when "inertia_svelte" then "svelte"
6 | else
7 | raise "Unknown Inertia template: #{TEMPLATE_NAME}"
8 | end
9 |
10 | # Run Inertia Rails generator with the appropriate framework
11 | # Generator will install Vite automatically and modify application.html.erb
12 | # Options:
13 | # --framework - Use React/Vue/Svelte for UI
14 | # --typescript - Enable TypeScript support
15 | # --no-interactive - Skip interactive prompts
16 | # --tailwind - Install and configure Tailwind CSS
17 | # --vite - Use Vite for bundling
18 | # --no-example-page - Skip creating example page (we'll create our own)
19 | # --force - Force overwrite bin/dev and other files
20 | rails_command "generate inertia:install --framework=#{framework} --typescript --no-interactive --tailwind --vite --no-example-page --force"
21 |
22 | # Fix bin/vite creation - vite_ruby's install command uses deprecated bundler --path flag
23 | # Create the binstub manually using modern bundler syntax
24 | run "bundle binstub vite_ruby"
25 |
26 | # Create Inertia controller concern for shared data
27 | create_file "app/controllers/concerns/inertia_share.rb", <<-RUBY
28 | module InertiaShare
29 | extend ActiveSupport::Concern
30 |
31 | included do
32 | inertia_share do
33 | {
34 | app_name: MainConfig.app_name,
35 | flash: {
36 | notice: flash[:notice],
37 | alert: flash[:alert],
38 | error: flash[:error]
39 | }.compact
40 | }
41 | end
42 | end
43 | end
44 | RUBY
45 |
46 | # Update ApplicationController to include InertiaShare
47 | inject_into_file "app/controllers/application_controller.rb", after: "class ApplicationController < ActionController::Base\n" do
48 | " include InertiaShare\n"
49 | end
50 |
51 | # Copy base layouts, lib (with components), and pages
52 | directory "app/frontend/layouts", "app/frontend/layouts"
53 | directory "app/frontend/lib", "app/frontend/lib"
54 | directory "app/frontend/pages", "app/frontend/pages"
55 |
56 | # Create landing controller and route for home page
57 | create_file "app/controllers/landing_controller.rb", <<-RUBY
58 | class LandingController < ApplicationController
59 | def home
60 | render inertia: "Home"
61 | end
62 |
63 | def about
64 | render inertia: "About"
65 | end
66 | end
67 | RUBY
68 |
69 | # Create pages controller for static pages
70 | create_file "app/controllers/pages_controller.rb", <<-RUBY
71 | class PagesController < ApplicationController
72 | def terms
73 | render inertia: "Terms"
74 | end
75 |
76 | def privacy
77 | render inertia: "Privacy"
78 | end
79 | end
80 | RUBY
81 |
82 | # Copy routes.rb and route partials (after Inertia generator to avoid overwriting)
83 | copy_file "config/routes.rb", force: true
84 |
85 | # Copy individual route files from variants
86 | # Note: directory command doesn't work well with source_paths priority
87 | empty_directory "config/routes"
88 | copy_file "config/routes/landing.rb", force: true
89 | copy_file "config/routes/pages.rb", force: true
90 | copy_file "config/routes/support.rb", force: true
91 |
92 | # These files are in inertia_shared (lower priority in source_paths)
93 | # so we need to copy them explicitly from the template source
94 | inside "config/routes" do
95 | create_file "dev.rb", File.read(File.join(source_paths[1], "config/routes/dev.rb"))
96 | create_file "errors.rb", File.read(File.join(source_paths[1], "config/routes/errors.rb"))
97 | end
98 |
99 | # Configure Inertia Rails
100 | create_file "config/initializers/inertia.rb", <<~RUBY
101 | # frozen_string_literal: true
102 |
103 | # Inertia Rails Configuration
104 | # https://inertia-rails.dev
105 |
106 | InertiaRails.configure do |config|
107 | # Always include errors hash for Inertia protocol compliance (InertiaRails 4.0+)
108 | config.always_include_errors_hash = true
109 | end
110 | RUBY
111 |
112 | say "✓ Inertia Rails configured with #{framework}", :green
113 |
--------------------------------------------------------------------------------
/src/inertia_shared/finalize_layouts.rb:
--------------------------------------------------------------------------------
1 | # Finalize layouts after Inertia generator has run
2 | # This ensures we have our custom layout with all necessary tags
3 |
4 | say "Finalizing Inertia layouts..."
5 |
6 | # Copy our final application layout (with proper Inertia and Vite tags)
7 | copy_file "app/views/layouts/application.html.erb", force: true
8 |
9 | # Update tsconfig to add $lib/* path mapping
10 | # React/Vue use tsconfig.app.json, Svelte uses tsconfig.json
11 | tsconfig_file = if File.exist?("tsconfig.app.json")
12 | "tsconfig.app.json"
13 | elsif File.exist?("tsconfig.json")
14 | "tsconfig.json"
15 | end
16 |
17 | if tsconfig_file
18 | inject_into_file tsconfig_file, after: '"paths": {' do
19 | "\n \"$lib/*\": [\"app/frontend/lib/*\"],"
20 | end
21 | say "✓ Added $lib/* path mapping to #{tsconfig_file}", :green
22 | else
23 | say "⚠ Warning: tsconfig file not found, skipping $lib/* path mapping", :yellow
24 | end
25 |
26 | # Install @types/node for TypeScript support in vite.config
27 | # This provides type definitions for path, __dirname, etc.
28 | run "yarn add -D @types/node"
29 |
30 | # Set package.json type to module for ES modules support
31 | # Svelte generator doesn't add these fields, so we add them after yarn install
32 | if File.exist?("package.json")
33 | require "json"
34 | package_json_path = "package.json"
35 | package_data = JSON.parse(File.read(package_json_path))
36 |
37 | needs_update = false
38 |
39 | # Add "private": true if not present
40 | unless package_data.key?("private")
41 | package_data["private"] = true
42 | needs_update = true
43 | say "✓ Adding private: true to package.json", :green
44 | end
45 |
46 | # Add "type": "module" if not present
47 | unless package_data.key?("type")
48 | package_data["type"] = "module"
49 | needs_update = true
50 | say "✓ Adding type: module to package.json", :green
51 | end
52 |
53 | if needs_update
54 | # Write back to file, preserving JSON format
55 | File.write(package_json_path, JSON.pretty_generate(package_data) + "\n")
56 | say "✓ Updated package.json", :green
57 | end
58 | end
59 |
60 | # Update tsconfig.node.json to include node types
61 | # This allows TypeScript to recognize node modules in vite.config
62 | if File.exist?("tsconfig.node.json")
63 | inject_into_file "tsconfig.node.json", after: '"noEmit": true' do
64 | ',
65 | "types": ["node"]'
66 | end
67 | say "✓ Added node types to tsconfig.node.json", :green
68 | end
69 |
70 | # Update vite.config to add resolve.alias for runtime resolution
71 | # This is needed for Vite to resolve the paths correctly
72 | vite_config = if File.exist?("vite.config.ts")
73 | "vite.config.ts"
74 | elsif File.exist?("vite.config.mts")
75 | "vite.config.mts"
76 | end
77 |
78 | if vite_config
79 | is_mts = vite_config.end_with?(".mts")
80 |
81 | # Add imports at the top
82 | if is_mts
83 | # For ES modules (.mts), need to define __dirname
84 | inject_into_file vite_config, after: "import RubyPlugin from 'vite-plugin-ruby'\n" do
85 | <<~JS
86 | import path from "path";
87 | import { fileURLToPath } from "url";
88 |
89 | const __dirname = path.dirname(fileURLToPath(import.meta.url));
90 | JS
91 | end
92 | else
93 | # For regular .ts files
94 | inject_into_file vite_config, after: "import RubyPlugin from 'vite-plugin-ruby'\n" do
95 | 'import path from "path"\n'
96 | end
97 | end
98 |
99 | # Add resolve.alias config
100 | inject_into_file vite_config, after: "export default defineConfig({\n" do
101 | <<~JS
102 | resolve: {
103 | alias: {
104 | "@": path.resolve(__dirname, "./app/frontend"),
105 | "~": path.resolve(__dirname, "./app/frontend"),
106 | "$lib": path.resolve(__dirname, "./app/frontend/lib"),
107 | },
108 | },
109 | JS
110 | end
111 | say "✓ Added resolve.alias to #{vite_config}", :green
112 | else
113 | say "⚠ Warning: vite.config not found, skipping alias setup", :yellow
114 | end
115 |
116 | say "Layouts finalized with Inertia and Vite configuration"
117 |
--------------------------------------------------------------------------------
/api.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | #==============================================================================
4 | # CONSTANTS - Template configuration and shared values
5 | #==============================================================================
6 |
7 | REPO_LINK = "https://github.com/alec-c4/kickstart.git"
8 | AVAILABLE_TEMPLATE_NAMES = %w[api importmap_tailwind esbuild_tailwind inertia_svelte inertia_react inertia_vue].freeze
9 | TEMPLATE_NAME = "api"
10 | RAILS_REQUIREMENT = ">= 8.1.0"
11 |
12 | TEMPLATE_METADATA = {
13 | name: "api",
14 | description: "Rails API-only application with essential setup",
15 | features: %w[postgresql devcontainer rspec rubocop uuid i18n kamal solid_queue solid_cache solid_cable],
16 | rails_version: RAILS_REQUIREMENT
17 | }.freeze
18 |
19 | #==============================================================================
20 | # SHARED CODE - Embedded functions (auto-generated, do not edit manually)
21 | #==============================================================================
22 |
23 | require "rails/all"
24 |
25 | def assert_minimum_rails_version
26 | requirement = Gem::Requirement.new(RAILS_REQUIREMENT)
27 | rails_version = Gem::Version.new(Rails::VERSION::STRING)
28 | return if requirement.satisfied_by?(rails_version)
29 |
30 | prompt = "This template requires Rails #{RAILS_REQUIREMENT}. "\
31 | "You are using #{rails_version}. Continue anyway?"
32 | exit 1 if no?(prompt)
33 | end
34 |
35 | def gemfile_requirement(name)
36 | @original_gemfile ||= IO.read("Gemfile")
37 | req = @original_gemfile[/gem\s+['"]#{name}['"]\s*(,[><~= \t\d.\w'"]*)?.*$/, 1]
38 | req && req.tr("'", %(")).strip.sub(/^,\s*"/, ', "')
39 | end
40 |
41 | def add_template_repository_to_source_path
42 | if __FILE__.match?(%r{\Ahttps?://})
43 | require "tmpdir"
44 | source_paths.unshift(tempdir = Dir.mktmpdir("kickstart-tmp"))
45 | at_exit { FileUtils.remove_entry(tempdir) }
46 | git clone: [
47 | "--quiet",
48 | REPO_LINK,
49 | tempdir
50 | ].map(&:shellescape).join(" ")
51 |
52 | # Check for specific branch in URL path (e.g., /kickstart/branch_name/template.rb)
53 | template_pattern = "(?:#{AVAILABLE_TEMPLATE_NAMES.join('|')})"
54 | if (branch = __FILE__[%r{kickstart/(.+)/#{template_pattern}\.rb}, 1])
55 | Dir.chdir(tempdir) { git checkout: branch }
56 | end
57 | else
58 | # For local files, add the template root directory
59 | template_root = File.dirname(__FILE__)
60 | source_paths.unshift(template_root)
61 | end
62 | end
63 |
64 | def set_variant_source_path(variant_name = nil)
65 | template_root = if __FILE__.match?(%r{\Ahttps?://})
66 | source_paths.first
67 | else
68 | File.dirname(__FILE__)
69 | end
70 |
71 | if variant_name
72 | variant_path = File.join(template_root, "variants", variant_name)
73 | source_paths.unshift(variant_path) if File.directory?(variant_path)
74 | end
75 |
76 | shared_path = File.join(template_root, "variants", "shared")
77 | source_paths.unshift(shared_path) if File.directory?(shared_path)
78 | end
79 |
80 | def show_post_install_message
81 | say "\n
82 | #########################################################################################
83 |
84 | Rails application '#{app_name}' created successfully!
85 |
86 | Next steps:
87 | $ cd #{app_name}
88 | $ bundle install
89 | $ rails db:create db:migrate
90 | $ rails parallel:create # Creates parallel test databases (ignore 'already exists' message)
91 | $ bin/dev
92 |
93 | #########################################################################################\n", :green
94 | end
95 |
96 | #==============================================================================
97 | # TEMPLATE LOGIC - Template-specific configuration and workflow
98 | #==============================================================================
99 |
100 | assert_minimum_rails_version
101 | add_template_repository_to_source_path
102 | set_variant_source_path(TEMPLATE_NAME)
103 |
104 | apply "src/shared/general.rb"
105 | apply "src/shared/packages.rb"
106 |
107 | after_bundle do
108 | apply "src/shared/init_generators.rb"
109 | apply "src/shared/init_db_cli.rb"
110 | apply "src/shared/solid_queue_setup.rb"
111 | apply "src/shared/init_i18n.rb"
112 |
113 | apply "src/shared/env_rubocop.rb"
114 | apply "src/shared/migrations_uuid.rb"
115 |
116 | apply "src/shared/gems_anyway_config.rb"
117 | apply "src/shared/gems_pagy.rb"
118 | apply "src/shared/gems_active_interaction.rb"
119 | apply "src/shared/gems_rspec.rb"
120 | apply "src/shared/gems_i18n_tasks.rb"
121 |
122 | apply "src/shared/gems_lockbox.rb"
123 | apply "src/shared/gems_shrine.rb"
124 |
125 | apply "src/shared/ci.rb"
126 |
127 | apply "src/shared/docs.rb"
128 | apply "src/shared/staging_env.rb"
129 | apply "src/shared/run_rubocop.rb"
130 | apply "src/shared/git_init.rb"
131 |
132 | show_post_install_message
133 | end
134 |
--------------------------------------------------------------------------------
/variants/shared/config/database.yml.tt:
--------------------------------------------------------------------------------
1 | # PostgreSQL. Versions 9.3 and up are supported.
2 | #
3 | # Install the pg driver:
4 | # gem install pg
5 | # On macOS with Homebrew:
6 | # gem install pg -- --with-pg-config=/opt/homebrew/bin/pg_config
7 | # On Windows:
8 | # gem install pg
9 | # Choose the win32 build.
10 | # Install PostgreSQL and put its /bin directory on your path.
11 | #
12 | # Configure Using Gemfile
13 | # gem "pg"
14 | #
15 | default: &default
16 | adapter: postgresql
17 | encoding: unicode
18 | # For details on connection pooling, see Rails configuration guide
19 | # https://guides.rubyonrails.org/configuring.html#database-pooling
20 | max_connections: <%%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
21 | <%% if ENV["DB_HOST"] %>
22 | host: <%%= ENV["DB_HOST"] %>
23 | username: postgres
24 | password: postgres
25 | <%% end %>
26 |
27 |
28 | development:
29 | primary: &primary_development
30 | <<: *default
31 | database: <%= app_name %>_development
32 | cache:
33 | <<: *primary_development
34 | database: <%= app_name %>_development_cache
35 | migrations_paths: db/cache_migrate
36 | queue:
37 | <<: *primary_development
38 | database: <%= app_name %>_development_queue
39 | migrations_paths: db/queue_migrate
40 | cable:
41 | <<: *primary_development
42 | database: <%= app_name %>_development_cable
43 | migrations_paths: db/cable_migrate
44 |
45 | # The specified database role being used to connect to PostgreSQL.
46 | # To create additional roles in PostgreSQL see `$ createuser --help`.
47 | # When left blank, PostgreSQL will use the default role. This is
48 | # the same name as the operating system user running Rails.
49 | #username: <%= app_name %>
50 |
51 | # The password associated with the PostgreSQL role (username).
52 | #password:
53 |
54 | # Connect on a TCP socket. Omitted by default since the client uses a
55 | # domain socket that doesn't need configuration. Windows does not have
56 | # domain sockets, so uncomment these lines.
57 | #host: localhost
58 |
59 | # The TCP port the server listens on. Defaults to 5432.
60 | # If your server runs on a different port number, change accordingly.
61 | #port: 5432
62 |
63 | # Schema search path. The server defaults to $user,public
64 | #schema_search_path: myapp,sharedapp,public
65 |
66 | # Minimum log levels, in increasing order:
67 | # debug5, debug4, debug3, debug2, debug1,
68 | # log, notice, warning, error, fatal, and panic
69 | # Defaults to warning.
70 | #min_messages: notice
71 |
72 | # Warning: The database defined as "test" will be erased and
73 | # re-generated from your development database when you run "rake".
74 | # Do not set this db to the same as development or production.
75 | test:
76 | <<: *default
77 | database: <%= app_name %>_test<%%= ENV['TEST_ENV_NUMBER'] %>
78 |
79 | # As with config/credentials.yml, you never want to store sensitive information,
80 | # like your database password, in your source code. If your source code is
81 | # ever seen by anyone, they now have access to your database.
82 | #
83 | # Instead, provide the password or a full connection URL as an environment
84 | # variable when you boot the app. For example:
85 | #
86 | # DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase"
87 | #
88 | # If the connection URL is provided in the special DATABASE_URL environment
89 | # variable, Rails will automatically merge its configuration values on top of
90 | # the values provided in this file. Alternatively, you can specify a connection
91 | # URL environment variable explicitly:
92 | #
93 | # production:
94 | # url: <%%= ENV["MY_APP_DATABASE_URL"] %>
95 | #
96 | # Connection URLs for non-primary databases can also be configured using
97 | # environment variables. The variable name is formed by concatenating the
98 | # connection name with `_DATABASE_URL`. For example:
99 | #
100 | # CACHE_DATABASE_URL="postgres://cacheuser:cachepass@localhost/cachedatabase"
101 | #
102 | # Read https://guides.rubyonrails.org/configuring.html#configuring-a-database
103 | # for a full overview on how database connection configuration can be specified.
104 | #
105 | production:
106 | primary: &primary_production
107 | <<: *default
108 | database: <%= app_name %>_production
109 | username: <%= app_name %>
110 | password: <%%= ENV["<%= app_name.upcase %>_DATABASE_PASSWORD"] %>
111 | cache:
112 | <<: *primary_production
113 | database: <%= app_name %>_production_cache
114 | migrations_paths: db/cache_migrate
115 | queue:
116 | <<: *primary_production
117 | database: <%= app_name %>_production_queue
118 | migrations_paths: db/queue_migrate
119 | cable:
120 | <<: *primary_production
121 | database: <%= app_name %>_production_cable
122 | migrations_paths: db/cable_migrate
123 |
124 | staging:
125 | primary: &primary_staging
126 | <<: *default
127 | database: <%%= ENV.fetch("DATABASE_NAME") { "<%= app_name %>_staging" } %>
128 | username: <%%= ENV.fetch("DATABASE_USERNAME") { "<%= app_name %>" } %>
129 | password: <%%= ENV["DATABASE_PASSWORD"] %>
130 | cache:
131 | <<: *primary_staging
132 | database: <%%= ENV.fetch("DATABASE_NAME") { "<%= app_name %>_staging_cache" } %>
133 | migrations_paths: db/cache_migrate
134 | queue:
135 | <<: *primary_staging
136 | database: <%%= ENV.fetch("DATABASE_NAME") { "<%= app_name %>_staging_queue" } %>
137 | migrations_paths: db/queue_migrate
138 | cable:
139 | <<: *primary_staging
140 | database: <%%= ENV.fetch("DATABASE_NAME") { "<%= app_name %>_staging_cable" } %>
141 | migrations_paths: db/cable_migrate
142 |
--------------------------------------------------------------------------------
/inertia_vue.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | #==============================================================================
4 | # CONSTANTS - Template configuration and shared values
5 | #==============================================================================
6 |
7 | REPO_LINK = "https://github.com/alec-c4/kickstart.git"
8 | AVAILABLE_TEMPLATE_NAMES = %w[api importmap_tailwind esbuild_tailwind inertia_svelte inertia_react inertia_vue].freeze
9 | TEMPLATE_NAME = "inertia_vue"
10 | RAILS_REQUIREMENT = ">= 8.1.0"
11 |
12 | TEMPLATE_METADATA = {
13 | name: "inertia_vue",
14 | description: "Rails app with Inertia.js and Vue for modern SPA development",
15 | features: %w[postgresql devcontainer rspec rubocop uuid i18n tailwind vite inertia vue kamal solid_queue
16 | solid_cache solid_cable],
17 | rails_version: RAILS_REQUIREMENT
18 | }.freeze
19 |
20 | #==============================================================================
21 | # SHARED CODE - Embedded functions (auto-generated, do not edit manually)
22 | #==============================================================================
23 |
24 | require "rails/all"
25 |
26 | def assert_minimum_rails_version
27 | requirement = Gem::Requirement.new(RAILS_REQUIREMENT)
28 | rails_version = Gem::Version.new(Rails::VERSION::STRING)
29 | return if requirement.satisfied_by?(rails_version)
30 |
31 | prompt = "This template requires Rails #{RAILS_REQUIREMENT}. "\
32 | "You are using #{rails_version}. Continue anyway?"
33 | exit 1 if no?(prompt)
34 | end
35 |
36 | def gemfile_requirement(name)
37 | @original_gemfile ||= IO.read("Gemfile")
38 | req = @original_gemfile[/gem\s+['"]#{name}['"]\s*(,[><~= \t\d.\w'"]*)?.*$/, 1]
39 | req && req.tr("'", %(")).strip.sub(/^,\s*"/, ', "')
40 | end
41 |
42 | def add_template_repository_to_source_path
43 | if __FILE__.match?(%r{\Ahttps?://})
44 | require "tmpdir"
45 | source_paths.unshift(tempdir = Dir.mktmpdir("kickstart-tmp"))
46 | at_exit { FileUtils.remove_entry(tempdir) }
47 | git clone: [
48 | "--quiet",
49 | REPO_LINK,
50 | tempdir
51 | ].map(&:shellescape).join(" ")
52 |
53 | # Check for specific branch in URL path (e.g., /kickstart/branch_name/template.rb)
54 | template_pattern = "(?:#{AVAILABLE_TEMPLATE_NAMES.join('|')})"
55 | if (branch = __FILE__[%r{kickstart/(.+)/#{template_pattern}\.rb}, 1])
56 | Dir.chdir(tempdir) { git checkout: branch }
57 | end
58 | else
59 | # For local files, add the template root directory
60 | template_root = File.dirname(__FILE__)
61 | source_paths.unshift(template_root)
62 | end
63 | end
64 |
65 | def set_variant_source_path(variant_name = nil)
66 | template_root = if __FILE__.match?(%r{\Ahttps?://})
67 | source_paths.first
68 | else
69 | File.dirname(__FILE__)
70 | end
71 |
72 | # 1. Universal shared (lowest priority)
73 | shared_path = File.join(template_root, "variants", "shared")
74 | source_paths.unshift(shared_path) if File.directory?(shared_path)
75 |
76 | # 2. Inertia shared (medium priority)
77 | inertia_shared_path = File.join(template_root, "variants", "inertia_shared")
78 | source_paths.unshift(inertia_shared_path) if File.directory?(inertia_shared_path)
79 |
80 | # 3. Variant-specific (highest priority)
81 | if variant_name
82 | variant_path = File.join(template_root, "variants", variant_name)
83 | source_paths.unshift(variant_path) if File.directory?(variant_path)
84 | end
85 | end
86 |
87 | def show_post_install_message
88 | say "\n
89 | #########################################################################################
90 |
91 | Rails application '#{app_name}' created successfully with Inertia.js + Vue!
92 |
93 | Next steps:
94 | $ cd #{app_name}
95 | $ bundle install
96 | $ yarn install
97 | $ rails db:create db:migrate
98 | $ rails parallel:create # Creates parallel test databases (ignore 'already exists' message)
99 | $ bin/dev
100 |
101 | #########################################################################################\n", :green
102 | end
103 |
104 | #==============================================================================
105 | # TEMPLATE LOGIC - Template-specific configuration and workflow
106 | #==============================================================================
107 |
108 | assert_minimum_rails_version
109 | add_template_repository_to_source_path
110 | set_variant_source_path(TEMPLATE_NAME)
111 |
112 | apply "src/inertia_shared/general.rb"
113 | apply "src/shared/yarnconfig.rb"
114 | apply "src/shared/packages.rb"
115 |
116 | after_bundle do
117 | apply "src/inertia_shared/gems_inertia.rb"
118 | apply "src/inertia_shared/cleanup.rb"
119 |
120 | apply "src/inertia_shared/init_generators.rb"
121 | apply "src/shared/init_db_cli.rb"
122 | apply "src/shared/solid_queue_setup.rb"
123 | apply "src/shared/init_i18n.rb"
124 |
125 | apply "src/shared/env_rubocop.rb"
126 | apply "src/shared/migrations_uuid.rb"
127 |
128 | apply "src/shared/gems_anyway_config.rb"
129 | apply "src/shared/gems_pagy.rb"
130 | apply "src/shared/gems_active_interaction.rb"
131 | apply "src/shared/gems_active_decorator.rb"
132 |
133 | apply "src/shared/gems_rspec.rb"
134 | apply "src/inertia_shared/rspec_inertia.rb"
135 | apply "src/shared/gems_i18n_tasks.rb"
136 | apply "src/shared/gems_lockbox.rb"
137 | apply "src/shared/gems_shrine.rb"
138 |
139 | apply "src/inertia_shared/custom_error_pages.rb"
140 |
141 | apply "src/shared/ci.rb"
142 |
143 | apply "src/shared/docs.rb"
144 | apply "src/shared/staging_env.rb"
145 | apply "src/inertia_shared/finalize_layouts.rb"
146 | apply "src/shared/run_rubocop.rb"
147 | apply "src/shared/git_init.rb"
148 |
149 | show_post_install_message
150 | end
151 |
--------------------------------------------------------------------------------
/inertia_react.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | #==============================================================================
4 | # CONSTANTS - Template configuration and shared values
5 | #==============================================================================
6 |
7 | REPO_LINK = "https://github.com/alec-c4/kickstart.git"
8 | AVAILABLE_TEMPLATE_NAMES = %w[api importmap_tailwind esbuild_tailwind inertia_svelte inertia_react inertia_vue].freeze
9 | TEMPLATE_NAME = "inertia_react"
10 | RAILS_REQUIREMENT = ">= 8.1.0"
11 |
12 | TEMPLATE_METADATA = {
13 | name: "inertia_react",
14 | description: "Rails app with Inertia.js and React for modern SPA development",
15 | features: %w[postgresql devcontainer rspec rubocop uuid i18n tailwind vite inertia react kamal solid_queue
16 | solid_cache solid_cable],
17 | rails_version: RAILS_REQUIREMENT
18 | }.freeze
19 |
20 | #==============================================================================
21 | # SHARED CODE - Embedded functions (auto-generated, do not edit manually)
22 | #==============================================================================
23 |
24 | require "rails/all"
25 |
26 | def assert_minimum_rails_version
27 | requirement = Gem::Requirement.new(RAILS_REQUIREMENT)
28 | rails_version = Gem::Version.new(Rails::VERSION::STRING)
29 | return if requirement.satisfied_by?(rails_version)
30 |
31 | prompt = "This template requires Rails #{RAILS_REQUIREMENT}. "\
32 | "You are using #{rails_version}. Continue anyway?"
33 | exit 1 if no?(prompt)
34 | end
35 |
36 | def gemfile_requirement(name)
37 | @original_gemfile ||= IO.read("Gemfile")
38 | req = @original_gemfile[/gem\s+['"]#{name}['"]\s*(,[><~= \t\d.\w'"]*)?.*$/, 1]
39 | req && req.tr("'", %(")).strip.sub(/^,\s*"/, ', "')
40 | end
41 |
42 | def add_template_repository_to_source_path
43 | if __FILE__.match?(%r{\Ahttps?://})
44 | require "tmpdir"
45 | source_paths.unshift(tempdir = Dir.mktmpdir("kickstart-tmp"))
46 | at_exit { FileUtils.remove_entry(tempdir) }
47 | git clone: [
48 | "--quiet",
49 | REPO_LINK,
50 | tempdir
51 | ].map(&:shellescape).join(" ")
52 |
53 | # Check for specific branch in URL path (e.g., /kickstart/branch_name/template.rb)
54 | template_pattern = "(?:#{AVAILABLE_TEMPLATE_NAMES.join('|')})"
55 | if (branch = __FILE__[%r{kickstart/(.+)/#{template_pattern}\.rb}, 1])
56 | Dir.chdir(tempdir) { git checkout: branch }
57 | end
58 | else
59 | # For local files, add the template root directory
60 | template_root = File.dirname(__FILE__)
61 | source_paths.unshift(template_root)
62 | end
63 | end
64 |
65 | def set_variant_source_path(variant_name = nil)
66 | template_root = if __FILE__.match?(%r{\Ahttps?://})
67 | source_paths.first
68 | else
69 | File.dirname(__FILE__)
70 | end
71 |
72 | # 1. Universal shared (lowest priority)
73 | shared_path = File.join(template_root, "variants", "shared")
74 | source_paths.unshift(shared_path) if File.directory?(shared_path)
75 |
76 | # 2. Inertia shared (medium priority)
77 | inertia_shared_path = File.join(template_root, "variants", "inertia_shared")
78 | source_paths.unshift(inertia_shared_path) if File.directory?(inertia_shared_path)
79 |
80 | # 3. Variant-specific (highest priority)
81 | if variant_name
82 | variant_path = File.join(template_root, "variants", variant_name)
83 | source_paths.unshift(variant_path) if File.directory?(variant_path)
84 | end
85 | end
86 |
87 | def show_post_install_message
88 | say "\n
89 | #########################################################################################
90 |
91 | Rails application '#{app_name}' created successfully with Inertia.js + React!
92 |
93 | Next steps:
94 | $ cd #{app_name}
95 | $ bundle install
96 | $ yarn install
97 | $ rails db:create db:migrate
98 | $ rails parallel:create # Creates parallel test databases (ignore 'already exists' message)
99 | $ bin/dev
100 |
101 | #########################################################################################\n", :green
102 | end
103 |
104 | #==============================================================================
105 | # TEMPLATE LOGIC - Template-specific configuration and workflow
106 | #==============================================================================
107 |
108 | assert_minimum_rails_version
109 | add_template_repository_to_source_path
110 | set_variant_source_path(TEMPLATE_NAME)
111 |
112 | apply "src/inertia_shared/general.rb"
113 | apply "src/shared/yarnconfig.rb"
114 | apply "src/shared/packages.rb"
115 |
116 | after_bundle do
117 | apply "src/inertia_shared/gems_inertia.rb"
118 | apply "src/inertia_shared/cleanup.rb"
119 |
120 | apply "src/inertia_shared/init_generators.rb"
121 | apply "src/shared/init_db_cli.rb"
122 | apply "src/shared/solid_queue_setup.rb"
123 | apply "src/shared/init_i18n.rb"
124 |
125 | apply "src/shared/env_rubocop.rb"
126 | apply "src/shared/migrations_uuid.rb"
127 |
128 | apply "src/shared/gems_anyway_config.rb"
129 | apply "src/shared/gems_pagy.rb"
130 | apply "src/shared/gems_active_interaction.rb"
131 | apply "src/shared/gems_active_decorator.rb"
132 |
133 | apply "src/shared/gems_rspec.rb"
134 | apply "src/inertia_shared/rspec_inertia.rb"
135 | apply "src/shared/gems_i18n_tasks.rb"
136 | apply "src/shared/gems_lockbox.rb"
137 | apply "src/shared/gems_shrine.rb"
138 |
139 | apply "src/inertia_shared/custom_error_pages.rb"
140 |
141 | apply "src/shared/ci.rb"
142 |
143 | apply "src/shared/docs.rb"
144 | apply "src/shared/staging_env.rb"
145 | apply "src/inertia_shared/finalize_layouts.rb"
146 | apply "src/shared/run_rubocop.rb"
147 | apply "src/shared/git_init.rb"
148 |
149 | show_post_install_message
150 | end
151 |
--------------------------------------------------------------------------------
/inertia_svelte.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | #==============================================================================
4 | # CONSTANTS - Template configuration and shared values
5 | #==============================================================================
6 |
7 | REPO_LINK = "https://github.com/alec-c4/kickstart.git"
8 | AVAILABLE_TEMPLATE_NAMES = %w[api importmap_tailwind esbuild_tailwind inertia_svelte inertia_react inertia_vue].freeze
9 | TEMPLATE_NAME = "inertia_svelte"
10 | RAILS_REQUIREMENT = ">= 8.1.0"
11 |
12 | TEMPLATE_METADATA = {
13 | name: "inertia_svelte",
14 | description: "Rails app with Inertia.js and Svelte 5 for modern SPA development",
15 | features: %w[postgresql devcontainer rspec rubocop uuid i18n tailwind vite inertia svelte5 kamal solid_queue
16 | solid_cache solid_cable],
17 | rails_version: RAILS_REQUIREMENT
18 | }.freeze
19 |
20 | #==============================================================================
21 | # SHARED CODE - Embedded functions (auto-generated, do not edit manually)
22 | #==============================================================================
23 |
24 | require "rails/all"
25 |
26 | def assert_minimum_rails_version
27 | requirement = Gem::Requirement.new(RAILS_REQUIREMENT)
28 | rails_version = Gem::Version.new(Rails::VERSION::STRING)
29 | return if requirement.satisfied_by?(rails_version)
30 |
31 | prompt = "This template requires Rails #{RAILS_REQUIREMENT}. "\
32 | "You are using #{rails_version}. Continue anyway?"
33 | exit 1 if no?(prompt)
34 | end
35 |
36 | def gemfile_requirement(name)
37 | @original_gemfile ||= IO.read("Gemfile")
38 | req = @original_gemfile[/gem\s+['"]#{name}['"]\s*(,[><~= \t\d.\w'"]*)?.*$/, 1]
39 | req && req.tr("'", %(")).strip.sub(/^,\s*"/, ', "')
40 | end
41 |
42 | def add_template_repository_to_source_path
43 | if __FILE__.match?(%r{\Ahttps?://})
44 | require "tmpdir"
45 | source_paths.unshift(tempdir = Dir.mktmpdir("kickstart-tmp"))
46 | at_exit { FileUtils.remove_entry(tempdir) }
47 | git clone: [
48 | "--quiet",
49 | REPO_LINK,
50 | tempdir
51 | ].map(&:shellescape).join(" ")
52 |
53 | # Check for specific branch in URL path (e.g., /kickstart/branch_name/template.rb)
54 | template_pattern = "(?:#{AVAILABLE_TEMPLATE_NAMES.join('|')})"
55 | if (branch = __FILE__[%r{kickstart/(.+)/#{template_pattern}\.rb}, 1])
56 | Dir.chdir(tempdir) { git checkout: branch }
57 | end
58 | else
59 | # For local files, add the template root directory
60 | template_root = File.dirname(__FILE__)
61 | source_paths.unshift(template_root)
62 | end
63 | end
64 |
65 | def set_variant_source_path(variant_name = nil)
66 | template_root = if __FILE__.match?(%r{\Ahttps?://})
67 | source_paths.first
68 | else
69 | File.dirname(__FILE__)
70 | end
71 |
72 | # 1. Universal shared (lowest priority)
73 | shared_path = File.join(template_root, "variants", "shared")
74 | source_paths.unshift(shared_path) if File.directory?(shared_path)
75 |
76 | # 2. Inertia shared (medium priority)
77 | inertia_shared_path = File.join(template_root, "variants", "inertia_shared")
78 | source_paths.unshift(inertia_shared_path) if File.directory?(inertia_shared_path)
79 |
80 | # 3. Variant-specific (highest priority)
81 | if variant_name
82 | variant_path = File.join(template_root, "variants", variant_name)
83 | source_paths.unshift(variant_path) if File.directory?(variant_path)
84 | end
85 | end
86 |
87 | def show_post_install_message
88 | say "\n
89 | #########################################################################################
90 |
91 | Rails application '#{app_name}' created successfully with Inertia.js + Svelte!
92 |
93 | Next steps:
94 | $ cd #{app_name}
95 | $ bundle install
96 | $ yarn install
97 | $ rails db:create db:migrate
98 | $ rails parallel:create # Creates parallel test databases (ignore 'already exists' message)
99 | $ bin/dev
100 |
101 | #########################################################################################\n", :green
102 | end
103 |
104 | #==============================================================================
105 | # TEMPLATE LOGIC - Template-specific configuration and workflow
106 | #==============================================================================
107 |
108 | assert_minimum_rails_version
109 | add_template_repository_to_source_path
110 | set_variant_source_path(TEMPLATE_NAME)
111 |
112 | apply "src/inertia_shared/general.rb"
113 | apply "src/shared/yarnconfig.rb"
114 | apply "src/shared/packages.rb"
115 |
116 | after_bundle do
117 | apply "src/inertia_shared/gems_inertia.rb"
118 | apply "src/inertia_shared/cleanup.rb"
119 |
120 | apply "src/inertia_shared/init_generators.rb"
121 | apply "src/shared/init_db_cli.rb"
122 | apply "src/shared/solid_queue_setup.rb"
123 | apply "src/shared/init_i18n.rb"
124 |
125 | apply "src/shared/env_rubocop.rb"
126 | apply "src/shared/migrations_uuid.rb"
127 |
128 | apply "src/shared/gems_anyway_config.rb"
129 | apply "src/shared/gems_pagy.rb"
130 | apply "src/shared/gems_active_interaction.rb"
131 | apply "src/shared/gems_active_decorator.rb"
132 |
133 | apply "src/shared/gems_rspec.rb"
134 | apply "src/inertia_shared/rspec_inertia.rb"
135 | apply "src/shared/gems_i18n_tasks.rb"
136 | apply "src/shared/gems_lockbox.rb"
137 | apply "src/shared/gems_shrine.rb"
138 |
139 | apply "src/inertia_shared/custom_error_pages.rb"
140 |
141 | apply "src/shared/ci.rb"
142 |
143 | apply "src/shared/docs.rb"
144 | apply "src/shared/staging_env.rb"
145 | apply "src/inertia_shared/finalize_layouts.rb"
146 | apply "src/shared/run_rubocop.rb"
147 | apply "src/shared/git_init.rb"
148 |
149 | show_post_install_message
150 | end
151 |
--------------------------------------------------------------------------------
/importmap_tailwind.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | #==============================================================================
4 | # CONSTANTS - Template configuration and shared values
5 | #==============================================================================
6 |
7 | REPO_LINK = "https://github.com/alec-c4/kickstart.git"
8 | AVAILABLE_TEMPLATE_NAMES = %w[api importmap_tailwind esbuild_tailwind inertia_svelte inertia_react inertia_vue].freeze
9 | TEMPLATE_NAME = "importmap_tailwind"
10 | RAILS_REQUIREMENT = ">= 8.1.0"
11 |
12 | TEMPLATE_METADATA = {
13 | name: "importmap_tailwind",
14 | description: "Rails app with Importmap and Tailwind CSS for modern frontend development",
15 | features: %w[postgresql devcontainer rspec rubocop uuid i18n tailwind importmap turbo stimulus kamal solid_queue
16 | solid_cache solid_cable],
17 | rails_version: RAILS_REQUIREMENT
18 | }.freeze
19 |
20 | #==============================================================================
21 | # SHARED CODE - Embedded functions (auto-generated, do not edit manually)
22 | #==============================================================================
23 |
24 | require "rails/all"
25 |
26 | def assert_minimum_rails_version
27 | requirement = Gem::Requirement.new(RAILS_REQUIREMENT)
28 | rails_version = Gem::Version.new(Rails::VERSION::STRING)
29 | return if requirement.satisfied_by?(rails_version)
30 |
31 | prompt = "This template requires Rails #{RAILS_REQUIREMENT}. "\
32 | "You are using #{rails_version}. Continue anyway?"
33 | exit 1 if no?(prompt)
34 | end
35 |
36 | def gemfile_requirement(name)
37 | @original_gemfile ||= IO.read("Gemfile")
38 | req = @original_gemfile[/gem\s+['"]#{name}['"]\s*(,[><~= \t\d.\w'"]*)?.*$/, 1]
39 | req && req.tr("'", %(")).strip.sub(/^,\s*"/, ', "')
40 | end
41 |
42 | def add_template_repository_to_source_path
43 | if __FILE__.match?(%r{\Ahttps?://})
44 | require "tmpdir"
45 | source_paths.unshift(tempdir = Dir.mktmpdir("kickstart-tmp"))
46 | at_exit { FileUtils.remove_entry(tempdir) }
47 | git clone: [
48 | "--quiet",
49 | REPO_LINK,
50 | tempdir
51 | ].map(&:shellescape).join(" ")
52 |
53 | # Check for specific branch in URL path (e.g., /kickstart/branch_name/template.rb)
54 | template_pattern = "(?:#{AVAILABLE_TEMPLATE_NAMES.join('|')})"
55 | if (branch = __FILE__[%r{kickstart/(.+)/#{template_pattern}\.rb}, 1])
56 | Dir.chdir(tempdir) { git checkout: branch }
57 | end
58 | else
59 | # For local files, add the template root directory
60 | template_root = File.dirname(__FILE__)
61 | source_paths.unshift(template_root)
62 | end
63 | end
64 |
65 | def set_variant_source_path(variant_name = nil)
66 | template_root = if __FILE__.match?(%r{\Ahttps?://})
67 | source_paths.first
68 | else
69 | File.dirname(__FILE__)
70 | end
71 |
72 | # 1. Universal shared (lowest priority)
73 | shared_path = File.join(template_root, "variants", "shared")
74 | source_paths.unshift(shared_path) if File.directory?(shared_path)
75 |
76 | # 2. Classic shared (medium priority)
77 | classic_shared_path = File.join(template_root, "variants", "classic_shared")
78 | source_paths.unshift(classic_shared_path) if File.directory?(classic_shared_path)
79 |
80 | # 3. Variant-specific (highest priority)
81 | if variant_name
82 | variant_path = File.join(template_root, "variants", variant_name)
83 | source_paths.unshift(variant_path) if File.directory?(variant_path)
84 | end
85 | end
86 |
87 | def show_post_install_message
88 | say "\n
89 | #########################################################################################
90 |
91 | Rails application '#{app_name}' created successfully!
92 |
93 | Next steps:
94 | $ cd #{app_name}
95 | $ bundle install
96 | $ rails db:create db:migrate
97 | $ rails parallel:create # Creates parallel test databases (ignore 'already exists' message)
98 | $ bin/dev
99 |
100 | #########################################################################################\n", :green
101 | end
102 |
103 | #==============================================================================
104 | # TEMPLATE LOGIC - Template-specific configuration and workflow
105 | #==============================================================================
106 |
107 | assert_minimum_rails_version
108 | add_template_repository_to_source_path
109 | set_variant_source_path(TEMPLATE_NAME)
110 |
111 | apply "src/shared/general.rb"
112 | apply "src/shared/packages.rb"
113 |
114 | after_bundle do
115 | apply "src/shared/init_generators.rb"
116 | apply "src/shared/init_db_cli.rb"
117 | apply "src/shared/solid_queue_setup.rb"
118 | apply "src/shared/init_i18n.rb"
119 |
120 | apply "src/classic_shared/routes.rb"
121 | apply "src/classic_shared/app_static_pages.rb"
122 | apply "src/classic_shared/custom_error_pages.rb"
123 |
124 | apply "src/shared/env_rubocop.rb"
125 | apply "src/shared/migrations_uuid.rb"
126 |
127 | apply "src/shared/gems_anyway_config.rb"
128 | apply "src/shared/gems_pagy.rb"
129 | apply "src/shared/gems_active_interaction.rb"
130 | apply "src/shared/gems_active_decorator.rb"
131 | apply "src/shared/gems_rspec.rb"
132 | apply "src/shared/gems_i18n_tasks.rb"
133 | apply "src/classic_shared/gems_better_html.rb"
134 | apply "src/classic_shared/gems_erblint.rb"
135 | apply "src/shared/gems_lockbox.rb"
136 | apply "src/shared/gems_shrine.rb"
137 | apply "src/classic_shared/gems_view_component.rb"
138 | apply "src/classic_shared/helpers.rb"
139 |
140 | apply "src/shared/ci.rb"
141 |
142 | apply "src/shared/docs.rb"
143 | apply "src/shared/staging_env.rb"
144 | apply "src/shared/run_rubocop.rb"
145 | apply "src/shared/git_init.rb"
146 |
147 | show_post_install_message
148 | end
149 |
--------------------------------------------------------------------------------
/esbuild_tailwind.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | #==============================================================================
4 | # CONSTANTS - Template configuration and shared values
5 | #==============================================================================
6 |
7 | REPO_LINK = "https://github.com/alec-c4/kickstart.git"
8 | AVAILABLE_TEMPLATE_NAMES = %w[api importmap_tailwind esbuild_tailwind inertia_svelte inertia_react inertia_vue].freeze
9 | TEMPLATE_NAME = "esbuild_tailwind"
10 | RAILS_REQUIREMENT = ">= 8.1.0"
11 |
12 | TEMPLATE_METADATA = {
13 | name: "esbuild_tailwind",
14 | description: "Rails app with ESBuild and Tailwind CSS for modern frontend development",
15 | features: %w[postgresql devcontainer rspec rubocop uuid i18n tailwind esbuild turbo stimulus kamal solid_queue
16 | solid_cache solid_cable],
17 | rails_version: RAILS_REQUIREMENT
18 | }.freeze
19 |
20 | #==============================================================================
21 | # SHARED CODE - Embedded functions (auto-generated, do not edit manually)
22 | #==============================================================================
23 |
24 | require "rails/all"
25 |
26 | def assert_minimum_rails_version
27 | requirement = Gem::Requirement.new(RAILS_REQUIREMENT)
28 | rails_version = Gem::Version.new(Rails::VERSION::STRING)
29 | return if requirement.satisfied_by?(rails_version)
30 |
31 | prompt = "This template requires Rails #{RAILS_REQUIREMENT}. "\
32 | "You are using #{rails_version}. Continue anyway?"
33 | exit 1 if no?(prompt)
34 | end
35 |
36 | def gemfile_requirement(name)
37 | @original_gemfile ||= IO.read("Gemfile")
38 | req = @original_gemfile[/gem\s+['"]#{name}['"]\s*(,[><~= \t\d.\w'"]*)?.*$/, 1]
39 | req && req.tr("'", %(")).strip.sub(/^,\s*"/, ', "')
40 | end
41 |
42 | def add_template_repository_to_source_path
43 | if __FILE__.match?(%r{\Ahttps?://})
44 | require "tmpdir"
45 | source_paths.unshift(tempdir = Dir.mktmpdir("kickstart-tmp"))
46 | at_exit { FileUtils.remove_entry(tempdir) }
47 | git clone: [
48 | "--quiet",
49 | REPO_LINK,
50 | tempdir
51 | ].map(&:shellescape).join(" ")
52 |
53 | # Check for specific branch in URL path (e.g., /kickstart/branch_name/template.rb)
54 | template_pattern = "(?:#{AVAILABLE_TEMPLATE_NAMES.join('|')})"
55 | if (branch = __FILE__[%r{kickstart/(.+)/#{template_pattern}\.rb}, 1])
56 | Dir.chdir(tempdir) { git checkout: branch }
57 | end
58 | else
59 | # For local files, add the template root directory
60 | template_root = File.dirname(__FILE__)
61 | source_paths.unshift(template_root)
62 | end
63 | end
64 |
65 | def set_variant_source_path(variant_name = nil)
66 | template_root = if __FILE__.match?(%r{\Ahttps?://})
67 | source_paths.first
68 | else
69 | File.dirname(__FILE__)
70 | end
71 |
72 | # 1. Universal shared (lowest priority)
73 | shared_path = File.join(template_root, "variants", "shared")
74 | source_paths.unshift(shared_path) if File.directory?(shared_path)
75 |
76 | # 2. Classic shared (medium priority)
77 | classic_shared_path = File.join(template_root, "variants", "classic_shared")
78 | source_paths.unshift(classic_shared_path) if File.directory?(classic_shared_path)
79 |
80 | # 3. Variant-specific (highest priority)
81 | if variant_name
82 | variant_path = File.join(template_root, "variants", variant_name)
83 | source_paths.unshift(variant_path) if File.directory?(variant_path)
84 | end
85 | end
86 |
87 | def show_post_install_message
88 | say "\n
89 | #########################################################################################
90 |
91 | Rails application '#{app_name}' created successfully!
92 |
93 | Next steps:
94 | $ cd #{app_name}
95 | $ bundle install
96 | $ rails db:create db:migrate
97 | $ rails parallel:create # Creates parallel test databases (ignore 'already exists' message)
98 | $ bin/dev
99 |
100 | #########################################################################################\n", :green
101 | end
102 |
103 | #==============================================================================
104 | # TEMPLATE LOGIC - Template-specific configuration and workflow
105 | #==============================================================================
106 |
107 | assert_minimum_rails_version
108 | add_template_repository_to_source_path
109 | set_variant_source_path(TEMPLATE_NAME)
110 |
111 | apply "src/shared/general.rb"
112 | apply "src/shared/yarnconfig.rb"
113 | apply "src/shared/packages.rb"
114 |
115 | after_bundle do
116 | apply "src/shared/init_generators.rb"
117 | apply "src/shared/init_db_cli.rb"
118 | apply "src/shared/solid_queue_setup.rb"
119 | apply "src/shared/init_i18n.rb"
120 |
121 | apply "src/classic_shared/routes.rb"
122 | apply "src/classic_shared/app_static_pages.rb"
123 | apply "src/classic_shared/custom_error_pages.rb"
124 |
125 | apply "src/shared/env_rubocop.rb"
126 | apply "src/shared/migrations_uuid.rb"
127 |
128 | apply "src/shared/gems_anyway_config.rb"
129 | apply "src/shared/gems_pagy.rb"
130 | apply "src/shared/gems_active_interaction.rb"
131 | apply "src/shared/gems_active_decorator.rb"
132 | apply "src/shared/gems_rspec.rb"
133 | apply "src/shared/gems_i18n_tasks.rb"
134 | apply "src/classic_shared/gems_better_html.rb"
135 | apply "src/classic_shared/gems_erblint.rb"
136 | apply "src/shared/gems_lockbox.rb"
137 | apply "src/shared/gems_shrine.rb"
138 | apply "src/classic_shared/gems_view_component.rb"
139 | apply "src/classic_shared/helpers.rb"
140 |
141 | apply "src/shared/ci.rb"
142 |
143 | apply "src/shared/docs.rb"
144 | apply "src/shared/staging_env.rb"
145 | apply "src/shared/run_rubocop.rb"
146 | apply "src/shared/git_init.rb"
147 |
148 | show_post_install_message
149 | end
150 |
--------------------------------------------------------------------------------
/TEMPLATES.md:
--------------------------------------------------------------------------------
1 | # Template Management System
2 |
3 | This document describes the template management system for Kickstart Rails Templates.
4 |
5 | ## Overview
6 |
7 | The Kickstart project uses a sophisticated template management system that provides:
8 |
9 | - **Embedded shared code**: All templates are self-contained with embedded shared functions
10 | - **Template validation**: Automated consistency checking across all templates
11 | - **Template synchronization**: Tools to keep shared code in sync across templates
12 | - **Metadata system**: Rich metadata for each template including features and descriptions
13 |
14 | ## Template Structure
15 |
16 | Each template follows a consistent structure:
17 |
18 | ```ruby
19 | # frozen_string_literal: true
20 |
21 | #==============================================================================
22 | # CONSTANTS - Template configuration and shared values
23 | #==============================================================================
24 |
25 | REPO_LINK = "https://github.com/alec-c4/kickstart.git"
26 | AVAILABLE_TEMPLATE_NAMES = %w[api importmap_tailwind esbuild_tailwind].freeze
27 | TEMPLATE_NAME = "template_name".freeze
28 | RAILS_REQUIREMENT = ">= 8.1.0.beta.1"
29 |
30 | TEMPLATE_METADATA = {
31 | name: "template_name",
32 | description: "Template description",
33 | features: %w[list of features],
34 | rails_version: RAILS_REQUIREMENT
35 | }.freeze
36 |
37 | #==============================================================================
38 | # SHARED CODE - Embedded functions (auto-generated, do not edit manually)
39 | #==============================================================================
40 |
41 | # All shared functions are embedded here...
42 |
43 | #==============================================================================
44 | # TEMPLATE LOGIC - Template-specific configuration and workflow
45 | #==============================================================================
46 |
47 | # Template-specific logic here...
48 | ```
49 |
50 | ## Available Templates
51 |
52 | ### API Template (`api.rb`)
53 |
54 | - **Description**: Rails API-only application with essential setup
55 | - **Features**: postgresql, devcontainer, rspec, rubocop, uuid, i18n, kamal, solid_queue, solid_cache, solid_cable
56 | - **Use case**: Backend APIs and microservices
57 |
58 | ### Importmap + Tailwind Template (`importmap_tailwind.rb`)
59 |
60 | - **Description**: Rails app with Importmap and Tailwind CSS for modern frontend development
61 | - **Features**: postgresql, devcontainer, rspec, rubocop, uuid, i18n, tailwind, importmap, turbo, stimulus, kamal, solid_queue, solid_cache, solid_cable
62 | - **Use case**: Simple web applications with modern CSS framework
63 |
64 | ### ESBuild + Tailwind Template (`esbuild_tailwind.rb`)
65 |
66 | - **Description**: Rails app with ESBuild and Tailwind CSS for modern frontend development
67 | - **Features**: postgresql, devcontainer, rspec, rubocop, uuid, i18n, tailwind, esbuild, turbo, stimulus, kamal, solid_queue, solid_cache, solid_cable
68 | - **Use case**: Modern web applications with advanced JavaScript bundling
69 |
70 | ## Rake Tasks
71 |
72 | ### `rake templates:validate`
73 |
74 | Validates that all templates are consistent and properly formatted.
75 |
76 | ```bash
77 | $ rake templates:validate
78 | 🔍 Validating template consistency...
79 | ✅ All templates are valid and consistent!
80 | ```
81 |
82 | **Checks performed:**
83 |
84 | - All required constants are present
85 | - All required functions are defined
86 | - Shared code sections are identical across templates
87 | - Template files exist and are readable
88 |
89 | ### `rake templates:info`
90 |
91 | Displays detailed information about all templates.
92 |
93 | ```bash
94 | $ rake templates:info
95 | 📊 Kickstart Rails Templates Information
96 | ==================================================
97 |
98 | 🚀 Template: IMPORTMAP_TAILWIND
99 | File: importmap_tailwind.rb
100 | Lines: 118
101 | Description: Rails app with Importmap, Tailwind CSS and basic configuration
102 | Features: postgresql, devcontainer, rspec, rubocop, uuid, i18n, tailwind, importmap, turbo, stimulus, kamal, solid_queue, solid_cache, solid_cable
103 | Apply statements: 11
104 | ```
105 |
106 | ### `rake templates:sync`
107 |
108 | Synchronizes shared code across all templates to ensure consistency.
109 |
110 | ```bash
111 | $ rake templates:sync
112 | 🔄 Syncing shared code across all templates...
113 | ✅ Updated api.rb
114 | ✅ Updated importmap_tailwind.rb
115 | ✅ Updated esbuild_tailwind.rb
116 | 🎉 Shared code synchronization complete!
117 | ```
118 |
119 | **⚠️ Important**: This task auto-generates the shared code section. Do not edit the shared code manually.
120 |
121 | ### `rake templates:create[name,source]`
122 |
123 | Creates a new template based on an existing one.
124 |
125 | ```bash
126 | $ rake templates:create[mobile,importmap_tailwind]
127 | ✅ Created new template: mobile.rb
128 | ✅ Created directories: src/mobile/, variants/mobile/
129 | 💡 Don't forget to update AVAILABLE_TEMPLATES constant!
130 | ```
131 |
132 | ## Usage Examples
133 |
134 | ### Local Development
135 |
136 | ```bash
137 | # Use a template locally
138 | rails new myapp -m /path/to/kickstart/importmap_tailwind.rb --database=postgresql --devcontainer --css=tailwind
139 |
140 | # Validate templates before committing
141 | rake templates:validate
142 |
143 | # Get template information
144 | rake templates:info
145 | ```
146 |
147 | ### Remote Installation
148 |
149 | ```bash
150 | # Install via curl (most common usage)
151 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/alec-c4/kickstart/master/install.sh)" -- myapp importmap_tailwind
152 | ```
153 |
154 | ## Maintenance Workflow
155 |
156 | ### Adding New Shared Functions
157 |
158 | 1. **Add the function to one template** (preferably `importmap_tailwind.rb`)
159 | 2. **Run sync task** to propagate to all templates:
160 | ```bash
161 | rake templates:sync
162 | ```
163 | 3. **Validate consistency**:
164 | ```bash
165 | rake templates:validate
166 | ```
167 |
168 | ### Creating New Templates
169 |
170 | 1. **Create from existing template**:
171 | ```bash
172 | rake templates:create[new_template,importmap_tailwind]
173 | ```
174 | 2. **Update constants** in `Rakefile` and new template
175 | 3. **Customize template-specific logic**
176 | 4. **Validate the new template**:
177 | ```bash
178 | rake templates:validate
179 | ```
180 |
181 | ### Modifying Existing Templates
182 |
183 | 1. **Edit template-specific sections only** (below `# TEMPLATE LOGIC`)
184 | 2. **Never edit the shared code section directly** (it's auto-generated)
185 | 3. **Always validate after changes**:
186 | ```bash
187 | rake templates:validate
188 | ```
189 |
190 | ## Architecture Benefits
191 |
192 | ### Self-Contained Templates
193 |
194 | - ✅ Work with remote URLs (curl installation)
195 | - ✅ No external dependencies
196 | - ✅ Easy to debug - everything in one file
197 | - ✅ Fast loading
198 |
199 | ### Automated Management
200 |
201 | - ✅ Consistent shared code across templates
202 | - ✅ Validation prevents configuration drift
203 | - ✅ Easy to add new templates
204 | - ✅ Clear separation of concerns
205 |
206 | ### Rich Metadata
207 |
208 | - ✅ Self-documenting templates
209 | - ✅ Feature tracking
210 | - ✅ Version compatibility information
211 | - ✅ Usage statistics
212 |
213 | ## Contributing
214 |
215 | When contributing to templates:
216 |
217 | 1. **Always run validation** before submitting PRs
218 | 2. **Use the sync task** if you modify shared functions
219 | 3. **Update metadata** when adding/removing features
220 | 4. **Test both local and remote usage**
221 | 5. **Document any new conventions**
222 |
223 | ## Troubleshooting
224 |
225 | ### Template Validation Fails
226 |
227 | ```bash
228 | ❌ Shared code sections are not identical across templates
229 | ```
230 |
231 | **Solution**: Run `rake templates:sync` to synchronize shared code.
232 |
233 | ### Missing Constants/Functions
234 |
235 | ```bash
236 | ❌ Missing constant TEMPLATE_METADATA in api.rb
237 | ```
238 |
239 | **Solution**: Add the missing constant following the standard structure.
240 |
241 | ### Template Creation Issues
242 |
243 | ```bash
244 | ❌ Template 'mobile' already exists!
245 | ```
246 |
247 | **Solution**: Use a different name or remove the existing template first.
248 |
249 | ---
250 |
251 | For more information, see the [main README](README.md) or check the [Rakefile](Rakefile) for implementation details.
252 |
--------------------------------------------------------------------------------