Skip to content

Instantly share code, notes, and snippets.

@simonc
Last active June 25, 2016 17:34
Show Gist options
  • Save simonc/328b46571f1c470a5366 to your computer and use it in GitHub Desktop.
Save simonc/328b46571f1c470a5366 to your computer and use it in GitHub Desktop.
Rails Template FTW!
ruby_version = '2.3.1'
def yes!(statement)
!!(ask("#{statement} [yes]") =~ /^(y(es)?)?$/)
end
project_name = @app_name.parameterize
doing_tests = yes!('Do you plan on writing tests?')
doing_front_auth = yes!('Do you need authentication?')
doing_admin = yes!('Do you need an admin?')
doing_auth = doing_front_auth || doing_admin
user_model = if doing_front_auth
ask('User model name [User]:').presence || 'User'
end
doing_ui = yes!('Do you need UI stuff?')
say '--------------------------------------------------------------------- Gems', :yellow
file 'Gemfile', <<~CODE, force: true
source 'https://rubygems.org'
ruby '#{ruby_version}'
gem 'rails', '4.2.6'
CODE
gem 'aws-sdk', '~> 2.1'
gem 'bootstrap-sass', '~> 3.3' if doing_ui
gem 'carrierwave', '~> 0.11.1'
gem 'coffee-rails', '~> 4.1.0' if doing_ui
gem 'decent_exposure', '~> 2.3'
gem 'devise', '~> 4.1' if doing_auth
gem 'draper', '~> 2.1'
gem 'enumerize', '~> 1.0'
gem 'fog', '~> 1.28'
gem 'friendly_id', '~> 5.1'
gem 'interactor', '~> 3.1'
gem 'jquery-rails' if doing_ui
gem 'meta-tags', '~> 2.0', require: 'meta_tags' if doing_ui
gem 'mini_magick', '~> 4.1'
gem 'mustdown', '~> 0.1'
gem 'passenger', '~> 5.0'
gem 'pg'
gem 'rails-i18n', '~> 4.0'
gem 'redis-namespace', '~> 1.5'
gem 'sass-rails', '~> 5.0' if doing_ui
gem 'sidekiq', '~> 4.1'
gem 'sidetiq', '~> 0.7.0'
gem 'simple_form', '~> 3.1' if doing_ui
gem 'sinatra', '~> 1.4'
gem 'slim', '~> 3.0' if doing_ui
gem 'uglifier', '>= 1.3.0' if doing_ui
gem_group :development, :test do
gem 'better_errors'
gem 'binding_of_caller'
gem 'bullet'
gem 'capybara', require: false if doing_tests
gem 'dotenv-rails'
gem 'guard-rspec', require: false if doing_tests
gem 'irbtools', require: 'irbtools/configure'
gem 'launchy', require: false if doing_tests
gem 'poltergeist', require: false if doing_tests
gem 'quiet_assets'
gem 'rb-fchange', require: false if doing_tests
gem 'rb-fsevent', require: false if doing_tests
gem 'rb-inotify', require: false if doing_tests
gem 'rubocop', require: false
gem 'rspec-rails', require: false if doing_tests
gem 'scss_lint', require: false
gem 'spring', require: false
gem 'spring-commands-rspec', require: false if doing_tests
end
gem_group :production do
gem 'rack-rewrite', '~> 1.5'
gem 'rails_12factor'
gem 'rollbar'
end
say '------------------------------------------------------------- Ruby Version', :yellow
file '.ruby-version', "ruby-#{ruby_version}\n"
say '------------------------------------------------------------- Initializers', :yellow
initializer 'i18n.rb', <<~CODE
module I18n::AlwaysCascade
def lookup(locale, key, scope = [], options = {})
super locale, key, scope, options.merge(cascade: true)
end
end
I18n.backend.class.send :include, I18n::Backend::Cascade
I18n.backend.class.send :include, I18n::AlwaysCascade
CODE
initializer 'mustdown.rb', <<~CODE
Mustdown.configure do |config|
config.renderer_options = {
no_styles: true,
with_toc_data: true
}
end
CODE
initializer 'rack_tracker.rb', <<~CODE
if defined?(Rack::Tracker) && Rails.env.production?
Rails.application
.config
.middleware
.use(Rack::Tracker) do
handler :google_analytics, tracker: ENV.fetch('GOOGLE_ANALYTICS_ID')
end
end
CODE
initializer 'rollbar.rb', <<~CODE
if Rails.env.production? || Rails.env.staging?
require 'rollbar/rails'
Rollbar.configure do |config|
config.access_token = ENV.fetch('ROLLBAR_ACCESS_TOKEN')
config.person_username_method = 'email'
end
end
CODE
initializer 'sidekiq.rb', <<~CODE
require 'sidekiq/web'
redis_config = {
namespace: "#{project_name}-\#{Rails.env}",
url: ENV.fetch('REDIS_URL')
}
Sidekiq.configure_client do |config|
config.redis = redis_config.merge(size: 1)
end
Sidekiq.configure_server do |config|
config.redis = redis_config
if Rails.env.production? && defined?(ActiveRecord::Base)
db_config = ActiveRecord::Base.configurations[Rails.env] ||
Rails.application.config.database_configuration[Rails.env]
db_config['reaping_frequency'] = ENV.fetch('DB_REAP_FREQ') { 10 }
db_config['pool'] = ENV.fetch('SIDEKIQ_WORKERS') { 25 }
ActiveRecord::Base.establish_connection(db_config)
end
end
CODE
say '------------------------------------------------------------------ Envfile', :yellow
file '.env', <<~CODE
APP_DOMAIN=
AWS_ID=
AWS_BUCKET=#{project_name}-by-tinci
AWS_REGION=
AWS_SECRET=
DEVISE_PEPPER=123
DEVISE_SECRET_KEY=123
GOOGLE_ANALYTICS_ID=UA-XXXXX-XX
NO_REPLY_EMAIL=no-reply@#{project_name}.fr
REDIS_URL=redis://localhost:6379/0
CODE
say '------------------------------------------------------------- Environments', :yellow
insert_into_file 'config/environments/development.rb', <<-CODE, after: "# config.action_view.raise_on_missing_translations = true\n"
config.action_mailer.default_url_options = { host: '#{project_name}.dev' }
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = { host: 'localhost', port: 1025 }
config.action_mailer.asset_host = 'https://#{project_name}.dev'
config.after_initialize do
Bullet.enable = true
Bullet.bullet_logger = true
Bullet.console = true
Bullet.rails_logger = true
Bullet.add_footer = true
end
CODE
if doing_tests
insert_into_file 'config/environments/test.rb', <<~CODE, after: "# config.action_view.raise_on_missing_translations = true\n"
config.action_mailer.default_url_options = { host: '#{project_name}.dev' }
config.action_mailer.asset_host = 'https://#{project_name}.dev'
config.after_initialize do
Bullet.enable = true
Bullet.bullet_logger = true
Bullet.console = true
Bullet.rails_logger = true
Bullet.add_footer = true
end
CODE
end
insert_into_file 'config/environments/production.rb', <<-CODE, after: "config.active_record.dump_schema_after_migration = false\n"
config.action_mailer.default_url_options = { host: ENV.fetch('APP_DOMAIN') }
config.action_mailer.asset_host = "https://\#{ENV.fetch('APP_DOMAIN')}"
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
port: '587',
address: ENV.fetch('EMAIL_HOST'),
user_name: ENV.fetch('EMAIL_USERNAME'),
password: ENV.fetch('EMAIL_APIKEY'),
domain: 'heroku.com',
authentication: :plain
}
if defined?(Rack::Rewrite)
config.middleware.insert_before Rack::Runtime, Rack::Rewrite do
r301 /.*/, "https://\#{ENV.fetch 'APP_DOMAIN'}$&", if: proc { |rack_env|
rack_env['SERVER_NAME'] != ENV.fetch('APP_DOMAIN')
}
end
end
CODE
run 'cp config/environments/production.rb config/environments/staging.rb'
say '----------------------------------------------------- Application Settings', :yellow
file 'config/application.rb', <<~CODE, force: true
require 'active_model/railtie'
require 'active_job/railtie'
require 'active_record/railtie'
require 'action_controller/railtie'
require 'action_mailer/railtie'
require 'action_view/railtie'
require 'sprockets/railtie'
Bundler.require(*Rails.groups)
module Tinci
class Application < Rails::Application
config.active_job.queue_adapter = :sidekiq
config.active_record.raise_in_transactional_callbacks = true
config.i18n.available_locales = [:en, :fr]
config.i18n.default_locale = :fr
config.exceptions_app = self.routes
config.time_zone = 'Europe/Paris'
end
end
CODE
say '------------------------------------------------------------------ Locales', :yellow
run 'rm -f config/locales/en.yml'
file 'config/locales/fr.yml', <<~CODE
fr:
error_pages:
not_found:
title: Page non trouvée
text: 'Erreur 404 : La page recherchée n’a pu être trouvée.'
internal_server_error:
title: Erreur
text: >
Erreur 500 : L’application a rencontré une erreur. Notre équipe en a
été informée. Nous nous excusons pour la gêne occasionnée.
[Revenir à la page d’accueil](/)
CODE
file 'config/locales/devise_custom.fr.yml', <<~CODE
fr:
devise:
confirmations:
new:
title: Renvoyer les instructions de confirmation
submit: Envoyer
mailer:
signature: "L'équipe #{@app_name.humanize}"
confirmation_instructions:
confirm_account: Confirmer mon compte
message: >
Bienvenue sur #{@app_name.humanize} ! Pour confirmer votre inscription,
cliquez sur le lien ci-dessous :
title: "Confirmer mon compte #{@app_name.humanize}"
reset_password_instructions:
change_my_password: Modifier mon mot de passe
message: >
Une demande de modification de mot de passe a été effectuée sur #{@app_name.humanize}.
Vous pouvez l'effectuer en suivant le lien ci-dessous :
not_you: |
Vous n'êtes pas à l'origine de cette demande ? Ignorez cet email.
Votre mot de passe ne sera pas modifié tant que vous ne suivrez pas le
lien ci-dessus pour en créer un nouveau.
title: "Modifier mon mot de passe #{@app_name.humanize}"
unlock_instructions:
message: >
Votre compte #{@app_name.humanize} a été bloqué. Cliquez sur le lien
ci-dessous pour procéder à son débloquage :
title: "Compte #{@app_name.humanize} bloqué"
unlock_my_account: Débloquer mon compte
passwords:
edit:
submit: Enregistrer
title: Modifier mon mot de passe
new:
submit: Envoyez moi les instructions
title: "J'ai oublié mon mot de passe"
registrations:
edit:
account_cancellation: Fermeture de compte
back: Retour
cancel_account_link: Fermer mon compte
cancel_account_text: >
Vous pouvez fermer votre compte #{@app_name.humanize} ?
Il vous suffit de cliquer sur le bouton suivant :
confirm: >
Êtes-vous sûr de souhaiter la suppression de votre compte ?
Cette action est irréversible !
current_password_hint: Nécéssaire pour enregistrer vos informations
password_hint: Ne remplir que si vous souhaitez changer de mot de passe
submit: Enregistrer
title: Mon Compte
waiting_confirmation: "Validation en attente pour l'email %{email}"
new:
password_min_length:
one: '(au moins %{count} caractère)'
other: '(au moins %{count} caractères)'
submit: Envoyer
title: Inscription
sessions:
new:
submit: Envoyer
title: Connexion
shared:
links:
confirmation_instructions: >
Recevoir les instructions pour confirmer mon compte
forgot_your_password: Mot de passe oublié ?
sign_in: Connexion
sign_in_with: 'Me connecter avec %{provider}'
sign_up: Inscription
unlock_instructions: >
Recevoir les instructions pour débloquer mon compte
unlocks:
new:
submit: Renvoyer les instructions de débloquage
title: Débloquer mon compte
CODE
if doing_ui
after_bundle do
say '--------------------------------------------------------------- SimpleForm', :yellow
generate 'simple_form:install', '--bootstrap', '--quiet'
run 'rm -f config/locales/simple_form.en.yml'
run 'rm -f lib/templates/erb/scaffold/_form.html.erb'
file 'config/locales/simple_form.fr.yml', <<~CODE
fr:
simple_form:
"yes": 'Oui'
"no": 'Non'
required:
text: 'requis'
mark: '*'
error_notification:
default_message: 'Merci de corriger les erreurs suivantes :'
CODE
end
end
say '------------------------------------------------------------------ Sidekiq', :yellow
if doing_admin
route <<~CODE
authenticated :admin_user do
mount Sidekiq::Web, at: 'sidekiq'
end
CODE
else
route <<~CODE
mount Sidekiq::Web, at: 'sidekiq'
CODE
end
file 'config/sidekiq.yml', <<~CODE
---
:queues:
- default
- mailers
CODE
say '----------------------------------------------------------- Error Handling', :yellow
route <<~CODE
get '/404' => 'error_pages#not_found'
get '/500' => 'error_pages#internal_server_error'
CODE
file 'app/controllers/error_pages_controller.rb', <<~CODE
class ErrorPagesController < ApplicationController
def not_found
render status: :not_found
end
def internal_server_error
render status: :internal_server_error
end
end
CODE
run 'rm -f public/404.html'
run 'rm -f public/500.html'
insert_into_file 'config/application.rb',
" config.time_zone = 'Europe/Paris'\n",
after: "# config.time_zone = 'Central Time (US & Canada)'\n"
if doing_auth
after_bundle do
say '------------------------------------------------------------------- Devise', :yellow
generate 'devise:install', '--quiet'
file 'app/views/devise/confirmations/new.html.slim', <<~CODE
- title t('.title')
h1 = t('.title')
= simple_form_for resource, as: resource_name, url: confirmation_path(resource_name) do |f|
= f.full_error :confirmation_token
= f.input :email, required: true, autofocus: true
= f.button :submit, t('.submit')
= render 'devise/shared/links'
CODE
file 'app/views/devise/shared/_links.html.slim', <<~CODE
ul
- if controller_name != 'sessions'
li = link_to t('.sign_in'), new_session_path(resource_name)
- if devise_mapping.registerable? && controller_name != 'registrations'
li = link_to t('.sign_up'), new_registration_path(resource_name)
- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations'
li = link_to t('.forgot_your_password'), new_password_path(resource_name)
- if devise_mapping.confirmable? && controller_name != 'confirmations'
li = link_to t('.confirmation_instructions'), new_confirmation_path(resource_name)
- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks'
li = link_to t('.unlock_instructions'), new_unlock_path(resource_name)
- if devise_mapping.omniauthable?
- resource_class.omniauth_providers.each do |provider|
li = link_to t('.sign_in_with', provider: provider.to_s.titleize), omniauth_authorize_path(resource_name, provider)
CODE
file 'app/views/devise/passwords/edit.html.slim', <<~CODE
- title t('.title')
h1 = t('.title')
= simple_form_for resource, as: resource_name, url: password_path(resource_name), html: { method: :put } do |f|
= f.input :reset_password_token, as: :hidden
= f.full_error :reset_password_token
= f.input :password, required: true, autofocus: true
= f.input :password_confirmation, required: true
.form-group
= f.button :submit, t('.submit')
= render 'devise/shared/links'
CODE
file 'app/views/devise/passwords/new.html.slim', <<~CODE
- title t('.title')
h1 = t('.title')
= simple_form_for resource, as: resource_name, url: password_path(resource_name) do |f|
= f.input :email, required: true, autofocus: true
.form-group
= f.button :submit, t('.submit')
= render 'devise/shared/links'
CODE
file 'app/views/devise/registrations/edit.html.slim', <<~CODE
- title t('.title')
h1 = t('.title')
= simple_form_for resource, as: resource_name, url: registration_path(resource_name), html: { method: :put } do |f|
= f.input :email, required: true, autofocus: true
- if devise_mapping.confirmable? && resource.pending_reconfirmation?
p.devise-confirmationWaiting
= t('.waiting_confirmation', email: resource.unconfirmed_email)
= f.input :password, hint: t('.password_hint'), autocomplete: :off, required: false
= f.input :password_confirmation, required: false
= f.input :current_password, hint: t('.current_password_hint'), required: true
.form-group
= f.button :submit, t('.submit')
h2 = t('.account_cancellation')
p
=> t('.cancel_account_text')
= link_to t('.cancel_account_link'), registration_path(resource_name), data: { confirm: t('.confirm') }, method: :delete, class: 'btn btn-danger'
= link_to t('.back'), :back
CODE
file 'app/views/devise/registrations/new.html.slim', <<~CODE
- title t('.title')
h1 = t('.title')
= simple_form_for resource, as: resource_name, url: registration_path(resource_name) do |f|
= f.input :email, required: true, autofocus: true
= f.input :password, required: true, hint: (t('.password_min_length', count: @minimum_password_length) if @validatable)
= f.input :password_confirmation, required: true
.form-group
= f.button :submit, t('.submit')
= render 'devise/shared/links'
CODE
file 'app/views/devise/sessions/new.html.slim', <<~CODE
- title t('.title')
h1 = t('.title')
= simple_form_for resource, as: resource_name, url: session_path(resource_name) do |f|
= f.input :email, required: false, autofocus: true
= f.input :password, required: false
= f.input :remember_me, as: :boolean if devise_mapping.rememberable?
.form-group
= f.button :submit, t('.submit')
= render 'devise/shared/links'
CODE
file 'app/views/devise/unlocks/new.html.slim', <<~CODE
- title t('.title')
h1 = t('.title')
= simple_form_for resource, as: resource_name, url: unlock_path(resource_name) do |f|
= f.full_error :unlock_token
= f.input :email, required: true, autofocus: true
.form-group
= f.button :submit, t('.submit')
= render 'devise/shared/links'
CODE
file 'app/views/devise/mailer/confirmation_instructions.html.slim', <<~CODE
h1 = mustache t('.title'), @resource
= simple_format t('.message')
p = link_to t('.confirm_my_account'), confirmation_url(@resource, confirmation_token: @token)
= simple_format t('.signature')
CODE
file 'app/views/devise/mailer/confirmation_instructions.text.erb', <<~CODE
<%= mustache t('.title'), @resource %>
-----------------------------------
<%= t('.message') -%>
<%= confirmation_url @resource, confirmation_token: @token %>
<%= t('.signature') %>
CODE
file 'app/views/devise/mailer/reset_password_instructions.html.slim', <<~CODE
h1 = mustache t('.title'), @resource
= simple_format t('.message')
p = link_to t('.change_my_password'), edit_password_url(@resource, reset_password_token: @token)
= simple_format t('.not_you')
= simple_format t('.signature')
CODE
file 'app/views/devise/mailer/reset_password_instructions.text.erb', <<~CODE
<%= mustache t('.title'), @resource %>
-----------------------------------
<%= t('.message') -%>
<%= edit_password_url @resource, reset_password_token: @token %>
<%= t('.not_you') %>
<%= t('.signature') %>
CODE
file 'app/views/devise/mailer/unlock_instructions.html.slim', <<~CODE
h1 = mustache t('.title'), @resource
= simple_format t('.message')
p = link_to t('.unlock_my_account'), unlock_url(@resource, unlock_token: @token)
= simple_format t('.signature')
CODE
file 'app/views/devise/mailer/unlock_instructions.text.erb', <<~CODE
<%= mustache t('.title'), @resource %>
-----------------------------------
<%= t('.message') -%>
<%= unlock_url @resource, unlock_token: @token %>
<%= t('.signature') %>
CODE
file 'app/mailers/devise/custom_mailer.rb', <<~CODE
module Devise
class CustomMailer < Mailer
helper :'mustdown/mustdown'
end
end
CODE
insert_into_file 'config/initializers/devise.rb',
" config.secret_key = ENV.fetch('DEVISE_SECRET_KEY')\n",
after: /secret_key.*\n/
insert_into_file 'config/initializers/devise.rb',
" config.mailer = 'Devise::CustomMailer'\n",
after: " # config.mailer = 'Devise::Mailer'\n"
insert_into_file 'config/initializers/devise.rb',
" config.pepper = ENV.fetch('DEVISE_PEPPER')\n",
after: /config.pepper.*\n/
gsub_file 'config/initializers/devise.rb',
'8..128',
'Rails.env.production? ? 8..128 : 1..128'
gsub_file 'config/initializers/devise.rb',
"'please-change-me-at-config-initializers-devise@example.com'",
"ENV.fetch('NO_REPLY_EMAIL')"
run "wget https://gist.githubusercontent.com/GRoguelon/13a7d64eb6f1351363e8/raw/3cc776dc79936e81cab10e39d21a3bc0475a1768/devise.fr.yml -O config/locales/devise.fr.yml -q"
if doing_front_auth
file "app/models/#{user_model.underscore}.rb", <<~CODE
class #{user_model.classify} < ActiveRecord::Base
devise :confirmable,
:database_authenticatable,
:recoverable,
:registerable,
:rememberable,
:validatable
end
CODE
file "db/migrate/#{Time.now.strftime('%Y%m%d%H%M01')}_devise_create_#{user_model.tableize}.rb", <<~CODE
class DeviseCreate#{user_model.classify.pluralize} < ActiveRecord::Migration
def change
create_table(:#{user_model.tableize}) do |t|
t.string :email, null: false, default: ''
t.string :encrypted_password, null: false, default: ''
t.string :reset_password_token
t.datetime :reset_password_sent_at
t.datetime :remember_created_at
t.string :confirmation_token
t.datetime :confirmed_at
t.datetime :confirmation_sent_at
t.string :unconfirmed_email
t.timestamps
end
add_index :#{user_model.tableize}, :email, unique: true
add_index :#{user_model.tableize}, :reset_password_token, unique: true
add_index :#{user_model.tableize}, :confirmation_token, unique: true
end
end
CODE
route "devise_for :#{user_model.tableize}"
end
end
end
if doing_admin
say '-------------------------------------------------------------------- Admin', :yellow
run 'bin/rails plugin new engines/admin --mountable'
gem 'admin', path: 'engines/admin' if doing_admin
route <<~CODE
mount Admin::Engine, at: 'admin'
CODE
file 'engines/admin/admin.gemspec', <<~CODE, force: true
$:.push File.expand_path('../lib', __FILE__)
# Maintain your gem's version:
require 'admin/version'
# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
s.name = 'admin'
s.version = Admin::VERSION
s.authors = ['Simon Courtois']
s.email = ['scourtois@tinci.fr']
s.summary = 'Admin Engine'
s.license = 'MIT'
s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.rdoc"]
s.add_dependency 'rails', '~> 4.2.5.1'
s.add_dependency 'godmin', '~> 1.2'
end
CODE
file 'engines/admin/config/routes.rb', <<~CODE, force: true
Admin::Engine.routes.draw do
root to: 'dashboard#show'
end
CODE
file 'engines/admin/app/controllers/admin/dashboard_controller.rb', <<~CODE
module Admin
class DashboardController < Admin::ApplicationController
end
end
CODE
file 'engines/admin/app/views/admin/dashboard/show.html.slim', <<~CODE
h1 = t('.title')
CODE
file 'engines/admin/config/locales/fr.yml', <<~CODE
fr:
admin:
dashboard:
show:
title: Dashboard d’administration
CODE
file "app/models/admin_user.rb", <<~CODE
class AdminUser < ActiveRecord::Base
devise :database_authenticatable,
:rememberable,
:validatable
end
CODE
file "db/migrate/#{Time.now.strftime('%Y%m%d%H%M02')}_devise_create_admin_users.rb", <<~CODE
class DeviseCreateAdminUsers < ActiveRecord::Migration
def change
create_table(:admin_users) do |t|
t.string :email, null: false, default: ''
t.string :encrypted_password, null: false, default: ''
t.datetime :remember_created_at
t.timestamps
end
add_index :admin_users, :email, unique: true
end
end
CODE
after_bundle do
run 'admin/bin/rails generate godmin:install'
route "devise_for :admin_users"
end
end
say '--------------------------------------------------------- Basic Components', :yellow
file 'app/services/.keep'
file 'app/uploaders/.keep'
file 'app/jobs/.keep'
if doing_ui
file 'app/views/shared/_navbar.html.slim'
file 'app/views/shared/_footer.html.slim'
end
file 'app/decorators/application_decorator.rb', <<~CODE
class ApplicationDecorator < Draper::Decorator
include Draper::LazyHelpers
delegate_all
end
CODE
file 'app/controllers/application_controller.rb', <<~CODE, force: true
class ApplicationController < ActionController::Base
if ENV['DEV_AUTH']
http_basic_authenticate_with(
name: ENV['DEV_AUTH'],
password: ENV['DEV_AUTH'])
end
decent_configuration { strategy DecentExposure::StrongParametersStrategy }
protect_from_forgery with: :exception
end
CODE
say '------------------------------------------------------------------ Ignores', :yellow
run 'echo "\n/public/assets\n/public/uploads" >> .gitignore'
say '------------------------------------------------------ Initialize Database', :yellow
run 'pg_ctl init -s -D vendor/postgresql'
file 'config/database.yml', <<~CODE, force: true
default: &default
adapter: postgresql
encoding: unicode
pool: 5
development:
<<: *default
database: #{project_name.underscore}_development
test:
<<: *default
database: #{project_name.underscore}_test
CODE
say '----------------------------------------------------------------- Business', :yellow
file 'app/business/process_invoker.rb', <<~CODE
class ProcessInvoker
def self.call(process, context = {})
process.call(context)
end
end
CODE
file 'app/business/concerns/process.rb', <<~CODE
module Concerns
module Process
extend ActiveSupport::Concern
included do
include Interactor::Organizer
include Concerns::WithTransaction
end
end
end
CODE
file 'app/business/concerns/step.rb', <<~CODE
module Concerns
module Step
extend ActiveSupport::Concern
included do
include Interactor
end
end
end
CODE
file 'app/business/concerns/with_transaction.rb', <<~CODE
module Concerns
module WithTransaction
extend ActiveSupport::Concern
included do
around do |interactor|
ActiveRecord::Base.transaction do
interactor.call
end
end
end
end
end
CODE
file "app/business/processes/.keep"
file "app/business/steps/.keep"
if doing_ui
say '------------------------------------------------------------------- Alerts', :yellow
file 'app/views/shared/_alerts.html.slim', <<~CODE
.alerts
- flash.each do |type, message|
- next if message.blank?
.alert class=flash_class(type)
button.close type="button" data-dismiss="alert"
i.fa.fa-times
p = message
CODE
file 'app/helpers/layout_helper.rb', <<~CODE
module LayoutHelper
def flash_class(flash_type)
case flash_type
when 'alert' then 'alert-danger'
when 'error' then 'alert-danger'
when 'notice' then 'alert-success'
else 'alert-info'
end
end
end
CODE
say '--------------------------------------------------- Create CSS Directories', :yellow
file 'app/assets/stylesheets/components/.keep'
file 'app/assets/stylesheets/base/_headings.scss', <<~CODE
.h-title {
}
CODE
file 'app/assets/stylesheets/base/_colors.scss', <<~CODE
$color-blue: #347ab7;
$color-lightGray: #ededed;
CODE
file 'app/assets/stylesheets/base/_type.scss', <<~CODE
$fontSize-micro: 9px;
$fontSize-smallest: 10px;
$fontSize-smaller: 12px;
$fontSize-small: 14px;
$fontSize-base: 16px;
$fontSize-large: 20px;
$fontSize-larger: 24px;
$fontSize-largest: 36px;
$fontSize-jumbo: 48px;
$fontWeight-light: 300;
$fontWeight-normal: 400;
$fontWeight-semibold: 600;
$fontWeight-bold: 700;
$fontWeight-extrabold: 900;
$letterSpacing-tight: -1px;
$letterSpacing-normal: 0;
$letterSpacing-loose: 1px;
$lineHeight-oneLine: 1;
$lineHeight-tight: 1.1;
$lineHeight-base: 1.4;
$lineHeight-loose: 1.7;
CODE
file 'app/assets/stylesheets/base/_bootstrap_override.scss', <<~CODE
$brand-primary: $color-blue;
$font-size-base: $fontSize-base;
$line-height-base: $lineHeight-base;
$navbar-margin-bottom: 0;
$grid-columns: 24;
@import 'bootstrap';
@mixin xxs { @media (max-width: $screen-xs) { @content; } }
@mixin xs { @media (min-width: $screen-xs) { @content; } }
@mixin sm { @media (min-width: $screen-sm) { @content; } }
@mixin md { @media (min-width: $screen-md) { @content; } }
@mixin lg { @media (min-width: $screen-lg) { @content; } }
.text-xs-left { @include xs { text-align: left; } }
.text-xs-center { @include xs { text-align: center; } }
.text-xs-right { @include xs { text-align: right; } }
.text-sm-left { @include sm { text-align: left; } }
.text-sm-center { @include sm { text-align: center; } }
.text-sm-right { @include sm { text-align: right; } }
.text-md-left { @include md { text-align: left; } }
.text-md-center { @include md { text-align: center; } }
.text-md-right { @include md { text-align: right; } }
.text-lg-left { @include lg { text-align: left; } }
.text-lg-center { @include lg { text-align: center; } }
.text-lg-right { @include lg { text-align: right; } }
CODE
file 'app/assets/stylesheets/base/_mixins.scss', <<~CODE
@mixin retina {
@media only screen and (-Webkit-min-device-pixel-ratio: 1.5),
only screen and (-moz-min-device-pixel-ratio: 1.5),
only screen and (-o-min-device-pixel-ratio: 3/2),
only screen and (min-device-pixel-ratio: 1.5) {
@content;
}
}
CODE
file 'app/assets/stylesheets/application.css.scss', <<~CODE, force: true
@import 'base/colors';
@import 'base/type';
@import 'base/bootstrap_override';
@import 'base/mixins';
@import 'base/headings';
// @import 'components/...';
CODE
say '---------------------------------------------------- Create JS Directories', :yellow
file 'app/assets/javascripts/components/.keep'
file 'app/assets/javascripts/application.js', <<~CODE, force: true
//= require jquery
//= require jquery_ujs
//= require bootstrap
//= require_tree ./components
CODE
say '--------------------------------------------------------------------- Slim', :yellow
file 'app/views/layouts/application.html.slim', <<~CODE
doctype html
html
head
= display_meta_tags reverse: true, separator: '-', site: '#{@app_name}'
meta charset="utf-8"
meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible"
meta content="width=device-width, initial-scale=1.0" name="viewport"
= stylesheet_link_tag 'application', media: 'all'
= csrf_meta_tags
body class=content_for(:page_classes)
= render 'shared/navbar'
= render 'shared/alerts'
= content_tag :div, class: (content_for(:container_class) || 'container') do
= yield
= render 'shared/footer'
= javascript_include_tag 'application'
CODE
end
if doing_tests
after_bundle do
say '-------------------------------------------------------------------- RSpec', :yellow
generate 'rspec:install', '--quiet'
file 'Guardfile', <<~CODE
guard :rspec, cmd: 'bundle exec rspec' do
require 'guard/rspec/dsl'
dsl = Guard::RSpec::Dsl.new(self)
rspec = dsl.rspec
watch(rspec.spec_helper) { rspec.spec_dir }
watch(rspec.spec_support) { rspec.spec_dir }
watch(rspec.spec_files)
ruby = dsl.ruby
dsl.watch_spec_files_for(ruby.lib_files)
rails = dsl.rails(view_extensions: %w[slim])
dsl.watch_spec_files_for(rails.app_files)
dsl.watch_spec_files_for(rails.views)
watch(rails.controllers) do |m|
[ rspec.spec.("acceptance/\#{m[1]}") ]
end
watch(rails.spec_helper) { rspec.spec_dir }
watch(rails.view_dirs) { |m| rspec.spec.("features/\#{m[1]}") }
end
CODE
end
end
say '----------------------------------------------------------------- Procfile', :yellow
file 'Procfile', <<~CODE
web: bundle exec passenger start -p $PORT --max-pool-size ${PASSENGER_MAX-2}
worker: bundle exec sidekiq -C config/sidekiq.yml
CODE
file 'Procfile.perso', <<~CODE
web: bundle exec passenger start -p $PORT --max-pool-size ${PASSENGER_MAX-2}
worker: bundle exec sidekiq -C config/sidekiq.yml
db: postgres -D vendor/postgresql
smtp: mailcatcher --foreground --http-port $PORT
redis: redis-server --dir /tmp
CODE
after_bundle do
say '------------------------------------------ Remove useless files & Clean up', :yellow
run 'rm -f app/assets/stylesheets/application.css'
run 'rm -f app/views/layouts/application.html.erb'
run 'rm -f config/locales/devise.en.yml'
run 'rm -f config/locales/simple_form.en.yml'
run 'rm -f README.rdoc'
run 'rm -rf lib/templates'
gsub_file 'config/routes.rb', /^\s*#.*/, ''
gsub_file 'config/routes.rb', /^\s*$/, ''
end
say '---------------------------------------------------------------------- Git', :yellow
file 'bin/setup', <<~CODE, force: true
#!/usr/bin/env ruby
require 'pathname'
APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
Dir.chdir APP_ROOT do
puts '-----> Installing dependencies'
system 'gem install bundler --conservative'
system 'bundle check || bundle install'
puts '-----> Adding pre-commit hook'
File.open('.git/hooks/pre-commit', 'w') do |f|
f.puts "#!/usr/bin/env sh"
f.puts
f.puts "unset GIT_DIR"
f.puts
f.puts "rubocop -D app"
f.puts "if [ $? -eq 1 ]; then exit 1; fi"
f.puts
f.puts "bundle exec scss-lint"
f.puts "if [ $? -eq 1 ]; then exit 1; fi"
end
File.chmod(0755, '.git/hooks/pre-commit')
end
CODE
file '.rubocop.yml', <<~CODE
AllCops:
Exclude:
- 'Guardfile'
- 'bin/**/*'
- 'db/**/*'
TargetRubyVersion: 2.3
Lint/AmbiguousRegexpLiteral:
Enabled: false
Lint/RescueException:
Enabled: false
Metrics/AbcSize:
Max: 18
Metrics/LineLength:
Max: 90
Metrics/MethodLength:
Max: 11
Performance/RedundantBlockCall:
Enabled: false
Rails:
Enabled: true
Style/AlignParameters:
EnforcedStyle: with_fixed_indentation
Style/AndOr:
Enabled: false
Style/BlockDelimiters:
Exclude:
- 'spec/**/*'
Style/Documentation:
Enabled: false
Style/FrozenStringLiteralComment:
Enabled: false
Style/IndentHash:
EnforcedStyle: consistent
Style/MultilineBlockChain:
Enabled: false
Style/MultilineBlockLayout:
Exclude:
- 'spec/**/*'
Style/BlockDelimiters:
Exclude:
- 'spec/**/*'
Style/MultilineMethodCallBraceLayout:
EnforcedStyle: same_line
Style/MultilineOperationIndentation:
EnforcedStyle: indented
Style/PercentLiteralDelimiters:
PreferredDelimiters:
'%': '{}'
'%i': '[]'
'%q': '{}'
'%Q': '{}'
'%r': '{}'
'%s': '{}'
'%w': '[]'
'%W': '[]'
'%x': '{}'
Style/PredicateName:
NamePrefixBlacklist:
- is_
Style/RegexpLiteral:
EnforcedStyle: mixed
Style/SignalException:
EnforcedStyle: semantic
Style/SpaceInsideBlockBraces:
Exclude:
- 'spec/**/*'
Style/StringLiterals:
EnforcedStyle: single_quotes
Style/StringLiteralsInInterpolation:
EnforcedStyle: single_quotes
CODE
file '.scss-lint.yml', <<~CODE
---
linters:
NameFormat:
enabled: false
SelectorFormat:
enabled: false
SpaceBeforeBrace:
enabled: false
CODE
after_bundle do
git :init
git add: '.'
end
after_bundle do
say '===================================================================== TODO', :green
say
say 'Next steps:'
say "* Clean up Gemfile"
say "* Clean up config/routes.rb"
say "* Review config/devise.rb"
say "* Review config/simple_form_bootstrap.rb"
say "* Customize theme in app/assets/stylesheets/base/_colors.scss"
say "* Customize Boostrap in app/assets/stylesheets/base/_bootstrap_override.scss"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment