Minijohn's Base Stack
Rails 6 template, with Devise, RSpec, Webpacker, Bootstrap 4 and SEO best practices.
Used 211 times
M
Minijohn
Usage
MVP Ruby on Rails template
by @theminijohn
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