该部分我们主要把流程梳理到执行 ruby bin/rails server 的命令
1.首先我们执行which rails命令查看我们调用的是哪个脚本文件,因为我使用的是rbenv的环境所以我会使用 rbenv which rails
# 这是我在ubuntu上没有装rbenv的情况下的输出
$ which rails # /usr/local/bin/rails
# 我的mac装了rbenv所以会找到的rails脚本是经过rbenv处理的
$ which rails # /Users/will/.rbenv/shims/rails
# 所以要使用 rbenv的which命令 就能找到真正想要的结果
$ rbenv which rails # /Users/will/.rbenv/versions/2.4.4/bin/rails
接下来我们就看一下这个入口文件有什么
$ cat /Users/will/.rbenv/versions/2.4.4/bin/rails
首先上部分是判断rails命令后面是否指定了版本号,指定了版本号那么就使用指定的版本号.
#!/Users/will/.rbenv/versions/2.4.4/bin/ruby
#
# This file was generated by RubyGems.
#
# The application 'railties' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'rubygems'
version = ">= 0.a"
if ARGV.first
str = ARGV.first
str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding
if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then
version = $1
ARGV.shift
end
end
if Gem.respond_to?(:activate_bin_path)
load Gem.activate_bin_path('railties', 'rails', version)
else
gem "railties", version
load Gem.bin_path("railties", "rails", version)
end
https://stackoverflow.com/questions/29787336/why-is-force-encodingbinary-used-here
既然之前的代码都是为了加载railties里exe下的rails脚本那么我们先看下rails脚本里的代码:
Gem.bin_path("railties", "rails", ">= 0.a")
# "/Users/will/.rbenv/versions/2.4.4/lib/ruby/gems/2.4.0/gems/railties-5.2.1/exe/rails"
# /Users/will/.rbenv/versions/2.4.4/lib/ruby/gems/2.4.0/gems/railties-5.2.1/exe/rails
#!/usr/bin/env ruby
# frozen_string_literal: true
git_path = File.expand_path("../../.git", __dir__)
if File.exist?(git_path)
railties_path = File.expand_path("../lib", __dir__)
$:.unshift(railties_path)
end
require "rails/cli"
git path 可能是为开发rails的程序员准备的,我们不做探索,那么我们看一下 rails/cli 文件里做了什么。
# rails/cli
# 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
首先,cli文件require了app_loader, AppLoader.exec_app 主要是为了找到一个 bin/rails这个文件并且执行它,如果当前目录找不到会向上层的文件夹寻找
# rails/app_loader
# frozen_string_literal: true
require "pathname"
require "rails/version"
module Rails
module AppLoader # :nodoc:
extend self
RUBY = Gem.ruby
EXECUTABLES = ["bin/rails", "script/rails"]
BUNDLER_WARNING = <<EOS
Beginning in Rails 4, Rails ships with a `rails` binstub at ./bin/rails that
should be used instead of the Bundler-generated `rails` binstub.
If you are seeing this message, your binstub at ./bin/rails was generated by
Bundler instead of Rails.
You might need to regenerate your `rails` binstub locally and add it to source
control:
rails app:update:bin # Bear in mind this generates other binstubs
# too that you may or may not want (like yarn)
If you already have Rails binstubs in source control, you might be
inadverently overwriting them during deployment by using bundle install
with the --binstubs option.
If your application was created prior to Rails 4, here's how to upgrade:
bundle config --delete bin # Turn off Bundler's stub generator
rails app:update:bin # Use the new Rails executables
git add bin # Add bin/ to source control
You may need to remove bin/ from your .gitignore as well.
When you install a gem whose executable you want to use in your app,
generate it and add it to source control:
bundle binstubs some-gem-name
git add bin/new-executable
EOS
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")
$stderr.puts(BUNDLER_WARNING)
Object.const_set(:APP_PATH, File.expand_path("config/application", Dir.pwd))
require File.expand_path("../boot", APP_PATH)
require "rails/commands"
break
end
end
# If we exhaust the search there is no executable, this could be a
# call to generate a new application, so restore the original cwd.
Dir.chdir(original_cwd) && return if Pathname.new(Dir.pwd).root?
# Otherwise keep moving upwards in search of an executable.
Dir.chdir("..")
end
end
def find_executable
EXECUTABLES.find { |exe| File.file?(exe) }
end
end
end
执行 bin/rails 的命令后cli 之后的代码就不会执行了
ruby 的exec命令执行完会直接退出脚本
网友评论