├── templates ├── dotenv.tt ├── githook.tt ├── oj_initializer.tt ├── devise │ └── views │ │ ├── mailer │ │ ├── password_change.html.haml │ │ ├── confirmation_instructions.html.haml │ │ ├── unlock_instructions.html.haml │ │ ├── email_changed.html.haml │ │ └── reset_password_instructions.html.haml │ │ ├── unlocks │ │ └── new.html.haml │ │ ├── passwords │ │ ├── new.html.haml │ │ └── edit.html.haml │ │ ├── confirmations │ │ └── new.html.haml │ │ ├── sessions │ │ └── new.html.haml │ │ ├── registrations │ │ ├── new.html.haml │ │ └── edit.html.haml │ │ └── shared │ │ └── _links.html.haml ├── default_locale.tt ├── rubocop.tt ├── simplecov.tt ├── bootswatch │ ├── stylesheets │ │ └── application.scss.tt │ └── views │ │ ├── shared │ │ ├── _flash_messages.html.haml │ │ └── _navbar.html.haml │ │ └── layouts │ │ └── application.html.haml ├── Procfile.tt ├── Rakefile.tt ├── application_system_test.tt ├── README.tt ├── minitest_test_helper.tt ├── rspec_test_helper.tt ├── Gemfile.tt └── PANACEA.tt ├── .gitignore ├── .travis.yml ├── bin ├── setup └── console ├── lib └── panacea │ ├── rails │ ├── version.rb │ ├── arguments_parser.rb │ ├── runner.rb │ ├── stats.rb │ ├── template.rb │ ├── customizer.rb │ └── generator.rb │ └── rails.rb ├── test ├── test_helper.rb ├── panacea │ └── rails_test.rb ├── arguments_parser_test.rb └── customizer_test.rb ├── Gemfile ├── exe └── panacea ├── .rubocop.yml ├── Rakefile ├── LICENSE.txt ├── panacea-rails.gemspec ├── Gemfile.lock ├── README.md ├── CODE_OF_CONDUCT.md └── config └── questions.yml /templates/dotenv.tt: -------------------------------------------------------------------------------- 1 | # Add here your ENV variables, i.e. 2 | # VAR_NAME=value 3 | -------------------------------------------------------------------------------- /templates/githook.tt: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | bundle exec rubocop && bundle exec rake 4 | -------------------------------------------------------------------------------- /templates/oj_initializer.tt: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Oj.optimize_rails 4 | -------------------------------------------------------------------------------- /templates/devise/views/mailer/password_change.html.haml: -------------------------------------------------------------------------------- 1 | %p= t(".greeting", recipient: @resource.email) 2 | 3 | %p= t(".message") 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp 9 | *.gem 10 | .panacea 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | sudo: false 3 | language: ruby 4 | cache: bundler 5 | rvm: 6 | - 2.5.1 7 | before_install: gem install bundler -v 1.16.3 8 | -------------------------------------------------------------------------------- /templates/default_locale.tt: -------------------------------------------------------------------------------- 1 | # Add here your translations 2 | 3 | <%= @panacea.dig("locale").split("- ").last -%>: 4 | hello: "Hello world" 5 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /lib/panacea/rails/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Panacea # :nodoc: 4 | module Rails # :nodoc: 5 | VERSION = "0.2.1" 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /templates/rubocop.tt: -------------------------------------------------------------------------------- 1 | inherit_gem: 2 | rubocop-rails_config: 3 | - config/rails.yml 4 | 5 | AllCops: 6 | TargetRubyVersion: <%= @panacea.dig("ruby_version")[0..2] %> 7 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | $LOAD_PATH.unshift File.expand_path("../lib", __dir__) 4 | require "panacea/rails" 5 | 6 | require "minitest/autorun" 7 | -------------------------------------------------------------------------------- /templates/simplecov.tt: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "simplecov" 4 | 5 | SimpleCov.start "rails" 6 | 7 | SimpleCov.minimum_coverage <%= @panacea.dig("expected_coverage") %> 8 | -------------------------------------------------------------------------------- /templates/devise/views/mailer/confirmation_instructions.html.haml: -------------------------------------------------------------------------------- 1 | %p= t(".greeting", recipient: email) 2 | 3 | %p= t(".instruction") 4 | 5 | %p= link_to t(".action"), confirmation_url(@resource, confirmation_token: @token) 6 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } 6 | 7 | # Specify your gem's dependencies in panacea-rails.gemspec 8 | gemspec 9 | -------------------------------------------------------------------------------- /exe/panacea: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | lib = File.expand_path("../lib", __dir__) 5 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 6 | 7 | require "panacea/rails" 8 | 9 | Panacea::Rails.init 10 | -------------------------------------------------------------------------------- /templates/devise/views/mailer/unlock_instructions.html.haml: -------------------------------------------------------------------------------- 1 | %p= t(".greeting", recipient: @resource.email) 2 | 3 | %p= t(".message") 4 | 5 | %p= t(".instruction") 6 | 7 | %p= link_to t(".action"), unlock_url(@resource, unlock_token: @token) 8 | 9 | -------------------------------------------------------------------------------- /templates/devise/views/mailer/email_changed.html.haml: -------------------------------------------------------------------------------- 1 | %p= t(".greeting", recipient: @email) 2 | 3 | - if @resource.try(:unconfirmed_email?) 4 | %p= t(".message", email: @resource.unconfirmed_email) 5 | - else 6 | %p= t(".message", email: @resource.email) 7 | -------------------------------------------------------------------------------- /test/panacea/rails_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class Panacea::RailsTest < Minitest::Test 6 | def test_that_it_has_a_version_number 7 | refute_nil ::Panacea::Rails::VERSION 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /templates/devise/views/mailer/reset_password_instructions.html.haml: -------------------------------------------------------------------------------- 1 | %p= t(".greeting", recipient: @resource.email) 2 | 3 | %p= t(".instruction") 4 | 5 | %p= link_to t(".action"), edit_password_url(@resource, reset_password_token: @token) 6 | 7 | %p= t(".instruction_2") 8 | 9 | %p= t(".instruction_3") 10 | -------------------------------------------------------------------------------- /templates/bootswatch/stylesheets/application.scss.tt: -------------------------------------------------------------------------------- 1 | <%- template_name = @panacea.dig("bootswatch_template_name") %> 2 | @import "bootstrap-sprockets"; 3 | @import <%= "'bootswatch/#{template_name}/variables';" %> 4 | @import "bootstrap"; 5 | @import <%= "'bootswatch/#{template_name}/bootswatch';" %> 6 | 7 | @import "font-awesome"; 8 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | TargetRubyVersion: 2.5 3 | Exclude: 4 | - test/**/* 5 | - panacea-rails.gemspec 6 | 7 | Metrics/LineLength: 8 | Max: 120 9 | 10 | Metrics/MethodLength: 11 | Max: 15 12 | 13 | Style/StringLiterals: 14 | EnforcedStyle: double_quotes 15 | 16 | Style/Documentation: 17 | Enabled: false 18 | -------------------------------------------------------------------------------- /templates/bootswatch/views/shared/_flash_messages.html.haml: -------------------------------------------------------------------------------- 1 | - if alert 2 | .alert.alert-dismissible.alert-danger 3 | %button.close{ type: 'button', 'data-dismiss': 'alert' } × 4 | = alert 5 | - if notice 6 | .alert.alert-dismissible.alert-success 7 | %button.close{ type: 'button', 'data-dismiss': 'alert' } × 8 | = notice 9 | -------------------------------------------------------------------------------- /templates/Procfile.tt: -------------------------------------------------------------------------------- 1 | web: bin/rails s -p $PORT 2 | <% if @panacea.dig("background_job") == "sidekiq" -%> 3 | redis: redis-server 4 | sidekiq: bundle exec sidekiq -q default -q mailers 5 | <% elsif @panacea.dig("background_job") == "resque" -%> 6 | redis: redis-server 7 | resque: bundle exec rake resque:work QUEUE='*' 8 | <% end -%> 9 | webpack: bin/webpack-dev-server 10 | -------------------------------------------------------------------------------- /templates/Rakefile.tt: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative "config/application" 5 | <% if @panacea.dig("background_job") == "resque" -%> 6 | require "resque/tasks" 7 | task "resque:setup" => :environment 8 | <% end -%> 9 | 10 | Rails.application.load_tasks 11 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require "bundler/setup" 5 | require "panacea/rails" 6 | 7 | # You can add fixtures and/or initialization code here to make experimenting 8 | # with your gem easier. You can also use a different console, if you like. 9 | 10 | # (If you use this, don't forget to add pry to your Gemfile!) 11 | # require "pry" 12 | # Pry.start 13 | 14 | require "irb" 15 | IRB.start(__FILE__) 16 | -------------------------------------------------------------------------------- /templates/bootswatch/views/shared/_navbar.html.haml: -------------------------------------------------------------------------------- 1 | %nav.navbar.navbar-default 2 | .container-fluid 3 | .navbar-header 4 | %button.navbar-toggle.collapsed{ type: 'button', 'data-toggle': 'collapse', 5 | 'data-target': '#navbar', 'aria-expanded': false } 6 | 7 | = link_to '<%= "#{app_name.titleize}" %>', root_path, class: 'navbar-brand light' 8 | 9 | .collapse.navbar-collapse#navbar 10 | %ul.nav.navbar-nav.navbar-right 11 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "bundler/gem_tasks" 4 | require "rake/testtask" 5 | require "sdoc" 6 | require "rdoc/task" 7 | 8 | Rake::TestTask.new(:test) do |t| 9 | t.libs << "test" 10 | t.libs << "lib" 11 | t.test_files = FileList["test/**/*_test.rb"] 12 | end 13 | 14 | RDoc::Task.new do |rdoc| 15 | rdoc.rdoc_dir = "doc/rdoc" 16 | rdoc.rdoc_files.include("lib/**/*.rb") 17 | rdoc.options << "--format=sdoc" 18 | rdoc.template = "rails" 19 | end 20 | 21 | task default: :test 22 | -------------------------------------------------------------------------------- /test/arguments_parser_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class Panacea::Rails::ArgumentsParserTest < Minitest::Test 6 | def setup 7 | @subject = Class.new do 8 | extend Panacea::Rails::ArgumentsParser 9 | end 10 | end 11 | 12 | def test_it_transform_args_hash_to_string 13 | args_hash = { database: "postgresql", "skip-git": true } 14 | result_string = @subject.parse_arguments(args_hash) 15 | 16 | assert_equal result_string, "--database=postgresql --skip-git" 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /templates/bootswatch/views/layouts/application.html.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/ 5 | %title <%= app_name.titleize %> 6 | = csrf_meta_tags 7 | = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' 8 | = javascript_include_tag 'application', 'data-turbolinks-track': 'reload' 9 | %body 10 | = render partial: 'shared/navbar' 11 | 12 | .container 13 | = render partial: 'shared/flash_messages' 14 | 15 | = yield 16 | -------------------------------------------------------------------------------- /test/customizer_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | require "fileutils" 5 | 6 | class Panacea::Rails::CustomizerTest < Minitest::Test 7 | ROOT_DIR = File.expand_path("..", __dir__) 8 | 9 | def setup 10 | @panacea_config_file = File.join(ROOT_DIR, ".panacea") 11 | @subject = Panacea::Rails::Customizer.new("my_app", "--database=postgresql") 12 | end 13 | 14 | def test_it_generates_a_config_file 15 | FileUtils.rm(@panacea_config_file) if File.exist?(@panacea_config_file) 16 | @subject.start 17 | 18 | assert File.exist?(@panacea_config_file) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /templates/devise/views/unlocks/new.html.haml: -------------------------------------------------------------------------------- 1 | %legend.center= t(".resend_unlock_instructions") 2 | 3 | .col-md-4.col-md-offset-4 4 | .well 5 | = form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| 6 | = devise_error_messages! 7 | 8 | %fielset.form-group 9 | .col-md-12 10 | = f.label :email 11 | = f.email_field :email, autofocus: true, autocomplete: "email", class: "form-control" 12 | 13 | .col-md-12 14 | %br 15 | = f.submit t(".resend_unlock_instructions)", class: "btn btn-success" 16 | 17 | = render "devise/shared/links" 18 | -------------------------------------------------------------------------------- /templates/devise/views/passwords/new.html.haml: -------------------------------------------------------------------------------- 1 | %legend.center= t(".forgot_your_password") 2 | 3 | .col-md-4.col-md-offset-4 4 | .well 5 | = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| 6 | = devise_error_messages! 7 | 8 | %fielset.form-group 9 | .col-md-12 10 | = f.label :email 11 | = f.email_field :email, autofocus: true, autocomplete: "email", class: "form-control" 12 | 13 | .col-md-12 14 | %br 15 | = f.submit t(".send_me_reset_password_instructions"), class: "btn btn-success" 16 | 17 | = render "devise/shared/links" 18 | -------------------------------------------------------------------------------- /templates/devise/views/confirmations/new.html.haml: -------------------------------------------------------------------------------- 1 | %legend.center= t("resend_confirmation_instructions") 2 | 3 | .col-md-4.col-md-offset-4 4 | .well 5 | = form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| 6 | = devise_error_messages! 7 | 8 | %fielset.form-group 9 | .col-md-12 10 | = f.label :email 11 | = f.email_field :email, autofocus: true, autocomplete: "email", class: "form-control" 12 | value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) 13 | 14 | .col-md-12 15 | %br 16 | = f.submit t(".resend_confirmation_instructions") 17 | 18 | = render "devise/shared/links" 19 | -------------------------------------------------------------------------------- /templates/devise/views/sessions/new.html.haml: -------------------------------------------------------------------------------- 1 | %legend.center= t(".sign_in") 2 | 3 | .col-md-4.col-md-offset-4 4 | .well 5 | = form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| 6 | %fielset.form-group 7 | .col-md-12 8 | = f.label :email 9 | = f.email_field :email, autofocus: true, autocomplete: "email", class: "form-control" 10 | 11 | .col-md-12 12 | = f.label :password 13 | = f.password_field :password, autocomplete: "off", class: "form-control" 14 | 15 | - if devise_mapping.rememberable? 16 | .col-md-12 17 | %br 18 | = f.check_box :remember_me 19 | = f.label :remember_me 20 | 21 | .col-md-12 22 | = f.submit t(".sign_in"), class: "btn btn-success" 23 | 24 | = render "devise/shared/links" 25 | -------------------------------------------------------------------------------- /templates/application_system_test.tt: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | require "webdrivers" 5 | require "webdrivers/chromedriver" 6 | 7 | Webdrivers.cache_time = 86_400 8 | Webdrivers::Chromedriver.update 9 | 10 | Capybara.register_driver :headless_chrome do |app| 11 | options = ::Selenium::WebDriver::Chrome::Options.new 12 | 13 | options.add_argument("--headless") 14 | options.add_argument("--no-sandbox") 15 | options.add_argument("--disable-gpu") 16 | options.add_argument("--window-size=1920,1080") 17 | 18 | Capybara::Selenium::Driver.new(app, browser: :chrome, options: options) 19 | end 20 | 21 | Capybara.javascript_driver = :headless_chrome 22 | 23 | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase 24 | <% if @panacea.dig("devise") -%> 25 | include Warden::Test::Helpers 26 | 27 | <% end -%> 28 | 29 | driven_by :headless_chrome 30 | end 31 | -------------------------------------------------------------------------------- /lib/panacea/rails/arguments_parser.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Panacea # :nodoc: 4 | module Rails # :nodoc: 5 | ### 6 | # == Panacea::Rails::ArgumentsParser 7 | # 8 | # This module is in charge Parsing Slop Arguments. 9 | module ArgumentsParser 10 | ### 11 | # This method builds an arguments String from the Slop args Hash. 12 | # 13 | # The string will be passed to the `rails new` command and it will be also 14 | # tracked if the end user agrees to share Panacea's usage information 15 | def parse_arguments(arguments) 16 | arguments.each_with_object([]) do |arg, parsed_args| 17 | case arg.last.class.to_s 18 | when "String" 19 | parsed_args << "--#{arg.first}=#{arg.last}" 20 | when "TrueClass" 21 | parsed_args << "--#{arg.first}" 22 | end 23 | end.join(" ") 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /templates/devise/views/passwords/edit.html.haml: -------------------------------------------------------------------------------- 1 | %legend.center= t(".change_your_password') 2 | 3 | .col-md-4.col-md-offset-4 4 | .well 5 | = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| 6 | = devise_error_messages! 7 | 8 | %fielset.form-group 9 | = f.hidden_field :reset_password_token 10 | .col-md-12 11 | = f.label :password, t(".new_password') 12 | %br/ 13 | - if @minimum_password_length 14 | %em= t("devise.shared.minimum_password_length", count: @minimum_password_length) 15 | = f.password_field :password, autofocus: true, autocomplete: "off", class: "form-control" 16 | 17 | .col-md-12 18 | = f.label :password_confirmation, t(".confirm_new_password") 19 | = f.password_field :password_confirmation, autocomplete: "off", class: "form-control" 20 | 21 | .col-md-12 22 | %br 23 | = f.submit t(".change_my_password") 24 | 25 | = render "devise/shared/links" 26 | -------------------------------------------------------------------------------- /templates/devise/views/registrations/new.html.haml: -------------------------------------------------------------------------------- 1 | %legend.center= t(".sign_up") 2 | 3 | .col-md-4.col-md-offset-4 4 | .well 5 | = form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| 6 | = devise_error_messages! 7 | 8 | %fieldset.form-group 9 | .col-md-12 10 | = f.label :email 11 | = f.email_field :email, autofocus: true, autocomplete: "email", class: "form-control" 12 | 13 | .col-md-12 14 | = f.label :password 15 | - if @minimum_password_length 16 | %em= t("devise.shared.minimum_password_length", count: @minimum_password_length) 17 | = f.password_field :password, autocomplete: "off", class: "form-control" 18 | 19 | .col-md-12 20 | = f.label :password_confirmation 21 | = f.password_field :password_confirmation, autocomplete: "off", class: "form-control" 22 | 23 | .col-md-12 24 | %br 25 | = f.submit t(".sign_up"), class: "btn btn-success" 26 | 27 | = render "devise/shared/links" 28 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Eduardo Figarola 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /templates/devise/views/shared/_links.html.haml: -------------------------------------------------------------------------------- 1 | - if controller_name != 'sessions' 2 | = link_to t(".sign_in"), new_session_path(resource_name) 3 | %br/ 4 | 5 | - if devise_mapping.registerable? && controller_name != 'registrations' 6 | = link_to t(".sign_up"), new_registration_path(resource_name) 7 | %br/ 8 | 9 | - if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' 10 | = link_to t(".forgot_your_password"), new_password_path(resource_name) 11 | %br/ 12 | 13 | - if devise_mapping.confirmable? && controller_name != 'confirmations' 14 | = link_to t(".didn_t_receive_confirmation_instructions"), new_confirmation_path(resource_name) 15 | %br/ 16 | 17 | - if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' 18 | = link_to t(".didn_t_receive_unlock_instructions"), new_unlock_path(resource_name) 19 | %br/ 20 | 21 | - if devise_mapping.omniauthable? 22 | - resource_class.omniauth_providers.each do |provider| 23 | = link_to t(".sign_in_with_provider", provider: "#{OmniAuth::Utils.camelize(provider)}"), omniauth_authorize_path(resource_name, provider) 24 | %br/ 25 | -------------------------------------------------------------------------------- /templates/README.tt: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | This README would normally document whatever steps are necessary to get the 4 | application up and running. 5 | 6 | Edit/Remove the following sections as you wish. 7 | 8 | ## System Requirements 9 | 10 | - Ruby 2.x.x 11 | - Rails 5.2.2 12 | - Yarn 13 | 14 | ## Dependencies 15 | 16 | ### Ruby 17 | 18 | bundle install 19 | 20 | ### Javascript 21 | 22 | yarn install 23 | 24 | ### System 25 | 26 | ## Database Management 27 | 28 | To setup database you either run: 29 | 30 | bundle exec rails db:setup 31 | 32 | or: 33 | 34 | bundle exec rails db:create 35 | bundle exec rails db:migrate 36 | bundle exec rails db:seed 37 | 38 | ### Running migrations 39 | 40 | To run migrations use the following command: 41 | 42 | bundle exec rails db:migrate 43 | 44 | ## Services (job queues, cache servers, search engines, etc.) 45 | 46 | ## Tests 47 | 48 | You should run the tests with the following command: 49 | 50 | bundle exec rake 51 | 52 | ## Linting 53 | 54 | You can lint the code running Rubocop: 55 | 56 | bundle exec rubocop 57 | 58 | ## Security 59 | 60 | Run: 61 | 62 | bundle exec brakeman 63 | 64 | ## Deployment Instructions 65 | 66 | ## How to Contribute 67 | 68 | ## License 69 | -------------------------------------------------------------------------------- /templates/minitest_test_helper.tt: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | ENV["RAILS_ENV"] = "test" 4 | 5 | require_relative "support/simplecov" 6 | require_relative "../config/environment" 7 | require "rails/test_help" 8 | require "minitest/rails" 9 | <% if @panacea.dig("faker") -%> 10 | require "faker" 11 | <% end -%> 12 | <% if @panacea.dig("http_stubs") == "vcr" -%> 13 | require "vcr" 14 | <% end -%> 15 | <% if @panacea.dig("http_stubs") == "webmock" -%> 16 | require "webmock/minitest" 17 | <% end -%> 18 | 19 | # To add Capybara feature tests add `gem "minitest-rails-capybara"` 20 | # to the test group in the Gemfile and uncomment the following: 21 | # require "minitest/rails/capybara" 22 | 23 | # Uncomment for awesome colorful output 24 | # require "minitest/pride" 25 | 26 | <% if @panacea.dig("http_stubs") == "vcr" -%> 27 | VCR.configure do |config| 28 | config.cassette_library_dir = "test/vcr_cassettes" 29 | config.hook_into :webmock 30 | end 31 | <% end -%> 32 | 33 | class ActiveSupport::TestCase 34 | <% if @panacea.dig("factory_bot") -%> 35 | include FactoryBot::Syntax::Methods 36 | <% end -%> 37 | 38 | fixtures :all 39 | end 40 | 41 | <% if @panacea.dig("devise") -%> 42 | class ActionDispatch::IntegrationTest 43 | include Devise::Test::IntegrationHelpers 44 | end 45 | <% end -%> 46 | -------------------------------------------------------------------------------- /panacea-rails.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | lib = File.expand_path("lib", __dir__) 4 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 5 | 6 | require "panacea/rails/version" 7 | 8 | Gem::Specification.new do |spec| 9 | spec.name = "panacea-rails" 10 | spec.version = Panacea::Rails::VERSION 11 | spec.authors = ["Guillermo Moreno", "Rafael Ramos", "Eduardo Figarola"] 12 | spec.email = ["guillermo@michelada.io", "rafael@michelada.io", "eduardo@michelada.io"] 13 | 14 | spec.summary = "Rails Apps Generator" 15 | spec.homepage = "https://www.panacea.website" 16 | spec.license = "MIT" 17 | 18 | spec.files = Dir.chdir(File.expand_path(__dir__)) do 19 | `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 20 | end 21 | spec.bindir = "exe" 22 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 23 | spec.require_paths = ["lib"] 24 | 25 | spec.add_dependency "bundler", "~> 1" 26 | spec.add_dependency "slop", "~> 4.6" 27 | spec.add_dependency "tty-prompt", "~> 0.17" 28 | 29 | spec.add_development_dependency "minitest", "~> 5.0" 30 | spec.add_development_dependency "rake", "~> 13.0" 31 | spec.add_development_dependency "rubocop", "~> 0.58" 32 | spec.add_development_dependency "sdoc", "~> 1.0" 33 | end 34 | -------------------------------------------------------------------------------- /lib/panacea/rails/runner.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "arguments_parser" 4 | require_relative "customizer" 5 | 6 | module Panacea # :nodoc: 7 | module Rails # :nodoc: 8 | ### 9 | # == Panacea::Rails::Runner 10 | # 11 | # This module is where Panacea's work start. 12 | module Runner 13 | extend ArgumentsParser 14 | 15 | class << self 16 | ### 17 | # This method receives the App's name and the arguments passed to Panacea command. 18 | # 19 | # It uses the Panacea::Rails::ArgumentsParser.parse_arguments method to transform the passed arguments. 20 | # 21 | # It also starts the Panacea::Rails::Customizer which is in charge of asking the configuration questions. 22 | # 23 | # Then, it appends the Panacea's Template option to the list of parsed arguments. 24 | # 25 | # It finally runs the rails new command with the App's name and the final list of arguments. 26 | def call(app_name, rails_args) 27 | parsed_arguments = parse_arguments(rails_args) 28 | Customizer.start(app_name, parsed_arguments.dup) 29 | 30 | panacea_template = __dir__ + "/template.rb" 31 | parsed_arguments << " --template=#{panacea_template}" 32 | parsed_arguments = parsed_arguments.split(" ") 33 | 34 | system("rails", "new", app_name, *parsed_arguments) 35 | end 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | panacea-rails (0.2.1) 5 | bundler (~> 1) 6 | slop (~> 4.6) 7 | tty-prompt (~> 0.17) 8 | 9 | GEM 10 | remote: https://rubygems.org/ 11 | specs: 12 | ast (2.4.0) 13 | equatable (0.6.1) 14 | jaro_winkler (1.5.1) 15 | minitest (5.11.3) 16 | necromancer (0.5.0) 17 | parallel (1.12.1) 18 | parser (2.5.1.2) 19 | ast (~> 2.4.0) 20 | pastel (0.7.3) 21 | equatable (~> 0.6) 22 | tty-color (~> 0.5) 23 | powerpack (0.1.2) 24 | rainbow (3.0.0) 25 | rake (13.0.1) 26 | rdoc (6.0.4) 27 | rubocop (0.58.2) 28 | jaro_winkler (~> 1.5.1) 29 | parallel (~> 1.10) 30 | parser (>= 2.5, != 2.5.1.1) 31 | powerpack (~> 0.1) 32 | rainbow (>= 2.2.2, < 4.0) 33 | ruby-progressbar (~> 1.7) 34 | unicode-display_width (~> 1.0, >= 1.0.1) 35 | ruby-progressbar (1.9.0) 36 | sdoc (1.0.0) 37 | rdoc (>= 5.0) 38 | slop (4.7.0) 39 | tty-color (0.5.0) 40 | tty-cursor (0.7.0) 41 | tty-prompt (0.19.0) 42 | necromancer (~> 0.5.0) 43 | pastel (~> 0.7.0) 44 | tty-reader (~> 0.6.0) 45 | tty-reader (0.6.0) 46 | tty-cursor (~> 0.7) 47 | tty-screen (~> 0.7) 48 | wisper (~> 2.0.0) 49 | tty-screen (0.7.0) 50 | unicode-display_width (1.4.0) 51 | wisper (2.0.1) 52 | 53 | PLATFORMS 54 | ruby 55 | 56 | DEPENDENCIES 57 | minitest (~> 5.0) 58 | panacea-rails! 59 | rake (~> 13.0) 60 | rubocop (~> 0.58) 61 | sdoc (~> 1.0) 62 | 63 | BUNDLED WITH 64 | 1.17.2 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Panacea::Rails 2 | 3 | Rails Apps generator that helps you to focus on what really matters: your app's business logic. 4 | 5 | ## Installation 6 | 7 | Run the following command: 8 | 9 | $ gem install panacea-rails 10 | 11 | ## Usage 12 | 13 | Once you have installed `panacea-rails` gem, run: 14 | 15 | $ panacea your-app-name 16 | 17 | You can see the list of accepted arguments running: 18 | 19 | $ panacea --help 20 | 21 | Panacea will ask you some questions in order to help you setup your fresh Rails app. 22 | 23 | ## Website 24 | 25 | You can read the full documentation here: 26 | 27 | **[https://panacea-rails.github.io/website/](https://panacea-rails.github.io/website/)** 28 | 29 | ## Suggesting Integrations 30 | 31 | We accept suggestions via Github's Issues. Please create one issue per suggested gem and mark it with the `suggestion` label. We will do our best to integrate the suggested gem into the Panacea Generator. 32 | 33 | ## Contributing 34 | 35 | Bug reports and pull requests are welcome on GitHub at [https://github.com/panacea-rails/panacea](https://github.com/panacea-rails/panacea). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. 36 | 37 | ## License 38 | 39 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). 40 | 41 | ## Code of Conduct 42 | 43 | Everyone interacting in the Panacea::Rails project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/panacea-rails/panacea/blob/master/CODE_OF_CONDUCT.md). 44 | -------------------------------------------------------------------------------- /lib/panacea/rails/stats.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "json" 4 | require "net/http" 5 | 6 | module Panacea # :nodoc: 7 | module Rails # :nodoc: 8 | ### 9 | # == Panacea::Rails::Stats 10 | # 11 | # This class tracks the end users answers if they agree to. 12 | class Stats 13 | ### 14 | # Hash with the question's answers + ruby version + passed arguments 15 | attr_reader :params 16 | 17 | ### 18 | # Panacea's Stats App endpoint 19 | API_BASE = "https://stats.panacea.website/statistics" 20 | 21 | ### 22 | # It sends the end user's answers to the Panacea's Stats App. 23 | def self.track(params) 24 | new(params) 25 | end 26 | 27 | ### 28 | # Panacea::Rails::Stats initialize method. 29 | def initialize(params) 30 | @params = params 31 | track 32 | end 33 | 34 | ### 35 | # Makes an async call to the Panacea's Stats App. 36 | def track 37 | request_async_post(params) 38 | end 39 | 40 | private 41 | 42 | def request_async_post(params) 43 | Thread.new do 44 | request_post(params) 45 | end 46 | end 47 | 48 | def request_post(params) 49 | uri = URI(API_BASE) 50 | response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http| 51 | request = Net::HTTP::Post.new(uri) 52 | request["Accept"] = "application/json" 53 | request["Content-Type"] = "application/json" 54 | request.body = params.to_json 55 | 56 | http.request(request) 57 | end 58 | 59 | response.code == "201" || response.code != "422" 60 | rescue Net::OpenTimeout, Errno::ECONNREFUSED 61 | false 62 | end 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /templates/devise/views/registrations/edit.html.haml: -------------------------------------------------------------------------------- 1 | %legend.center= t(".title", resource: resource_name.to_s.humanize) 2 | 3 | .col-md-4.col-md-offset-4 4 | .well 5 | = form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| 6 | = devise_error_messages! 7 | 8 | %fieldset.form-group 9 | .col-md-12 10 | = f.label :email 11 | = f.email_field :email, autofocus: true, autocomplete: "email", class: "form-control" 12 | 13 | - if devise_mapping.confirmable? && resource.pending_reconfirmation? 14 | %div= t(".currently_waiting_confirmation_for_email", email: resource.unconfirmed_email) 15 | 16 | .col-md-12 17 | = f.label :password 18 | %br 19 | %i= t(".leave_blank_if_you_don_t_want_to_change_it") 20 | = f.password_field :password, autocomplete: "off", class: "form-control" 21 | - if @minimum_password_length 22 | %em= t("devise.shared.minimum_password_length"), count: @minimum_password_length 23 | 24 | .col-md-12 25 | = f.label :password_confirmation 26 | = f.password_field :password_confirmation, autocomplete: "off", class: "form-control" 27 | 28 | .col-md-12 29 | = f.label :current_password 30 | %br 31 | %i= t(".we_need_your_current_password_to_confirm_your_changes") 32 | = f.password_field :current_password, autocomplete: "off", class: "form-control" 33 | 34 | .col-md-12 35 | %br 36 | = f.submit t(".update"), class: "btn btn-success" 37 | 38 | = link_to t("devise.shared.links.back"), :back 39 | 40 | .row.center 41 | %hr 42 | %h4= t(".cancel_my_account") 43 | %p= t(".unhappy") 44 | = button_to t("cancel_my_account"), registration_path(resource_name), data: { confirm: t(".are_you_sure") }, method: :delete, class: "btn btn-sm btn-danger" } 45 | -------------------------------------------------------------------------------- /lib/panacea/rails/template.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | ### 4 | # == Panacea::Rails::Template 5 | # 6 | # Template passed to the rails new command. 7 | # 8 | # If you want more information about what's happening you can read Panacea::Rails::Generator docs. 9 | 10 | require "yaml" 11 | require_relative "generator" 12 | 13 | ### 14 | # Panacea's Installation directory 15 | ROOT_DIR = File.expand_path("../../../", __dir__) 16 | 17 | # Read .panacea configurations file 18 | configurations_file = File.join(ROOT_DIR, ".panacea") 19 | panacea_config = YAML.safe_load(File.read(configurations_file)) 20 | panacea_config["ruby_version"] = RUBY_VERSION 21 | 22 | # Start running Panacea Generator Actions via Rails Generator / Thor 23 | panacea_generator = Panacea::Rails::Generator.new(self, panacea_config, ROOT_DIR) 24 | 25 | panacea_generator.update_source_paths 26 | panacea_generator.copy_gemfile 27 | panacea_generator.copy_readme 28 | panacea_generator.generate_panacea_document 29 | panacea_generator.run_bundle 30 | 31 | panacea_generator.setup_rubocop 32 | panacea_generator.setup_letter_opener 33 | panacea_generator.setup_timezone 34 | panacea_generator.setup_default_locale 35 | panacea_generator.create_database 36 | panacea_generator.setup_oj if panacea_config.dig("oj") 37 | panacea_generator.setup_dotenv if panacea_config.dig("dotenv") 38 | panacea_generator.setup_bullet 39 | panacea_generator.setup_test_suite 40 | panacea_generator.override_test_helper 41 | panacea_generator.setup_simplecov 42 | panacea_generator.setup_background_job if panacea_config.dig("background_job") != "none" 43 | panacea_generator.override_application_system_test if panacea_config.dig("headless_chrome") 44 | panacea_generator.setup_devise if panacea_config.dig("devise") 45 | panacea_generator.setup_money_rails if panacea_config.dig("money_rails") 46 | panacea_generator.setup_kaminari if panacea_config.dig("kaminari") 47 | panacea_generator.setup_foreman if panacea_config.dig("foreman") 48 | panacea_generator.setup_pundit if panacea_config.dig("pundit") 49 | 50 | panacea_generator.fix_offenses! 51 | panacea_generator.commit! if panacea_config.dig("autocommit") 52 | panacea_generator.setup_githook if panacea_config.dig("githook") && !options[:skip_git] 53 | panacea_generator.bye_message 54 | -------------------------------------------------------------------------------- /templates/rspec_test_helper.tt: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "support/simplecov" 4 | require "spec_helper" 5 | ENV["RAILS_ENV"] ||= "test" 6 | require_relative "../config/environment" 7 | 8 | # Prevent database truncation if the environment is production 9 | abort("The Rails environment is running in production mode!") if Rails.env.production? 10 | 11 | require "rspec/rails" 12 | require "capybara/rspec" 13 | <% if @panacea.dig("faker") -%> 14 | require "faker" 15 | <% end -%> 16 | <% if @panacea.dig("http_stubs") == "vcr" -%> 17 | require "vcr" 18 | <% end -%> 19 | <% if @panacea.dig("http_stubs") == "webmock" -%> 20 | require "webmock/rspec" 21 | <% end -%> 22 | 23 | # Add additional requires below this line. Rails is not loaded until this point! 24 | 25 | # Checks for pending migrations and applies them before tests are run. 26 | # If you are not using ActiveRecord, you can remove these lines. 27 | begin 28 | ActiveRecord::Migration.maintain_test_schema! 29 | rescue ActiveRecord::PendingMigrationError => e 30 | puts e.to_s.strip 31 | exit 1 32 | end 33 | 34 | <% if @panacea.dig("http_stubs") == "vcr" -%> 35 | VCR.configure do |config| 36 | config.cassette_library_dir = "spec/vcr_cassettes" 37 | config.hook_into :webmock 38 | end 39 | <% end -%> 40 | 41 | RSpec.configure do |config| 42 | config.fixture_path = "#{::Rails.root}/spec/fixtures" 43 | config.use_transactional_fixtures = true 44 | config.infer_spec_type_from_file_location! 45 | config.filter_rails_from_backtrace! 46 | <% if @panacea.dig("devise") -%> 47 | config.include Devise::Test::IntegrationHelpers, type: :request 48 | <% end -%> 49 | <% if @panacea.dig("factory_bot") -%> 50 | config.include FactoryBot::Syntax::Methods 51 | <% end -%> 52 | 53 | <% if @panacea.dig("headless_chrome") -%> 54 | Capybara.register_driver :chrome do |app| 55 | Capybara::Selenium::Driver.new(app, browser: :chrome) 56 | end 57 | 58 | Capybara.register_driver :headless_chrome do |app| 59 | capabilities = Selenium::WebDriver::Remote::Capabilities.chrome( 60 | chromeOptions: { args: %w(headless disable-gpu no-sandbox) } 61 | ) 62 | 63 | Capybara::Selenium::Driver.new app, browser: :chrome, desired_capabilities: capabilities 64 | end 65 | 66 | Capybara.javascript_driver = :headless_chrome 67 | <% end %> 68 | end 69 | -------------------------------------------------------------------------------- /templates/Gemfile.tt: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | <%- db_gem = database_gemfile_entry -%> 3 | 4 | source "https://rubygems.org" 5 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 6 | 7 | ruby "<%= @panacea.dig("ruby_version") %>" 8 | 9 | gem "rails", "~> 6.0.0" 10 | gem "<%= db_gem.name %>"<%= %(, '#{db_gem.version}') if db_gem.version %> 11 | 12 | gem "bootsnap", ">= 1.4.4", require: false 13 | <% if @panacea.dig("devise") -%> 14 | gem "devise" 15 | <% end -%> 16 | gem "haml-rails" 17 | gem "image_processing", "~> 1.2" 18 | gem "interactor-rails" 19 | <% if @panacea.dig("kaminari") -%> 20 | gem "kaminari" 21 | <% end -%> 22 | <% if @panacea.dig("oj") -%> 23 | gem "oj", "~> 3" 24 | <% end -%> 25 | <% if @panacea.dig("money_rails") -%> 26 | gem "money-rails", "~> 1" 27 | <% end -%> 28 | <% if @panacea.dig("pg_search") -%> 29 | gem "pg_search" 30 | <% end -%> 31 | gem "puma", "~> 4.1" 32 | <% if @panacea.dig("pundit") -%> 33 | gem "pundit" 34 | <% end -%> 35 | <% if @panacea.dig("markdown") -%> 36 | gem "redcarpet" 37 | <% end -%> 38 | gem "sass-rails", "~> 5.0" 39 | gem "turbolinks", "~> 5" 40 | gem "uglifier", ">= 1.3.0" 41 | gem "webpacker", "~> 4.0" 42 | 43 | <% if @panacea.dig("background_job") != "none" -%> 44 | # Background Jobs Adapter 45 | gem "<%= @panacea.dig("background_job") %>" 46 | <% end -%> 47 | 48 | # Use Redis adapter to run Action Cable in production 49 | # gem 'redis', '~> 4.0' 50 | 51 | <% if @panacea.dig("bootswatch") %> 52 | # Bootswatch Dependencies 53 | gem "bootstrap-sass", "~> 3.3.7" 54 | gem "bootswatch-rails" 55 | gem "font-awesome-rails" 56 | gem "jquery-rails" 57 | <% end %> 58 | 59 | # Test Suite Gem 60 | gem "<%= @panacea.dig("test_suite") %>-rails", group: [:development, :test] 61 | 62 | group :development, :test do 63 | <% if @panacea.dig("awesome_print") -%> 64 | gem "awesome_print" 65 | <% end -%> 66 | <% if @panacea.dig("dotenv") -%> 67 | gem "dotenv-rails" 68 | <% end -%> 69 | <% if @panacea.dig("factory_bot") -%> 70 | gem "factory_bot_rails", "~> 5.0" 71 | <% end -%> 72 | gem "pry" 73 | gem "rubocop-rails_config" 74 | end 75 | 76 | group :development do 77 | gem "brakeman" 78 | gem "bullet" 79 | gem "web-console", ">= 3.3.0" 80 | gem "listen", ">= 3.0.5", "< 3.2" 81 | gem "spring" 82 | gem "spring-watcher-listen", "~> 2.0.0" 83 | gem "letter_opener" 84 | end 85 | 86 | group :test do 87 | gem "capybara", ">= 2.15" 88 | <% if @panacea.dig("faker") -%> 89 | gem "faker" 90 | <% end -%> 91 | gem "selenium-webdriver" 92 | gem "webdrivers" 93 | gem "simplecov", require: false 94 | <% if @panacea.dig("http_stubs") != "none" -%> 95 | gem "<%= @panacea.dig("http_stubs") %>" 96 | <% end -%> 97 | end 98 | 99 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 100 | gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] 101 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at eduardofigarola@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /lib/panacea/rails.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "slop" 4 | require "panacea/rails/version" 5 | require "panacea/rails/runner" 6 | 7 | module Panacea # :nodoc: 8 | ### 9 | # == Panacea::Rails 10 | # 11 | # This module is the access point to the configuration questions and the Panacea::Rails::Generator. 12 | module Rails 13 | class << self 14 | # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/LineLength, Metrics/BlockLength 15 | 16 | ### 17 | # This method uses Slop to handle the CLI command and its arguments. 18 | # It accepts most of the default Rails arguments. 19 | # 20 | # If App's name is not passed as an argument we show the command help. 21 | # 22 | # When the App's name is passed, we start the Panacea::Rails::Runner 23 | def init 24 | opts = Slop.parse do |o| 25 | o.banner = "usage: panacea your-app-name [options]" 26 | o.separator "" 27 | o.string "-d", "--database", "# Options (mysql/postgresql/sqlite3/oracle/frontbase/im_db/sqlserver/jdbcmysql/jdbcsqlite3/jdbcpostgresql/jdbc)", default: "postgresql" 28 | o.bool "--skip-namespace", "# Skip namespace (affects only isolated applications)", default: false 29 | o.bool "--skip-yarn", "# Don't use Yarn for managing JavaScript dependencies", default: false 30 | o.bool "--skip-git", "# Skip .gitignore file", default: false 31 | o.bool "--skip-keeps", "# Skip source control .keep files", default: false 32 | o.bool "--skip-action-mailer", "# Skip Action Mailer files", default: false 33 | o.bool "--skip-active-record", "# Skip Active Record files", default: false 34 | o.bool "--skip-active-storage", "# Skip Active Storage files", default: false 35 | o.bool "--skip-puma", "# Skip Puma related files", default: false 36 | o.bool "--skip-action-cable", "# Skip Action Cable files", default: false 37 | o.bool "--skip-sprockets", "# Skip Sprockets files", default: false 38 | o.bool "--skip-spring", "# Don't install Spring application preloader", default: false 39 | o.bool "--skip-listen", "# Don't generate configuration that depends on the listen gem", default: false 40 | o.bool "--skip-coffee", "# Don't use CoffeeScript", default: false 41 | o.bool "--skip-javascript", "# Skip JavaScript files", default: false 42 | o.bool "--skip-turbolinks", "# Skip turbolinks gem", default: false 43 | o.bool "--skip-test", "# Skip test files", default: false 44 | o.bool "--skip-system-test", "# Skip system test files", default: false 45 | o.bool "--skip-bootsnap", "# Skip bootsnap gem", default: false 46 | o.bool "--dev", "# Setup the application with Gemfile pointing to your Rails checkout", default: false 47 | o.bool "--edge", "# Setup the application with Gemfile pointing to Rails repository", default: false 48 | o.string "--rc", "# Path to file containing extra configuration options for rails command", default: nil 49 | o.bool "--no-rc", "# Skip loading of extra configuration options from .railsrc file", default: false 50 | o.bool "--api", "# Preconfigure smaller stack for API only apps", default: false 51 | o.bool "--skip-bundle", "# Don't run bundle install", default: false 52 | o.separator "" 53 | o.separator "Runtime options:" 54 | o.bool "--force", "# Overwrite files that already exist", default: false 55 | o.bool "--pretend", "# Run but do not make any changes", default: false 56 | o.bool "--quiet", "# Suppress status output", default: false 57 | o.bool "--skip", "# Skip files that already exist", default: false 58 | o.separator "" 59 | o.separator "Panacea options:" 60 | o.on "-v", "--version" do 61 | puts Panacea::Rails::VERSION 62 | exit 63 | end 64 | o.on "-h", "--help" do 65 | puts o 66 | exit 67 | end 68 | end 69 | 70 | return puts(opts) if opts.arguments.empty? 71 | 72 | Runner.call(opts.arguments.first, opts.to_hash) 73 | end 74 | # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/LineLength, Metrics/BlockLength 75 | end 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /templates/PANACEA.tt: -------------------------------------------------------------------------------- 1 | # Panacea::Rails 2 | 3 | Here is what Panacea has setup for you: 4 | 5 | ### Application Settings 6 | 7 | #### Timezone 8 | 9 | Default timezone was set to: **<%= @panacea.dig("timezone") %>** in *config/application.rb* 10 | 11 | #### Locale 12 | 13 | Default locale was set to: **<%= @panacea.dig("locale") %>** in *config/application.rb* 14 | 15 | ### Development Tools 16 | 17 | <% if @panacea.dig("awesome_print") -%> 18 | #### awesome_print 19 | The gem was included in Gemfile. 20 | <% end -%> 21 | 22 | #### Bullet 23 | * The gem was included in Gemfile. 24 | * `config/environments/development.rb` was modified. 25 | 26 | <% if @panacea.dig("foreman") -%> 27 | #### Foreman 28 | * Foreman gem was installed in system if needed. 29 | * **Procfile** was created. 30 | <% end -%> 31 | 32 | <% if @panacea.dig("githook") -%> 33 | #### Git hook 34 | The Git hook was installed in *.git/hooks/<%= @panacea.dig("githook_type") %>* 35 | <% end -%> 36 | 37 | ### General Purpose 38 | 39 | <% if @panacea.dig("devise") -%> 40 | #### devise 41 | * The gem was included in Gemfile. 42 | * `app/models/<%= @panacea.dig("devise_model_name") %>.rb` was created. 43 | * <%= @panacea.dig("devise_override_views") ? "Devise views were created." : "Devise views were not created." %> 44 | <% end -%> 45 | 46 | <% if @panacea.dig("dotenv") -%> 47 | #### dotenv-rails 48 | * The gem was included in Gemfile. 49 | * `.env` file was created 50 | * `.env` file was added to .gitignore 51 | <% end -%> 52 | 53 | #### haml-rails 54 | The gem was included in Gemfile. 55 | 56 | #### interactor-rails 57 | The gem was included in Gemfile. 58 | 59 | <% if @panacea.dig("kaminari") -%> 60 | #### Kaminari 61 | * The gem was included in Gemfile. 62 | * The `rails generate kaminari:config` generator was ran. 63 | <% end -%> 64 | 65 | #### letter_opener 66 | * The gem was included in Gemfile. 67 | * `config/environments/development.rb` was modified. 68 | 69 | <% if @panacea.dig("money_rails") -%> 70 | #### money-rails 71 | * The gem was included in Gemfile. 72 | * The `rails generate money_rails:initializer` generator was ran. 73 | <% end -%> 74 | 75 | <% if @panacea.dig("oj") -%> 76 | #### oj 77 | * The gem was included in Gemfile. 78 | * The `config/initializers/oj.rb` file was created 79 | <% end -%> 80 | 81 | #### pry 82 | The gem was included in Gemfile. 83 | 84 | <% if @panacea.dig("pg_search") -%> 85 | #### pg_search 86 | The gem was included in Gemfile. 87 | <% end -%> 88 | 89 | <% if @panacea.dig("pundit") -%> 90 | #### pundit 91 | * The gem was included in Gemfile. 92 | * `app/controllers/application_controller.rb` was modified. 93 | * The `rails generate pundit:install` generator was ran. 94 | <% end -%> 95 | 96 | <% if @panacea.dig("markdown") -%> 97 | #### redcarpet 98 | The gem was included in Gemfile. 99 | <% end -%> 100 | 101 | <% if @panacea.dig("webpack") -%> 102 | ### Assets 103 | 104 | #### webpacker 105 | * The gem was included in Gemfile. 106 | * **<%= @panacea.dig("webpack_type") %>** was the chosen framework. 107 | <% end -%> 108 | 109 | ### Background Job 110 | 111 | #### <%= @panacea.dig("background_job") %> 112 | <% if @panacea.dig("background_job") != "none" -%> 113 | * The gem was included in Gemfile. 114 | * `config/application.rb` was modified. 115 | <% end -%> 116 | <% if @panacea.dig("background_job") =~ /sidekiq|resque/ -%> 117 | * `config/routes.rb` were updated. 118 | <% end -%> 119 | <% if @panacea.dig("background_job") == "resque" -%> 120 | * `Rakefile` was updated 121 | <% end -%> 122 | 123 | ### Security 124 | 125 | #### brakeman 126 | The gem was included in Gemfile. 127 | 128 | ### Code Coverage 129 | 130 | #### simplecov 131 | * The gem was included in Gemfile. 132 | * The expected code coverage was set to: <%= @panacea.dig("expected_coverage") %> 133 | <% if @panacea.dig("test_suite") == "minitest" -%> 134 | * `test/support/simplecov.rb` was created. 135 | <% else -%> 136 | * `spec/support/simplecov.rb` was created. 137 | <% end -%> 138 | 139 | ### Linting 140 | 141 | #### rubocop-rails_config 142 | * The gem was included in Gemfile. 143 | * The `.rubocop.yml` file was created. 144 | 145 | ### Testing 146 | 147 | <% if @panacea.dig("factory_bot") -%> 148 | #### factory_bot 149 | * The gem was included in Gemfile. 150 | <% if @panacea.dig("test_suite") == "minitest" -%> 151 | * `test/test_helper.rb` was modified. 152 | <% else -%> 153 | * `spec/rails_helper.rb` was modified. 154 | <% end -%> 155 | <% end -%> 156 | 157 | <% if @panacea.dig("faker") -%> 158 | #### Faker 159 | * The gem was included in Gemfile. 160 | <% if @panacea.dig("test_suite") == "minitest" -%> 161 | * `test/test_helper.rb` was modified. 162 | <% else -%> 163 | * `spec/rails_helper.rb` was modified. 164 | <% end -%> 165 | <% end -%> 166 | 167 | #### <%= @panacea.dig("test_suite") %>-rails 168 | * The gem was included in Gemfile. 169 | <% if @panacea.dig("test_suite") == "minitest" -%> 170 | * `test/test_helper.rb` was created. 171 | <% else -%> 172 | * `spec/rails_helper.rb` was created. 173 | <% end -%> 174 | 175 | <% if @panacea.dig("headless_chrome") -%> 176 | #### Headless Chrome 177 | The System Tests were configured to run using a Headless Chrome Driver. 178 | <% end -%> 179 | 180 | <% if @panacea.dig("http_stubs") != "none" -%> 181 | #### <%= @panacea.dig("http_stubs") %> 182 | * The gem was included in Gemfile. 183 | <% if @panacea.dig("test_suite") == "minitest" -%> 184 | * `test/test_helper.rb` was modified. 185 | <% else -%> 186 | * `spec/rails_helper.rb` was modified. 187 | <% end -%> 188 | <% end -%> 189 | -------------------------------------------------------------------------------- /lib/panacea/rails/customizer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "tty/prompt" 4 | require "yaml" 5 | require_relative "stats" 6 | 7 | module Panacea # :nodoc: 8 | module Rails # :nodoc: 9 | ### 10 | # == Panacea::Rails::Customizer 11 | # 12 | # This class is in charge of asking the configuration questions. 13 | # It saves the answers on the .panacea file. 14 | class Customizer 15 | # rubocop:disable Style/FormatStringToken 16 | 17 | ### 18 | # ASCII art displayed at the start of the Panacea command 19 | WELCOME_MESSAGE = <<~MSG 20 | _____________________________ 21 | | ..... // | 22 | | _d^^^^^^^^^b_ // | 23 | | .d'' `` | 24 | | .p' /| 25 | | .d' .//| Welcome to Panacea! 26 | | .d' ----------- - `b. | You are about to boost your fresh 27 | | :: --------------------- :: | %{app_name} Rails App 28 | | :: --- P A N A C E A --- :: | 29 | | :: --------------------- :: | Full documentation here: https://panacea.website 30 | | `p. ------------------- .q' | Most of the defaults are false or disabled, 31 | | `p. ----------------- .q' | if you want to enable a feature please answer yes 32 | | `b. --------------- .d' | 33 | | `q.. -------- ..p' | 34 | | ^q........p^ | 35 | |____________''''_____________| 36 | 37 | MSG 38 | # rubocop:enable Style/FormatStringToken 39 | 40 | ### 41 | # This method receives the App's name and the passed arguments. 42 | # 43 | # It creates a new instance of Panacea::Rails::Customizer class and 44 | # executes the start instance method. 45 | def self.start(app_name, passed_args) 46 | new(app_name, passed_args).start 47 | end 48 | 49 | ### 50 | # A Hash with the questions loaded from config/questions.yaml file 51 | attr_reader :questions 52 | 53 | ### 54 | # A TTY::Prompt instance 55 | attr_reader :prompt 56 | 57 | ### 58 | # A Hash where each question's answer is stored 59 | attr_reader :answers 60 | 61 | ### 62 | # App's name 63 | attr_reader :app_name 64 | 65 | ### 66 | # A String with the arguments passed to the Panacea command 67 | attr_reader :passed_args 68 | 69 | ### 70 | # Panacea::Rails::Customizer initialize method 71 | # 72 | # A TTY::Prompt instance is used as default prompt. 73 | def initialize(app_name, passed_args, prompt: TTY::Prompt.new) 74 | @app_name = app_name 75 | @passed_args = passed_args 76 | @answers = {} 77 | @prompt = prompt 78 | end 79 | 80 | ### 81 | # This method shows the Welcome message. 82 | # Then, it ask the questions using the default prompt. 83 | # It also tracks the answers only if the end user agrees to. 84 | # At the end, it saves the answers to the .panacea file. 85 | def start 86 | welcome_message 87 | ask_questions 88 | save_answers 89 | end 90 | 91 | private 92 | 93 | def welcome_message 94 | message = format(WELCOME_MESSAGE, app_name: app_name.capitalize) 95 | prompt.say(message, color: :blue) 96 | end 97 | 98 | def ask_questions(questions = load_questions) 99 | questions.each do |key, question| 100 | disable_with = questions.dig(key, "disable_with") 101 | 102 | answer = ask_question(key, question) unless answers.dig(disable_with) 103 | subquestions = question.dig("subquestions") 104 | 105 | ask_questions(subquestions) if !subquestions.nil? && answer 106 | end 107 | end 108 | 109 | # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Security/Eval 110 | def ask_question(key, question) 111 | title = question.dig("title") 112 | default = question.dig("default") 113 | condition = question.dig("condition") 114 | 115 | unless condition.nil? 116 | return unless eval(condition) 117 | end 118 | 119 | answer = case question.dig("type") 120 | when "boolean" 121 | prompt.yes?(title) { |q| q.default(default) } 122 | when "range" 123 | prompt.ask(title, default: default) { |q| q.in(question.dig("range")) } 124 | when "text" 125 | prompt.ask(title, default: default) 126 | when "select" 127 | prompt.select(title, question.dig("options")) 128 | else 129 | raise StandardError, "Question type not supported." 130 | end 131 | 132 | update_answer(key, answer) 133 | end 134 | # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Security/Eval 135 | 136 | def update_answer(key, answer) 137 | answers[key] = answer 138 | end 139 | 140 | def load_questions 141 | questions_file = File.join(File.expand_path("../../../config", __dir__), "questions.yml") 142 | @questions ||= YAML.safe_load(File.read(questions_file)) 143 | end 144 | 145 | def save_answers 146 | root_dir = File.expand_path("../../../", __dir__) 147 | configurations_file = File.join(root_dir, ".panacea") 148 | 149 | File.open(configurations_file, "w") { |f| f.write(answers.to_yaml) } 150 | end 151 | 152 | def track_answers 153 | share_usage_info = answers.delete("share_usage_info") 154 | return unless share_usage_info 155 | 156 | params = answers.dup 157 | params[:ruby_version] = RUBY_VERSION 158 | params[:arguments] = passed_args 159 | Stats.track(params) 160 | end 161 | end 162 | end 163 | end 164 | -------------------------------------------------------------------------------- /config/questions.yml: -------------------------------------------------------------------------------- 1 | timezone: 2 | title: Please choose your app's default time zone 3 | type: select 4 | options: 5 | - UTC - UTC +00:00 6 | - Central Time (US & Canada) - UTC -06:00 7 | - International Date Line West - UTC -12:00 8 | - American Samoa - UTC -11:00 9 | - Midway Island - UTC -11:00 10 | - Hawaii - UTC -10:00 11 | - Alaska - UTC -09:00 12 | - Pacific Time (US & Canada) - UTC -08:00 13 | - Tijuana - UTC -08:00 14 | - Arizona - UTC -07:00 15 | - Chihuahua - UTC -07:00 16 | - Mazatlan - UTC -07:00 17 | - Mountain Time (US & Canada) - UTC -07:00 18 | - Central America - UTC -06:00 19 | - Guadalajara - UTC -06:00 20 | - Mexico City - UTC -06:00 21 | - Monterrey - UTC -06:00 22 | - Saskatchewan - UTC -06:00 23 | - Bogota - UTC -05:00 24 | - Eastern Time (US & Canada) - UTC -05:00 25 | - Indiana (East) - UTC -05:00 26 | - Lima - UTC -05:00 27 | - Quito - UTC -05:00 28 | - Atlantic Time (Canada) - UTC -04:00 29 | - Caracas - UTC -04:00 30 | - Georgetown - UTC -04:00 31 | - La Paz - UTC -04:00 32 | - Puerto Rico - UTC -04:00 33 | - Santiago - UTC -04:00 34 | - Newfoundland - UTC -03:30 35 | - Brasilia - UTC -03:00 36 | - Buenos Aires - UTC -03:00 37 | - Greenland - UTC -03:00 38 | - Montevideo - UTC -03:00 39 | - Mid-Atlantic - UTC -02:00 40 | - Azores - UTC -01:00 41 | - Cape Verde Is. - UTC -01:00 42 | - Casablanca - UTC +00:00 43 | - Dublin - UTC +00:00 44 | - Edinburgh - UTC +00:00 45 | - Lisbon - UTC +00:00 46 | - London - UTC +00:00 47 | - Monrovia - UTC +00:00 48 | - Amsterdam - UTC +01:00 49 | - Belgrade - UTC +01:00 50 | - Berlin - UTC +01:00 51 | - Bern - UTC +01:00 52 | - Bratislava - UTC +01:00 53 | - Brussels - UTC +01:00 54 | - Budapest - UTC +01:00 55 | - Copenhagen - UTC +01:00 56 | - Ljubljana - UTC +01:00 57 | - Madrid - UTC +01:00 58 | - Paris - UTC +01:00 59 | - Prague - UTC +01:00 60 | - Rome - UTC +01:00 61 | - Sarajevo - UTC +01:00 62 | - Skopje - UTC +01:00 63 | - Stockholm - UTC +01:00 64 | - Vienna - UTC +01:00 65 | - Warsaw - UTC +01:00 66 | - West Central Africa - UTC +01:00 67 | - Zagreb - UTC +01:00 68 | - Zurich - UTC +01:00 69 | - Athens - UTC +02:00 70 | - Bucharest - UTC +02:00 71 | - Cairo - UTC +02:00 72 | - Harare - UTC +02:00 73 | - Helsinki - UTC +02:00 74 | - Jerusalem - UTC +02:00 75 | - Kaliningrad - UTC +02:00 76 | - Kyiv - UTC +02:00 77 | - Pretoria - UTC +02:00 78 | - Riga - UTC +02:00 79 | - Sofia - UTC +02:00 80 | - Tallinn - UTC +02:00 81 | - Vilnius - UTC +02:00 82 | - Baghdad - UTC +03:00 83 | - Istanbul - UTC +03:00 84 | - Kuwait - UTC +03:00 85 | - Minsk - UTC +03:00 86 | - Moscow - UTC +03:00 87 | - Nairobi - UTC +03:00 88 | - Riyadh - UTC +03:00 89 | - St. Petersburg - UTC +03:00 90 | - Volgograd - UTC +03:00 91 | - Tehran - UTC +03:30 92 | - Abu Dhabi - UTC +04:00 93 | - Baku - UTC +04:00 94 | - Muscat - UTC +04:00 95 | - Samara - UTC +04:00 96 | - Tbilisi - UTC +04:00 97 | - Yerevan - UTC +04:00 98 | - Kabul - UTC +04:30 99 | - Ekaterinburg - UTC +05:00 100 | - Islamabad - UTC +05:00 101 | - Karachi - UTC +05:00 102 | - Tashkent - UTC +05:00 103 | - Chennai - UTC +05:30 104 | - Kolkata - UTC +05:30 105 | - Mumbai - UTC +05:30 106 | - New Delhi - UTC +05:30 107 | - Sri Jayawardenepura - UTC +05:30 108 | - Kathmandu - UTC +05:45 109 | - Almaty - UTC +06:00 110 | - Astana - UTC +06:00 111 | - Dhaka - UTC +06:00 112 | - Urumqi - UTC +06:00 113 | - Rangoon - UTC +06:30 114 | - Bangkok - UTC +07:00 115 | - Hanoi - UTC +07:00 116 | - Jakarta - UTC +07:00 117 | - Krasnoyarsk - UTC +07:00 118 | - Novosibirsk - UTC +07:00 119 | - Beijing - UTC +08:00 120 | - Chongqing - UTC +08:00 121 | - Hong Kong - UTC +08:00 122 | - Irkutsk - UTC +08:00 123 | - Kuala Lumpur - UTC +08:00 124 | - Perth - UTC +08:00 125 | - Singapore - UTC +08:00 126 | - Taipei - UTC +08:00 127 | - Ulaanbaatar - UTC +08:00 128 | - Osaka - UTC +09:00 129 | - Sapporo - UTC +09:00 130 | - Seoul - UTC +09:00 131 | - Tokyo - UTC +09:00 132 | - Yakutsk - UTC +09:00 133 | - Adelaide - UTC +09:30 134 | - Darwin- UTC +09:30 135 | - Brisbane - UTC +10:00 136 | - Canberra - UTC +10:00 137 | - Guam - UTC +10:00 138 | - Hobart - UTC +10:00 139 | - Melbourne - UTC +10:00 140 | - Port Moresby - UTC +10:00 141 | - Sydney - UTC +10:00 142 | - Vladivostok - UTC +10:00 143 | - Magadan - UTC +11:00 144 | - New Caledonia - UTC +11:00 145 | - Solomon Is. - UTC +11:00 146 | - Srednekolymsk - UTC +11:00 147 | - Auckland - UTC +12:00 148 | - Fiji - UTC +12:00 149 | - Kamchatka - UTC +12:00 150 | - Marshall Is. - UTC +12:00 151 | - Wellington - UTC +12:00 152 | - Chatham Is. - UTC +12:45 153 | - Nuku'alofa - UTC +13:00 154 | - Samoa - UTC +13:00 155 | - Tokelau Is. - UTC +13:00 156 | 157 | locale: 158 | title: Please choose your app's default locale 159 | type: select 160 | options: 161 | - English - en 162 | - Spanish - es 163 | - Arabic - ar 164 | - Chinese - zh 165 | - French - fr 166 | - German - de 167 | - Hindi - hi 168 | - Japanese - ja 169 | - Portuguese - pt 170 | - Russian - ru 171 | 172 | background_job: 173 | title: Which background jobs adapter do you want to use? 174 | type: select 175 | options: 176 | - none 177 | - sidekiq 178 | - resque 179 | - sucker_punch 180 | 181 | test_suite: 182 | title: Which tool do you want to use for your tests? 183 | type: select 184 | options: 185 | - minitest 186 | - rspec 187 | 188 | http_stubs: 189 | title: Which tool do you want to use to stub out HTTP requests? 190 | type: select 191 | options: 192 | - none 193 | - vcr 194 | - webmock 195 | 196 | expected_coverage: 197 | title: What should be the minimum coverage expected by Simplecov? 198 | default: 80 199 | type: range 200 | range: 0-100 201 | 202 | headless_chrome: 203 | title: Do you want to setup Headless Chrome as default driver for System Tests? 204 | default: false 205 | type: boolean 206 | 207 | factory_bot: 208 | title: Do you want to use FactoryBot in your tests? 209 | default: false 210 | type: boolean 211 | 212 | faker: 213 | title: Do you want to use Faker in your tests? 214 | default: false 215 | type: boolean 216 | 217 | dotenv: 218 | title: Do you want to use Dotenv to manage ENV variables in development? 219 | default: false 220 | type: boolean 221 | 222 | pg_search: 223 | title: Do you want to use pg_search gem to enable PostgreSQL's full text search? 224 | default: false 225 | type: boolean 226 | condition: "passed_args =~ /--database=postgresql/" 227 | 228 | devise: 229 | title: Do you want to use Devise to handle users in your app? 230 | default: false 231 | type: boolean 232 | subquestions: 233 | devise_model_name: 234 | title: What should be your Devise model name? 235 | default: user 236 | type: text 237 | devise_override_views: 238 | title: Do you want to add the Devise's views in your app? 239 | default: false 240 | type: boolean 241 | 242 | pundit: 243 | title: Do you want to use Pundit to handle authorizations? 244 | default: false 245 | type: boolean 246 | 247 | kaminari: 248 | title: Do you want to use Kaminari to handle pagination? 249 | default: false 250 | type: boolean 251 | 252 | oj: 253 | title: Do you want to use OJ to optimize JSON handling in your app? 254 | default: false 255 | type: boolean 256 | 257 | money_rails: 258 | title: Do you want to use Money Rails to handle money amounts in your app? 259 | default: false 260 | type: boolean 261 | 262 | markdown: 263 | title: Do you want to use Redcarpet to handle Markdown in your app? 264 | default: false 265 | type: boolean 266 | 267 | foreman: 268 | title: Do you want to use Foreman to manage your app's processes? 269 | default: false 270 | type: boolean 271 | 272 | awesome_print: 273 | title: Do you want to use AwesomePrint in your console sessions? 274 | default: false 275 | type: boolean 276 | 277 | githook: 278 | title: Do you want to use a Git hook to help you to improve your workflow? 279 | default: false 280 | type: boolean 281 | subquestions: 282 | githook_type: 283 | title: Which Git hook do you want to add? 284 | type: select 285 | options: 286 | - pre-commit 287 | - pre-push 288 | 289 | autocommit: 290 | title: Do you want to auto-generate the initial commit? 291 | default: false 292 | type: boolean 293 | subquestions: 294 | commit_msg: 295 | title: What should be the commit's message? 296 | default: Initialize Rails App powered with Panacea 297 | type: text 298 | -------------------------------------------------------------------------------- /lib/panacea/rails/generator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Panacea # :nodoc: 4 | module Rails # :nodoc: 5 | # rubocop:disable Metrics/ClassLength 6 | 7 | ### 8 | # == Panacea::Rails::Generator 9 | # 10 | # This class is in charge of grouping all the actions to be executed via Panacea::Rails::Template 11 | class Generator 12 | ### 13 | # The Rails::Generators::AppGenerator context 14 | attr_reader :app_generator 15 | 16 | ### 17 | # A String with Panacea's installation directory 18 | attr_reader :root_dir 19 | 20 | ### 21 | # The Panacea's Configuration Hash 22 | attr_reader :config 23 | 24 | ### 25 | # This methods receive the context of the Rails::Generators::AppGenerator 26 | # So it executes all methods against it. 27 | # 28 | # It also receives the Panacea's Config Hash and the Panacea's Gem Root dir 29 | # in order to update Rails::Generators::AppGenerator source paths. 30 | def initialize(app_generator, panacea_config, root_dir) 31 | @app_generator = app_generator 32 | @config = panacea_config 33 | @app_generator.instance_variable_set :@panacea, @config 34 | @root_dir = root_dir 35 | end 36 | 37 | ### 38 | # Send any unknown method to the Rails::Generators::AppGenerator context. 39 | # That context also knows Thor actions. 40 | def method_missing(method_name, *args, &block) 41 | super unless app_generator.respond_to?(method_name) 42 | app_generator.send(method_name, *args, &block) 43 | end 44 | 45 | ### 46 | # Whitelist of Rails::Generator::AppGenerator and Thor methods. 47 | # 48 | # Add here any new method to be used from Thor / Rails Generator 49 | def respond_to_missing?(method_name, include_private = false) 50 | %i[ 51 | generate rails_command template 52 | run git source_paths empty_directory append_to_file 53 | environment application say inject_into_class 54 | inject_into_file directory 55 | ].include?(method_name) || super 56 | end 57 | 58 | ### 59 | # All the methods passed via block are executed on the Rails::Generators::AppGenerator 60 | # after_bundle hook. 61 | def after_bundle_hook 62 | after_bundle do 63 | run "spring stop" 64 | yield self 65 | end 66 | end 67 | 68 | ### 69 | # Update Rails::Generators::AppGenerator source paths, making Panacea's Templates under 70 | # templates/ directory available. 71 | def update_source_paths 72 | source_paths.unshift(root_dir) 73 | end 74 | 75 | ### 76 | # Update the Gemfile 77 | def copy_gemfile 78 | template "templates/Gemfile.tt", "Gemfile", force: true 79 | end 80 | 81 | ### 82 | # Update the README.md 83 | def copy_readme 84 | template "templates/README.tt", "README.md", force: true 85 | end 86 | 87 | ### 88 | # Creates the PANACEA.md file 89 | def generate_panacea_document 90 | template "templates/PANACEA.tt", "PANACEA.md" 91 | end 92 | 93 | ### 94 | # Create .rubocop.yml in generated Rails app. 95 | def setup_rubocop 96 | template "templates/rubocop.tt", ".rubocop.yml" 97 | end 98 | 99 | ### 100 | # Setup the test suite (rspec or minitest). 101 | def setup_test_suite 102 | return unless config.dig("test_suite") == "rspec" 103 | 104 | generate "rspec:install" 105 | run "rm -r test" 106 | end 107 | 108 | ### 109 | # Setup Simplecov based on the chosen test suite. 110 | def setup_simplecov 111 | path = if config.dig("test_suite") == "minitest" 112 | "test/support" 113 | else 114 | "spec/support" 115 | end 116 | 117 | template "templates/simplecov.tt", "#{path}/simplecov.rb" 118 | append_to_file ".gitignore", "\n# Ignore Coverage files \n/coverage\n" 119 | end 120 | 121 | ### 122 | # Override test helper based on the chosen test suite. 123 | def override_test_helper 124 | if config.dig("test_suite") == "minitest" 125 | template "templates/minitest_test_helper.tt", "test/test_helper.rb", force: true 126 | else 127 | template "templates/rspec_test_helper.tt", "spec/rails_helper.rb", force: true 128 | end 129 | end 130 | 131 | ### 132 | # Setup Headless Chrome Driver based on chosen test suite. 133 | def override_application_system_test 134 | return unless config.dig("test_suite") == "minitest" 135 | template "templates/application_system_test.tt", "test/application_system_test_case.rb", force: true 136 | end 137 | 138 | ### 139 | # Setup OJ gem. 140 | def setup_oj 141 | template "templates/oj_initializer.tt", "config/initializers/oj.rb" 142 | end 143 | 144 | ### 145 | # Setup Dotenv gem. 146 | def setup_dotenv 147 | template "templates/dotenv.tt", ".env" 148 | append_to_file ".gitignore", "\n# Ignore .env file \n.env\n" 149 | end 150 | 151 | ### 152 | # Setup chosen Background Job gem. 153 | def setup_background_job 154 | background_job = config.dig("background_job") 155 | 156 | application nil do 157 | <<~CONFS 158 | # Default adapter queue 159 | config.active_job.queue_adapter = :#{background_job} 160 | 161 | CONFS 162 | end 163 | 164 | if background_job == "sidekiq" 165 | route "mount Sidekiq::Web => '/sidekiq'" 166 | route "require 'sidekiq/web'" 167 | elsif background_job == "resque" 168 | route "mount Resque::Server, at: '/jobs'" 169 | route "require 'resque/server'" 170 | 171 | template "templates/Rakefile.tt", "Rakefile", force: true 172 | end 173 | end 174 | 175 | ### 176 | # Setup the application's timezone. 177 | def setup_timezone 178 | timezone = config.dig("timezone").split("-").first.chomp(" ") 179 | 180 | application nil do 181 | <<~CONFS 182 | # Default timezone 183 | config.time_zone = "#{timezone}" 184 | 185 | CONFS 186 | end 187 | end 188 | 189 | ### 190 | # Setup the application's locale. 191 | def setup_default_locale 192 | locale = config.dig("locale").split("- ").last 193 | 194 | application nil do 195 | <<~CONFS 196 | # Default i18n locale 197 | config.i18n.default_locale = :#{locale} 198 | 199 | CONFS 200 | end 201 | 202 | template "templates/default_locale.tt", "config/locales/#{locale}.yml" if locale != "en" 203 | end 204 | 205 | ### 206 | # Create database 207 | def create_database 208 | rails_command "db:create" 209 | end 210 | 211 | ### 212 | # Setup Bullet gem 213 | def setup_bullet 214 | environment nil, env: "development" do 215 | <<~CONFS 216 | # Settings for Bullet gem 217 | config.after_initialize do 218 | Bullet.enable = true 219 | Bullet.alert = true 220 | Bullet.bullet_logger = true 221 | Bullet.console = true 222 | Bullet.rails_logger = true 223 | Bullet.add_footer = true 224 | end 225 | 226 | CONFS 227 | end 228 | end 229 | 230 | ### 231 | # Setup letter_opener gem. 232 | def setup_letter_opener 233 | environment nil, env: "development" do 234 | <<~CONFS 235 | # Settings for Letter Opener 236 | config.action_mailer.delivery_method = :letter_opener 237 | config.action_mailer.perform_deliveries = true 238 | config.action_mailer.default_url_options = { host: "localhost", port: 3000 } 239 | 240 | CONFS 241 | end 242 | end 243 | 244 | ### 245 | # Setup Devise gem. 246 | def setup_devise 247 | model_name = config.dig("devise_model_name").downcase 248 | plural_model_name = model_name.downcase.pluralize 249 | locale = config.dig("locale").split("- ").last 250 | 251 | generate "devise:install" 252 | generate "devise", model_name 253 | generate "devise:views" if config.dig("devise_override_views") 254 | 255 | rails_command "db:migrate" 256 | end 257 | 258 | ### 259 | # Setup money_rails gem. 260 | def setup_money_rails 261 | generate "money_rails:initializer" 262 | end 263 | 264 | ### 265 | # Setup Kaminari gem. 266 | def setup_kaminari 267 | generate "kaminari:config" 268 | end 269 | 270 | ### 271 | # Setup webpacker gem. 272 | def setup_webpack 273 | rails_command "webpacker:install" 274 | rails_command "webpacker:install:#{config.dig('webpack_type')}" if config.dig("webpack_type") != "none" 275 | end 276 | 277 | ### 278 | # Setup foreman gem 279 | def setup_foreman 280 | run "gem install foreman" unless system("gem list -i foreman") 281 | 282 | template "templates/Procfile.tt", "Procfile" 283 | end 284 | 285 | ### 286 | # Setup Pundit gem 287 | def setup_pundit 288 | generate "pundit:install" 289 | inject_into_class "app/controllers/application_controller.rb", "ApplicationController", " include Pundit\n" 290 | end 291 | 292 | ### 293 | # Creates chosen Git hook. 294 | def setup_githook 295 | hook_file = ".git/hooks/#{config.dig('githook_type')}" 296 | template "templates/githook.tt", hook_file 297 | run "chmod ug+x #{hook_file}" 298 | end 299 | 300 | ### 301 | # This needs to be run before commiting. 302 | # 303 | # Fix existing application's style offenses. 304 | def fix_offenses! 305 | run "rubocop -a --format=simple" 306 | end 307 | 308 | ### 309 | # Commit only if end users want to. 310 | def commit! 311 | git add: "." 312 | git commit: "-m '#{config.dig('commit_msg')}'" 313 | end 314 | 315 | ### 316 | # Display good bye message. 317 | def bye_message 318 | message = "Panacea's work is done, enjoy!" 319 | say "\n\n\e[34m#{message}\e[0m" 320 | end 321 | end 322 | # rubocop:enable Metrics/ClassLength 323 | end 324 | end 325 | --------------------------------------------------------------------------------