Last active
June 25, 2016 17:34
-
-
Save simonc/328b46571f1c470a5366 to your computer and use it in GitHub Desktop.
Rails Template FTW!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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