【Rails】俺のための初期設定 + Gem導入


rails newをするときに、随時設定をしたりGemを入れるのが面倒だったので、自分用にcloneするだけでいいリポジトリを作りました。
https://github.com/aiandrox/rails_app

ついでに作業をざっくりまとめました。

DockerやCircleCI用の設定はしていません。
実際に導入する際にはGitHubでバージョン指定や手順などを確認してください。

バージョン

  • Ruby 2.7.1
  • Rails 6.0.3.3
  • yarn 1.22.5

初期コマンドはrails new rails_app --skip-test-unit --database=mysqlです。

.gitignore/vendor/bundleを追加

/vendor/bundle

を追加する。

日本語化・時刻の設定

config/application.rb
...
module RailsApp
  class Application < Rails::Application
    ...
    # 言語・タイムゾーンを日本に設定
    config.i18n.default_locale = :ja
    config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]
    config.time_zone = 'Tokyo'
    config.active_record.default_timezone = :local
  end
end
Gemfile
gem 'rails-i18n'
gem 'enum_help'

ついでにモデル用の日本語データも準備しておく。
en.ymlは不要なら消してしまってもいい。

config/locales/model.ja.yml
ja:
  activerecord:
    # User.model_name.human
    models:
      user: ユーザー
    # User.human_attributes_name(:name)
    attributes:
      id: ID
      created_at: 作成日時
      updated_at: 更新日時
      user:
        name: 名前

  enums:
    user:
      role:
        admin: 管理者
        general: 一般
        guest: ゲスト

リンク

デバッグ用のGem

binding.pryを使いたいので、byebugを削除してpryを入れる。

Gemfile
group :development, :test do
  # byebugは不要なら削除してよい
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  # 以下3行を追加
  gem 'bullet'
  gem 'pry-byebug'
  gem 'pry-rails'
end

group :development do
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '~> 3.2'
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
  # 以下2行を追加
  gem 'better_errors'
  gem 'binding_of_caller'
end
config/environments/development.rb
Rails.application.configure do
  ...
  # bulletの設定
  config.after_initialize do
    Bullet.enable = true
    Bullet.bullet_logger = true
    Bullet.console = true
    Bullet.rails_logger = true
    Bullet.add_footer = true
  end
end

better_errorsとbinding_of_callerによって、エラー画面がデフォルトのものから詳細なものに変えて、エラー画面でコンソール操作ができる。

pry-byebugとpry-railsによって、コードの途中にbinding.pryを書いておくと、そこで動作を止めてデバッグができる。

リンク

フォーマッター

Rails Best PracticesとRubocopの導入

Gemfile
group :development do
  # 以下4行を追加
  gem 'rails_best_practices'
  gem 'rubocop', require: false
  gem 'rubocop-performance', require: false
  gem 'rubocop-rails', require: false
  gem 'rubocop-rspec', require: false
end

.rubocop.ymlでrubocop-performanceとrubocop-railsを読み込む。
また、カスタマイズする場合は.rubocop.ymlに記述を追加することでオーバーライドできる。

.rubocop.yml
# This file overrides https://github.com/bbatsov/rubocop/blob/master/config/default.yml

require:
  - rubocop-performance
  - rubocop-rails
  - rubocop-rspec

AllCops:
  Exclude:
    - 'tmp/**/*'
    - 'vendor/**/*'
    - 'db/**/*'
    - 'bin/**/*'
    - 'spec/**/*'
    - 'node_modules/**/*'
  DisplayCopNames: true

# 日本語でのコメントを許可
Style/AsciiComments:
  Enabled: false

# Admin::BaseControllerのような書き方を許可
Style/ClassAndModuleChildren:
  Enabled: false

# frozen_string_literal: trueはなくていい
Style/FrozenStringLiteralComment:
  Enabled: false

# each_key, value, transform_keys, valueを使用する
Style/HashEachMethods:
  Enabled: true
Style/HashTransformKeys:
  Enabled: true
Style/HashTransformValues:
  Enabled: true

# クラスにコメントを残さない
Style/Documentation:
  Enabled: false

# コントローラのメソッド名に「set_」「get_」を許可
Naming/AccessorMethodName:
  Exclude:
    - "app/controllers/**/*"

# 一行の長さは100字まで。コメントは制限しない
Metrics/LineLength:
  Max: 100
  IgnoredPatterns: ['\A#']
  Exclude:
    - "db/migrate/*.rb"

# メソッドの行数を20行までにする
Metrics/MethodLength:
  CountComments: false
  Max: 20

# ABC sizeは緩めにする
Metrics/AbcSize:
  Max: 30 # default 15

ついでにbundle exec rubocop -aで自動整形しておく。

ESlintの導入

$ yarn add -D eslint eslint-loader
config/webpack/loaders/eslint.js
module.exports = {
  test: /\.js$/,
  loader: "eslint-loader",
  enforce: "pre",
  options: {},
};
config/webpack/environment.js
const { environment } = require("@rails/webpacker");
const eslint = require("./loaders/eslint");  // 追加

environment.loaders.append("eslint", eslint);  // 追加
module.exports = environment;
$ yarn run eslint --init
✔ How would you like to use ESLint? · problems
✔ What type of modules does your project use? · commonjs
✔ Which framework does your project use? · none
✔ Does your project use TypeScript? · No / Yes
✔ Where does your code run? · browser
✔ What format do you want your config file to be in? · JSON
Successfully created .eslintrc.json file in /Users/k_end/study/rails_app
✨  Done in 92.50s.

