Rails newの処理を追ってみた

# 動機

せっかくアドベントカレンダーの初日なので、`rails new`のオプションの解説をしようと思ったんですが、それだけだとありきたりなのでコードを読んでみることにしました。

# 前提条件

ソースコードは`v6.0.1`のタグが付いているもの（執筆時点の最新安定版）。

# 注意書き

この記事は筆者がガチでコードを読みつつ書いたものです。順番の前後や過度のライブ感がありますがご了承ください。

## とっかかり

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/commands/new/new_command.rb

ここかなあ…

```ruby
module Rails
  module Command
    class NewCommand < Base # :nodoc:
      no_commands do
        def help
          Rails::Command.invoke :application, [ "--help" ]
        end
      end

      def perform(*)
        say "Can't initialize a new Rails application within the directory of another, please change to a non-Rails directory first.\n"
        say "Type 'rails' for help."
        exit 1
      end
    end
  end
end
```

あっ、違う。ここに置かれているのはRailsアプリを作ってから使われるものらしい。
じゃあ別のところにあるってことなので、雑に`rg 'rails new'`するか（`rg`は`grep`のすごい版）。

お、なんか見つけた。

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/generators/rails/app/app_generator.rb

見覚えのあるオプションっぽいものがあるし、ここが見るべきコードらしい。

## リーディング開始

とりあえず、オプションなしの挙動を調べよう。

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/generators/rails/app/app_generator.rb#L247

なるほど、`AppBase`というやつを継承しているのか。

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/generators/app_base.rb

さらに`Base`を継承しているっぽい。

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/generators/base.rb

`Base`は`Thor::Group`というのを継承しているのか…さすがに追うのに疲れてきたし、`Thor`はRubyでCLIを書くためのツールキットみたいなやつだったかな。これ以上追ってもRails固有の処理は出てこないだろうから、ここから読み始めよう。
…とは言うものの、書いてある処理が汎用的過ぎるなあ、`AppBase`に戻ろう。

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/generators/app_base.rb#L14

ここで`Database`なるモジュールを`include`している。見てみよう。

## Database

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/generators/database.rb

なるほど、ここではDBの種類とgemへのマッピングとかをやっているのか。

```ruby
def gem_for_database(database = options[:database])
  case database
    when "mysql"          then ["mysql2", [">= 0.4.4"]]
    when "postgresql"     then ["pg", [">= 0.18", "< 2.0"]]
    when "sqlite3"        then ["sqlite3", ["~> 1.4"]]
    when "oracle"         then ["activerecord-oracle_enhanced-adapter", nil]
    when "frontbase"      then ["ruby-frontbase", nil]
    when "sqlserver"      then ["activerecord-sqlserver-adapter", nil]
    when "jdbcmysql"      then ["activerecord-jdbcmysql-adapter", nil]
    when "jdbcsqlite3"    then ["activerecord-jdbcsqlite3-adapter", nil]
    when "jdbcpostgresql" then ["activerecord-jdbcpostgresql-adapter", nil]
    when "jdbc"           then ["activerecord-jdbc-adapter", nil]
    else [database, nil]
  end
end
```

MySQLのソケット、地道…

```ruby
def mysql_socket
  @mysql_socket ||= [
    "/tmp/mysql.sock",                        # default
    "/var/run/mysqld/mysqld.sock",            # debian/gentoo
    "/var/tmp/mysql.sock",                    # freebsd
    "/var/lib/mysql/mysql.sock",              # fedora
    "/opt/local/lib/mysql/mysql.sock",        # fedora
    "/opt/local/var/run/mysqld/mysqld.sock",  # mac + darwinports + mysql
    "/opt/local/var/run/mysql4/mysqld.sock",  # mac + darwinports + mysql4
    "/opt/local/var/run/mysql5/mysqld.sock",  # mac + darwinports + mysql5
    "/opt/lampp/var/mysql/mysql.sock"         # xampp for linux
  ].find { |f| File.exist?(f) } unless Gem.win_platform?
end
```

## AppName

`Database`モジュールは短かったけど、もう一つ`include`されているものがあった。`AppName`のほうはどうだろう。

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/generators/app_name.rb

こちらも短いし全部プライベートメソッドだけど、面白いものを見つけた。

