Active Record で複数のデータベース利用

このガイドでは、Active Recordで複数のデータベースを利用する方法について説明します。

このガイドの内容:

  • アプリケーションで複数のデータベースをセットアップする方法
  • コネクションの自動切り替えの仕組み
  • 複数のデータベースにおける水平シャーディングの利用方法
  • legacy_connection_handlingから新しいコネクションハンドリングに移行する方法
  • サポートされている機能と現在進行中の機能

アプリケーションが人気を得て利用されるようになってくると、新しいユーザーやユーザーのデータをサポートするためにアプリケーションをスケールする必要が生じてきます。アプリケーションをスケールする方法の1つが、データベースレベルでのスケールでしょう。Railsが複数のデータベース(Multiple Databases)をサポートするようになったので、すべてのデータを1箇所に保存する必要はありません。

現時点でサポートされている機能は以下のとおりです。

  • 複数の「writer」データベースと、それぞれに対応する「replica」データベース
  • 作業中のモデルでのコネクション自動切り替え
  • HTTP verbや直近の書き込みに応じたwriterとreplicaの自動スワップ
  • 複数のデータベースの作成、削除、マイグレーション、各種操作を行うRailsタスク

以下の機能は現時点では(まだ)サポートされていません。

  • replicaのロードバランシング

1 アプリケーションのセットアップ

アプリケーションで複数のデータベースを利用する場合、大半の機能についてはRailsが代わりに行いますが、一部の手順は手動で行う必要があります。

たとえばwriterデータベースが1つあるアプリケーションに、新しいテーブルがいくつかあるデータベースを1つ追加するとします。新しいデータベースの名前は「animal」とします。

この場合のdatabase.ymlは以下のような感じになります。

production:
  database: my_primary_database
  adapter: mysql2
  username: root
  password: <%= ENV['ROOT_PASSWORD'] %>

最初の設定に対するreplicaを追加し、さらにanimalという2個目のデータベースとそれのreplicaも追加してみましょう。これを行うには、database.ymlを以下のように2層(2-tier)設定から3層(3-tier)設定に変更する必要があります。

primary設定がある場合、これが「デフォルト」の設定として使われます。「primary」と名付けられた設定がない場合、Railsは最初の設定を各環境で使います。 デフォルトの設定ではデフォルトのRailsのファイル名が使われます。たとえば、primary設定のスキーマファイル名にはschema.rbが使われ、その他のエントリではファイル名に設定の名前空間_schema.rbが使われます。

production:
  primary:
    database: my_primary_database
    username: root
    password: <%= ENV['ROOT_PASSWORD'] %>
    adapter: mysql2
  primary_replica:
    database: my_primary_database
    username: root_readonly
    password: <%= ENV['ROOT_READONLY_PASSWORD'] %>
    adapter: mysql2
    replica: true
  animals:
    database: my_animals_database
    username: animals_root
    password: <%= ENV['ANIMALS_ROOT_PASSWORD'] %>
    adapter: mysql2
    migrations_paths: db/animals_migrate
  animals_replica:
    database: my_animals_database
    username: animals_readonly
    password: <%= ENV['ANIMALS_READONLY_PASSWORD'] %>
    adapter: mysql2
    replica: true

複数のデータベースを用いる場合に重要な設定がいくつかあります。

第1に、primaryprimary_replicaのデータベース名は同じにすべきです。理由は、primaryとreplicaが同じデータを持つからです。animalsanimals_replicaについても同様です。

第2に、writerとreplicaでは異なるデータベースユーザー名を使い、かつreplicaのパーミッションは(writeではなく)readのみにすべきです。

replicaデータベースを使う場合、database.ymlのreplicaにはreplica: trueというエントリを1つ追加する必要があります。このエントリがないと、どちらがreplicaでどちらがwriterかをRailsが区別できなくなるためです。Railsは、マイグレーションなどの特定のタスクについてはreplicaに対して実行しません。

最後に、新しいwriterデータベースで利用するために、そのデータベースのマイグレーションを置くディレクトリをmigrations_pathsに設定する必要があります。migrations_pathsについては本ガイドで後述します。

新しいデータベースができたら、コネクションモデルをセットアップしましょう。新しいデータベースを使うには、抽象クラスを1つ作成してanimalsデータベースに接続する必要があります。

