Minijohn's Base Stack

Rails 6 template, with Devise, RSpec, Webpacker, Bootstrap 4 and SEO best practices.
Icons/chart bar
Used 209 times
Created by
M Minijohn

Usage

MVP Ruby on Rails template


WIP, please use with caution. Not 💯 ready yet.

My base stack and config of a rails app for quick prototyping.

rails new XXX --skip-sprockets --skip-test --skip-system-test --skip-action-cable --webpack=react -d=postgresql -m https://railsbytes.com/script/zmns64

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

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

Review the code before running this template on your machine.

require 'fileutils'

## Helpers ##########
#####################
def yarn(*packages)
  run("yarn add #{packages.join(" ")}")
end

def find_and_replace_in_file(file_name, old_content, new_content)
  text = File.read(file_name)
  new_contents = text.gsub(old_content, new_content)
  File.open(file_name, 'w') { |file| file.write new_contents }
end

def source_paths
  # https://guides.rubyonrails.org/rails_application_templates.html#advanced-usage
  [__dir__]
end

## Blocks #############
#####################
def add_gems
  gem 'friendly_id'
  gem 'config'
  gem 'rack-rewrite'

  gem 'meta-tags'
  gem 'devise'

  gem_group :development, :test do
    gem 'pry'
    gem 'rspec-rails'
    gem 'factory_bot_rails'
    gem 'faker'
    gem 'awesome_print'
  end
  
  gem_group :test do
    gem 'database_cleaner'
  end

  gem_group :production do
    gem 'heroku-deflater'
  end
end

def setup_app
  inject_into_file 'config/application.rb', after: "config.generators.system_tests = nil\n" do <<~EOF

          config.generators do |g|
            g.test_framework  :rspec
            g.factory_bot dir: 'spec/factories'
            g.helper       false
            g.stylesheets  false
            g.javascripts  false
            g.helper_specs false
            g.view_specs   false
          end

          config.paths.add 'lib', eager_load: true

          # errors are now generated dynamically
          config.exceptions_app = self.routes

          # rewrites (removes) trailing slashes via a 301 redirect
          config.middleware.insert_before(Rack::Runtime, Rack::Rewrite) do
            r301 %r{^/(.*)/$}, '/$1'
          end
    EOF
  end
end

def setup_homepage
  generate(:controller, 'pages home')
  route "root 'pages#home'"
end

def setup_rspec
  generate "rspec:install"

  find_and_replace_in_file('spec/rails_helper.rb', "# Dir[Rails.root.join('spec'", "Dir[Rails.root.join('spec'")

  FileUtils.mkdir_p('spec/factories')
  FileUtils.mkdir_p('spec/support')

  create_file "spec/support/factory_bot.rb", <<~EOF
    RSpec.configure do |config|
      config.include FactoryBot::Syntax::Methods
    end
  EOF

  create_file "spec/support/database_cleaner.rb", <<~EOF
    RSpec.configure do |config|
      config.before(:suite) do
        DatabaseCleaner.clean_with(:truncation)
      end

      config.before(:each) do
        DatabaseCleaner.strategy = :transaction
      end

      config.before(:each, :js => true) do
        DatabaseCleaner.strategy = :truncation
      end

      config.before(:each) do
        DatabaseCleaner.start
      end

      config.after(:each) do
        DatabaseCleaner.clean
      end
    end
  EOF
end

def setup_friendly_id
  generate 'friendly_id'

  find_and_replace_in_file('config/initializers/friendly_id.rb', '# config.use :finders', 'config.use :finders')
  find_and_replace_in_file('config/initializers/friendly_id.rb', '# config.use :slugged', 'config.use :slugged')
end

def setup_devise
  generate "devise:install"
  generate :devise, 'User', 'slug:string:index', 'first_name:string', 'last_name:string', 'image:string', 'bio:text'
  rails_command 'db:migrate'

  generate 'devise:views', 'users'
  generate 'devise:controllers', 'users'

  environment "config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }", env: 'development'
  find_and_replace_in_file('config/initializers/devise.rb', '# config.scoped_views = false', 'config.scoped_views = true')
end

def setup_assets
  FileUtils.mkdir_p('app/frontend/images')
  FileUtils.mkdir_p('app/frontend/javascripts')
  FileUtils.mkdir_p('app/frontend/stylesheets')

  create_file "app/frontend/javascripts/application.js", <<~EOF
    require("@rails/ujs").start()
    require("turbolinks").start()

    var jQuery = require("jquery");

    global.$ = global.jQuery = jQuery;
    global.toastr = require("toastr");
    window.$ = window.jQuery = jQuery;

    import 'javascripts/bootstrap/init.js';
    import 'stylesheets/application';

    document.addEventListener("turbolinks:before-render", function() {
      Turbolinks.clearCache()
    })

    document.addEventListener('turbolinks:load', () => {
    });
  EOF
end