```ruby
RESERVED_NAMES = %w(application destroy plugin runner test)
def valid_const?
  if /^\d/.match?(app_const)
    raise Error, "Invalid application name #{original_app_name}. Please give a name which does not start with numbers."
  elsif RESERVED_NAMES.include?(original_app_name)
    raise Error, "Invalid application name #{original_app_name}. Please give a " \
                 "name which does not match one of the reserved rails " \
                 "words: #{RESERVED_NAMES.join(", ")}"
  elsif Object.const_defined?(app_const_base)
    raise Error, "Invalid application name #{original_app_name}, constant #{app_const_base} is already in use. Please choose another application name."
  end
end
```

ここで無効なアプリ名を弾いているのか。`generator`はアプリ名としては正当なもの…なのか？

## AppBase

### 見知ったオプションたち

`AppBase`に戻ってくると、ようやく見慣れたオプションを見つけることができた。

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/generators/app_base.rb#L26-L102

```ruby
def self.add_shared_options_for(name)
  class_option :template,            type: :string, aliases: "-m",
                                     desc: "Path to some #{name} template (can be a filesystem path or URL)"

  class_option :database,            type: :string, aliases: "-d", default: "sqlite3",
                                     desc: "Preconfigure for selected database (options: #{DATABASES.join('/')})"

  class_option :skip_gemfile,        type: :boolean, default: false,
                                     desc: "Don't create a Gemfile"

  class_option :skip_git,            type: :boolean, aliases: "-G", default: false,
                                     desc: "Skip .gitignore file"

  class_option :skip_keeps,          type: :boolean, default: false,
                                     desc: "Skip source control .keep files"

  class_option :skip_action_mailer,  type: :boolean, aliases: "-M",
                                     default: false,
                                     desc: "Skip Action Mailer files"

  class_option :skip_action_mailbox, type: :boolean, default: false,
                                     desc: "Skip Action Mailbox gem"

  class_option :skip_action_text,    type: :boolean, default: false,
                                     desc: "Skip Action Text gem"

  class_option :skip_active_record,  type: :boolean, aliases: "-O", default: false,
                                     desc: "Skip Active Record files"

  class_option :skip_active_storage, type: :boolean, default: false,
                                     desc: "Skip Active Storage files"

  class_option :skip_puma,           type: :boolean, aliases: "-P", default: false,
                                     desc: "Skip Puma related files"

  class_option :skip_action_cable,   type: :boolean, aliases: "-C", default: false,
                                     desc: "Skip Action Cable files"

  class_option :skip_sprockets,      type: :boolean, aliases: "-S", default: false,
                                     desc: "Skip Sprockets files"

  class_option :skip_spring,         type: :boolean, default: false,
                                     desc: "Don't install Spring application preloader"

  class_option :skip_listen,         type: :boolean, default: false,
                                     desc: "Don't generate configuration that depends on the listen gem"

  class_option :skip_javascript,     type: :boolean, aliases: "-J", default: name == "plugin",
                                     desc: "Skip JavaScript files"

  class_option :skip_turbolinks,     type: :boolean, default: false,
                                     desc: "Skip turbolinks gem"

  class_option :skip_test,           type: :boolean, aliases: "-T", default: false,
                                     desc: "Skip test files"

  class_option :skip_system_test,    type: :boolean, default: false,
                                     desc: "Skip system test files"

  class_option :skip_bootsnap,       type: :boolean, default: false,
                                     desc: "Skip bootsnap gem"

  class_option :dev,                 type: :boolean, default: false,
                                     desc: "Setup the #{name} with Gemfile pointing to your Rails checkout"

  class_option :edge,                type: :boolean, default: false,
                                     desc: "Setup the #{name} with Gemfile pointing to Rails repository"

  class_option :rc,                  type: :string, default: nil,
                                     desc: "Path to file containing extra configuration options for rails command"

  class_option :no_rc,               type: :boolean, default: false,
                                     desc: "Skip loading of extra configuration options from .railsrc file"

  class_option :help,                type: :boolean, aliases: "-h", group: :rails,
                                     desc: "Show this help message and quit"
end
```