class AnimalsRecord < ApplicationRecord
  self.abstract_class = true

  connects_to database: { writing: :animals, reading: :animals_replica }
end

続いてApplicationRecordクラスを以下のように更新し、新しいreplicaを認識させる必要があります。

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true

  connects_to database: { writing: :primary, reading: :primary_replica }
end

ApplicationRecordを別のクラス名に変えている場合は、primary_abstract_classを設定する必要があります。これにより、RailsはコネクションをどのクラスのActiveRecord::Baseと共有すべきかを認識できるようになります。

class PrimaryApplicationRecord < ActiveRecord::Base
  self.primary_abstract_class
end

primary/primary_replicaに接続するクラスは、通常のRailsアプリケーションと同様にApplicationRecordを継承できます。

class Person < ApplicationRecord
end

Railsはデフォルトで、primaryのデータベースロールはwriting、replicaのデータベースロールはreadingであることを期待します。レガシーなシステムでは、既に設定されているロールを変更したくないこともあるでしょう。その場合はアプリケーションで以下のように新しいロール名を設定できます。

config.active_record.writing_role = :default
config.active_record.reading_role = :readonly

ここで重要なのは、データベースへの接続を「単一のモデル内」で行うことと、そのモデルを継承してテーブルを利用することです(複数のモデルから同じデータベースに接続するのではなく)。データベースクライアントがコネクションをオープンできる数には上限があります。Railsはコネクションを指定する名前にモデル名を用いるので、同じデータベースに複数のモデルから接続するとコネクション数が増加します。

database.ymlと新しいモデルをセットアップできたので、いよいよデータベースを作成しましょう。Rails 6.0には複数のデータベースを使うのに必要なrailsタスクがすべて揃っています。

bin/rails -Tを実行すると、利用可能なコマンド一覧がすべて表示されます。出力は以下のようになります。

$ bin/rails -T
rails db:create                          # Creates the database from DATABASE_URL or config/database.yml for the ...
rails db:create:animals                  # Create animals database for current environment
rails db:create:primary                  # Create primary database for current environment
rails db:drop                            # Drops the database from DATABASE_URL or config/database.yml for the cu...
rails db:drop:animals                    # Drop animals database for current environment
rails db:drop:primary                    # Drop primary database for current environment
rails db:migrate                         # Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)
rails db:migrate:animals                 # Migrate animals database for current environment
rails db:migrate:primary                 # Migrate primary database for current environment
rails db:migrate:status                  # Display status of migrations
rails db:migrate:status:animals          # Display status of migrations for animals database
rails db:migrate:status:primary          # Display status of migrations for primary database
rails db:reset                           # Drops and recreates all databases from their schema for the current environment and loads the seeds
rails db:reset:animals                   # Drops and recreates the animals database from its schema for the current environment and loads the seeds
rails db:reset:primary                   # Drops and recreates the primary database from its schema for the current environment and loads the seeds
rails db:rollback                        # Rolls the schema back to the previous version (specify steps w/ STEP=n)
rails db:rollback:animals                # Rollback animals database for current environment (specify steps w/ STEP=n)
rails db:rollback:primary                # Rollback primary database for current environment (specify steps w/ STEP=n)
rails db:schema:dump                     # Creates a database schema file (either db/schema.rb or db/structure.sql  ...
rails db:schema:dump:animals             # Creates a database schema file (either db/schema.rb or db/structure.sql  ...
rails db:schema:dump:primary             # Creates a db/schema.rb file that is portable against any DB supported  ...
rails db:schema:load                     # Loads a database schema file (either db/schema.rb or db/structure.sql  ...
rails db:schema:load:animals             # Loads a database schema file (either db/schema.rb or db/structure.sql  ...
rails db:schema:load:primary             # Loads a database schema file (either db/schema.rb or db/structure.sql  ...
rails db:setup                           # Creates all databases, loads all schemas, and initializes with the seed data (use db:reset to also drop all databases first)
rails db:setup:animals                   # Creates the animals database, loads the schema, and initializes with the seed data (use db:reset:animals to also drop the database first)
rails db:setup:primary                   # Creates the primary database, loads the schema, and initializes with the seed data (use db:reset:primary to also drop the database first)

