Railsはbin/railsが無いとnew以外のコマンドを実行できません。今回はコードレベルでその理由を追っていこうと思います。railsのバージョンは5.1.4です。
まずbundle exec railsするとexe/railsが呼び出されます
#!/usr/bin/env ruby
git_path = File.expand_path("../../../.git", __FILE__)
if File.exist?(git_path)
railties_path = File.expand_path("../../lib", __FILE__)
$:.unshift(railties_path)
end
require "rails/cli"
exe/railsはさらにlib/rails/cli.rbを呼び出します
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::AppLoader.exec_appはbin/railsやscript/railsを探し出し、あればそのファイルを引数にexecを呼び出します。
require "pathname"
require "rails/version"
module Rails
module AppLoader # :nodoc:
extend self
RUBY = Gem.ruby
EXECUTABLES = ["bin/rails", "script/rails"]
# ...
def exec_app
original_cwd = Dir.pwd
loop do
if exe = find_executable
contents = File.read(exe)
if contents =~ /(APP|ENGINE)_PATH/
exec RUBY, exe, *ARGV
break # non reachable, hack to be able to stub exec in the test suite
elsif exe.end_with?("bin/rails") && contents.include?("This file was generated by Bundler")
# ...
def find_executable
EXECUTABLES.find { |exe| File.file?(exe) }
end
end
end
bin/railsはこんな感じになってます
#!/usr/bin/env ruby
begin
load File.expand_path('../spring', __FILE__)
rescue LoadError => e
raise unless e.message.include?('spring')
end
APP_PATH = File.expand_path('../config/application', __dir__)
require_relative '../config/boot'
require 'rails/commands'
rails/commandsはRails::Command.invokeを呼び出します。このRails::Commandがserverやconsole等を実行する実体となってます。
require "rails/command"
aliases = {
"g" => "generate",
"d" => "destroy",
"c" => "console",
"s" => "server",
"db" => "dbconsole",
"r" => "runner",
"t" => "test"
}
command = ARGV.shift
command = aliases[command] || command
Rails::Command.invoke command, ARGV
一方でbin/railsやscript/railsが存在しない場合は Rails::Command.invoke
の引数が:applicationか:pluginになります。Railsアプリを作成したい場合は Rails::Command.invoke :application, ARGV
が呼ばれ、Railsアプリのジェネレータが呼び出されます。new以外のサブコマンドを指定するとhelpを出す部分は Rails::AppBuilder::ARGVScrubber#handle_invalid_command!
メソッドになります。
module Rails
class AppBuilder
class ARGVScrubber # :nodoc:
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_invalid_command!(argument, argv)
if argument == "new"
yield
else
["--help"] + argv.drop(1)
end
end
ということでbin/railsがないとnewコマンドのみ、あればnewコマンド以外が使えるようになります。