これほぼ`rails new`のときのオプションだけど、でもここではまだクラスメソッドの定義に過ぎない。これを呼んでいる箇所を`rg`すると…いた！

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/generators/rails/app/app_generator.rb#L250

`add_shared_options_for "application"`

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/generators/rails/plugin/plugin_generator.rb#L168

`add_shared_options_for "plugin"`

そうか、Engine作るときとかも同じオプションだからここにあるのか。
こうやってクラスメソッドを呼び出すことでオプション定義に展開される、と。

### Gemfileの中身

生成される`Gemfile`の中身が定義されているっぽい箇所があるね。

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/generators/app_base.rb#L128-L139

```ruby
def gemfile_entries # :doc:
  [rails_gemfile_entry,
   database_gemfile_entry,
   web_server_gemfile_entry,
   assets_gemfile_entry,
   webpacker_gemfile_entry,
   javascript_gemfile_entry,
   jbuilder_gemfile_entry,
   psych_gemfile_entry,
   cable_gemfile_entry,
   @extra_entries].flatten.find_all(&@gem_filter)
end
```

で、`database_gemfile_entry`を見てみると、

```ruby
def database_gemfile_entry # :doc:
  return [] if options[:skip_active_record]
  gem_name, gem_version = gem_for_database
  GemfileEntry.version gem_name, gem_version,
                      "Use #{options[:database]} as the database for Active Record"
end
```

おー、`gem_for_database`はさっき`Database`モジュールの中で見たやつだ、ここで使うんだ！

### :doc:なプライベートメソッドたち

プライベートメソッドにたくさん`:doc`が付いているなあ。これってどういう意図なんだっけ？実装はプライベートだけどエンドユーザーにオーバーライドしてほしいやつみたいな感じだったっけ？

```ruby
def builder # :doc:
  @builder ||= begin
    builder_class = get_builder_class
    builder_class.include(ActionMethods)
    builder_class.new(self)
  end
end

def build(meth, *args) # :doc:
  builder.send(meth, *args) if builder.respond_to?(meth)
end
```

この辺気になるなあ。`get_builder_class`の結果はおそらくアプリケーションとプラグインで違うクラスになるんだろう。
`ActionMethods`はさっき見た気もする。

### ActionMethods

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/generators/rails/app/app_generator.rb#L6-L27

```ruby
module ActionMethods # :nodoc:
  attr_reader :options

  def initialize(generator)
    @generator = generator
    @options   = generator.options
  end

  private
    %w(template copy_file directory empty_directory inside
       empty_directory_with_keep_file create_file chmod shebang).each do |method|
      class_eval <<-RUBY, __FILE__, __LINE__ + 1
        def #{method}(*args, &block)
          @generator.send(:#{method}, *args, &block)
        end
      RUBY
    end

    def method_missing(meth, *args, &block)
      @generator.send(meth, *args, &block)
    end
end
```

おー、これはわかりやすいプロキシオブジェクト。初期化時に受け取った`generator`オブジェクトにガンガン移譲してる。一部のメソッドは事前に`class_eval`から`def`することで`method_missing`の多用を回避しているっぽい。

これを呼んでいるコードに戻ると、

```ruby
def builder # :doc:
  @builder ||= begin
    builder_class = get_builder_class
    builder_class.include(ActionMethods)
    builder_class.new(self)
  end
end
```

この`self`は各具象のジェネレータだと思うから、ここでようやく`AppGenerator`を読むタイミングが来たようだ。

## AppGenerator

### AppBuilder

さっきの流れだと、このファイルのどこかに`get_builder_class`があるはず…あった！

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/generators/rails/app/app_generator.rb#L513-L515

で、探すべきクラス（`AppBuilder`）はこちらに。

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/generators/rails/app/app_generator.rb#L44-L240

```ruby
# The application builder allows you to override elements of the application
# generator without being forced to reverse the operations of the default
# generator.
#
# This allows you to override entire operations, like the creation of the
# Gemfile, README, or JavaScript files, without needing to know exactly
# what those operations do so you can create another template action.
#
#  class CustomAppBuilder < Rails::AppBuilder
#    def test
#      @generator.gem "rspec-rails", group: [:development, :test]
#      run "bundle install"
#      generate "rspec:install"
#    end
#  end
```