bin/rails db:createなどのコマンドを実行すると、primaryとanimalsデータベースの両方が作成されます。ただしデータベースユーザーを作成するコマンドはないので、replicaでreadonlyをサポートするには手動でユーザーを作成する必要があります。animalデータベースだけを作成するには、bin/rails db:create:animalsを実行します。

2 スキーマ・マイグレーション管理を外してデータベースに接続する

スキーマ管理、マイグレーション、シードなどのデータベース管理作業を一切行わずに外部のデータベースに接続したい場合は、データベースごとに設定オプションdatabase_tasks: falseを設定できます。これはデフォルトではtrueに設定されます。

production:
  primary:
    database: my_database
    adapter: mysql2
  animals:
    database: my_animals_database
    adapter: mysql2
    database_tasks: false

3 ジェネレータとマイグレーション

複数のデータベースのマイグレーションファイルは、設定ファイルにあるデータベースキー名を冒頭に付けた個別のフォルダに配置してください。

また、データベース設定のmigrations_pathsを設定し、マイグレーションファイルを探索する場所をRailsに認識させる必要もあります。

たとえば、animalsデータベースのマイグレーションファイルはdb/animals_migrateディレクトリに配置し、primaryのマイグレーションファイルはdb/migrateディレクトリに配置する、という具合になります。Railsのジェネレータには、ファイルを正しいディレクトリで生成するための--databaseオプションを渡せます。このコマンドは以下のように実行します。

$ bin/rails generate migration CreateDogs name:string --database animals

ジェネレータを使う場合は、scaffoldとモデルジェネレータが抽象クラスを自動的に作成します。これは、以下のようにコマンドラインにデータベースのキーを渡すだけでできます。

$ bin/rails generate scaffold Dog name:string --database animals

データベース名の末尾にRecordを加えた抽象クラスが作成されます。この例ではデータベースがAnimalsなので、AnimalsRecordが作成されます。

class AnimalsRecord < ApplicationRecord
  self.abstract_class = true

  connects_to database: { writing: :animals }
end

生成されたモデルは自動的にAnimalsRecordクラスを継承します。

class Dog < AnimalsRecord
end

Note: Railsはどのデータベースがreplicaなのかを認識しないので、完了したら抽象クラスにreplicaを追加する必要があります。

Railsは新しいクラスを一度だけ生成します。新しいscaffoldによって上書きされることはなく、scaffoldが削除されると削除されます。

AnimalsRecordと異なる既存の抽象クラスがある場合、--parentオプションで別の抽象クラスを指定できます。

$ bin/rails generate scaffold Dog name:string --database animals --parent Animals::Record

上では別の親クラスの利用を指定しているため、AnimalsRecordの生成をスキップします。

4 ロールの自動切り替えを有効にする

最後に、アプリケーションでread-onlyのreplicaを利用するために、自動切り替え用のミドルウェアを有効にする必要があります。

自動切り替え機能によって、アプリケーションはHTTP verbや、リクエストしたユーザーによる直近の書き込みの有無に応じてwriterからreplica、またはreplicaからwriterへと切り替えます。

アプリケーションがPOST、PUT、DELETE、PATCHのいずれかのリクエストを受け取ると、自動的にwriterデータベースに書き込みます。書き込み後に指定の時間が経過するまでは、アプリケーションはwriterから読み出します。アプリケーションがGETリクエストやHEADリクエストを受け取ると、直近の書き込みがなければreplicaから読み出します。

コネクション自動切り替えのミドルウェアを有効にするには、以下のように自動スワップジェネレータを実行します。

$ bin/rails g active_record:multi_db

続いて設定ファイルの以下の行のコメントを解除して有効にします。

Rails.application.configure do
  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
end

Railsは「自分が書き込んだものを読み取る」ことを保証するので、delayウィンドウの期間内であればGETリクエストやHEADリクエストをwriterに送信します。このdelayは、デフォルトで2秒に設定されます。 この値を変更する場合は、利用するデータベースインフラストラクチャに基づいて行うべきです。Railsは、delayウィンドウの期間内で「他のユーザーが最近書き込んだものを読み取る」ことについては保証しないので、最近書き込まれたものでなければGETリクエストやHEADリクエストをreplicaに送信します。

Railsのコネクション自動切り替えは、どちらかというとプリミティブであり、多機能とは言えません。この機能は、アプリケーションの開発者でも十分カスタマイズ可能な柔軟性を備えたコネクション自動切り替えシステムをデモンストレーションするためのものです。

