Choiceable All in one (maybe ?)

RSpec + Spring, Guard RSpec & Livereload, ESLint, StandardRB + Spring, Lint-staged, Hotwire, Devise + Turbo Support (If use Hotwire), and Capistrano
Icons/chart bar
Used 82 times
Created by
F Fajarullah

Usage
Just apply it and answer some question to choose what you want, including
  • ESLint Standard
  • StandardRB with spring support
  • Lint-staged
  • Hotwire (if has webpacker gem)
  • Devise + Turbo Support (If use Hotwire)
  • Capistrano
  • Webpacker 6.0.0.beta.7

Installed by default
  • RSpec with spring support
  • Guard RSpec & Livereload
  • Rails Dotenv

Run this command in your Rails app directory in the terminal:

rails app:template LOCATION="https://www.railsbytes.com/script/VMys4Y"
Template Source

Review the code before running this template on your machine.

require "active_support/core_ext/hash/deep_merge"
require "json"

def add_gem_if_not_present(*args, **options)
  gem(*args, **options) unless Gem.loaded_specs.has_key?(args[0])
end

def confirm(question)
  response = ask("#{question} [Y/n]")
  response == "" || response.upcase == "Y"
end

def has_webpacker_gem?
  @has_webpacker_gem ||= begin
    webpacker_checker_regex = /gem (['"])webpacker\1.+/
    File.read("Gemfile")[webpacker_checker_regex]
  end
end

def has_webpacker?
  has_webpacker_gem? || USE_WEBPACKER6_BETA
end

def edit_file(path)
  File.write(path, yield(File.read(path)))
end

def edit_package_json
  return {} unless File.exist?("package.json")

  edit_file("./package.json") do |content|
    JSON.pretty_generate(yield JSON.parse(content))
  end
end

def install_eslint
  return unless USE_ESLINT_STANDARD

  puts "Installing ESLint Standard..."

  eslint_standard_packages = %w[
    @babel/eslint-parser
    eslint
    eslint-config-standard
    eslint-plugin-import
    eslint-plugin-node
    eslint-plugin-promise
  ]

  run "yarn add -D #{eslint_standard_packages.join(" ")}"

  edit_package_json do |json|
    json.deep_merge!({
      "scripts" => {
        "lint:js" => "./node_modules/.bin/eslint --cache app/javascript"
      }
    })
  end

  file ".eslintrc.js", <<~JS
    module.exports = {
      parser: '@babel/eslint-parser',
      env: {	
        browser: true,	
        es2021: true
      },	
      extends: [	
        'standard'	
      ],	
      parserOptions: {	
        ecmaVersion: 12,	
        sourceType: 'module'	
      },	
      rules: {}
    }
  JS
end

def install_devise
  return unless USE_DEVISE

  puts "Installing Devise..."
  if USE_HOTWIRE
    # template: https://railsbytes.com/templates/zOvsyy
    # install Devise Authentication + Hotwire Support (With Navigational Formats)
    run 'rails app:template LOCATION="https://railsbytes.com/script/zOvsyy"'
  else
    # template: https://railsbytes.com/templates/X8Bsjx
    # install Authentication with Devise
    run 'rails app:template LOCATION="https://railsbytes.com/script/X8Bsjx"'
  end
end

def install_lint_staged
  return unless USE_LINT_STAGED

  puts "Installing lint-staged..."
  run "npx mrm lint-staged"
  edit_package_json do |json|
    json.deep_merge!({
      "lint-staged" => {
        "*.js" => "eslint --cache --fix",
        "{Guardfile,Gemfile,*.rb}" => "bin/standardrb --fix"
      }
    })
  end
end

def install_standardrb
  return unless USE_STANDARDRB

  puts "Installing standardrb..."

  edit_package_json do |json|
    json.deep_merge!({
      "scripts" => {"lint:rb" => "bin/standardrb"}
    })
  end

  gem_group :development do
    add_gem_if_not_present "standard"
    add_gem_if_not_present "spring-commands-standard", require: false
  end
  run "bundle install"
  run "bundle exec spring binstub standardrb"

  file ".standard.yml", <<~YAML
    parallel: true          # default: false
    format: progress        # default: Standard::Formatter

    ignore:
      - "**/db/schema.rb"
      - "**/bin/*"
  YAML
end

def install_rspec
  puts "Installing rspec..."

  run "bundle binstub rspec"

  # fix rspec:install stuck by stop spring
  # https://github.com/rspec/rspec-rails/issues/1860#issuecomment-346687123
  run "spring stop"
  generate("rspec:install")

  # delete minitest directory
  run "rm -rf test" if yes?("Delete /test folder ? [y/n]")

  # make spec/support directory loaded
  gsub_file "spec/rails_helper.rb", %r{^# (?=Dir\[Rails.root.join\()}, ""

  # uncomment default rspec config in spec_helper.rb
  run 'rails app:template LOCATION="https://railsbytes.com/script/XnJspa"'

  # generate rspec supports
  file "spec/support/factory_bot.rb", <<~RUBY
    RSpec.configure do |config|
      config.before { FactoryBot.rewind_sequences }
    end
  RUBY

  edit_package_json do |json|
    json.deep_merge!({
      "scripts" => {
        "test" => "bin/rspec",
        "test:all" => "yarn run test -- --tag '~focus' --tag 'focus'",
        "test:failures" => "yarn run test:all --only-failures"
      }
    })
  end
  application "config.generators.test_framework :rspec, routing_spec: false"
  run "bundle exec spring binstub rspec"
end

def install_capistrano
  return unless USE_CAPISTRANO

  puts "Installing Capistrano..."

  gem_group :deploy do
    add_gem_if_not_present "capistrano", "~> 3.16", require: false
    add_gem_if_not_present "capistrano-rbenv", "~> 2.2", require: false
    add_gem_if_not_present "capistrano-rails", "~> 1.6", require: false
  end
  run "bundle install"
  run "bundle exec cap install"

  edit_file("./Capfile") do |content|
    content.sub!(%r{# (?=require "capistrano/rbenv")}, "")
    content.sub!(%r{# (?=require "capistrano/bundler")}, "")
    content.sub!(%r{# (?=require "capistrano/rails/assets")}, "")
    content.sub!(%r{# (?=require "capistrano/rails/migrations")}, "")
    content
  end

  edit_file("./config/deploy.rb") do |content|
    content.sub!(/# append :linked_dirs.+/, <<~RUBY)
      append :linked_dirs,
        "log",
        "tmp/pids",
        "tmp/cache",
        "tmp/sockets",
        "node_modules",
        "bundle"
    RUBY
    content.sub!(/# append :linked_files.+/, <<~RUBY)
      append :linked_files, ".env.production.local"
    RUBY
    content
  end

  append_file "config/deploy.rb", <<~RUBY
    Rake::Task["deploy:assets:backup_manifest"].clear_actions
  RUBY
end

def install_hotwire
  return unless USE_HOTWIRE

  puts "Installing Hotwire..."

  # hotwire install require sprockets to be able to run
  gsub_file "config/application.rb", %r{# (?=require "sprockets/railtie")}, ""
  run "bundle add hotwire-rails"
  run "bundle exec rails hotwire:install"
end

USE_ESLINT_STANDARD = confirm("Use ESLint standard style ?")
USE_STANDARDRB = confirm("Use Ruby standard style (https://github.com/testdouble/standard) ?")
USE_LINT_STAGED = confirm("Install lint-staged (https://github.com/okonet/lint-staged) ?")
USE_DEVISE = confirm("Install devise for authentication ?")
USE_CAPISTRANO = confirm("Install capistrano to automate deployment ?")
# webpacker 6 use webpack 5, which have better build performance
# https://medium.com/frontend-digest/whats-new-in-webpack-5-ef619bb74fae
# the one that makes it better is uses dart-sass instead of node-sass
# https://github.com/rails/webpacker/#sass
WEBPACKER_BETA_VERSION = "6.0.0.beta.7"
USE_WEBPACKER6_BETA = yes?("Use webpacker #{WEBPACKER_BETA_VERSION} ? [y/n]")
USE_HOTWIRE = has_webpacker? && confirm("Install hotwire ?")

gem_group :development, :test do
  add_gem_if_not_present "factory_bot_rails"
  add_gem_if_not_present "rspec-rails", "~> 4.0.1"
end

gem_group :development do
  # run RSpec after file changed when using guard
  add_gem_if_not_present "guard-rspec", require: false
  # app views livereload
  add_gem_if_not_present "guard-livereload", "~> 2.5.2", require: false
  add_gem_if_not_present "rack-livereload"
  add_gem_if_not_present "spring-commands-rspec", require: false
end

environment(
  "config.middleware.insert_after ActionDispatch::Static, Rack::LiveReload",
  env: "development"
)

add_gem_if_not_present "dotenv-rails"
run "bundle install"

install_rspec
install_eslint
install_lint_staged
install_standardrb
install_devise
install_capistrano

if USE_WEBPACKER6_BETA
  run "bundle remove webpacker" if has_webpacker_gem?
  run %(bundle add webpacker -v "#{WEBPACKER_BETA_VERSION}")
  run "bundle exec rails webpacker:install"

  edit_file("./config/webpacker.yml") do |content|
    content.sub!(%r{(?<=source_path: )app/javascript}, "app/packs")
    content.sub!(/(?<=source_entry_path: )packs/, "entrypoints")
    content
  end

  # CSS: https://github.com/rails/webpacker/#css
  # SASS: https://github.com/rails/webpacker/#sass
  css_support_packages = %w[
    css-loader
    mini-css-extract-plugin
    css-minimizer-webpack-plugin
    sass
    sass-loader
  ]
  run "yarn add #{css_support_packages.join(" ")}"
else
  run "bundle exec rails webpacker:install"
end

install_hotwire

run "bundle exec guard init"
run "bundle binstub guard"

# generate home page
generate(:controller, "home", "index", "--skip-routes")
route %(root to: "home#index")

if USE_WEBPACKER6_BETA
  puts <<~README
    You are using webpacker #{WEBPACKER_BETA_VERSION}, please update javascript file in app/packs/entrypoints
  README
end
Comments

Sign up or Login to leave a comment.