なんとなく読みながらオプションを選んでいくと.eslintrc.jsonが生成される。

.eslintrc.json
{
    "env": {
        "browser": true,
        "commonjs": true,
        "es2021": true
    },
    "extends": "eslint:recommended",
    "parserOptions": {
        "ecmaVersion": 12
    },
    "rules": {
    }
}

これでWebpackerのコンパイル時にESlintが走るようになる。bin/webpack-dev-serverで確認。
yarn run eslint --fixでESlint自動修正。

リンク

RSpec, FactoryBot

test/ディレクトリは削除する。

Gemfile
group :development, :test do
  # 以下2行を追加
  gem "rspec-rails"
  gem "factory_bot_rails"
end

bundle exec rails generate rspec:installでファイルが生成される。

spec/rails_helper.rb
# This file is copied to spec/ when you run 'rails generate rspec:install'
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../config/environment', __dir__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'factory_bot'  # 追加
require 'rspec/rails'

Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }  # コメントアウト解除

begin
  ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
  puts e.to_s.strip
  exit 1
end
RSpec.configure do |config|
  config.use_transactional_fixtures = true
  config.infer_spec_type_from_file_location!

  config.include FactoryBot::Syntax::Methods  # 追加
end

spec/spec_helper.rbconfig.filter_run_when_matching :focusをコメントアウト解除する。

.rspec
--require rails_helper
--format documentation
  • rails_helperでspec_helperを読み込んでいる
  • 素のRubyファイルをテストすることはまずない

という点から、--require rails_helperに変更しておく。
これにより、それぞれのspecファイルでrequire rails_helperを付けなくてもよくなる。

config/application.rb
module RailsApp
  class Application < Rails::Application
    ...
    # generateで作成するファイルの制限
    config.generators do |g|
      g.assets false
      g.skip_routes true
      g.helper false
      g.test_framework :rspec,
                       view_specs: false,
                       helper_specs: false,
                       routing_specs: false,
                       controller_specs: false,
                       request_specs: true,
                       model_spec: true,
                       fixtures: true
      g.fixture_replacement :factory_bot, dir: 'spec/factories'
    end
  end
end

ここのtrue or falseは必要に応じて変える。

リンク

seed_fu

Gemfile
gem 'seed-fu'

seed-fuが読み込むのはdb/fixtures/配下のファイルなので、fixtures/ディレクトリを作成しておく。

リンク

foreman

Gemfile
group :development do
  gem 'foreman'  # 追加
end
Procfile
rails: rails s --port=3000
webpacker: bin/webpack-dev-server
$ bundle exec foreman check
valid procfile detected (rails, webpacker)
$ bundle exec foreman start

リンク

slim

Gemfile
gem 'slim-rails'
gem 'html2slim'

bundle exec erb2slim app/views app/views -dで置換する。ERB 2 SLIMを使うのもあり。
置換したらhtml2slimは不要なので削除する。

config/application.rb
module RailsApp
  class Application < Rails::Application
    ...
    config.generators do |g|
      ...
      g.template_engine = :slim  # 追加
    end
  end
end

リンク

annotate

Gemfile
group :development do
  gem 'annotate'
end

bundle exec rails g annotate:installを実行する。

lib/tasks/auto_annotate_models.rake
# NOTE: only doing this in development as some production environments (Heroku)
# NOTE: are sensitive to local FS writes, and besides -- it's just not proper
# NOTE: to have a dev-mode tool do its thing in production.
if Rails.env.development?
  require 'annotate'
  task set_annotation_options: :environment do
    # You can override any of these by setting an environment variable of the
    # same name.
    Annotate.set_defaults(
      'active_admin' => 'false',
      'additional_file_patterns' => [],
      'routes' => 'false',
      'models' => 'true',
      'position_in_routes' => 'before',
      'position_in_class' => 'before',
      'position_in_test' => 'before',
      'position_in_fixture' => 'before',
      'position_in_factory' => 'before',
      'position_in_serializer' => 'before',
      'show_foreign_keys' => 'true',
      'show_complete_foreign_keys' => 'false',
      'show_indexes' => 'true',
      'simple_indexes' => 'false',
      'model_dir' => 'app/models',
      'root_dir' => '',
      'include_version' => 'false',
      'require' => '',
      'exclude_tests' => 'false',
      'exclude_fixtures' => 'false',
      'exclude_factories' => 'false',
      'exclude_serializers' => 'false',
      'exclude_scaffolds' => 'true',
      'exclude_controllers' => 'true',
      'exclude_helpers' => 'true',
      'exclude_sti_subclasses' => 'false',
      'ignore_model_sub_dir' => 'false',
      'ignore_columns' => nil,
      'ignore_routes' => nil,
      'ignore_unknown_models' => 'false',
      'hide_limit_column_types' => 'integer,bigint,boolean',
      'hide_default_column_types' => 'json,jsonb,hstore',
      'skip_on_db_migrate' => 'false',
      'format_bare' => 'true',
      'format_rdoc' => 'false',
      'format_yard' => 'false',
      'format_markdown' => 'false',
      'sort' => 'false',
      'force' => 'false',
      'frozen' => 'false',
      'classified_sort' => 'true',
      'trace' => 'false',
      'wrapper_open' => nil,
      'wrapper_close' => nil,
      'with_comment' => 'true'
    )
  end

  Annotate.load_tasks
end

リンク