Railsでのコネクション自動切り替え方法や、切り替えに使うパラメータは、セットアップで簡単に変更できます。たとえば、コネクションをスワップするかどうかを、セッションではなくcookieで行いたいのであれば、以下のように独自のクラスを作成できます。

class MyCookieResolver
  # cookieクラスで使うコードをここに書く
end

続いて、これをミドルウェアに渡します。

config.active_record.database_selector = { delay: 2.seconds }
config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
config.active_record.database_resolver_context = MyCookieResolver

5 コネクションを手動で切り替える

アプリケーションでwriterやreplicaに接続するときに、コネクションの自動切り替えを使うのは適切ではないことがあります。たとえば、特定のリクエストについては、たとえPOSTリクエストパスにいる場合であっても常にreplicaに送信したいとします。

Railsはこのような場合のために、必要なコネクションに切り替えるconnected_toメソッドを提供しています。

ActiveRecord::Base.connected_to(role: :reading) do
  # このブロック内のコードはすべてreadingロールで接続される
end

connected_to呼び出しで「ロール(role)」を指定すると、そのコネクションハンドラ(またはロール)で接続されたコネクションを探索します。readingコネクションハンドラは、readingというロール名を持つconnects_toを介して接続されたすべてのコネクションを維持します。

ここで注意したいのは、ロールを設定したconnected_toでは、既存のコネクションの探索や切り替えにそのコネクションのspecification名が用いられることです。つまり、connected_to(role: :nonexistent)のように不明なロールを渡すと、ActiveRecord::ConnectionNotEstablished (No connection pool with 'ActiveRecord::Base' found for the 'nonexistent' role.)エラーが発生します。

Railsが実行するクエリを確実に読み取り専用にするには、prevent_writes: trueを渡します。 これは単に、書き込みと思われるクエリがデータベースに送信されるのを防ぐだけです。 また、replicaデータベースも読み取り専用モードで実行されるよう設定する必要があります。

ActiveRecord::Base.connected_to(role: :reading, prevent_writes: true) do
  # Railsは読み取りクエリであることをクエリごとに確認する
end

6 水平シャーディング

水平シャーディングとは、データベースを分割して各データベースサーバーの行数を減らしながら「シャード(shard)」全体で同じスキーマを維持することです。これは一般に「マルチテナント」シャーディングと呼ばれます。

Railsで水平シャーディングをサポートするAPIは、Rails6.0以降の複数のデータベースや垂直シャーディングAPIに似ています。

シャードは次のように3層(3-tier)構成で宣言されます。

production:
  primary:
    database: my_primary_database
    adapter: mysql2
  primary_replica:
    database: my_primary_database
    adapter: mysql2
    replica: true
  primary_shard_one:
    database: my_primary_shard_one
    adapter: mysql2
  primary_shard_one_replica:
    database: my_primary_shard_one
    adapter: mysql2
    replica: true

次に、モデルは shardsキーを介してconnects_toAPIに接続されます。

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true

  connects_to shards: {
    default: { writing: :primary, reading: :primary_replica },
    shard_one: { writing: :primary_shard_one, reading: :primary_shard_one_replica }
  }
end

これで、モデルはconnected_toAPIを用いて手動でコネクションを切り替えられるようになります。シャーディングを使う場合は、roleshardの両方を渡す必要があります。

ActiveRecord::Base.connected_to(role: :writing, shard: :default) do
  @id = Person.create! # Creates a record in shard default
end

ActiveRecord::Base.connected_to(role: :writing, shard: :shard_one) do
  Person.find(@id) # Can't find record, doesn't exist because it was created
                   # in the default shard
end

水平シャーディングAPIはread replicaもサポートしています。以下のようにconnected_toAPIでロールとシャードを切り替えられます。

ActiveRecord::Base.connected_to(role: :reading, shard: :shard_one) do
  Person.first # Lookup record from read replica of shard one
end

7 自動シャード切り替えを有効にする

アプリケーションで提供されているミドルウェアを使うと、リクエスト単位でシャードを自動切り替えできるようになります。

ShardSelectorミドルウェアは、シャードを自動スワップするフレームワークを提供します。Railsは、どのシャードに切り替えるかを判断する基本的なフレームワークを提供し、必要に応じてアプリケーションでスワップのカスタム戦略を記述できます。