def setup_bootstrap
  yarn 'jquery', 'popper.js', 'bootstrap'

  inject_into_file 'config/webpack/environment.js', after: "const { environment } = require('@rails/webpacker')\n" do <<~EOF
    const webpack = require('webpack')

    environment.plugins.append('Provide', new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery',
      'window.jQuery': 'jquery',
      Popper: ['popper.js', 'default']
    }))
    EOF
  end

  inject_into_file 'app/views/layouts/application.html.erb', before: '</head>' do <<~EOF
      <%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    EOF
  end

  # build js assets
  FileUtils.mkdir_p('app/frontend/javascripts/bootstrap')
  create_file "app/frontend/javascripts/bootstrap/init.js", <<~EOF
    import 'bootstrap/js/dist/alert'
    import 'bootstrap/js/dist/button'
    import 'bootstrap/js/dist/carousel'
    import 'bootstrap/js/dist/collapse'
    import 'bootstrap/js/dist/dropdown'
    import 'bootstrap/js/dist/index'
    import 'bootstrap/js/dist/modal'
    import 'bootstrap/js/dist/popover'
    import 'bootstrap/js/dist/scrollspy'
    import 'bootstrap/js/dist/tab'
    import 'bootstrap/js/dist/toast'
    import 'bootstrap/js/dist/tooltip'
    import 'bootstrap/js/dist/util'
  EOF

  # build scss assets
  FileUtils.mkdir_p('app/frontend/stylesheets/bootstrap')
  create_file 'app/frontend/stylesheets/application.scss', "@import './bootstrap/init';"
  create_file "app/frontend/stylesheets/bootstrap/_variables.scss"
  create_file "app/frontend/stylesheets/bootstrap/init.scss", <<~EOF
    @import './_variables.scss';

    @import '~bootstrap/scss/_functions.scss';
    @import '~bootstrap/scss/_variables.scss';
    @import '~bootstrap/scss/_mixins.scss';
    @import '~bootstrap/scss/_root.scss';
    @import '~bootstrap/scss/_reboot.scss';
    @import '~bootstrap/scss/_type.scss';
    @import '~bootstrap/scss/_alert.scss';
    @import '~bootstrap/scss/_badge';
    @import '~bootstrap/scss/_breadcrumb';
    @import '~bootstrap/scss/_button-group';
    @import '~bootstrap/scss/_buttons';
    @import '~bootstrap/scss/_buttons.scss';
    @import '~bootstrap/scss/_card.scss';
    @import '~bootstrap/scss/_carousel.scss';
    @import '~bootstrap/scss/_close.scss';
    @import '~bootstrap/scss/_code.scss';
    @import '~bootstrap/scss/_custom-forms.scss';
    @import '~bootstrap/scss/_dropdown.scss';
    @import '~bootstrap/scss/_forms.scss';
    @import '~bootstrap/scss/_grid.scss';
    @import '~bootstrap/scss/_images.scss';
    @import '~bootstrap/scss/_input-group.scss';
    @import '~bootstrap/scss/_jumbotron.scss';
    @import '~bootstrap/scss/_list-group.scss';
    @import '~bootstrap/scss/_media.scss';
    @import '~bootstrap/scss/_modal.scss';
    @import '~bootstrap/scss/_nav.scss';
    @import '~bootstrap/scss/_navbar.scss';
    @import '~bootstrap/scss/_pagination.scss';
    @import '~bootstrap/scss/_popover.scss';
    @import '~bootstrap/scss/_print.scss';
    @import '~bootstrap/scss/_progress.scss';
    @import '~bootstrap/scss/_spinners.scss';
    @import '~bootstrap/scss/_tables.scss';
    @import '~bootstrap/scss/_toasts.scss';
    @import '~bootstrap/scss/_tooltip.scss';
    @import '~bootstrap/scss/_transitions.scss';
    @import '~bootstrap/scss/_utilities.scss';
  EOF

  inject_into_file 'app/frontend/javascripts/application.js', after: "window.$ = window.jQuery = jQuery;\n" do <<~EOF
      import 'javascripts/bootstrap/init.js';
      import 'stylesheets/application';
    EOF
  end
end

def add_bootstrap_navbar
  create_file 'app/views/layouts/_navbar.html.erb', <<~EOF
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
      <%= link_to Rails.application.class.parent_name, root_path, class:"navbar-brand" %>
      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>

      <div class="collapse navbar-collapse" id="navbarSupportedContent">
        <ul class="navbar-nav mr-auto">
          <li class="nav-item active">
            <%= link_to "Home", root_path, class:"nav-link" %>
          </li>

          <li class="nav-item">
            <%= link_to "About", "#", class:"nav-link" %>
          </li>
        </ul>

        <ul class="navbar-nav ml-auto">
          <% if current_user %>
            <li class="nav-item dropdown">
              <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                <%= current_user.email %>
              </a>

              <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
                  <%= link_to "Account Settings", edit_user_registration_path, class:"dropdown-item" %>
                  <div class="dropdown-divider"></div>
                  <%= link_to "Logout", destroy_user_session_path, method: :delete, class:"dropdown-item" %>
              </div>
            </li>
          <% else %>
            <li class="nav-item">
                <%= link_to "Register", new_user_registration_path, class:"nav-link" %>
            </li>

            <li class="nav-item">
              <%= link_to "Login", new_user_session_path, class:"nav-link" %>
            </li>
          <% end %>
        </ul>
      </div>
    </nav>
  EOF

  inject_into_file 'app/views/layouts/application.html.erb', before: '<%= yield %>' do
    "    <%= render 'layouts/navbar' %>\n"
  end
end

## Build ############
#####################
source_paths
add_gems

after_bundle do
  rails_command 'db:create'

  setup_app
  setup_homepage

  setup_rspec
  setup_friendly_id
  setup_devise

  setup_assets
  setup_bootstrap
  add_bootstrap_navbar
end
Comments

Sign up or Login to leave a comment.