なるほど、このクラスは上書きされる想定なのか。つまり、ジェネレータではなくてこのビルダクラスを継承して書き換えることで、ジェネレータの全体像を知らなくてもピンポイントで処理を変更できるということか。よくできているなあ。

で、中身を見てみると、なんか見たことあるようなメソッドが大量に定義されているなあ…`AppBase`に`build`があって、そこではこんな感じのことが書いてあった。

```ruby
def build(meth, *args) # :doc:
  builder.send(meth, *args) if builder.respond_to?(meth)
end
```

この`builder`が今見たクラスなら、コード中に`build(:readme)`とかがあって、それは実際には`AppBuilder.new.readme`的なコードになっているということになる。というわけで`build`を探すとすぐ見つかった。

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/generators/rails/app/app_generator.rb#L288-L295

ここは`AppGenerator`の中。

### 全体像

ようやく全体像が見えてきた。無理を承知で単純化すると、`AppBuilder`クラスは`rails new`するときの各工程における具体的な処理が書いてある。各工程は`AppGenerator`クラスのパブリックメソッドとして列挙してある。

後者については実はズルをした。あのあと気になって`Thor::Group`について調べたらWikiがあって。

https://github.com/erikhuda/thor/wiki/Groups

要はメソッドを上から順に呼ぶコマンドを定義するらしい。なるほど、`rails new`したときにやっていることって`AppGenerator`のメソッドを上から読むと確かにそのままだ。

というわけで、`rails new`が何をしているのかを知りたければこの辺を読めばいいらしい。でもオプションが渡されたときの挙動も面白い。

### options

`AppGenerator`には`api`オプションが定義されている。これはRails 5から導入されたAPIモードのためのオプションだ。このオプションは各所の挙動を変更している、例えば：

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/generators/rails/app/app_generator.rb#L277-L279

```ruby
# Force sprockets and yarn to be skipped when generating API only apps.
# Can't modify options hash as it's frozen by default.
if options[:api]
  self.options = options.merge(skip_sprockets: true, skip_javascript: true).freeze
end
```

なるほど、APIモードならSprocketsもYarnもいらない、確かに。
他にも、`_if_api_option`で終わるメソッドがたくさんあって、ファイルの削除をしているようだ。

あとは、`options[:pretend]`というものがある。これは定義箇所が見つからなかったのでおそらく`Thor`の内部で定義されているオプションだと思われる。このオプションが指定されていると色々とスキップされる（そのためのオプションなので当然ではある）。

```ruby
def master_key
  return if options[:pretend] || options[:dummy_app]

  require "rails/generators/rails/master_key/master_key_generator"
  master_key_generator = Rails::Generators::MasterKeyGenerator.new([], quiet: options[:quiet], force: options[:force])
  master_key_generator.add_master_key_file_silently
  master_key_generator.ignore_master_key_file_silently
end

def credentials
  return if options[:pretend] || options[:dummy_app]

  require "rails/generators/rails/credentials/credentials_generator"
  Rails::Generators::CredentialsGenerator.new([], quiet: options[:quiet]).add_credentials_file_silently
end
```

こんな感じ。
余談ながら、こうやって途中で`require`を呼べたりするのはRubyの柔軟性の為せる技だなあと思う。

## 中間まとめ

ここまででわかったことは、

* オプションの定義は`AppBase`と`AppGenerator`クラスにある
* `rails new`の処理の概要に関しては`AppGenerator`クラスの各パブリックメソッドに書かれている
* 具体的な処理は`AppBuilder`に書かれており、こちらは継承してメソッドをオーバーライドすることで処理の変更が可能

そしてその他の細かい色々（`destroy`という名前のアプリケーションは作れない、とか！）。

では、そもそもそうやってこの`AppGenerator`クラスが起動されるのかまで調べてみよう。

## ジェネレータが呼ばれるまで

### `rails`実行ファイル

冷静に考えると、`rails new`するとき、`rails`の部分は実行可能ファイルとして扱われていることになる。実行可能ファイルを提供しているgemでは、それらは`exe`ディレクトリの下に置かれている。Railsも同じだとすると…あった！

https://github.com/rails/rails/blob/v6.0.1/railties/exe/rails

`require "rails/cli"`しているのが本体っぽいので、そちらに移動しよう。