ShardSelectorには、ミドルウェアの動作を変更できるオプションのセットを渡せます(現在はlockのみをサポート)。lockはデフォルトではtrueで、ブロック内でのシャード切り替えを禁止します。lockfalseの場合はシャードのスワップが許可されます。 テナントベースのシャーディングでは、アプリケーションコードが誤ってテナントを切り替えることのないよう、lockは常にtrueにする必要があります。

以下のようにデータベースセレクタと同じジェネレータを用いて、シャードの自動スワップ用ファイルを生成できます。

$ bin/rails g active_record:multi_db

次に、設定ファイルの以下の行をコメント解除して有効にします。

Rails.application.configure do
  config.active_record.shard_selector = { lock: true }
  config.active_record.shard_resolver = ->(request) { Tenant.find_by!(host: request.host).shard }
end

アプリケーションは、リゾルバにコードを提供しなければなりません(リゾルバはアプリケーション固有のモデルに依存するため)。以下はリゾルバの例です。

config.active_record.shard_resolver = ->(request) {
  subdomain = request.subdomain
  tenant = Tenant.find_by_subdomain!(subdomain)
  tenant.shard
}

8 新しいコネクションハンドリングに移行する

Rails 6.1以降のActive Recordでは、コネクション管理用の新しい内部APIが提供されています。 ほとんどの場合、アプリケーションでconfig.active_record.legacy_connection_handling = falseを設定して新しい振る舞いを有効にするだけでよく、それ以外の変更は不要です(Rails 6.0以前からアップグレードする場合)。データベースが1つしかないアプリケーションの場合は、その他の変更は不要です。複数のデータベースを利用しているアプリケーションで以下のメソッドを利用している場合は、以下の変更が必要です。

  • connection_handlersおよびconnection_handlers=は新しいコネクションハンドリングでは動作しなくなります。いずれかのコネクションハンドラでメソッドを呼び出している場合(connection_handlers[:reading].retrieve_connection_pool("ActiveRecord::Base")など)は、そのメソッド呼び出しをconnection_handlers.retrieve_connection_pool("ActiveRecord::Base", role: :reading)のように更新する必要があります。

  • ActiveRecord::Base.connection_handler.prevent_writes呼び出しは、ActiveRecord::Base.connection.preventing_writes?に更新する必要があります。

  • 書き込みと読み出しを含むすべてのプールが必要な場合は、ハンドラで新しいメソッドが提供されます。これを使うにはconnection_handler.all_connection_poolsを呼び出します。しかしほとんどの場合、connection_handler.connection_pool_list(:writing)またはconnection_handler.connection_pool_list(:reading)を用いるプールへの書き込みや読み出しが必要になるでしょう。

  • アプリケーションでlegacy_connection_handlingをオフにすると、サポートされていないメソッド( connection_handlers=など)でエラーが生じます。

9 粒度の細かいデータベース接続切り替え

Rails 6.1では、すべてのデータベースに対してグローバルにコネクションを切り替えるのではなく、1つのデータベースでコネクションを切り替えることが可能です。この機能を使うには、まずアプリケーションの設定でconfig.active_record.legacy_connection_handlingfalseに設定する必要があります。パブリックAPIの振る舞いは変わらないため、ほとんどのアプリケーションではそれ以外の変更は不要です。legacy_connection_handlingを有効にして移行する方法については上のセクションを参照してください。

legacy_connection_handlingfalseに設定すると、任意の抽象コネクションクラスで、他のコネクションに影響を与えずにコネクションを切り替えられます。これはApplicationRecordのクエリがプライマリに送信されることを保証しつつ、AnimalsRecordのクエリをレプリカから読み込むように切り替えるときに便利です。

AnimalsRecord.connected_to(role: :reading) do
  Dog.first     # animals_replicaから読み出す
  Person.first  # プライマリから読み出す
end

以下のようにシャードへの接続をより細かい粒度で切り替えることも可能です。

AnimalsRecord.connected_to(role: :reading, shard: :shard_one) do
  Dog.first # shard_one_replicaから読み出す。
            # shard_one_replicaのコネクションが存在しない場合は
            # ConnectionNotEstablishedエラーが発生する
  Person.first # プライマリライターから読み出す
end

