Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
8
Help us understand the problem. What are the problem?

Rails新規開発でまずやっている設定とか参考記事を紹介する

motivation

yoshixjと申します。
最近、Golangを書き始めてしまいました。
Railsを浮気しそうです。
2020年12月個人的Railsで新規のAPIを開発をするとしたら、こんな感じでやっていくという流れをざっくり書いていきます。
筆者の経験はざっくり、Railsの経験は3-4年くらい、スタートアップでの新規開発を経験多め、運用経験すくなめです。

内容はRailsをとりあえずかけるようになった人がこの辺やっておくとよいとbetterかもてきな感じです。
この記事は、この数年間経験が浅い私が、ひたすらググり、実装してよかったものをまとめたような記事です。
長く冗長的な部分もあるかなと思いますが、おてやわらかに。

agenda

  • git repository 作成
  • Gemfileを設置
  • dockerの設定
  • rails new
  • setting rubocop
  • install rspec
  • install factry bot
  • timezoneの設定
  • CORSの設定
  • エラーレスポンスの統一
  • ガイドラインの統一
  • やったことないけど、今後新規やるとしたらやるかなぁと思うこと

git repository 作成

今回は、githubですでにレポジトリがある状態からスタートします。
railsを作成する際、rails new で自動的にgitが設定されますが、rails newをする前にgitを設定が設定されているという状態です。

$ git pull repository_name
$ cd repository_name
$ ls -al

drwxr-xr-x  14 yoshikimasubuchi  staff       448  5  2 11:08 .git

dockerの設定

Dockerの設定していきます。
rails new から docker-compose でやっていきます。 この辺を参考にしています。
rails newの先輩オススメ手順

どういうディレクトリ構成にするかはチームに合わせます。

僕が経験したなかだと、
rails単体でdocker-composeを切る場合はこんな感じ。

--repository_name
  ├── Gemfile
  ├── docker-compose.yml
  ├── app
  ├── docker
      ├── api
           ├── Dockerfile

Dockefileの中身はこんなかんじ

FROM ruby:2.7.1
ENV LANG C.UTF-8
ENV TZ Asia/Tokyo

RUN apt-get update -qq && apt-get install -y \
    build-essential \
    nodejs \
 && rm -rf /var/lib/apt/lists/*

ENV ENTRYKIT_VERSION 0.4.0
RUN wget https://github.com/progrium/entrykit/releases/download/v${ENTRYKIT_VERSION}/entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
  && tar -xvzf entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
  && rm entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz \
  && mv entrykit /bin/entrykit \
  && chmod +x /bin/entrykit \
  && entrykit --symlink

WORKDIR /usr/src/app
COPY . /usr/src/app

RUN gem install bundler

ENTRYPOINT [ \
    "prehook", "ruby -v", "--", \
    "prehook", "bundle install -j3 --quiet", "--"]
docker-compose.yml
version: '3'
services:
  db:
    image: postgres:11.3
    ports:
      - "5432:5432"
    volumes:
      - db:/var/lib/postgresql/data:z
  api:
    build:
      context: .
      dockerfile: ./docker/api
    volumes:
      - .:/usr/src/app
    command: /bin/sh -c "rm -f /usr/src/app/tmp/pids/server.pid && bundle exec rails s -p 8080 -b '0.0.0.0'"
    ports:
      - "8080:8080"
    depends_on:
      - db
    env_file:
      - rails.env
    environment:
      - DB_USER=postgres
      - DB_PASS=
      - DB_HOST=db
      - DB_PORT=5432
      # for ruby 2.6 alart https://k-koh.hatenablog.com/entry/2020/02/07/145957
      # - RUBYOPT='-W:no-deprecated -W:no-experimental'
    tty: true
    stdin_open: true

volumes:
   db:

Gemfileを設置 railsをinstall

RailsをinstallするためのGemfileを設定します。

$ docker-compose build
$ docker-compose run api bundle init

をすると、Gemfileが設置されます。
設置されたGemfileに gem 'rails' を追記します。

# frozen_string_literal: true

source 'https://rubygems.org'

git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }

gem 'rails'

追記したら、bundle します。

$ docker-compose run api bundle install --path vendor/bundle

railsがinstallされました!

docker環境だとあまり関係ないかもしれませんが、bundleの設定も付け加えておきます。

---
BUNDLE_PATH: vendor/bundle
BUNDLE_JOBS: 4
BUNDLE_DISABLE_SHARED_GEMS: '1'

rails new

ようやくrails newをします。
今回はAPIモードでrails new を行います。
今回はテストフレームワークにrspecを使用するので、 --skip-test します。
以下以外でも active-strage などもskipできるますが、もしかしたら使うかもしれないのでskipしません。

$ docker-compose run api bundle exec rails new . --api -d=postgresql --skip-git --skip-test --api --skip-bundle

rails new でアプリができたら、Gemfileを記述していきます。
developmentにはとりあえず最初に入れておこうってやつをいれています。

# frozen_string_literal: true

source 'https://rubygems.org'

git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }

ruby '2.7.1'

gem 'rails'

gem 'puma', '~> 3.11'
gem 'bootsnap', '>= 1.4.2', require: false
gem 'rack-cors'
gem 'pg'

group :development, :test do
  gem 'brakeman' # for security
  gem 'dotenv-rails'
  gem 'guard-rspec', require: false
  gem 'rspec-benchmark'
  gem 'rspec-rails'
  gem 'rspec_junit_formatter'
  gem 'rubocop'
  gem 'factory_bot_rails'
  gem 'faker'
  gem 'faker-japanese'
  gem 'pry-rails'
  gem 'pry-byebug'
end

group :development do
  gem 'listen', '>= 3.0.5', '< 3.2'
  gem 'hirb'
end
$ docker-compose run api bundle

DBの設定も書いていきます。

database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: <%= ENV['DB_USER'] %>
  password: <%= ENV['DB_PASS'] %>
  host: <%= ENV.fetch('DB_HOST') { 'localhost' } %>
  port: <%= ENV.fetch('DB_PORT') { 5432 } %>

development:
  <<: *default
  database: earlybird-api_development

test:
  <<: *default
  database: earlybird-api_test
production:
  <<: *default
  database: earlybird-api_production
  username: earlybird-api
  password: <%= ENV['earlybird-api_DATABASE_PASSWORD'] %>

defaultの部分の設定は docker-compose.ymlの記述にあわせます。

$ docker-compose run api bundle exec rails db:create

これで、railsでAPIを作れる状態になりました!

rubocopの設定

チーム開発でrubocopをちゃんと設定することは重要です。
会社でrubyのプロジェクトがなかく、rubocopの設定に決まりがない場合は、こんな感じで設定しておきます。

rubocop-rails

rubocop.yml
require: rubocop-rails

Rails:
  Enabled: true

AllCops:
  TargetRubyVersion: 2.7.3
  TargetRailsVersion: 6.0.2
  Exclude:
    - bin/*
    - db/**/*
    - vendor/**/*
    - test/**/*
    - config/**/*
    - lib/**/*
    - Gemfile
    - db/schema.rb

