はじめに
先日、Railsガイドを読んでいたら、sandbox_by_defaultという設定がRails7.1から追加されたことを知りました。
本番環境での操作ミスを低減できるので良いなと感じ、早速業務で使っているアプリでもこの設定を有効にしました。
この設定ってどういうコードで実現しているんだろうというのを疑問に思ったので、sandbox_by_defaultのPRを見ながらざっくりRailsのコードリーディングしてみました。
github.com
(まとめ)PRと関連のRailsコードを読んでわかったこと
- Railsでconfigを管理しているのは
Rails::Application::Configurationクラス
config.xxxで書くような設定値はこのクラスのインスタンスのアクセサメソッド(attr_accessor)で管理されている
- コンソールでのオプション管理を行うのは
Rails::Command::ConsoleCommandクラス
class_optionでrailsコマンドのオプションを設定可能
- そのうち、オプションの値を管理しているのは
Thor::Optionsクラス
- 機能としてはただ単に設定を追加しただけに見えるが、PRを見ると、明示的にオプションをfalseで指定した時にはオプションを優先するといった考慮がされているのがわかった
コードリーディング内容
1. /rails/application/configuration.rb への変更

Rails::Application::Configurationクラスのattr_accessorにsandbox_by_defaultが追加されている。
Rails::Application::Configurationクラスとは?
=> config/application.rbの中にconfig.xxxの形で設定を書くが、このconfigがRails::Application::Configurationクラスのインスタンス。
initializeの中で以下を設定し、デフォルトはfalseとして設定する。
@sandbox_by_default = false
config.sandbox_by_default = trueとすることで値を設定できるのは、
Rails::Application::Configurationクラスのインスタンスのアクセサメソッドが定義されているので、falseをtrueに書き換えているということ。
2. /rails/commands/console/console_command.rb への変更
class_optionへの変更

class_optionとは?の説明はRailsガイドにあった。
railsguides.jp
default: falseというのを見て気づいたが、--sandbox(-s)のオプションには引数でbooleanが渡せる。
rails c -s false
#=> sandboxモードで起動しない
rails c -s true
#=> sandboxモードで起動する
疑問: 何も渡さない場合はtrueになるのはなんで?どこで制御している?
(1) railsコンソールを起動すると、class_optionを定義しているRails::Command::ConsoleCommandクラスのinitializeメソッドが呼ばれる
ruby/3.1.0/gems/railties-7.1.3.3/lib/rails/commands/console/console_command.rb
local_optionsという引数に ["-s", "false"]や["-s"]というかたちで値が渡されている
- その中でsuperで
Rails::Command::Baseのinitializeが呼ばれる
ruby/3.1.0/gems/thor-1.3.0/lib/thor/base.rb
module Command
class ConsoleCommand < Base
class_option :sandbox, aliases: "-s", type: :boolean, default: nil,
desc: "Rollback database modifications on exit."
def initialize(args = [], local_options = {}, config = {})
super(args, local_options, config)
end
end
end
(2) Rails::Command::Baseのinitializeの中のopts.parse(array_options)でオプションのパースを行っている
array_optionsには ["-s", "false"]や["-s"]の値が入っている
optsはThor::Optionsのインスタンス
module Base
def initialize(args = [], local_options = {}, config = {})
if local_options.is_a?(Array)
array_options = local_options
hash_options = {}
else
end
opts = Thor::Options.new(parse_options, hash_options, stop_on_unknown, disable_required_check, relations)
self.options = opts.parse(array_options)
end
end
(3) opts.parseの中のparse_peek(switch, options)の中で実際にパースしている
ruby/3.1.0/gems/thor-1.3.0/lib/thor/parser/options.rb
parseの中でparse_peekを呼ぶ
parse_peekの中でparse_booleanが呼ばれて、オプションのキー(--sandbox)に対する値が渡されているかどうかを見て、値が渡されている場合は値をパースする
class Thor
class Options < Arguments
def parse(args)
result = parse_peek(switch, option)
assign_result!(option, result)
assigns
end
private
def parse_peek(switch, option)
send(:"parse_#{option.type}", switch)
end
def parse_boolean(switch)
if current_is_value?
if ["true", "TRUE", "t", "T", true].include?(peek)
shift
true
elsif ["false", "FALSE", "f", "F", false].include?(peek)
shift
false
else
@switches.key?(switch) || !no_or_skip?(switch)
end
else
@switches.key?(switch) || !no_or_skip?(switch)
end
end
end
end
sandbox?メソッドへの変更

class_optionの変更にあるように、デフォルト値がnilになっている。
つまり、--sandboxのオプションを使用せずrailsコンソール起動した場合、Rails7.0までは、options[:sandbox]がfalseだったが、Rails7.1からはnilを返すことになる。
この修正によって、以下の2点がわかる。
rails c --sandbox falseのように「オプションを明示的にfalseで指定した時」とrails cのように「オプションを指定しないとき」の区別がつくようになる。
- オプションを明示的で指定したときは、configの設定に関わらずオプションの方を優先するようになる。(
rails c --sandbox falseだとsandboxモードは無効にする。)
# config.sandbox_by_default = true の設定を書いている時
$ rails c
#=> sandboxモードで起動
$ rails c --sandbox false
#=> sandboxモードは無効
ちなみに、この設定は本番環境で間違ってDB更新をすることを避けるために作られているため、developmentとtest環境では無効となっている。
この制御をするために使われているRails.env.local?もRails7.1から入ったメソッド。
techracho.bpsinc.jp