### CLIクラス

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/cli.rb

```ruby
# frozen_string_literal: true

require "rails/app_loader"

# If we are inside a Rails application this method performs an exec and thus
# the rest of this script is not run.
Rails::AppLoader.exec_app

require "rails/ruby_version_check"
Signal.trap("INT") { puts; exit(1) }

require "rails/command"

if ARGV.first == "plugin"
  ARGV.shift
  Rails::Command.invoke :plugin, ARGV
else
  Rails::Command.invoke :application, ARGV
end
```

なるほど、`rails new`の場合、`ARGV.first == "new"`となるはずなので、下のコード、`Rails::Command.invoke :application, ARGV`が呼ばれるのか。次は`Rails::Command.invoke`を見るべき。

### Commandクラス

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/command.rb

```ruby
# Receives a namespace, arguments and the behavior to invoke the command.
def invoke(full_namespace, args = [], **config)
  namespace = full_namespace = full_namespace.to_s

  if char = namespace =~ /:(\w+)$/
    command_name, namespace = $1, namespace.slice(0, char)
  else
    command_name = namespace
  end

  command_name, namespace = "help", "help" if command_name.blank? || HELP_MAPPINGS.include?(command_name)
  command_name, namespace = "version", "version" if %w( -v --version ).include?(command_name)

  command = find_by_namespace(namespace, command_name)
  if command && command.all_commands[command_name]
    command.perform(command_name, args, config)
  else
    find_by_namespace("rake").perform(full_namespace, args, config)
  end
end

# Rails finds namespaces similar to Thor, it only adds one rule:
#
# Command names must end with "_command.rb". This is required because Rails
# looks in load paths and loads the command just before it's going to be used.
#
#   find_by_namespace :webrat, :rails, :integration
#
# Will search for the following commands:
#
#   "rails:webrat", "webrat:integration", "webrat"
#
# Notice that "rails:commands:webrat" could be loaded as well, what
# Rails looks for is the first and last parts of the namespace.
def find_by_namespace(namespace, command_name = nil) # :nodoc:
  lookups = [ namespace ]
  lookups << "#{namespace}:#{command_name}" if command_name
  lookups.concat lookups.map { |lookup| "rails:#{lookup}" }

  lookup(lookups)

  namespaces = subclasses.index_by(&:namespace)
  namespaces[(lookups & namespaces.keys).first]
end
```

問題のコードは以上。`invoke`は`invoke('application', ['new'])`として起動されている（実際は`invoke('application', ['new', 'my_app']`とかの可能性が高いけど、一番シンプルなケース）。
`if char = namespace =~ /:(\w+)$/`の条件文は`false`だと思われる（コロンは入ってない）ので、`command_name`と`namespace`はともに`application`。
ヘルプとバージョンは今回は飛ばしてよいのだけど、`find_by_namespace`にある`lookup`ってどこにあるんだろうと思ったら`autoload`されている`Behaviour`モジュールの中にあった。

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/command/behavior.rb

```ruby
# Receives namespaces in an array and tries to find matching generators
# in the load path.
def lookup(namespaces)
  paths = namespaces_to_paths(namespaces)

  paths.each do |raw_path|
    lookup_paths.each do |base|
      path = "#{base}/#{raw_path}_#{command_type}"

      begin
        require path
        return
      rescue LoadError => e
        raise unless e.message =~ /#{Regexp.escape(path)}$/
      rescue Exception => e
        warn "[WARNING] Could not load #{command_type} #{path.inspect}. Error: #{e.message}.\n#{e.backtrace.join("\n")}"
      end
    end
  end
end
```

なんだかとらえどころのないコードだけど、要は`require`するためのコードらしい。

さっきのコードに戻ると、`find_by_namespace('application', 'application')`が呼び出されていることになる。`subclasses`を`index_by`でハッシュにしているけど、ここでのサブクラスって`Rails::Command`のサブクラスってことだよな。字面的に`Rails::Command::ApplicationCommand`クラスがありそう…あった！

### ApplicationCommand

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/commands/application/application_command.rb

おお、我らが`AppGenerator`があるじゃないか！

