0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

パーフェクトRuby on Rails 3章 メモ・雑感

Last updated at Posted at 2025-01-03

3章 押さえておきたいRailsの基本機能

雑感

この章ではテストやミドルウェア、データベースを取り扱う基本機能が説明されていました。基本機能といってもRackや複数DBなど、初見の知識がこの章から多くなってきた印象です。特に複数DBのユースケースはかなり実務に近い内容と感じたのでしっかりと押さえておきたいと感じました。以下、個人的な要所をまとめた忘備録です。

3-2 RackとRailsの関係

RackアプリケーションとしてのRails

  • RailsはUnicornやPumaなど様々なWebアプリケーションサーバで上で動作するが、これはRailsがRackインターフェースに則っているため
  • Rackはアプリケーションサーバとフレームワークの中間に存在し、この中間層を経ることで疎結合なやり取りが行える
  • Rackが利用するエントリーポイントとしてconfig.ruファイルが存在し、rails newすると生成される
config.ru
# This file is used by Rack-based servers to start the application.

require_relative 'config/environment'

run Rails.application # runメソッドにRailsオブジェクトを渡して実行している
  • railsをRackアプリケーションとして起動させる場合は、rails sではなくrack upコマンドを実行すると9292番ポートで立ち上がる
$ bundle exec rackup
Puma starting in single mode...
* Version 4.3.12 (ruby 2.7.8-p225), codename: Mysterious Traveller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://127.0.0.1:9292
* Listening on tcp://[::1]:9292
Use Ctrl-C to stop

Rackミドルウェアでサーバとアプリケーションの間に処理を追加する

  • Railsは初期状態で様々な中間処理を行うミドルウェアを利用している。useの右がミドルウェア名
$ bin/rails middleware
use Webpacker::DevServerProxy
use ActionDispatch::HostAuthorization
use Rack::Sendfile
.
.
.
use Rack::ETag
use UpcaseMiddleware
use Rack::TempfileReaper
run RailsRackSample::Application.routes
  • 自作のRackミドルウェアをRailsに追加して任意の中間処理を設定することもできる。ファイルを作成後に、config/environments内のファイルに設定を追加する
lib/middlewares/upcase_middleware.rb
# rubyを大文字へ変換するミドルウェア
class UpcaseMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, body = @app.call(env)
    body.each { |s| s.gsub!(/ruby/i, 'RUBY') }
    [status, headers, body]
  end
end
config/environments/development.rb
require "middlewares/upcase_middleware"
Rails.application.configure do
  .
  .
  .
-    # 通常は全ての処理の最後に追加
-    config.middleware.use UpcaseMiddleware
+    # 指定した別ミドルウェアの後に処理を追加する事もできる
+    config.middleware.insert_after Rack::ETag, UpcaseMiddleware
end

3-3 DBを管理する

複数DBの扱い方

  • 複数のDBを取り扱う場合、config/database.ymlへ他のDB設定を追加する
config/database.yml
default: &default
  adapter: sqlite3
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

development:
  primary:
    <<: *default
    database: db/development.sqlite3
+ sub: # サブDBを追加
+   <<: *default
+   database: db/development_sub.sqlite3 # サブDBの名前
+   migrations_paths: db/sub_migrate # migrateした場合にファイルが追加されるパス
test:
  <<: *default
  database: db/test.sqlite3

production:
  <<: *default
  database: db/production.sqlite3
  • 片方のDBにマイグレーションパスを指定することで2つのDBで異なるテーブル定義を反映する
  • db:create:subのようにタスクに利用するDB名を記述することで特定のDBのみをタスクの対象とできる
$ bin/rails db:create:sub # <-- subだけdb:createされる
Created database 'db/development_sub.sqlite3'
  • 各モデルクラスでestablish_connectionを設定しsubデータベースへ接続する。その際、役割を分割するためにDB接続設定を定義した抽象クラスを作成してそのクラスを継承すると良い。設定後は自動的にsubへ書き込みなどが行われる
app/models/sub_base.rb
# DB接続設定を定義する抽象クラス
class SubBase < ApplicationRecord
  self.abstract_class = true
  establish_connection :sub
end
app/models/author.rb
# 上記の抽象クラスを継承してsubデータベースへ接続する
class Author < SubBase
end

書き込みと読み込みの分離

  • 実際のユースケースでは書き込み用と読み取り用のDBに分割する構成が多い。この場合、読み取り専用のレプリカDBを作成するときは、database.ymlreplicaを指定する
config/database.yml
default: &default
  adapter: sqlite3
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: 
  host: 127.0.0.1

development:
  primary:
    <<: *default
    database: db_sample_development
    port: 33061
+   primary_replica:
+   <<: *default
+   database: db_sample_development
+   port: 33061
+   replica: true # 読み取り専用であることを宣言
test:
  <<: *default
  database: db/test.sqlite3

production:
  <<: *default
  database: db/production.sqlite3
  • 書き込みや読み込み先のDBを明示的に指定するにはActiveRecordモデル単位でconnect_toメソッドを利用する
app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
  connects_to database: {
  # ロール名   DB名
    writing: :primary,
    reading: :primary_replica
  }
end
  • ロール名はapplication.rbなどで変更も可能
config/application.rb
module DbSample
  
  class Application < Rails::Application
    config.active_record.writing_role = :writable
    config.active_record.reading_role = :readonly
  end
end
  • 特定のクエリでレプリカを参照したい場合は、connect_toにロールを指定したブロックを使う
ActiveRecord::Base.connect_to(role: :reading) do
  Blog.find(5)
end
  • 多くの場合は読み取りであれば自動的にレプリカDBを参照してほしいケースが多いので、そのために必要な設定がproduction.rbにコメントアウトされている。コメントアウトを解除し、development.rbへそのままコピーすることで開発環境でも有効にできる
config/environments/development.rb
config.active_record.database_selector = { delay: 2.seconds }
config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?