Skip to content

Instantly share code, notes, and snippets.

@jdickey
Last active October 30, 2021 16:16
Show Gist options
  • Select an option

  • Save jdickey/5f06dd10252ddd148b9660f4dfe0abe9 to your computer and use it in GitHub Desktop.

Select an option

Save jdickey/5f06dd10252ddd148b9660f4dfe0abe9 to your computer and use it in GitHub Desktop.

Revisions

  1. jdickey revised this gist Oct 30, 2021. 4 changed files with 1 addition and 566 deletions.
    Original file line number Diff line number Diff line change
    @@ -1,26 +0,0 @@
    ## Intro

    (NB: Forgive the many content-free revisions; it has been many moons since I have created a Gist with attachments, and getting the
    ordering currect is maddeningly non-trivial.)

    After too many years doing this, never mind how many, I'm attempting to automate the generation of Rails apps using a
    `.railsrc` file and an app-generation template. The `.railsrc` file, largely cribbed from numerous others, appears to work
    splendidly.

    The template file, which should automate setup of the newly-created app to current shop standards, is being developed incrementally; increments largely separated by *the next wall I hit*. The current "wall" has had me stopped cold for two days; my Stack Overflow skills and Google-fu are clearly not up to the task.

    ## Possibly Relevant Background Items

    1. Ruby is installed by way of [`rbenv`](https://github.com/rbenv/rbenv#readme), which we have used for *years* without known issues. We are presently on Ruby 3.0.2p107.
    2. I'm attempting to continue our long-time shop-standard usage of [`rbenv-gemsets`](https://github.com/jf/rbenv-gemset#readme), which has hitherto (before attempted use of an app generator) saved our proverbial bacon often enough that I still have *some* hair. It supports our experimentation and evolution of candidate Gems for use, without polluting the system Gem repository.

    ## Problem Statement

    When I generate a new app using the template, everything appears to work fine setting up the new app up to and including when `bundle install` is run. However, immediately after bundling reports success, the Gems are installed not in the Gemset, but in the `rbenv` Gem repository for the current Ruby version. Running the installation process manually stores the Gems in the expected place.

    When I started writing this Gist, I had several open questions that I was seeking help for; I found answers to most of them on my own, save for two:

    1. Why are the bundled Gems being installed in the `rbenv` Gem repository rather than my gemset, as occurs when I proceed manually?
    2. Why has `app/javascript/packs/application.js` changed after completion of the template, forcing `public/packs/manifest.json` to also change?

    Help?
    1 change: 1 addition & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    This has been moved to [this GitHub repo](https://github.com/jdickey/rails-app-template).
    35 changes: 0 additions & 35 deletions dot-railsrc
    Original file line number Diff line number Diff line change
    @@ -1,35 +0,0 @@
    # This is my ~/.railsrc file. There are many like it, but this one is mine.

    # known defaults
    --no-api
    --no-dev
    --no-edge
    --no-no-rc # obviously...
    --no-skip-action-cable
    --no-skip-active-record
    --no-skip-gemfile
    --no-skip-git
    --no-skip-javascript
    --no-skip-keeps
    --no-skip-listen
    --no-skip-puma
    --no-skip-sprockets
    --no-skip-turbolinks
    --no-skip-yarn
    --skip-coffee
    --skip-namespace
    --skip-system-test # ditto
    --skip-test # Don't use MiniTest; we'll add RSpec later
    # --ruby=/path/to/executable/ruby
    # --template=/path/to/app/template

    # Custom/non-default values
    --database=postgresql
    --skip-action-mailer
    --skip-active-storage
    --skip-bootsnap
    --skip-bundle
    --skip-spring
    --webpack=stimulus

    # --template=/path/to/template.rb
    505 changes: 0 additions & 505 deletions template.rb
    Original file line number Diff line number Diff line change
    @@ -1,505 +0,0 @@
    # frozen_string_literal: true

    # DRAFT Rails Application Template
    # Copyright 2021, Jeff Dickey/Seven Sigma Agility
    # Licensed for public use under the MIT License.
    #
    # This template sets up a newly-created Rails app, in conjunction with the
    # associated `.railsrc` file. This (mostly; see below) automates processes that
    # have been in use for a decade or so, over dozens of apps. Better late than
    # never, yes? B-)
    #
    # Sample usage would look something like
    # ```
    # rails new the_app -m ./template.rb
    # ```
    # where `the_app` would be replaced by the actual name of your new application.
    #
    # First, we set up our Gemfile. This is somewhat abbreviated from our standard
    # since this whole script runs essentially as a hook within Rails' template
    # processing, which has already created a Gemfile by the time we get here. It
    # Would Be Very Nice if that was documented a bit more clearly in the Rails
    # Guides, but oh well.
    #
    # This setup assumes `zsh`, `rbenv`, and `rbenv-gemset`. It further asdumes that
    # it is being run from what will become the parent directory of the new app, in
    # which exists a `.ruby-version` file, and possibly an `.rbenv-gemset` directory
    # that will be **DELETED** if it exists.
    ################################################################################

    # FIXME: Outdated, hardcoded Node packages are *evil*.

    gem 'redis', '~> 4.0'
    gem 'bcrypt', '~> 3.1.7'
    gem "bundler-audit", "~> 0.9.0"
    gem "dotenv-rails", "~> 2.7"
    gem "dry-monads", "~> 1.4"
    gem "ice_nine", "~> 0.11.2"
    gem "slim-rails", "~> 3.3"
    gem "foreman", "~> 0.87.2"
    gem "sassc-rails", "~> 2.1"

    gem "bundler", "~> 2.2"
    gem "database_cleaner", "~> 2.0"
    gem "pry-byebug", "~> 3.8"
    gem "pry-doc", "~> 1.2"
    gem "pry-rails", "~> 0.3.9"
    # gem "stimulus_reflex", "~> 3.4"

    gem "nokogiri", ">= 1.12.5"
    gem "view_component", "~> 2.40", require: 'view_component/engine'

    gem_group :development, :test do
    gem "flay", "~> 2.12"
    gem "flog", "~> 4.6"
    gem "inch", "~> 0.8.0"
    gem "reek", "~> 6.0"
    gem "rubocop-rails", "~> 2.12"
    gem "yard", "~> 0.9.26"

    gem "fabrication", "~> 2.22"
    gem "simplecov", "~> 0.21.2"
    gem "ffaker", "~> 2.19"
    gem "rspec-html-matchers", "~> 0.9.4"
    gem "html2slim", "~> 0.2.0"
    end

    gem_group :test do
    gem 'capybara', '>= 3.26'
    gem 'selenium-webdriver'
    # Easy installation and use of web drivers to run system tests with browsers
    gem 'webdrivers'
    gem "rspec-rails", "~> 5.0"
    end

    #
    # Gemfile created; on to
    # * initial setup of the `rbenv` gemset; and
    # * running (what as far as we care is the first) `bundle install`.
    #

    run "cp ../.ruby-version ."
    run 'rm -f .rbenv-gemset'

    run "mkdir -p bin tmp/gemset"
    run "rbenv gemset delete #{ENV['RBENV_VERSION']} ./tmp/gemset 2>&1 > /dev/null"
    run "rbenv gemset create #{ENV['RBENV_VERSION']} ./tmp/gemset"
    run 'rm -f .rbenv-gemset'
    run "rbenv gemset init ./tmp/gemset"
    # run "echo ./tmp/gemset > .rbenv-gemset"
    run 'cd #{@app_name}; rbenv rehash; echo "Active gemsets: " `rbenv gemset active`'
    run "bundle install"

    #
    # Make sure we've got all our binstubs set up so that Rails will simply use them
    # (instead of complaining that they're set up for BUndler.)
    #

    run "bundle binstubs --all --force"
    run "(yes | rails app:update:bin)", capture: true
    rails_command 'app:update:bin'

    #
    # Create the database role/user for the app.
    #

    run "createuser -dw #{@app_name}"

    #
    # Tell Rails to use the `fabrication` Gem fixture data. It's pretty cool that
    # Rails/Thor has the `inject_data_file` method; this used to be the first of
    # two steps performed manually by directly editing files.
    #

    inject_into_file "config/application.rb",
    after: " config.generators.system_tests = nil\n" do <<-EOS
    config.generators do |gen|
    gen.test_framework :rspec, fixture_replacement: :fabrication
    gen.fixture_replacement :fabrication, dir: "spec/fabricators"
    end
    EOS
    end

    #
    # Fix the generated `Rails.root.join` call in the development environment, else
    # Rubocop will kvetch about it whenever its Rake task is called, either directly
    # or as part of the default processing. (See the Rake task setup below.)
    #

    gsub_file('config/environments/development.rb',
    "Rails.root.join('tmp', 'caching-dev.txt')",
    "Rails.root.join('tmp/caching-dev.txt')")

    #
    # Create the initial (empty) development and test databases.
    #

    rails_command "db:create"
    rails_command "db:migrate"
    rails_command "db:seed"

    #
    # Now is a good time to get RSpec set up
    #

    rails_command 'generate rspec:install'

    #
    # Create a Procfile for `foreman` to use to run multiple processes, including
    # the web server running our app.
    #

    create_file "Procfile", <<-ENDIT
    webpack: bin/webpack-dev-server
    web: bin/rails server -p 3000
    ENDIT
    # TBD: Find out why Foreman changes ports & we must explicitly specify 3000

    run 'bin/foreman check'

    #
    # Set up Git and stage all existing files not specified in `.gitignore`. Note
    # that we aren't committing yet; the idea is that we'll have one single commit
    # after all steps in this generator script (and the surrounding Rails processing
    # code) will have been completed.
    #
    # TBD: Figure out how to define an after-all-generator-steps-completed hook. Not
    # just for this script; for the whole generator that this runs within.
    #
    run 'rm -rf .git'
    git init: '-b main'
    git add: '.'

    insert_into_file 'spec/spec_helper.rb', before: 'RSpec.configure do |config|' do <<-EOS
    require 'simplecov'
    SimpleCov.start do
    coverage_dir './tmp/coverage'
    add_filter '/lib/tasks'
    add_filter '/spec'
    add_filter '/tmp'
    end
    EOS
    end
    git add: 'spec/spec_helper.rb'

    #
    # Set up Webpacker.
    # TBD: In future, we'll likely add Stimulus JS, but not at present.
    #

    rails_command 'dev:cache'
    rails_command 'webpacker:install'
    rails_command 'webpacker:check_binstubs'
    git add: '.'

    #
    # We use Slim for view templates, not ERb as generated by default. Convert the
    # app-wide base view template.
    #

    run 'erb2slim -d app/views/layouts/application.html.erb'
    git add: 'app/views/layouts/'

    #
    # Add configuration files for Flay, Reek, and Rubocop
    #

    # Add .flayignore

    create_file '.flayignore', <<-ENDIT
    ./spec/**/*.rb
    ./tmp/**/*
    ENDIT
    git add: '.flayignore'

    # Add Reek config

    create_file 'config.reek', <<-ENDIT
    ---
    detectors:
    IrresponsibleModule:
    enabled: false
    MissingSafeMethod:
    enabled: false
    NestedIterators:
    max_allowed_nesting: 2
    ignore_iterators:
    - lambda
    UncommunicativeVariableName:
    exclude:
    - Conversagence::Application
    UnusedPrivateMethod:
    enabled: true
    UtilityFunction:
    public_methods_only: true
    LongParameterList:
    max_params: 4 # If it's good enough for Sandi, it's good enough for us
    ENDIT
    git add: 'config.reek'

    # Add Rubocop config
    create_file '.rubocop.yml', <<ENDIT
    require:
    - rubocop-rails
    AllCops:
    TargetRubyVersion: 2.6
    Bundler/OrderedGems:
    Exclude:
    - 'Gemfile'
    Metrics/BlockLength:
    Max: 300
    Include:
    - 'test/**/*.rb'
    Metrics/ModuleLength:
    Max: 300
    Include:
    - 'test/**/*.rb'
    # Style/AsciiComments:
    # Exclude:
    # - 'spec/web/views/home/index_spec.rb'
    Style/CommentedKeyword:
    Enabled: false
    # This silences 'Missing top-level class documentation comment.', which we
    # particularly want to do on (initially-)generated files.
    Style/Documentation:
    Enabled: false
    Include:
    - 'app/**/*.rb'
    - 'lib/**/*.rb'
    - 'test/**/*.rb'
    ###
    ### New cops added as of rubocop 0.85.1 and rubocop-rails 2.6.0
    Layout/EmptyLinesAroundAttributeAccessor:
    Enabled: true
    Layout/SpaceAroundMethodCallOperator:
    Enabled: true
    Lint/DeprecatedOpenSSLConstant:
    Enabled: true
    Lint/MixedRegexpCaptureTypes:
    Enabled: true
    Lint/RaiseException:
    Enabled: true
    Lint/StructNewOverride:
    Enabled: true
    Style/ExponentialNotation:
    Enabled: true
    Style/HashEachMethods:
    Enabled: true
    Style/HashTransformKeys:
    Enabled: true
    Style/HashTransformValues:
    Enabled: true
    Style/RedundantRegexpCharacterClass:
    Enabled: true
    Style/RedundantRegexpEscape:
    Enabled: true
    Style/SlicingWithRange:
    Enabled: true
    ENDIT
    git add: '.rubocop.yml'

    #
    # Add Rake task definitions, and the default set and order of tasks to be run
    # by `rake` when invoked without a task name.
    #

    ## For Flay

    create_file "lib/tasks/flay.rake", <<-ENDIT
    # frozen_string_literal: true
    require 'flay'
    require 'flay_task'
    FlayTask.new do |t|
    t.verbose = true
    t.dirs = %w[app config lib]
    end
    ENDIT
    git add: 'lib/tasks/flay.rake'

    ## For Flog

    create_file 'lib/tasks/flog.rake', <<-ENDIT
    # frozen_string_literal: true
    require 'flog'
    require 'flog_task'
    class FlogTask < Rake::TaskLib
    attr_accessor :methods_only
    end
    FlogTask.new do |t|
    t.verbose = true
    t.threshold = 200 # default is 200
    t.methods_only = true
    t.dirs = %w[app config lib] # Look, Ma; no specs! Run the tool manually every so often for those.
    end
    ENDIT
    git add: 'lib/tasks/flog.rake'

    ## For Inch

    create_file "lib/tasks/inch.rake", <<-ENDIT
    # frozen_string_literal: true
    require 'inch/rake'
    Inch::Rake::Suggest.new do |t|
    t.args = ['--no-undocumented', '--pedantic']
    end
    ENDIT
    git add: 'lib/tasks/inch.rake'

    ## For Reek

    create_file "lib/tasks/reek.rake", <<-ENDIT
    # frozen_string_literal: true
    require 'reek/rake/task'
    Reek::Rake::Task.new do |t|
    t.config_file = 'config.reek'
    t.source_files = '{app,config,lib}/**/*.rb'
    t.reek_opts = '--sort-by smelliness --no-progress -s'
    # t.verbose = true
    end
    ENDIT
    git add: 'lib/tasks/reek.rake'

    ## For Rubocop
    create_file "lib/tasks/rubocop.rake", <<-ENDIT
    # frozen_string_literal: true
    require 'rubocop/rake_task'
    RuboCop::RakeTask.new(:rubocop) do |task|
    task.patterns = [
    'app/**/*.rb',
    'config/**/*.rb',
    'lib/**/*.rb',
    'spec/**/*.rb'
    ]
    task.formatters = ['simple']
    task.fail_on_error = true
    task.requires << 'rubocop-rails'
    task.options << '--config=.rubocop.yml'
    task.options << '--display-cop-names'
    end
    ENDIT
    git add: 'lib/tasks/rubocop.rake'

    ## For Bundler sub-tasks

    create_file "lib/tasks/bundler-audit.rake", <<-ENDIT
    # frozen_string_literal: true
    namespace :bundler do
    desc 'Run bundler-audit, updating its database and then verbosely checking'
    task :audit do
    system('bundler-audit check -uv')
    end
    end
    ENDIT
    git add: 'lib/tasks/bundler-audit.rake'

    ## Defaults
    # frozen_string_literal: true

    create_file "lib/tasks/defaults.rake", <<-ENDIT
    # frozen_string_literal: true
    default_tasks=%i[spec flog flay reek inch rubocop:auto_correct rubocop]
    bundler_tasks=[]
    if ENV['RAKE_BUNDLER'] || ENV['RAKE_BUNDLER_GRAPH']
    bundler_tasks = %i[bundler:audit bundler:dependencies:count]
    if ENV['RAKE_BUNDLER_GRAPH']
    bundler_tasks = %i[bundler:audit bundler:dependencies:graph bundler:dependencies:count]
    end
    default_tasks += bundler_tasks
    end
    # Rake::Task['default'].clear
    task default: default_tasks
    default_tasks = nil
    bundler_tasks = nil
    ENDIT
    git add: 'lib/tasks/defaults.rake'

    #
    # Install TailwindCSS et al
    #

    run 'yarn add tailwindcss@npm:@tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9'
    git add: 'package.json yarn.lock'

    insert_into_file 'postcss.config.js', after: ' plugins: [' do <<-EOS
    require('tailwindcss'),
    EOS
    end
    git add: 'postcss.config.js'

    run 'node_modules/.bin/tailwindcss init'
    git add: 'tailwind.config.js'

    insert_into_file 'app/javascript/packs/application.js', after: 'Turbolinks.start()' do <<-EOS
    import "tailwindcss/tailwind.css"
    EOS
    end
    git add: 'app/javascript/packs/application.js'
    gsub_file('app/views/layouts/application.html.slim', 'stylesheet_link_tag',
    'stylesheet_pack_tag')
    git add: 'app/views/layouts/application.html.slim'

    #
    # Let's just make everything Rubocop-clean before we're done.
    #

    rails_command 'rubocop:auto_correct'
    git add: 'app/ config/ spec/'
    run 'git add --force public/packs/manifest.json'

    #
    # NOTE: Prior to the initial commit, you'll need to run `git add .` from the
    # command line before manually making the initial commit. The only file seemingly
    # affected is `public/packs/manifest.json`. We need to figure out how to automate
    # this. Is there an "after-everything" hook supported by the Rails app generator?
    #
    # We'll run `hr` a couple of times so that it's visually apparent in the output
    # from generating the app where the end of our template is, and the start of more
    # commands executed by the base generator.
    #

    run "hr"
    run "hr"
  2. jdickey revised this gist Oct 26, 2021. 2 changed files with 452 additions and 34 deletions.
    Original file line number Diff line number Diff line change
    @@ -18,20 +18,9 @@ The template file, which should automate setup of the newly-created app to curre

    When I generate a new app using the template, everything appears to work fine setting up the new app up to and including when `bundle install` is run. However, immediately after bundling reports success, the Gems are installed not in the Gemset, but in the `rbenv` Gem repository for the current Ruby version. Running the installation process manually stores the Gems in the expected place.

    Also, immediately after Bundler success, I see two error messages:
    When I started writing this Gist, I had several open questions that I was seeking help for; I found answers to most of them on my own, save for two:

    ```
    Skipping `rails webpacker:install` because `bundle install` was skipped.
    To complete setup, you must run `bundle install` followed by `rails webpacker:install`.
    ```

    While I *do* specify `--skip-bundle` in the `~/.railsrc` file, I run `bundler install` explicitly in the generator, after the `Gemfile` is set up to suit.

    I'm fairly certain that this will boil down to a *d'oh!* moment as my nose is rubbed in the difference between what I *think* I see (in the generator source) vs what is actually there. For that, I extend my plea to you folks.

    So, my questions are:

    1. Primarily, how do I get the generator to install Webpacker and continue with the existing bundle?
    2. Why are the bundled Gems being installed in the `rbenv` Gem repository rather than my gemset, as occurs when I proceed manually?
    1. Why are the bundled Gems being installed in the `rbenv` Gem repository rather than my gemset, as occurs when I proceed manually?
    2. Why has `app/javascript/packs/application.js` changed after completion of the template, forcing `public/packs/manifest.json` to also change?

    Help?
    469 changes: 449 additions & 20 deletions template.rb
    Original file line number Diff line number Diff line change
    @@ -1,15 +1,34 @@
    # This is my (WIP) ~/src/rails/template.rb file, patterned after an oft-used, documented manual procedure.
    # frozen_string_literal: true

    # DRAFT Rails Application Template
    # Copyright 2021, Jeff Dickey/Seven Sigma Agility
    # Licensed for public use under the MIT License.
    #
    # This template sets up a newly-created Rails app, in conjunction with the
    # associated `.railsrc` file. This (mostly; see below) automates processes that
    # have been in use for a decade or so, over dozens of apps. Better late than
    # never, yes? B-)
    #
    # Sample usage would look something like
    # ```
    # rails new the_app -m ./template.rb
    # ```
    # where `the_app` would be replaced by the actual name of your new application.
    #
    # First, we set up our Gemfile. This is somewhat abbreviated from our standard
    # since this whole script runs essentially as a hook within Rails' template
    # processing, which has already created a Gemfile by the time we get here. It
    # Would Be Very Nice if that was documented a bit more clearly in the Rails
    # Guides, but oh well.
    #
    # This setup assumes `zsh`, `rbenv`, and `rbenv-gemset`. It further asdumes that
    # it is being run from what will become the parent directory of the new app, in
    # which exists a `.ruby-version` file, and possibly an `.rbenv-gemset` directory
    # that will be **DELETED** if it exists.
    ################################################################################

    # Explicitly specify Gems' dependencies to eliminate Dependabot warnings.
    # FIXME: Outdated, hardcoded Node packages are *evil*.

    # gem 'rails', '~> 6.1.4', '>= 6.1.4.1'
    # gem 'pg', '~> 1.1'
    # gem 'puma', '~> 5.0'
    # gem 'webpacker', '~> 5.0'
    # gem 'turbolinks', '~> 5'
    # gem 'jbuilder', '~> 2.7'
    gem 'redis', '~> 4.0'
    gem 'bcrypt', '~> 3.1.7'
    gem "bundler-audit", "~> 0.9.0"
    @@ -27,12 +46,10 @@
    gem "pry-rails", "~> 0.3.9"
    # gem "stimulus_reflex", "~> 3.4"

    gem "html2slim", "~> 0.2.0"
    gem "nokogiri", ">= 1.12.5"
    gem "view_component", "~> 2.40", require: 'view_component/engine'

    gem_group :development, :test do
    # gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
    gem "flay", "~> 2.12"
    gem "flog", "~> 4.6"
    gem "inch", "~> 0.8.0"
    @@ -44,14 +61,9 @@
    gem "simplecov", "~> 0.21.2"
    gem "ffaker", "~> 2.19"
    gem "rspec-html-matchers", "~> 0.9.4"
    gem "html2slim", "~> 0.2.0"
    end

    # gem_group :development do
    # gem 'web-console', '>= 4.1.0'
    # gem 'rack-mini-profiler', '~> 2.0'
    # gem 'listen', '~> 3.3'
    # end

    gem_group :test do
    gem 'capybara', '>= 3.26'
    gem 'selenium-webdriver'
    @@ -60,7 +72,13 @@
    gem "rspec-rails", "~> 5.0"
    end

    run "cp .ruby-version #{@app_name}"
    #
    # Gemfile created; on to
    # * initial setup of the `rbenv` gemset; and
    # * running (what as far as we care is the first) `bundle install`.
    #

    run "cp ../.ruby-version ."
    run 'rm -f .rbenv-gemset'

    run "mkdir -p bin tmp/gemset"
    @@ -70,7 +88,418 @@
    run "rbenv gemset init ./tmp/gemset"
    # run "echo ./tmp/gemset > .rbenv-gemset"
    run 'cd #{@app_name}; rbenv rehash; echo "Active gemsets: " `rbenv gemset active`'
    run "ls -la ."
    run "pwd"
    run "bundle install"
    run "ls -la ./tmp/gemset/"

    #
    # Make sure we've got all our binstubs set up so that Rails will simply use them
    # (instead of complaining that they're set up for BUndler.)
    #

    run "bundle binstubs --all --force"
    run "(yes | rails app:update:bin)", capture: true
    rails_command 'app:update:bin'

    #
    # Create the database role/user for the app.
    #

    run "createuser -dw #{@app_name}"

    #
    # Tell Rails to use the `fabrication` Gem fixture data. It's pretty cool that
    # Rails/Thor has the `inject_data_file` method; this used to be the first of
    # two steps performed manually by directly editing files.
    #

    inject_into_file "config/application.rb",
    after: " config.generators.system_tests = nil\n" do <<-EOS
    config.generators do |gen|
    gen.test_framework :rspec, fixture_replacement: :fabrication
    gen.fixture_replacement :fabrication, dir: "spec/fabricators"
    end
    EOS
    end

    #
    # Fix the generated `Rails.root.join` call in the development environment, else
    # Rubocop will kvetch about it whenever its Rake task is called, either directly
    # or as part of the default processing. (See the Rake task setup below.)
    #

    gsub_file('config/environments/development.rb',
    "Rails.root.join('tmp', 'caching-dev.txt')",
    "Rails.root.join('tmp/caching-dev.txt')")

    #
    # Create the initial (empty) development and test databases.
    #

    rails_command "db:create"
    rails_command "db:migrate"
    rails_command "db:seed"

    #
    # Now is a good time to get RSpec set up
    #

    rails_command 'generate rspec:install'

    #
    # Create a Procfile for `foreman` to use to run multiple processes, including
    # the web server running our app.
    #

    create_file "Procfile", <<-ENDIT
    webpack: bin/webpack-dev-server
    web: bin/rails server -p 3000
    ENDIT
    # TBD: Find out why Foreman changes ports & we must explicitly specify 3000

    run 'bin/foreman check'

    #
    # Set up Git and stage all existing files not specified in `.gitignore`. Note
    # that we aren't committing yet; the idea is that we'll have one single commit
    # after all steps in this generator script (and the surrounding Rails processing
    # code) will have been completed.
    #
    # TBD: Figure out how to define an after-all-generator-steps-completed hook. Not
    # just for this script; for the whole generator that this runs within.
    #
    run 'rm -rf .git'
    git init: '-b main'
    git add: '.'

    insert_into_file 'spec/spec_helper.rb', before: 'RSpec.configure do |config|' do <<-EOS
    require 'simplecov'
    SimpleCov.start do
    coverage_dir './tmp/coverage'
    add_filter '/lib/tasks'
    add_filter '/spec'
    add_filter '/tmp'
    end
    EOS
    end
    git add: 'spec/spec_helper.rb'

    #
    # Set up Webpacker.
    # TBD: In future, we'll likely add Stimulus JS, but not at present.
    #

    rails_command 'dev:cache'
    rails_command 'webpacker:install'
    rails_command 'webpacker:check_binstubs'
    git add: '.'

    #
    # We use Slim for view templates, not ERb as generated by default. Convert the
    # app-wide base view template.
    #

    run 'erb2slim -d app/views/layouts/application.html.erb'
    git add: 'app/views/layouts/'

    #
    # Add configuration files for Flay, Reek, and Rubocop
    #

    # Add .flayignore

    create_file '.flayignore', <<-ENDIT
    ./spec/**/*.rb
    ./tmp/**/*
    ENDIT
    git add: '.flayignore'

    # Add Reek config

    create_file 'config.reek', <<-ENDIT
    ---
    detectors:
    IrresponsibleModule:
    enabled: false
    MissingSafeMethod:
    enabled: false
    NestedIterators:
    max_allowed_nesting: 2
    ignore_iterators:
    - lambda
    UncommunicativeVariableName:
    exclude:
    - Conversagence::Application
    UnusedPrivateMethod:
    enabled: true
    UtilityFunction:
    public_methods_only: true
    LongParameterList:
    max_params: 4 # If it's good enough for Sandi, it's good enough for us
    ENDIT
    git add: 'config.reek'

    # Add Rubocop config
    create_file '.rubocop.yml', <<ENDIT
    require:
    - rubocop-rails
    AllCops:
    TargetRubyVersion: 2.6
    Bundler/OrderedGems:
    Exclude:
    - 'Gemfile'
    Metrics/BlockLength:
    Max: 300
    Include:
    - 'test/**/*.rb'
    Metrics/ModuleLength:
    Max: 300
    Include:
    - 'test/**/*.rb'
    # Style/AsciiComments:
    # Exclude:
    # - 'spec/web/views/home/index_spec.rb'
    Style/CommentedKeyword:
    Enabled: false
    # This silences 'Missing top-level class documentation comment.', which we
    # particularly want to do on (initially-)generated files.
    Style/Documentation:
    Enabled: false
    Include:
    - 'app/**/*.rb'
    - 'lib/**/*.rb'
    - 'test/**/*.rb'
    ###
    ### New cops added as of rubocop 0.85.1 and rubocop-rails 2.6.0
    Layout/EmptyLinesAroundAttributeAccessor:
    Enabled: true
    Layout/SpaceAroundMethodCallOperator:
    Enabled: true
    Lint/DeprecatedOpenSSLConstant:
    Enabled: true
    Lint/MixedRegexpCaptureTypes:
    Enabled: true
    Lint/RaiseException:
    Enabled: true
    Lint/StructNewOverride:
    Enabled: true
    Style/ExponentialNotation:
    Enabled: true
    Style/HashEachMethods:
    Enabled: true
    Style/HashTransformKeys:
    Enabled: true
    Style/HashTransformValues:
    Enabled: true
    Style/RedundantRegexpCharacterClass:
    Enabled: true
    Style/RedundantRegexpEscape:
    Enabled: true
    Style/SlicingWithRange:
    Enabled: true
    ENDIT
    git add: '.rubocop.yml'

    #
    # Add Rake task definitions, and the default set and order of tasks to be run
    # by `rake` when invoked without a task name.
    #

    ## For Flay

    create_file "lib/tasks/flay.rake", <<-ENDIT
    # frozen_string_literal: true
    require 'flay'
    require 'flay_task'
    FlayTask.new do |t|
    t.verbose = true
    t.dirs = %w[app config lib]
    end
    ENDIT
    git add: 'lib/tasks/flay.rake'

    ## For Flog

    create_file 'lib/tasks/flog.rake', <<-ENDIT
    # frozen_string_literal: true
    require 'flog'
    require 'flog_task'
    class FlogTask < Rake::TaskLib
    attr_accessor :methods_only
    end
    FlogTask.new do |t|
    t.verbose = true
    t.threshold = 200 # default is 200
    t.methods_only = true
    t.dirs = %w[app config lib] # Look, Ma; no specs! Run the tool manually every so often for those.
    end
    ENDIT
    git add: 'lib/tasks/flog.rake'

    ## For Inch

    create_file "lib/tasks/inch.rake", <<-ENDIT
    # frozen_string_literal: true
    require 'inch/rake'
    Inch::Rake::Suggest.new do |t|
    t.args = ['--no-undocumented', '--pedantic']
    end
    ENDIT
    git add: 'lib/tasks/inch.rake'

    ## For Reek

    create_file "lib/tasks/reek.rake", <<-ENDIT
    # frozen_string_literal: true
    require 'reek/rake/task'
    Reek::Rake::Task.new do |t|
    t.config_file = 'config.reek'
    t.source_files = '{app,config,lib}/**/*.rb'
    t.reek_opts = '--sort-by smelliness --no-progress -s'
    # t.verbose = true
    end
    ENDIT
    git add: 'lib/tasks/reek.rake'

    ## For Rubocop
    create_file "lib/tasks/rubocop.rake", <<-ENDIT
    # frozen_string_literal: true
    require 'rubocop/rake_task'
    RuboCop::RakeTask.new(:rubocop) do |task|
    task.patterns = [
    'app/**/*.rb',
    'config/**/*.rb',
    'lib/**/*.rb',
    'spec/**/*.rb'
    ]
    task.formatters = ['simple']
    task.fail_on_error = true
    task.requires << 'rubocop-rails'
    task.options << '--config=.rubocop.yml'
    task.options << '--display-cop-names'
    end
    ENDIT
    git add: 'lib/tasks/rubocop.rake'

    ## For Bundler sub-tasks

    create_file "lib/tasks/bundler-audit.rake", <<-ENDIT
    # frozen_string_literal: true
    namespace :bundler do
    desc 'Run bundler-audit, updating its database and then verbosely checking'
    task :audit do
    system('bundler-audit check -uv')
    end
    end
    ENDIT
    git add: 'lib/tasks/bundler-audit.rake'

    ## Defaults
    # frozen_string_literal: true

    create_file "lib/tasks/defaults.rake", <<-ENDIT
    # frozen_string_literal: true
    default_tasks=%i[spec flog flay reek inch rubocop:auto_correct rubocop]
    bundler_tasks=[]
    if ENV['RAKE_BUNDLER'] || ENV['RAKE_BUNDLER_GRAPH']
    bundler_tasks = %i[bundler:audit bundler:dependencies:count]
    if ENV['RAKE_BUNDLER_GRAPH']
    bundler_tasks = %i[bundler:audit bundler:dependencies:graph bundler:dependencies:count]
    end
    default_tasks += bundler_tasks
    end
    # Rake::Task['default'].clear
    task default: default_tasks
    default_tasks = nil
    bundler_tasks = nil
    ENDIT
    git add: 'lib/tasks/defaults.rake'

    #
    # Install TailwindCSS et al
    #

    run 'yarn add tailwindcss@npm:@tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9'
    git add: 'package.json yarn.lock'

    insert_into_file 'postcss.config.js', after: ' plugins: [' do <<-EOS
    require('tailwindcss'),
    EOS
    end
    git add: 'postcss.config.js'

    run 'node_modules/.bin/tailwindcss init'
    git add: 'tailwind.config.js'

    insert_into_file 'app/javascript/packs/application.js', after: 'Turbolinks.start()' do <<-EOS
    import "tailwindcss/tailwind.css"
    EOS
    end
    git add: 'app/javascript/packs/application.js'
    gsub_file('app/views/layouts/application.html.slim', 'stylesheet_link_tag',
    'stylesheet_pack_tag')
    git add: 'app/views/layouts/application.html.slim'

    #
    # Let's just make everything Rubocop-clean before we're done.
    #

    rails_command 'rubocop:auto_correct'
    git add: 'app/ config/ spec/'
    run 'git add --force public/packs/manifest.json'

    #
    # NOTE: Prior to the initial commit, you'll need to run `git add .` from the
    # command line before manually making the initial commit. The only file seemingly
    # affected is `public/packs/manifest.json`. We need to figure out how to automate
    # this. Is there an "after-everything" hook supported by the Rails app generator?
    #
    # We'll run `hr` a couple of times so that it's visually apparent in the output
    # from generating the app where the end of our template is, and the start of more
    # commands executed by the base generator.
    #

    run "hr"
    run "hr"
  3. jdickey revised this gist Oct 18, 2021. 1 changed file with 3 additions and 0 deletions.
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,8 @@
    ## Intro

    (NB: Forgive the many content-free revisions; it has been many moons since I have created a Gist with attachments, and getting the
    ordering currect is maddeningly non-trivial.)

    After too many years doing this, never mind how many, I'm attempting to automate the generation of Rails apps using a
    `.railsrc` file and an app-generation template. The `.railsrc` file, largely cribbed from numerous others, appears to work
    splendidly.
  4. jdickey renamed this gist Oct 18, 2021. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  5. jdickey renamed this gist Oct 18, 2021. 1 changed file with 0 additions and 0 deletions.
  6. jdickey renamed this gist Oct 18, 2021. 1 changed file with 0 additions and 0 deletions.
  7. jdickey created this gist Oct 18, 2021.
    35 changes: 35 additions & 0 deletions .railsrc
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,35 @@
    # This is my ~/.railsrc file. There are many like it, but this one is mine.

    # known defaults
    --no-api
    --no-dev
    --no-edge
    --no-no-rc # obviously...
    --no-skip-action-cable
    --no-skip-active-record
    --no-skip-gemfile
    --no-skip-git
    --no-skip-javascript
    --no-skip-keeps
    --no-skip-listen
    --no-skip-puma
    --no-skip-sprockets
    --no-skip-turbolinks
    --no-skip-yarn
    --skip-coffee
    --skip-namespace
    --skip-system-test # ditto
    --skip-test # Don't use MiniTest; we'll add RSpec later
    # --ruby=/path/to/executable/ruby
    # --template=/path/to/app/template

    # Custom/non-default values
    --database=postgresql
    --skip-action-mailer
    --skip-active-storage
    --skip-bootsnap
    --skip-bundle
    --skip-spring
    --webpack=stimulus

    # --template=/path/to/template.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,34 @@
    ## Intro

    After too many years doing this, never mind how many, I'm attempting to automate the generation of Rails apps using a
    `.railsrc` file and an app-generation template. The `.railsrc` file, largely cribbed from numerous others, appears to work
    splendidly.

    The template file, which should automate setup of the newly-created app to current shop standards, is being developed incrementally; increments largely separated by *the next wall I hit*. The current "wall" has had me stopped cold for two days; my Stack Overflow skills and Google-fu are clearly not up to the task.

    ## Possibly Relevant Background Items

    1. Ruby is installed by way of [`rbenv`](https://github.com/rbenv/rbenv#readme), which we have used for *years* without known issues. We are presently on Ruby 3.0.2p107.
    2. I'm attempting to continue our long-time shop-standard usage of [`rbenv-gemsets`](https://github.com/jf/rbenv-gemset#readme), which has hitherto (before attempted use of an app generator) saved our proverbial bacon often enough that I still have *some* hair. It supports our experimentation and evolution of candidate Gems for use, without polluting the system Gem repository.

    ## Problem Statement

    When I generate a new app using the template, everything appears to work fine setting up the new app up to and including when `bundle install` is run. However, immediately after bundling reports success, the Gems are installed not in the Gemset, but in the `rbenv` Gem repository for the current Ruby version. Running the installation process manually stores the Gems in the expected place.

    Also, immediately after Bundler success, I see two error messages:

    ```
    Skipping `rails webpacker:install` because `bundle install` was skipped.
    To complete setup, you must run `bundle install` followed by `rails webpacker:install`.
    ```

    While I *do* specify `--skip-bundle` in the `~/.railsrc` file, I run `bundler install` explicitly in the generator, after the `Gemfile` is set up to suit.

    I'm fairly certain that this will boil down to a *d'oh!* moment as my nose is rubbed in the difference between what I *think* I see (in the generator source) vs what is actually there. For that, I extend my plea to you folks.

    So, my questions are:

    1. Primarily, how do I get the generator to install Webpacker and continue with the existing bundle?
    2. Why are the bundled Gems being installed in the `rbenv` Gem repository rather than my gemset, as occurs when I proceed manually?

    Help?
    76 changes: 76 additions & 0 deletions template.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,76 @@
    # This is my (WIP) ~/src/rails/template.rb file, patterned after an oft-used, documented manual procedure.


    # Explicitly specify Gems' dependencies to eliminate Dependabot warnings.
    # FIXME: Outdated, hardcoded Node packages are *evil*.

    # gem 'rails', '~> 6.1.4', '>= 6.1.4.1'
    # gem 'pg', '~> 1.1'
    # gem 'puma', '~> 5.0'
    # gem 'webpacker', '~> 5.0'
    # gem 'turbolinks', '~> 5'
    # gem 'jbuilder', '~> 2.7'
    gem 'redis', '~> 4.0'
    gem 'bcrypt', '~> 3.1.7'
    gem "bundler-audit", "~> 0.9.0"
    gem "dotenv-rails", "~> 2.7"
    gem "dry-monads", "~> 1.4"
    gem "ice_nine", "~> 0.11.2"
    gem "slim-rails", "~> 3.3"
    gem "foreman", "~> 0.87.2"
    gem "sassc-rails", "~> 2.1"

    gem "bundler", "~> 2.2"
    gem "database_cleaner", "~> 2.0"
    gem "pry-byebug", "~> 3.8"
    gem "pry-doc", "~> 1.2"
    gem "pry-rails", "~> 0.3.9"
    # gem "stimulus_reflex", "~> 3.4"

    gem "html2slim", "~> 0.2.0"
    gem "nokogiri", ">= 1.12.5"
    gem "view_component", "~> 2.40", require: 'view_component/engine'

    gem_group :development, :test do
    # gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
    gem "flay", "~> 2.12"
    gem "flog", "~> 4.6"
    gem "inch", "~> 0.8.0"
    gem "reek", "~> 6.0"
    gem "rubocop-rails", "~> 2.12"
    gem "yard", "~> 0.9.26"

    gem "fabrication", "~> 2.22"
    gem "simplecov", "~> 0.21.2"
    gem "ffaker", "~> 2.19"
    gem "rspec-html-matchers", "~> 0.9.4"
    end

    # gem_group :development do
    # gem 'web-console', '>= 4.1.0'
    # gem 'rack-mini-profiler', '~> 2.0'
    # gem 'listen', '~> 3.3'
    # end

    gem_group :test do
    gem 'capybara', '>= 3.26'
    gem 'selenium-webdriver'
    # Easy installation and use of web drivers to run system tests with browsers
    gem 'webdrivers'
    gem "rspec-rails", "~> 5.0"
    end

    run "cp .ruby-version #{@app_name}"
    run 'rm -f .rbenv-gemset'

    run "mkdir -p bin tmp/gemset"
    run "rbenv gemset delete #{ENV['RBENV_VERSION']} ./tmp/gemset 2>&1 > /dev/null"
    run "rbenv gemset create #{ENV['RBENV_VERSION']} ./tmp/gemset"
    run 'rm -f .rbenv-gemset'
    run "rbenv gemset init ./tmp/gemset"
    # run "echo ./tmp/gemset > .rbenv-gemset"
    run 'cd #{@app_name}; rbenv rehash; echo "Active gemsets: " `rbenv gemset active`'
    run "ls -la ."
    run "pwd"
    run "bundle install"
    run "ls -la ./tmp/gemset/"