Open9

Ruby on Rails 個人的メモ

ykttdnykttdn

APIアプリケーションを新規作成するときのオプション

rails --version
# Rails 7.2.1.1
rails new . \
    --database=mysql \
    --skip-docker \
    --skip-action-mailer \
    --skip-action-mailbox \
    --skip-action-text \
    --skip-active-job \
    --skip-active-storage \
    --skip-action-cable \
    --skip-hotwire \
    --skip-jbuilder \
    --skip-test \
    --skip-system-test \
    --skip-dev-gems \
    --skip-rubocop \
    --skip-ci \
    --api

--api--skip-asset-pipeline--skip-javascriptを指定したことと同じになるほか、APIアプリケーション用の設定を行ってくれる。
https://railsguides.jp/api_app.html#アプリケーションを新規作成する
全てのオプションはrails new --helpで確認できる。

ykttdnykttdn

config/environments/production.rbにRuboCopをかけたときの話

config/environments/production.rb
require "active_support/core_ext/integer/time"

Rails.application.configure do
  # 略
  # Log to STDOUT by default
  config.logger = ActiveSupport::Logger.new(STDOUT)
    .tap  { |logger| logger.formatter = ::Logger::Formatter.new }
    .then { |logger| ActiveSupport::TaggedLogging.new(logger) }
  # 略
end

にRuboCopをかけたら次のようになった

config/environments/production.rb
require "active_support/core_ext/integer/time"

Rails.application.configure do
  # 略
  # Log to STDOUT by default
  config.logger = ActiveSupport::Logger.new($stdout)
                                       .tap  { |logger| logger.formatter = Logger::Formatter.new }
                                       .then { |logger| ActiveSupport::TaggedLogging.new(logger) }
  # 略
end

適用されたCopは

ykttdnykttdn

コントローラーの名前空間とルーティング

GET /api/todosに対するコントローラーは

app/controllers/api/todos_controller.rb
module Api
  class TodosController < ApplicationController
    def index
      todos = Todo.all

      render json: todos
    end
  end
end
config/routes.rb
Rails.application.routes.draw do
  namespace :api do
    resources :todos, only: :index
  end
end

コントローラーのファイルはrails g controller api/todosで作成できる。rails g controllerのヘルプには次のように書いてある。

To create a controller within a module, specify the controller name as a path like 'parent_module/controller_name'.

ルーティングのグループ化はRails Guideに詳しく書いてある。
https://railsguides.jp/routing.html#コントローラの名前空間とルーティング

ykttdnykttdn

ActiveRecordのsavesave!の違い

どちらもDBに保存するメソッドだが、save!はバリデーションに失敗したときActiveRecord::RecordInvalidの例外を発生させる。またbefore_saveなどのコールバックが:abortをスローしたときActiveRecord::RecordNotSavedの例外を発生させる。saveは例外を発生せずにfalseを返す。成功したときはどちらもtrueを返す。

createcreate!updateupdate!destroydestroy!の違いも例外を発生させるかどうかの違いである。
https://api.rubyonrails.org/v7.2/classes/ActiveRecord/Persistence/ClassMethods.html
https://api.rubyonrails.org/classes/ActiveRecord/Persistence.html
https://railsguides.jp/active_record_basics.html#バリデーション(検証)

ykttdnykttdn

テーブルのカラムにNOT NULL制約を追加するときのマイグレーション

db/migrate/yyyymmddHHMMSS_add_not_null_constraint_to_todo_title.rb
class AddNotNullConstraintToTodoTitle < ActiveRecord::Migration[7.2]
  def change
    change_column_null(:todos, :title, false)
  end
end

このファイルを追加し、rails db:migrateを実行すればよい。rails db:rollbackするとchange_column_null(:todos, :title, true)が実行され、NOT NULL制約が外れる。
https://api.rubyonrails.org/v7.2/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-change_column_null

ykttdnykttdn

HTTPレスポンスをパースするメソッド

RSpecで次のようなテストコードを書いた。

  expect(JSON.parse(response.body)).to include('text')

これにRuboCopをかけたらRails/ResponseParsedBodyの警告が出た。
https://www.rubydoc.info/gems/rubocop-rails/RuboCop/Cop/Rails/ResponseParsedBody

# bad
JSON.parse(response.body)

# bad
Nokogiri::HTML.parse(response.body)

# bad
Nokogiri::HTML5.parse(response.body)

# good
response.parsed_body

parsed_bodyメソッドはレスポンスのMIMEタイプに応じてレスポンスボディーをパースする。
https://api.rubyonrails.org/v7.2/classes/ActionDispatch/TestResponse.html#method-i-parsed_body

ykttdnykttdn

RSpec導入手順

rspec-railsを入れる

https://github.com/rspec/rspec-rails?tab=readme-ov-file#installation
Gemfileに以下を追記し、bundle installする。

Gemfile
group :development, :test do
  gem 'rspec-rails', '~> 7.0.0'
end

その後rails generate rspec:installを実行すると、.rspec, spec/rails_helper.rb, spec/spec_helper.rbが生成される。

factory_botを入れる

https://github.com/thoughtbot/factory_bot/blob/main/GETTING_STARTED.md#setup
Gemfileに以下を追記し、bundle installする。

Gemfile
group :development, :test do
  gem 'factory_bot_rails'
end

続いてspec/support/factory_bot.rbを作成して、以下の内容を記述し、spec/rails_helper.rb内でrequireする。(spec/rails_helper.rbに直接記述してもよい。)

spec/support/factory_bot.rb
RSpec.configure do |config|
  config.include FactoryBot::Syntax::Methods
end

RuboCopの拡張機能を入れる

https://docs.rubocop.org/rubocop/1.67/extensions.html#official-extensions
Gemfileに以下を追記し、bundle installする。

Gemfile
group :development do
  gem 'rubocop-factory_bot', require: false
  gem 'rubocop-rspec', require: false
  gem 'rubocop-rspec_rails', require: false
end

.rubocop.ymlに以下を記述すると、ルールが適用されるようになる。

.rubocop.yml
require:
  - rubocop-factory_bot
  - rubocop-rspec
  - rubocop-rspec_rails