```ruby
module Command
  class ApplicationCommand < Base # :nodoc:
    hide_command!

    def help
      perform # Punt help output to the generator.
    end

    def perform(*args)
      Rails::Generators::AppGenerator.start \
        Rails::Generators::ARGVScrubber.new(args).prepare!
    end
  end
end
```

`invoke`内で`perform`が呼ばれていたし、これで全てつながった…

`Rails::Generators::ARGVScrubber.new(args).prepare!`が唐突感あるので、これだけ読んで終わりにしよう。

### ARGVScrubber

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/generators/rails/app/app_generator.rb#L518-L587

まさかの`AppGenerator`と同じファイル…

```ruby
# This class handles preparation of the arguments before the AppGenerator is
# called. The class provides version or help information if they were
# requested, and also constructs the railsrc file (used for extra configuration
# options).
#
# This class should be called before the AppGenerator is required and started
# since it configures and mutates ARGV correctly.
class ARGVScrubber # :nodoc:
  def initialize(argv = ARGV)
    @argv = argv
  end

  def prepare!
    handle_version_request!(@argv.first)
    handle_invalid_command!(@argv.first, @argv) do
      handle_rails_rc!(@argv.drop(1))
    end
  end

  def self.default_rc_file
    File.expand_path("~/.railsrc")
  end

  private

    def handle_version_request!(argument)
      if ["--version", "-v"].include?(argument)
        require "rails/version"
        puts "Rails #{Rails::VERSION::STRING}"
        exit(0)
      end
    end

    def handle_invalid_command!(argument, argv)
      if argument == "new"
        yield
      else
        ["--help"] + argv.drop(1)
      end
    end

    def handle_rails_rc!(argv)
      if argv.find { |arg| arg == "--no-rc" }
        argv.reject { |arg| arg == "--no-rc" }
      else
        railsrc(argv) { |rc_argv, rc| insert_railsrc_into_argv!(rc_argv, rc) }
      end
    end

    def railsrc(argv)
      if (customrc = argv.index { |x| x.include?("--rc=") })
        fname = File.expand_path(argv[customrc].gsub(/--rc=/, ""))
        yield(argv.take(customrc) + argv.drop(customrc + 1), fname)
      else
        yield argv, self.class.default_rc_file
      end
    end

    def read_rc_file(railsrc)
      extra_args = File.readlines(railsrc).flat_map(&:split)
      puts "Using #{extra_args.join(" ")} from #{railsrc}"
      extra_args
    end

    def insert_railsrc_into_argv!(argv, railsrc)
      return argv unless File.exist?(railsrc)
      extra_args = read_rc_file railsrc
      argv.take(1) + extra_args + argv.drop(1)
    end
end
```

`@argv`には`['new', 'my_app']`とかが入るのね。
で、まず`handle_version_request!`でバージョンの問い合わせをチェックしてから`handle_invalid_command!`で`new`のときだけブロックを呼び出し、最後に`railsrc`絡みの処理をして終了…簡単だね。

## まとめ

謎に順番が前後したけれど、これで`rails new`の謎はほぼ全て解けた。残る謎は自分でコードリーディングできるはず！

## おまけ：アプリ名はどこで取得されている？

実は、`ARGVScrubber`がアプリ名の引数を処理しているのかと思ったらそんなことはなかったので、さっきまで読んでいたコードを読み返すとそれらしきものがありました。

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/generators/app_base.rb#L20

`argument`はおそらく`Thor`由来。`rails new hoge`の場合の`hoge`をここで受け止めているのだろうと思われるけど流石に読み続ける気力がなくこれ以上の調査を断念…

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/generators/app_base.rb#L172-L183

ただ、ここでそれらしきメソッドが呼ばれているのを発見。

```ruby
def set_default_accessors! # :doc:
  self.destination_root = File.expand_path(app_path, destination_root)
  self.rails_template = \
    case options[:template]
    when /^https?:\/\//
      options[:template]
    when String
      File.expand_path(options[:template], Dir.pwd)
    else
      options[:template]
    end
end
```

そして、このメソッドは`AppGenerator`で参照されている。

https://github.com/rails/rails/blob/v6.0.1/railties/lib/rails/generators/rails/app/app_generator.rb#L284

おそらくはここでアプリ名を取得し、同名のディレクトリを作成していると思われる。