rspecのinstall

rspecをinstallします。
最初にinstallをし忘れると、modelのspecやら、controllerのspecやら、自動でつくられないのであとあと面倒になります。

$ docker-compose run api bundle exec rails generate rspec:install

factry botのinstall

factory botもinstallし忘れると、modelを作成と同時にfileが作られないので、あとあと面倒になります。

spec/rails_helper.rb
RSpec.configure do |config|
  config.fixture_path = "#{::Rails.root}/spec/fixtures"
  config.include FactoryBot::Syntax::Methods # add

timezoneの設定

  • ruby プロセスのタイムゾーン
  • config.time_zone
  • config.active_record.default_timezone
  • DBサーバ(postgres等)のタイムゾーン

の設定をします。RailsはTimezoneがややっこしいので最初にそれぞれの設定をしてしまいます。
参考: Railsと周辺のTimeZone設定を整理する (active_record.default_timezoneの罠)

Railsタイムゾーンまとめ

今回は日本でのみつかうプロダクトを想定しています。
海外展開もある場合は、一旦全部 UTCで揃えるようにするとよいと思います。

ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
config/application.rb
config.time_zone = 'Tokyo'
config.active_record.default_timezone = :local
set time zone 'Asia/Tokyo';

-- postgresql.conf に timezone='Japan' も記載

コーディングで時間を扱う際には、TimeWithZoneを使用するようにします。

CORSの設定

CROSの設定をします。
これをしないと、cross origin error でapiをフロントアプリケーションから たたけ無いと思います。

require_relative 'boot'

require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
require "active_job/railtie"
require "active_record/railtie"
require "active_storage/engine"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_view/railtie"
require "action_cable/engine"
# require "sprockets/railtie"
# require "rails/test_unit/railtie"

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module App
  class Application < Rails::Application
    config.time_zone = 'Tokyo'
    config.active_record.default_timezone = :local
    config.active_record.time_zone_aware_types = [:datetime, :time]

    config.i18n.default_locale = :ja
    config.api_only = true
    # 以下を追加
    config.middleware.use Rack::Cors do
      allow do
        origins ENV['CORS_ALLOWED_ORIGINS']
        resource "*",
                 headers: :any,
                 methods: [:get, :post, :put, :patch, :delete],
                 expose: []
      end
    end
  end
end

このあたりで、動くRails アプリの設定ができたと思います。
以下からは、運用を考えて設定を追記していきます。

