├── .browserslistrc
├── config
├── locales
│ ├── defaults
│ │ ├── en.yml
│ │ └── fr.yml
│ ├── views
│ │ ├── en.yml
│ │ └── fr.yml
│ └── models
│ │ ├── en.yml
│ │ └── fr.yml
├── initializers
│ ├── sidekiq.rb
│ ├── i18n.rb
│ ├── generators.rb
│ └── redis.rb
├── sidekiq.yml
├── routes.rb
└── template.rb
├── .rubocop
├── .stylelintrc
├── app
├── controllers
│ ├── pages_controller.rb
│ └── application_controller.rb
├── views
│ ├── pages
│ │ └── home.html.erb
│ └── layouts
│ │ └── application.html.erb
├── template.rb
├── assets
│ └── stylesheets
│ │ └── application.scss
└── javascript
│ └── css
│ └── application.css
├── Procfile
├── Procfile.dev
├── .overcommit.yml
├── db
└── migrate
│ ├── 20180208061510_enable_pg_crypto_extension.rb
│ └── 20180208061509_create_friendly_id_slugs.rb
├── .eslintrc
├── .rubocop.yml
├── README.md.tt
├── LICENSE
├── Gemfile.tt
├── README.md
└── template.rb
/.browserslistrc:
--------------------------------------------------------------------------------
1 | >1%
--------------------------------------------------------------------------------
/config/locales/defaults/en.yml:
--------------------------------------------------------------------------------
1 | en:
--------------------------------------------------------------------------------
/config/locales/defaults/fr.yml:
--------------------------------------------------------------------------------
1 | fr:
--------------------------------------------------------------------------------
/config/locales/views/en.yml:
--------------------------------------------------------------------------------
1 | en:
--------------------------------------------------------------------------------
/config/locales/views/fr.yml:
--------------------------------------------------------------------------------
1 | fr:
--------------------------------------------------------------------------------
/.rubocop:
--------------------------------------------------------------------------------
1 | --rails
2 | --extra-details
3 |
--------------------------------------------------------------------------------
/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "stylelint-config-standard"
3 | }
--------------------------------------------------------------------------------
/config/locales/models/en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | activerecord:
3 | models:
4 | attributes:
--------------------------------------------------------------------------------
/config/locales/models/fr.yml:
--------------------------------------------------------------------------------
1 | fr:
2 | activerecord:
3 | models:
4 | attributes:
--------------------------------------------------------------------------------
/config/initializers/sidekiq.rb:
--------------------------------------------------------------------------------
1 | Rails.application.config.active_job.queue_adapter = :sidekiq
--------------------------------------------------------------------------------
/app/controllers/pages_controller.rb:
--------------------------------------------------------------------------------
1 | class PagesController < ApplicationController
2 | def home
3 | end
4 | end
--------------------------------------------------------------------------------
/config/sidekiq.yml:
--------------------------------------------------------------------------------
1 | :concurrency: 3
2 | :timeout: 60
3 | :verbose: true
4 | :queues:
5 | - default
6 | - mailers
--------------------------------------------------------------------------------
/app/views/pages/home.html.erb:
--------------------------------------------------------------------------------
1 |
Hello from <%= Rails.application.class.parent_name %>!
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 |
3 | scope '(:locale)', locale: /fr/ do
4 | root to: 'pages#home'
5 | end
6 | end
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | release: bundle exec rails db:migrate
2 | web: bundle exec puma -C config/puma.rb
3 | worker: bundle exec sidekiq -C config/sidekiq.yml
4 |
--------------------------------------------------------------------------------
/Procfile.dev:
--------------------------------------------------------------------------------
1 | web: bin/rails server
2 | assets: bin/webpack-dev-server
3 | worker: bundle exec sidekiq -C config/sidekiq.yml
4 | guard: bundle exec guard
5 |
--------------------------------------------------------------------------------
/.overcommit.yml:
--------------------------------------------------------------------------------
1 | PreCommit:
2 | RuboCop:
3 | enabled: true
4 | command: ['bundle', 'exec', 'rubocop']
5 |
6 | PrePush:
7 | Brakeman:
8 | enabled: true
9 |
--------------------------------------------------------------------------------
/config/initializers/i18n.rb:
--------------------------------------------------------------------------------
1 | I18n.default_locale = :en
2 | I18n.available_locales = [:en, :fr]
3 | I18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}')]
--------------------------------------------------------------------------------
/db/migrate/20180208061510_enable_pg_crypto_extension.rb:
--------------------------------------------------------------------------------
1 | class EnablePgCryptoExtension < ActiveRecord::Migration[5.2]
2 | def change
3 | enable_extension 'pgcrypto'
4 | end
5 | end
--------------------------------------------------------------------------------
/app/template.rb:
--------------------------------------------------------------------------------
1 | copy_file 'app/controllers/application_controller.rb', force: true
2 | copy_file 'app/controllers/pages_controller.rb'
3 | copy_file 'app/views/layouts/application.html.erb', force: true
4 | copy_file 'app/views/pages/home.html.erb'
5 |
--------------------------------------------------------------------------------
/config/initializers/generators.rb:
--------------------------------------------------------------------------------
1 | Rails.application.config.generators do |g|
2 | # Disable generators we don't need.
3 | g.javascripts false
4 | g.stylesheets false
5 | g.assets false
6 | g.helper false
7 | g.test_framework false
8 | g.channel assets: false
9 | end
--------------------------------------------------------------------------------
/config/initializers/redis.rb:
--------------------------------------------------------------------------------
1 | $redis = Redis.new
2 |
3 | url = ENV["REDISCLOUD_URL"]
4 |
5 | if url
6 | Sidekiq.configure_server do |config|
7 | config.redis = { url: url }
8 | end
9 |
10 | Sidekiq.configure_client do |config|
11 | config.redis = { url: url }
12 | end
13 | $redis = Redis.new(:url => url)
14 | end
--------------------------------------------------------------------------------
/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | protect_from_forgery with: :exception
3 |
4 | before_action :set_locale
5 |
6 | def set_locale
7 | I18n.locale = params.fetch(:locale, I18n.default_locale).to_sym
8 | end
9 |
10 | def default_url_options
11 | { locale: I18n.locale == I18n.default_locale ? nil : I18n.locale }
12 | end
13 | end
--------------------------------------------------------------------------------
/app/assets/stylesheets/application.scss:
--------------------------------------------------------------------------------
1 | // Graphical variables
2 | // @import "config/fonts";
3 | // @import "config/colors";
4 | // @import "config/bootstrap_variables";
5 |
6 | // External libraries
7 | // @import "bootstrap-sprockets";
8 | // @import "bootstrap";
9 | // @import "font-awesome-sprockets";
10 | // @import "font-awesome";
11 |
12 | // Your CSS partials
13 | // @import "layouts/index";
14 | // @import "components/index";
15 | // @import "pages/index";
16 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["eslint-config-airbnb-base", "prettier"],
3 |
4 | "plugins": ["prettier"],
5 |
6 | "env": {
7 | "browser": true
8 | },
9 |
10 | "rules": {
11 | "prettier/prettier": "error"
12 | },
13 |
14 | "parser": "babel-eslint",
15 |
16 | "settings": {
17 | "import/resolver": {
18 | "webpack": {
19 | "config": {
20 | "resolve": {
21 | "modules": ["frontend", "node_modules"]
22 | }
23 | }
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= Rails.application.class.parent_name %>
7 | <%= csrf_meta_tags %>
8 | <%= action_cable_meta_tag %>
9 | <%= stylesheet_pack_tag 'application', media: 'all' %>
10 |
11 |
12 | <%= yield %>
13 | <%= javascript_include_tag 'application' %>
14 | <%= javascript_pack_tag 'application' %>
15 |
16 |
--------------------------------------------------------------------------------
/db/migrate/20180208061509_create_friendly_id_slugs.rb:
--------------------------------------------------------------------------------
1 | class CreateFriendlyIdSlugs < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :friendly_id_slugs do |t|
4 | t.string :slug, :null => false
5 | t.integer :sluggable_id, :null => false
6 | t.string :sluggable_type, :limit => 50
7 | t.string :scope
8 | t.datetime :created_at
9 | end
10 | add_index :friendly_id_slugs, :sluggable_id
11 | add_index :friendly_id_slugs, [:slug, :sluggable_type], length: { slug: 140, sluggable_type: 50 }
12 | add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope], length: { slug: 70, sluggable_type: 50, scope: 70 }, unique: true
13 | add_index :friendly_id_slugs, :sluggable_type
14 | end
15 | end
--------------------------------------------------------------------------------
/config/template.rb:
--------------------------------------------------------------------------------
1 | copy_file 'config/initializers/generators.rb'
2 | copy_file 'config/initializers/i18n.rb'
3 | copy_file 'config/initializers/sidekiq.rb'
4 | copy_file 'config/initializers/redis.rb'
5 |
6 | copy_file 'config/routes.rb', force: true
7 |
8 | insert_into_file 'config/environments/development.rb', after: /config\.action_mailer\.raise_delivery_errors = false\n/ do
9 | <<-RUBY
10 | config.action_mailer.default_url_options = { :host => "localhost:3000" }
11 | config.action_mailer.asset_host = "http://localhost:3000"
12 | RUBY
13 | end
14 |
15 | copy_file 'config/sidekiq.yml'
16 |
17 | remove_file 'config/locales/en.yml'
18 | copy_file 'config/locales/defaults/en.yml'
19 | copy_file 'config/locales/models/en.yml'
20 | copy_file 'config/locales/views/en.yml'
21 | copy_file 'config/locales/defaults/fr.yml'
22 | copy_file 'config/locales/models/fr.yml'
23 | copy_file 'config/locales/views/fr.yml'
24 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | Rails:
2 | Enabled: true
3 |
4 | Metrics/LineLength:
5 | Max: 100
6 |
7 | AllCops:
8 | Exclude:
9 | - db/migrate/*
10 | - db/schema.rb
11 | - bin/*
12 | - node_modules/**/*
13 |
14 | Style/Documentation:
15 | Enabled: false
16 |
17 | Style/DoubleNegation:
18 | Enabled: false
19 |
20 | Style/ClassAndModuleChildren:
21 | Enabled: false
22 |
23 | Style/StructInheritance:
24 | Exclude:
25 | - app/policies/*
26 |
27 | Rails/InverseOf:
28 | Enabled: false
29 |
30 | Metrics/BlockLength:
31 | Exclude:
32 | - config/environments/development.rb
33 | - Guardfile
34 | - lib/tasks/auto_annotate_models.rake
35 | - config/initializers/simple_form_bootstrap.rb
36 | - lib/tasks/*
37 | - app/admin/*
38 |
39 | Layout/AlignHash:
40 | Enabled: False
41 |
42 | GlobalVars:
43 | AllowedVariables:
44 | - $redis
45 |
46 | Rails/Output:
47 | Exclude:
48 | - db/seeds.rb
49 |
--------------------------------------------------------------------------------
/README.md.tt:
--------------------------------------------------------------------------------
1 | # <%= app_name %>
2 |
3 | This is a Rails <%= Rails::VERSION::MAJOR %> app created using [modern-rails-template][]. Please check README of the template repository to see the features available out of the box. To have a look to the new features introduced by Rails 5.2 (credentials for example), check this [article][].
4 |
5 | ## How to use this project
6 |
7 | * We advise you to use [overmind][] to launch your processes in development. You just need to run `overmind s -f Procfile.dev`. Of course, another process manager, like [foreman][], would work too.
8 | * We advise you to deploy to [Heroku][]. Do not forget to add the [Redis] add-on.
9 |
10 | [article]: https://evilmartians.com/chronicles/rails-5-2-active-storage-and-beyond
11 | [modern-rails-template]: https://github.com/damienlethiec/modern-rails-template
12 | [here]: http://nvie.com/posts/a-successful-git-branching-model/
13 | [overmind]: https://github.com/DarthSim/overmind
14 | [foreman]: https://github.com/ddollar/foreman
15 | [heroku]: https://www.heroku.com/
16 | [redis]: https://devcenter.heroku.com/articles/heroku-redis
17 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Matt Brictson
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Gemfile.tt:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3 |
4 | ruby '<%= RUBY_VERSION %>'
5 |
6 | gem 'pg'<%= gemfile_requirement('pg') %>
7 | gem 'puma'<%= gemfile_requirement('puma') %>
8 | gem 'rails', '<%= Rails.version %>'
9 | gem 'sass-rails'<%= gemfile_requirement('sass-rails') %>
10 |
11 | gem 'uglifier'<%= gemfile_requirement('uglifier') %>
12 | gem 'webpacker'
13 |
14 | gem 'jbuilder'<%= gemfile_requirement('jbuilder') %>
15 | gem 'redis'<%= gemfile_requirement('redis') %>
16 | gem 'turbolinks'<%= gemfile_requirement('turbolinks') %>
17 |
18 | gem 'bootsnap', '>= 1.1.0', require: false
19 |
20 | gem 'rails-i18n'
21 |
22 | gem 'sidekiq'
23 | gem 'sidekiq-failures'
24 |
25 | gem 'friendly_id'
26 |
27 | group :development, :test do
28 | gem 'pry-byebug'
29 | gem 'pry-rails'
30 | end
31 |
32 | group :development do
33 | gem 'listen'<%= gemfile_requirement('listen') %>
34 | gem 'spring'
35 | gem 'spring-watcher-listen'<%= gemfile_requirement('spring-watcher-listen') %>
36 |
37 | gem 'annotate'
38 | gem 'awesome_print'
39 | gem 'bullet'
40 | gem 'rails-erd'
41 |
42 | gem 'brakeman', require: false
43 | gem 'overcommit'
44 | gem 'rubocop', require: false
45 |
46 | gem 'guard'
47 | gem 'guard-bundler', require: false
48 | gem 'guard-livereload', require: false
49 | gem 'rack-livereload'
50 |
51 | gem 'better_errors'
52 | gem 'binding_of_caller'
53 | gem 'web-console'<%= gemfile_requirement('web-console') %>
54 | end
55 |
--------------------------------------------------------------------------------
/app/javascript/css/application.css:
--------------------------------------------------------------------------------
1 | /**
2 | * This injects Tailwind's base styles, which is a combination of
3 | * Normalize.css and some additional base styles.
4 | *
5 | * You can see the styles here:
6 | * https://github.com/tailwindcss/tailwindcss/blob/master/css/preflight.css
7 | *
8 | * If using `postcss-import`, you should import this line from it's own file:
9 | *
10 | * @import "./tailwind-preflight.css";
11 | *
12 | * See: https://github.com/tailwindcss/tailwindcss/issues/53#issuecomment-341413622
13 | */
14 | @tailwind preflight;
15 |
16 | /**
17 | * Here you would add any of your custom component classes; stuff that you'd
18 | * want loaded *before* the utilities so that the utilities could still
19 | * override them.
20 | *
21 | * Example:
22 | *
23 | * .btn { ... }
24 | * .form-input { ... }
25 | *
26 | * Or if using a preprocessor or `postcss-import`:
27 | *
28 | * @import "components/buttons";
29 | * @import "components/forms";
30 | */
31 |
32 | /**
33 | * This injects all of Tailwind's utility classes, generated based on your
34 | * config file.
35 | *
36 | * If using `postcss-import`, you should import this line from it's own file:
37 | *
38 | * @import "./tailwind-utilities.css";
39 | *
40 | * See: https://github.com/tailwindcss/tailwindcss/issues/53#issuecomment-341413622
41 | */
42 | @tailwind utilities;
43 |
44 | /**
45 | * Here you would add any custom utilities you need that don't come out of the
46 | * box with Tailwind.
47 | *
48 | * Example :
49 | *
50 | * .bg-pattern-graph-paper { ... }
51 | * .skew-45 { ... }
52 | *
53 | * Or if using a preprocessor or `postcss-import`:
54 | *
55 | * @import "utilities/background-patterns";
56 | * @import "utilities/skew-transforms";
57 | */
58 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Modern Rails Template
2 |
3 | ## Description
4 |
5 | This is the rails template I used for my Rails 5.2 projects as a freelance developer. Its goal is to allow to begin new rails application easily, with a modern and efficient configuration and with good set of defaults. The project is still very much a work in progress. So do not expect it to be 100% bug free. [Contributions][], ideas and help are really welcome.
6 |
7 | This project is inspired by the template developed by Matt Brictson. Have a look [here][] to compare both.
8 |
9 | ## Requirements
10 |
11 | This template currently works with:
12 |
13 | * Rails 5.2.x
14 | * PostgreSQL
15 |
16 | ## Installation
17 |
18 | _Optional._
19 |
20 | To make this the default Rails application template on your system, create a `~/.railsrc` file with these contents:
21 |
22 | ```
23 | --skip-coffee
24 | --webpack
25 | -d postgresql
26 | -T
27 | -m https://raw.githubusercontent.com/damienlethiec/modern-rails-template/master/template.rb
28 | ```
29 |
30 | ## Usage
31 |
32 | To generate a Rails application using this template, pass the options below to `rails new`, like this:
33 |
34 | ```
35 | rails new blog \
36 | --skip-coffee \
37 | --webpack \
38 | -d postgresql \
39 | -T \
40 | -m https://raw.githubusercontent.com/damienlethiec/modern-rails-template/master/template.rb
41 | ```
42 |
43 | _Remember that options must go after the name of the application._ The only database supported by this template is `postgresql`.
44 |
45 | If you’ve installed this template as your default (using `~/.railsrc` as described above), then all you have to do is run:
46 |
47 | ```
48 | rails new blog
49 | ```
50 |
51 | ## What does it do?
52 |
53 | The template will perform the following steps:
54 |
55 | 1. Ask for which option you want in this project
56 | 1. Generate your application files and directories
57 | 1. Add useful gems and good configs
58 | 1. Add the optional config specified
59 | 1. Commit everything to git
60 |
61 | ## What is included?
62 |
63 | Below is an extract of what this generator does. You can check all the features by following the code, especially in `template.rb` and in the `Gemfile`.
64 |
65 | ### Standard configuration
66 |
67 | * Change the default generators config (cf `config/initializers/generators.rb`)
68 | * Setup [I18n][] for English and French
69 | * Improve the main layout (cf `app/views/layouts/application.haml.erb`) to include webpack in the asset pipeline
70 | * Create a basic PagesController to have something to show when the app launch
71 | * Add a [Procfile][] for dev and a Procfile for production to manage the different processes you need to manage in a modern web application.
72 | * Add Javascript ([ESLint][]) and CSS ([Stylelint][]) linters with webpack
73 | * Add and configure the [friendly_id][] gem for slugging
74 | * Add and configure the [annotate][] gem to add useful comments in our models
75 | * Add and configure the [bullet][] gem to track N+1 queries
76 | * Add and configure the [rails_erd][] gem to generate automatically a schema of our database relationships
77 | * Add and configure the [sidekiq][] gem for background jobs. You can access the sidekiq dashboard in the app at `/sidekiq` (don't forget to limit its access in the routes if needed)
78 | * Add and configure [rubocop][] for style and [brakeman][] for security and add them to [overcommit][] git hooks
79 | * Add livereload for view and automatic bundle installs using [guard][], [guard-livereload][] and [guard-bundler][]
80 | * Add [better-errors][] for easier debugging
81 | * Add [awesome-print][] for easier exploration in the terminal
82 |
83 | ### Additional options
84 |
85 | When you launch a new rails app with the template, a few questions will be asked. Answer 'y' or 'yes' to unable the given option.
86 |
87 | * If you need authentication, the [devise][] gem can be added and configured directly
88 | * If you also need authorization, [pundit][] can be added and configured too.
89 | * You can choose to use [Haml][] instead of `erb`
90 | * You can decide to use [UUID][] as the primary key for Active Record
91 | * If you feel adventurous, you can choose to use the [komponent][] gem and build your front-end following the workflow describe in this great [article][]
92 | * You can include [tailwindcss][] in your project. The configure is inspired by the following Gorails [episode][]
93 | * Finally, you can choose to create a Github repository for you project and push it directly.
94 |
95 | ## How does it work?
96 |
97 | This project works by hooking into the standard Rails application templates system, with some caveats. The entry point is the `template.rb` file in the root of this repository.
98 |
99 | Normally, Rails only allows a single file to be specified as an application template (i.e. using the `-m ` option). To work around this limitation, the first step this template performs is a `git clone` of the `damienlethiec/modern-rails-template` repository to a local temporary directory.
100 |
101 | This temporary directory is then added to the `source_paths` of the Rails generator system, allowing all of its ERb templates and files to be referenced when the application template script is evaluated.
102 |
103 | Rails generators are very lightly documented; what you’ll find is that most of the heavy lifting is done by [Thor][]. The most common methods used by this template are Thor’s `copy_file`, `template`, and `gsub_file`.
104 |
105 | ## Contributing
106 |
107 | If you want to contribute, please have a look to the issues in this repository and pick one you are interested in. You can then clone the project and submit a pull request. We also happily welcome new idea and, of course, bug reports.
108 |
109 | [thor]: https://github.com/erikhuda/thor
110 | [here]: https://github.com/mattbrictson/rails-template
111 | [contributions]: https://github.com/damienlethiec/modern-rails-template#contributing
112 | [procfile]: https://devcenter.heroku.com/articles/procfile
113 | [i18n]: http://guides.rubyonrails.org/i18n.html
114 | [uuid]: https://lab.io/articles/2017/04/13/uuids-rails-5-1/
115 | [eslint]: https://eslint.org/
116 | [stylelint]: https://stylelint.io/
117 | [friendly_id]: https://github.com/norman/friendly_id
118 | [annotate]: https://github.com/ctran/annotate_models
119 | [bullet]: https://github.com/flyerhzm/bullet
120 | [rails_erd]: https://github.com/voormedia/rails-erd
121 | [sidekiq]: https://github.com/mperham/sidekiq
122 | [rubocop]: http://rubocop.readthedocs.io/en/latest/
123 | [brakeman]: https://brakemanscanner.org/
124 | [overcommit]: https://github.com/brigade/overcommit
125 | [guard]: https://github.com/guard/guard
126 | [guard-livereload]: https://github.com/guard/guard-livereload
127 | [guard-bundler]: https://github.com/guard/guard-bundler
128 | [better-errors]: https://github.com/charliesome/better_errors
129 | [xray-rails]: https://github.com/brentd/xray-rails
130 | [awesome-print]: https://github.com/michaeldv/awesome_print
131 | [table-print]: https://github.com/arches/table_print
132 | [devise]: https://github.com/plataformatec/devise
133 | [pundit]: https://github.com/varvet/pundit
134 | [haml]: http://haml.info/
135 | [komponent]: https://github.com/komposable/komponent
136 | [article]: https://evilmartians.com/chronicles/evil-front-part-1
137 | [tailwindcss]: tailwindcss.com
138 | [episode]: https://gorails.com/episodes/tailwind-css-framework-with-rails
139 |
--------------------------------------------------------------------------------
/template.rb:
--------------------------------------------------------------------------------
1 | require "fileutils"
2 | require "shellwords"
3 | require "tmpdir"
4 |
5 | RAILS_REQUIREMENT = ">= 5.2.2"
6 |
7 | def apply_template!
8 | assert_minimum_rails_version
9 | add_template_repository_to_source_path
10 |
11 | # temporary fix bootsnap bug
12 | # comment_lines 'config/boot.rb', /bootsnap/
13 |
14 | template "Gemfile.tt", force: true
15 | template 'README.md.tt', force: true
16 | apply 'config/template.rb'
17 | apply 'app/template.rb'
18 | copy_file 'Procfile'
19 | copy_file 'Procfile.dev'
20 |
21 | ask_optional_options
22 |
23 | install_optional_gems
24 |
25 | after_bundle do
26 | setup_uuid if @uuid
27 |
28 | setup_front_end
29 | setup_npm_packages
30 | optional_options_front_end
31 |
32 | setup_gems
33 |
34 | run 'bundle binstubs bundler --force'
35 |
36 | run 'rails db:create db:migrate'
37 |
38 | setup_git
39 | push_github if @github
40 | setup_overcommit
41 | end
42 | end
43 |
44 | def assert_minimum_rails_version
45 | requirement = Gem::Requirement.new(RAILS_REQUIREMENT)
46 | rails_version = Gem::Version.new(Rails::VERSION::STRING)
47 | return if requirement.satisfied_by?(rails_version)
48 |
49 | prompt = "This template requires Rails #{RAILS_REQUIREMENT}. "\
50 | "You are using #{rails_version}. Continue anyway?"
51 | exit 1 if no?(prompt)
52 | end
53 |
54 | # Add this template directory to source_paths so that Thor actions like
55 | # copy_file and template resolve against our source files. If this file was
56 | # invoked remotely via HTTP, that means the files are not present locally.
57 | # In that case, use `git clone` to download them to a local temporary dir.
58 | def add_template_repository_to_source_path
59 | if __FILE__ =~ %r{\Ahttps?://}
60 | source_paths.unshift(tempdir = Dir.mktmpdir("rails-template-"))
61 | at_exit { FileUtils.remove_entry(tempdir) }
62 | git :clone => [
63 | "--quiet",
64 | "https://github.com/damienlethiec/modern-rails-template",
65 | tempdir
66 | ].map(&:shellescape).join(" ")
67 | else
68 | source_paths.unshift(File.dirname(__FILE__))
69 | end
70 | end
71 |
72 | def gemfile_requirement(name)
73 | @original_gemfile ||= IO.read("Gemfile")
74 | req = @original_gemfile[/gem\s+['"]#{name}['"]\s*(,[><~= \t\d\.\w'"]*)?.*$/, 1]
75 | req && req.gsub("'", %(")).strip.sub(/^,\s*"/, ', "')
76 | end
77 |
78 |
79 |
80 | def ask_optional_options
81 | @devise = yes?('Do you want to implement authentication in your app with the Devise gem?')
82 | @pundit = yes?('Do you want to manage authorizations with Pundit?') if @devise
83 | @uuid = yes?('Do you want to use UUID for active record primary?')
84 | @haml = yes?('Do you want to use Haml instead of EBR?')
85 | @komponent = yes?('Do you want to adopt a component based design for your front-end?')
86 | @tailwind = yes?('Do you want to use Tailwind as a CSS framework?')
87 | @github = yes?('Do you want to push your project to Github?')
88 | end
89 |
90 | def install_optional_gems
91 | add_devise if @devise
92 | add_pundit if @pundit
93 | add_komponent if @komponent
94 | add_haml if @haml
95 | end
96 |
97 | def add_devise
98 | insert_into_file 'Gemfile', "gem 'devise'\n", after: /'friendly_id'\n/
99 | insert_into_file 'Gemfile', "gem 'devise-i18n'\n", after: /'friendly_id'\n/
100 | end
101 |
102 | def add_pundit
103 | insert_into_file 'Gemfile', "gem 'pundit'\n", after: /'friendly_id'\n/
104 | end
105 |
106 | def add_haml
107 | insert_into_file 'Gemfile', "gem 'haml'\n", after: /'friendly_id'\n/
108 | insert_into_file 'Gemfile', "gem 'haml-rails', git: 'git://github.com/indirect/haml-rails.git'\n", after: /'friendly_id'\n/
109 | end
110 |
111 | def add_komponent
112 | insert_into_file 'Gemfile', "gem 'komponent'\n", after: /'friendly_id'\n/
113 | end
114 |
115 |
116 |
117 | def setup_uuid
118 | copy_file 'db/migrate/20180208061510_enable_pg_crypto_extension.rb'
119 | insert_into_file 'config/initializers/generators.rb', " g.orm :active_record, primary_key_type: :uuid\n", after: /assets: false\n/
120 | end
121 |
122 |
123 |
124 | def setup_front_end
125 | copy_file '.browserslistrc'
126 | copy_file 'app/assets/stylesheets/application.scss'
127 | remove_file 'app/assets/stylesheets/application.css'
128 | append_to_file 'Procfile', "assets: bin/webpack-dev-server\n"
129 | end
130 |
131 | def setup_npm_packages
132 | add_linters
133 | end
134 |
135 | def add_linters
136 | run 'yarn add eslint babel-eslint eslint-config-airbnb-base eslint-config-prettier eslint-import-resolver-webpack eslint-plugin-import eslint-plugin-prettier lint-staged prettier stylelint stylelint-config-standard --dev'
137 | copy_file '.eslintrc'
138 | copy_file '.stylelintrc'
139 | run 'yarn add normalize.css'
140 | end
141 |
142 | def optional_options_front_end
143 | add_css_framework if @tailwind
144 | end
145 |
146 | def add_css_framework
147 | run 'yarn add tailwindcss --dev'
148 | run './node_modules/.bin/tailwind init app/javascript/css/tailwind.js'
149 | copy_file 'app/javascript/css/application.css'
150 | append_to_file 'app/javascript/packs/application.js', "import '../css/application.css';\n"
151 | if @komponent
152 | append_to_file '.postcssrc.yml', " tailwindcss: './frontend/css/tailwind.js'"
153 | else
154 | append_to_file '.postcssrc.yml', " tailwindcss: './app/javascript/css/tailwind.js'"
155 | end
156 | end
157 |
158 |
159 |
160 | def setup_gems
161 | setup_friendly_id
162 | setup_annotate
163 | setup_bullet
164 | setup_erd
165 | setup_sidekiq
166 | setup_rubocop
167 | setup_brakeman
168 | setup_guard
169 | setup_komponent if @komponent
170 | setup_devise if @devise
171 | setup_pundit if @pundit
172 | setup_haml if @haml
173 | end
174 |
175 | def setup_friendly_id
176 | # temporal fix bug friendly_id generator
177 | copy_file 'db/migrate/20180208061509_create_friendly_id_slugs.rb'
178 | end
179 |
180 | def setup_annotate
181 | run 'rails g annotate:install'
182 | run 'bundle binstubs annotate'
183 | end
184 |
185 | def setup_bullet
186 | insert_into_file 'config/environments/development.rb', before: /^end/ do
187 | <<-RUBY
188 | Bullet.enable = true
189 | Bullet.alert = true
190 | RUBY
191 | end
192 | end
193 |
194 | def setup_erd
195 | run 'rails g erd:install'
196 | append_to_file '.gitignore', 'erd.pdf'
197 | end
198 |
199 | def setup_sidekiq
200 | run 'bundle binstubs sidekiq'
201 | append_to_file 'Procfile.dev', "worker: bundle exec sidekiq -C config/sidekiq.yml\n"
202 | append_to_file 'Procfile', "worker: bundle exec sidekiq -C config/sidekiq.yml\n"
203 | end
204 |
205 | def setup_rubocop
206 | run 'bundle binstubs rubocop'
207 | copy_file '.rubocop'
208 | copy_file '.rubocop.yml'
209 | run 'rubocop'
210 | end
211 |
212 | def setup_brakeman
213 | run 'bundle binstubs brakeman'
214 | end
215 |
216 | def setup_guard
217 | run 'bundle binstubs guard'
218 | run 'guard init livereload bundler'
219 | append_to_file 'Procfile.dev', "guard: bundle exec guard\n"
220 | insert_into_file 'config/environments/development.rb', " config.middleware.insert_after ActionDispatch::Static, Rack::LiveReload\n", before: /^end/
221 | if @komponent
222 | insert_into_file 'Guardfile', %q( watch(%r{frontend/.+\.(#{rails_view_exts * '|'})$})) + "\n", after: /extensions.values.uniq\n/
223 | end
224 | end
225 |
226 | def setup_komponent
227 | install_komponent
228 | add_basic_components
229 | end
230 |
231 | def install_komponent
232 | run 'rails g komponent:install --stimulus'
233 | insert_into_file 'config/initializers/generators.rb', " g.komponent stimulus: true, locale: true\n", after: /assets: false\n/
234 | FileUtils.rm_rf 'app/javascript'
235 | insert_into_file 'app/controllers/application_controller.rb', " prepend_view_path Rails.root.join('frontend')\n", after: /exception\n/
236 | end
237 |
238 | def add_basic_components
239 | run 'rails g component flash'
240 | insert_into_file 'app/views/layouts/application.html.erb', " <%= component 'flash' %>\n", after: /\n/
241 | run 'rails g component button'
242 | run 'rails g component card'
243 | run 'rails g component form'
244 | end
245 |
246 | def setup_devise
247 | run 'rails generate devise:install'
248 | run 'rails g devise:i18n:views'
249 | insert_into_file 'config/routes.rb', after: /draw do\n/ do
250 | <<-RUBY
251 | require "sidekiq/web"
252 | mount Sidekiq::Web => '/sidekiq'
253 | RUBY
254 | end
255 | insert_into_file 'config/initializers/devise.rb', " config.secret_key = Rails.application.credentials.secret_key_base\n", before: /^end/
256 | run 'rails g devise User'
257 | insert_into_file 'app/controllers/application_controller.rb', " before_action :authenticate_user!\n", after: /exception\n/
258 | insert_into_file 'app/controllers/pages_controller.rb', " skip_before_action :authenticate_user!, only: :home\n", after: /ApplicationController\n/
259 | end
260 |
261 | def setup_pundit
262 | insert_into_file 'app/controllers/application_controller.rb', before: /^end/ do
263 | <<-RUBY
264 | def user_not_authorized
265 | flash[:alert] = "You are not authorized to perform this action."
266 | redirect_to(root_path)
267 | end
268 |
269 | private
270 |
271 | def skip_pundit?
272 | devise_controller? || params[:controller] =~ /(^(rails_)?admin)|(^pages$)/
273 | end
274 | RUBY
275 | end
276 | insert_into_file 'app/controllers/application_controller.rb', after: /exception\n/ do
277 | <<-RUBY
278 | include Pundit
279 |
280 | after_action :verify_authorized, except: :index, unless: :skip_pundit?
281 | after_action :verify_policy_scoped, only: :index, unless: :skip_pundit?
282 |
283 | rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
284 | RUBY
285 | end
286 | run 'spring stop'
287 | run 'rails g pundit:install'
288 | end
289 |
290 | def setup_haml
291 | run 'HAML_RAILS_DELETE_ERB=true rake haml:erb2haml'
292 | end
293 |
294 |
295 |
296 | def setup_git
297 | git add: '.'
298 | git commit: '-m "End of the template generation"'
299 | end
300 |
301 | def push_github
302 | @hub = run 'brew ls --versions hub'
303 | if @hub
304 | run 'hub create'
305 | run 'git push origin master'
306 | run 'git push origin develop'
307 | run 'hub browse'
308 | else
309 | puts 'You first need to install the hub command line tool'
310 | end
311 | end
312 |
313 | def setup_overcommit
314 | run 'overcommit --install'
315 | copy_file '.overcommit.yml', force: true
316 | run 'overcommit --sign'
317 | end
318 |
319 | run 'pgrep spring | xargs kill -9'
320 |
321 | # launch the main template creation method
322 | apply_template!
323 |
--------------------------------------------------------------------------------