primaryデータベースクラスタのみを切り替えたい場合は、以下のようにApplicationRecordを使います。

ApplicationRecord.connected_to(role: :reading, shard: :shard_one) do
  Person.first # Reads from primary_shard_one_replica
  Dog.first # Reads from animals_primary
end

ActiveRecord::Base.connected_toは、グローバルに接続を切り替える機能を管理します。

9.1 データベース間でJOINする関連付けを扱う

Rails 7.0以降のActive Recordには、複数のデータベースにまたがってJOINを実行する関連付けを扱うオプションが提供されています。has many through関連付けやhas one through関連付けでJOINを無効にして複数のクエリを実行したい場合は、以下のようにdisable_joins: trueオプションを渡します。

class Dog < AnimalsRecord
  has_many :treats, through: :humans, disable_joins: true
  has_many :humans

  has_one :home
  has_one :yard, through: :home, disable_joins: true
end

class Home
  belongs_to :dog
  has_one :yard
end

class Yard
  belongs_to :home
end

従来は、disable_joinsを指定しない@dog.treatsや、disable_joinsを指定しない@dog.yardを呼び出すと、データベースがクラスタ間のJOINを処理できないためエラーが発生しました。disable_joinsオプションを指定することで、複数のSELECTクエリを生成してクラスタ間のJOIN回避を試みるようになります。上述の関連付けの場合、@dog.treatsは以下のSQLを生成します。

SELECT "humans"."id" FROM "humans" WHERE "humans"."dog_id" = ?  [["dog_id", 1]]
SELECT "treats".* FROM "treats" WHERE "treats"."human_id" IN (?, ?, ?)  [["human_id", 1], ["human_id", 2], ["human_id", 3]]

@dog.yardは以下のSQLを生成します。

SELECT "home"."id" FROM "homes" WHERE "homes"."dog_id" = ? [["dog_id", 1]]
SELECT "yards".* FROM "yards" WHERE "yards"."home_id" = ? [["home_id", 1]]

このオプションには以下の注意点があります。

  1. JOINの代わりに2つ以上のクエリが実行されるので、関連付けによってはパフォーマンスに影響が生じる可能性があります。humansをSELECTしたときに多数のIDが返されると、treatsのSELECTによって多数のIDが送信される可能性があります。

  2. JOINが実行されなくなるので、クエリのORDERやLIMITはメモリ上でソートされます(あるテーブルのORDERを別のテーブルに適用できないため)。

  3. この設定は、JOINを無効にしたいすべての関連付けに追加しなければなりません。 Railsはこれを自動で推測できません(関連付けはlazyに読み込まれるので、@dog.treatstreatsを読み込むには、どんなSQLを生成すべきかをRailsが事前に認識しておく必要があります)。

9.2 スキーマのキャッシュ

スキーマキャッシュをデータベースごとに読み込みたい場合は、データベースごとにschema_cache_pathを設定し、かつアプリケーション設定でconfig.active_record.lazily_load_schema_cache = trueを設定しなければなりません。この場合、データベース接続が確立されたときにキャッシュがlazyに読み込まれる点にご注意ください。

10 注意点

10.1 replicaのロードバランシング

replicaのロードバランシングはインフラストラクチャに強く依存するため、これもRailsではサポート対象外です。今後、基本的かつプリミティブなreplicaロードバランシング機能が実装されるかもしれませんが、アプリケーションをスケールさせるためにも、Railsの外部でアプリケーションを扱えるものにすべきです。

フィードバックについて

Railsガイドは GitHub の yasslab/railsguides.jp で管理・公開されております。本ガイドを読んで気になる文章や間違ったコードを見かけたら、気軽に Pull Request を出して頂けると嬉しいです。Pull Request の送り方については GitHub の README をご参照ください。

原著における間違いを見つけたら『Rails のドキュメントに貢献する』を参考にしながらぜひ Rails コミュニティに貢献してみてください 🛠💨✨

本ガイドの品質向上に向けて、皆さまのご協力が得られれば嬉しいです。よろしくお願いします。

YassLab 株式会社
https://yasslab.jp/

支援・協賛

Railsガイドは下記のサポーターから継続的な支援を受けています。Railsガイドへの支援・協賛にご興味あれば info@yasslab.jp までお問い合わせください。

  1. Star
  2. このエントリーをはてなブックマークに追加