エラーレスポンスの統一

エラーのレスポンスを統一するための設定をします。
私は以前まで、ApplicationControllerでレスポンスはresuceしていましたが、どうやらRailsのexception_app というのを使うのが良さそうです。
これは、実際に運用したことは無いのですが、たぶん以下のように設定すると思います。

参考: Railsアプリの例外ハンドリングとエラーページの表示についてまとめてみた

# app/controllers/errors_controller.rb
class ErrorsController < ActionController::Base
  rescue_from StandardError, with: :render_500
  rescue_from ActiveRecord::RecordNotFound, with: :render_404
  rescue_from ActiveRecord::RecordInvalid, with: :render_400

  def show
    raise env['action_dispatch.exception']
  end

  def render_400(err)
    render_error(err, 400)
  end

  def render_404(err)
    render_error(err, 404)
  end

  def render_500(err)
    render_error(err, 404)
  end

  def render_error(err, status)
      render json: ErrorSerializer.new(errr), status: status
    end
  end
end

# config/initializers/exceptions_app.rb
Rails.application.configure do
  config.exceptions_app = ErrorsController.action(:show)
end

ガイドラインの統一

APIを開発するにあたって、ロジックをどのように、どこに書いていくかなどのガイドラインを最初に決めます。
基本的な思想は、リソース思考です。リソース=Model にロジックは詰めていきます。
思い当たるのをいかにリストアップし、参考のURLをつらつらとのせていきます。

Validationは原則いれる。きつく→ゆるくはできるが、ゆるく→きつくはできない

制約はきつくしておいたほうがよいです。

同じ理由で、Migrationの外部キー制約は基本いれる。

制約はきつくします。

# frozen_string_literal: true

class CreateShopPhotos < ActiveRecord::Migration[5.2]
  def change
    create_table :shop_photos do |t|
      t.references :shop, index: true, foreign_key: true, null: false
      t.string :image_url, null: false
      t.timestamps
    end
  end
end
こまったら、FatModelにとりあえずしておく

Modelにロジックを書いておけば、unit testが書きやすいので、あとから手を入れるときも安全に手をいれられます。

whereなどを使う検索ロジックは、scopeで定義する。インスタンスメソッド, クラスメソッドでは定義しない。

scopeで定義すれば、ClassからもInstanceからもつかえますが、 インスタンスメソッド、クラスメソッドで検索ロジックを定義していまうとどちらかでしかつかえません。

controller には基本 defaultの index, show, create, update, delete しか書かない

他の人が見た時に、謎のアクションがないようにできるだけ注意します。

DHH流のルーティングで得られるメリットと、取り入れる上でのポイント - KitchHike Tech Blog
DHHはどのようにRailsのコントローラを書くのか /| POSTD

APIのレスポンスには原則Serialzierを使う

マイクロサービス指向 Rails API 開発ガイド/building rails api on microservices - Speaker Deck
Rails アプリに RESTful API のレールを敷いて生産性が大きく上がった話 | Wantedly Engineer Blog
Rails に RESTful API のレールを敷く - Qiita

Sidekiqの引数に注意する

引数は増やしたくなったように、最後にhash型のpayloadをとりあえずいれておきます。

Sidekiq アンチパターン: 序

やったことないけど、今後新規やるとしたらやるかなぁと思うこと

  • committee×OpenAPI をいれて、request specでレスポンスのテストもしたいです

その他

Ruby は書き方が柔軟なので、書き方にまよったら、とりあえずcookpad さんのスタイルガイドを参考にします。
cookpadさんのスタイルガイド

終わりに

一人で開発することが多かったので、webでひたすらdigって自分の経験を伸ばしてきました。
今回の記事はそのなかで影響を受けている記事の総まとめのような記事です。
もし、ここがおかしいなどあればコメントくださると幸いです。
2020年もおつかRails!

参考記事一覧

rubocop-rails
Railsと周辺のTimeZone設定を整理する (active_record.default_timezoneの罠)
Railsタイムゾーンまとめ
Railsアプリの例外ハンドリングとエラーページの表示についてまとめてみた
DHH流のルーティングで得られるメリットと、取り入れる上でのポイント - KitchHike Tech Blog
DHHはどのようにRailsのコントローラを書くのか /| POSTD
マイクロサービス指向 Rails API 開発ガイド/building rails api on microservices - Speaker Deck
Rails アプリに RESTful API のレールを敷いて生産性が大きく上がった話 | Wantedly Engineer Blog
Rails に RESTful API のレールを敷く - Qiita
cookpadさんのスタイルガイド
Sidekiq アンチパターン: 序
運用に耐えるRailsによるWebアプリケーションの作り方
Fat Modelの倒し方
serodriguez68/rails-antipatterns
rspec style guide

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
8
Help us understand the problem. What are the problem?