当你输入了rails server之后1

2019-11-05  本文已影响0人  will2yang

该部分我们主要把流程梳理到执行 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命令执行完会直接退出脚本

上一篇下一篇

猜你喜欢

热点阅读