Skip to content

Instantly share code, notes, and snippets.

@somenugget
Created July 28, 2024 20:14
Show Gist options
  • Save somenugget/acc052a3680921354ecaa2c17fb7b27d to your computer and use it in GitHub Desktop.
Save somenugget/acc052a3680921354ecaa2c17fb7b27d to your computer and use it in GitHub Desktop.
Rails template with Vite, Tailwind, Devise
# rails new app_name -m ./template.rb
self.instance_variable_set(:@options, self.instance_variable_get(:@options).merge(
skip_javascript: true,
skip_hotwire: true,
skip_action_mailbox: true,
skip_action_text: true,
database: 'postgres'
))
remove_file 'Gemfile'
create_file 'Gemfile', <<-RUBY
source "https://rubygems.org"
ruby "3.2.2"
# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
gem "rails", "~> 7.1.3", ">= 7.1.3.4"
gem "pg", "~> 1.1"
# Use the Puma web server [https://github.com/puma/puma]
gem "puma", ">= 5.0"
# Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]
gem "turbo-rails"
gem "stimulus-rails"
gem "devise"
gem "good_job"
gem "vite_rails"
gem "service_actor"
# Use Redis adapter to run Action Cable in production
# gem "redis", ">= 4.0.1"
# Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis]
# gem "kredis"
# Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword]
# gem "bcrypt", "~> 3.1.7"
# Reduces boot times through caching; required in config/boot.rb
gem "bootsnap", require: false
# Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images]
# gem "image_processing", "~> 1.2"
group :development, :test do
gem "byebug"
gem 'dotenv-rails'
gem 'bundle-audit', require: false
gem 'rubocop'
gem 'rubocop-factory_bot'
gem 'rubocop-rails'
gem 'rubocop-rspec'
gem 'factory_bot'
gem 'rspec-rails', '~> 6.0.0'
end
group :development do
# Use console on exceptions pages [https://github.com/rails/web-console]
gem "web-console"
end
RUBY
inside('config') do
# Use PostgreSQL as the database
remove_file 'database.yml'
create_file 'database.yml', <<-YAML
default: &default
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: <%= ENV.fetch("DATABASE_USERNAME") { 'postgres' } %>
password: <%= ENV.fetch("DATABASE_PASSWORD") { '' } %>
host: <%= ENV.fetch("DATABASE_HOST") { 'localhost' } %>
development:
<<: *default
database: #{app_name}_development
test:
<<: *default
database: #{app_name}_test
production:
<<: *default
database: #{app_name}_production
username: #{app_name}
password: <%= ENV['#{app_name.upcase}_DATABASE_PASSWORD'] %>
YAML
create_file 'vite.json', <<-JSON
{
"all": {
"sourceCodeDir": "app/frontend",
"watchAdditionalPaths": ["app/views/**/*"]
},
"development": {
"autoBuild": true,
"publicOutputDir": "vite-dev",
"port": 3036
},
"test": {
"autoBuild": true,
"publicOutputDir": "vite-test",
"port": 3037
}
}
JSON
# Add custom generator configuration and ActiveJob queue adapter to application.rb
inject_into_file 'application.rb', after: "# config.eager_load_paths << Rails.root.join(\"extras\")\n" do
<<-RUBY
config.generators do |g|
g.helper false
g.system_tests false
g.test_framework :rspec,
view_specs: false,
helper_specs: false,
routing_specs: false
end
config.active_job.queue_adapter = :good_job
RUBY
end
inside('initializers') do
remove_file 'assets.rb'
end
inside('environments') do
gsub_file('development.rb', "# Suppress logger output for asset requests.\n", '')
gsub_file('development.rb', "config.assets.quiet = true\n", '')
gsub_file('production.rb', "# Compress CSS using a preprocessor.\n", '')
gsub_file('production.rb', "# config.assets.css_compressor = :sass\n", '')
gsub_file('production.rb', "# Do not fall back to assets pipeline if a precompiled asset is missed.\n", '')
gsub_file('production.rb', "config.assets.compile = false\n", '')
end
end
create_file '.rubocop.yml', <<-YAML
require:
- rubocop-factory_bot
- rubocop-rails
- rubocop-rspec
AllCops:
NewCops: enable
DisplayStyleGuide: true
Exclude:
- 'bin/**/*'
- 'db/**/*'
- 'vendor/**/*'
Layout/LineLength:
Exclude:
- 'config/**/*'
Layout/FirstArgumentIndentation:
EnforcedStyle: consistent
Lint/MissingSuper:
Exclude:
- 'spec/**/*'
Metrics/BlockLength:
Exclude:
- 'spec/**/*'
- 'config/**/*'
- 'db/**/*'
- 'vendor/**/*'
Metrics/MethodLength:
Max: 15
Naming/BlockForwarding:
Enabled: false
Rails/I18nLocaleTexts:
Enabled: false
Style/ArgumentsForwarding:
Enabled: false
Style/ClassAndModuleChildren:
Enabled: false
Style/Documentation:
Enabled: false
Style/DocumentationMethod:
Enabled: true
Include:
- 'app/models/**/*.rb'
- 'app/services/**/*.rb'
- 'app/components/**/*.rb'
- 'app/jobs/**/*.rb'
- 'app/mailers/**/*.rb'
- 'app/channels/**/*.rb'
Style/HashSyntax:
Enabled: false
Style/FetchEnvVar:
Enabled: false
Style/FrozenStringLiteralComment:
Enabled: false
Style/TrailingCommaInHashLiteral:
Enabled: false
YAML
create_file '.env'
create_file '.env.example'
# Run bundle install
run 'bundle install'
# Initialize Vite
run 'bundle exec vite install'
# Initialize the database
rails_command 'db:drop'
rails_command 'db:create'
rails_command 'db:migrate'
# Initialize GoodJob
generate 'good_job:install'
rails_command 'db:migrate'
# Initialize Devise
generate 'devise:install'
generate 'devise User'
rails_command 'db:migrate'
# Initialize RSpec
generate 'rspec:install'
# Create a home controller with an index action
generate(:controller, 'home', 'index')
# Set up the root route
route "root to: 'home#index'"
remove_file 'vite.config.ts'
create_file 'vite.config.mjs', <<-JS
import { defineConfig } from 'vite'
import FullReload from 'vite-plugin-full-reload'
import RubyPlugin from 'vite-plugin-ruby'
export default defineConfig({
plugins: [
RubyPlugin(),
FullReload(
[
'config/routes.rb',
'config/locales/**/*',
'app/views/**/*',
'app/components/**/*',
],
{
delay: 250,
},
),
],
server: {
hmr: {
port: 3036,
},
},
})
JS
run 'npm install vite-plugin-full-reload -D'
# Install Tailwind CSS manually
run 'npm install tailwindcss postcss autoprefixer'
run 'npx tailwindcss init'
# Configure Tailwind CSS
remove_file 'tailwind.config.js'
create_file 'tailwind.config.js', <<-JS
module.exports = {
content: [
'./app/frontend/**/*.js',
'./app/views/**/*'
],
theme: {
extend: {},
},
plugins: [],
}
JS
create_file 'postcss.config.js', <<-JS
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
JS
# Create the Tailwind CSS input file
create_file 'app/frontend/entrypoints/application.css', <<-CODE
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
CODE
gsub_file 'app/views/layouts/application.html.erb',
'<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>',
'<%= vite_stylesheet_tag "entrypoints/application", "data-turbo-track": "reload" %>'
gsub_file 'package.json', "\"private\": true,\n", ''
gsub_file 'package.json', "\"type\": \"module\",\n", ''
# Create BaseService file
create_file 'app/services/base_service.rb', <<-RUBY
class BaseService
module DefaultOutput
# save service call output to the `output` attribute
# to not define output variable in every service
def _call
self.output = super
end
end
# Essential mechanics
include(ServiceActor::Core)
include(ServiceActor::Configurable)
include(ServiceActor::Attributable)
include(ServiceActor::Playable)
# @!attribute output
# @return [Any] the result of the service call
output :output, allow_nil: true, default: -> {}
include(DefaultOutput)
# Extra concerns
include(ServiceActor::Checkable)
include(ServiceActor::Defaultable)
include(ServiceActor::Failable)
end
RUBY
run 'rm -rf ./test'
run 'rm -rf ./vendor'
run 'bundle exec rubocop -A'
after_bundle do
# Git initialization
git :init
git add: '.'
git commit: "-m 'Initial commit with GoodJob, Vite, Tailwind CSS (manual), Devise, ServiceActor, RSpec, and PostgreSQL setup'"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment