首页 > 其他分享 >rails 启动

rails 启动

时间:2022-10-19 22:33:29浏览次数:85  
标签:end 启动 Rails server rails config options

Rails 是怎么启动的

1 启动 rails

执行rails serverrails s命令后,执行脚本./bin/rails

1.1 bin/rails

#!/usr/bin/env ruby
APP_PATH = File.expand_path("../config/application", __dir__)
require_relative "../config/boot"
require "rails/commands"

bin/rails中引入了config/boot.rbrails/commands.rb,作用分别如下所述。

1.2 config/boot.rb

ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

require "bundler/setup" # Set up gems listed in the Gemfile.
require "bootsnap/setup" # Speed up boot time by caching expensive operations.

第一句负责将ENV["BUNDLE_GEMFILE"]设置为当前项目Gemfile的位置,加载Bundler,接着引入"bundler/setup"来为 Bundler 提供 Gemfile 所需的依赖(dependencies)。

1.3 rails/commands.rb

config/boot.rb执行完后,bin/rails继续引入rails/commands.rb,代码如下:

# frozen_string_literal: true

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

首先,rails/commands.rb会帮助我们扩展别名(如:rails s扩展成rails server);

接着,调用模块Rails下的子模块Commandinvoke方法。

1.4 rails/command.rb

module Rails
  module Command
    class << self
      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
    end
  end
end

代码实现检索你所输入的rails xxx命令,如:

command_name, namespace = "help", "help" if command_name.blank? || HELP_MAPPINGS.include?(command_name)

该代码实现了当 namespace 为空,即输入的命令为rails时,返回 rails 的 help 界面。

如果执行的命令是rails server,则会继续执行同样在模块Rails下的子模块Command中子类ServerCommandperform方法:

1.5 rails/commands/server/server_command.rb

module Rails
  module Command
    class ServerCommand < Base # :nodoc:
      def perform
        extract_environment_option_from_argument
        set_application_directory!
        prepare_restart

        Rails::Server.new(server_options).tap do |server|
          # Require application after server sets environment to propagate
          # the --environment option.
          require APP_PATH
          Dir.chdir(Rails.application.root)

          if server.serveable?
            print_boot_information(server.server, server.served_url)
            after_stop_callback = -> { say "Exiting" unless options[:daemon] }
            server.start(after_stop_callback)
          else
            say rack_server_suggestion(using)
          end
        end
      end
      # ......
    end
  end
end
  • set_application_directory!:此时,如果在config.ru中没有配置root路径,则会将此文件的目录配置为网页路由的根目录。这就是为什么在使用默认跟路由的情况下,点击 http://localhost:3000 后看到的是下面这个默认界面:

    rails-index
  • Rails::Server.new(server_options).tap do |server| ...... :创建Rails::server类对象,部分代码如下:

    module Rails
      class Server < ::Rack::Server
        def initialize(options = nil)
          @default_options = options || {}
          super(@default_options)
          set_environment
        end
      end
    end
    

    该类Rails::Server继承自Rack::Server,在创建该类对象时调用构造方法initialize

    首先通过super(@default_options)调用父类Rack::Server的构造方法:

1.6 Rack: lib/rack/server.rb

Rack::Server 对基于 Rack 的 rails 应用提供了一组接口。

首先,initialize方法初始化了一系列变量:

module Rack
  class Server
    def initialize(options = nil)
      @ignore_options = []

      if options
        @use_default_options = false
        @options = options
        @app = options[:app] if options[:app]
      else
        argv = defined?(SPEC_ARGV) ? SPEC_ARGV : ARGV
        @use_default_options = true
        @options = parse_options(argv)
      end
    end
  end
end

Rails::Command::ServerCommand#server_options:模块 Rails 子模块 CommandServerCommand 类的 server_options 方法的返回值会被赋给实例变量 @ignore_options

该方法代码返回值包含了服务器、是否在标准输出打印日志、host、port等用户配置的选项信息,代码如下:

module Rails
  module Command
    class ServerCommand
      no_commands do
        def server_options
          {
            user_supplied_options: user_supplied_options,
            server:                using,
            log_stdout:            log_to_stdout?,
            Port:                  port,
            Host:                  host,
            DoNotReverseLookup:    true,
            config:                options[:config],
            environment:           environment,
            daemonize:             options[:daemon],
            pid:                   pid,
            caching:               options[:dev_caching],
            restart_cmd:           restart_command,
            early_hints:           early_hints
          }
        end
      end
    end
  end
end

父类Rack::Server构造方法执行完后,回到rails/commands/server/server_command.rb继续执行构造方法initialize中的set_environment方法:

module Rails
  module Server
    def set_environment
      ENV["RAILS_ENV"] ||= options[:environment]
    end
  end
end

构造完成。

1.7 Rails::Server#start

加载 config/application.rb后,调用模块 Rails中类Serverstart方法:

module Rails
  class Server < ::Rack::Server
    def start(after_stop_callback = nil)
      trap(:INT) { exit }
      create_tmp_directories
      setup_dev_caching
      log_to_stdout if options[:log_stdout]

      super()
      # ...
    end
  # ......

其中,trap(:INT) { exit }保证控制台能够捕捉中断CTRL + C信号来停止服务器;create_tmp_directories会生成tmp/cache, tmp/pids, 和 tmp/sockets目录;同时,setup_dev_caching允许缓存数据。最后,调用父类Rack::Server.start方法(这里略去代码)。在父类代码中会用到options[:config]变量,它的值由config.ru决定,Rack::Builder.parse_file方法会解析config.ru的内容。config.ru代码如下

1.8 config.ru

require_relative "config/environment"

run Rails.application
Rails.application.load_server

首先引入config/environment.rb

1.9 config/environment.rb

# Load the Rails application.
require_relative "application"

# Initialize the Rails application.
Rails.application.initialize!

引入config/application

注意:

config/application在之前就已经在bin/rails中通过APP_PATH = File.expand_path("../config/application", __dir__)加入到APP_PATH中,随后在模块Rails、子模块Command、类ServerCommandperform方法中通过require APP_PATH代码加载过config/application的第一行require_relative "boot"了!!!

此时所有内容已经通过 Rack 和 Rails 设置好了!!!

至此,Rails启动完成。

2 加载rails

2.1 config/application

config/application第二行代码:

require "rails/all"

引入 railties/lib/rails/all.rb

2.2 railties/lib/rails/all.rb

该文件负责加载 rails 所需的每个文件(gem包):

require "rails"

%w(
  active_record/railtie
  active_storage/engine
  action_controller/railtie
  action_view/railtie
  action_mailer/railtie
  active_job/railtie
  action_cable/engine
  action_mailbox/engine
  action_text/engine
  rails/test_unit/railtie
).each do |railtie|
  begin
    require railtie
  rescue LoadError
  end
end

3.3 启动 puma 服务器

此前省略了一部分操作

通过Rack::Server.run调用如下run方法:

module Rack
  module Handler
    module Puma
      # ...
      def self.run(app, options = {})
        conf   = self.config(app, options)

        events = options.delete(:Silent) ? ::Puma::Events.strings : ::Puma::Events.stdio

        launcher = ::Puma::Launcher.new(conf, :events => events)

        yield launcher if block_given?
        begin
          launcher.run
        rescue Interrupt
          puts "* Gracefully stopping, waiting for requests to finish"
          launcher.stop
          puts "* Goodbye!"
        end
      end
      # ...
    end
  end
end

成功启动服务器,终端会返回连接状态信息:(下面为执行rails s的信息)

$ rails s
=> Booting Puma
=> Rails 7.0.4 application starting in development
=> Run `bin/rails server --help` for more startup options
Puma starting in single mode...
* Puma version: 5.6.5 (ruby 3.1.2-p20) ("Birdie's Version")
*  Min threads: 5
*  Max threads: 5
*  Environment: development
*          PID: 19684
* Listening on http://[::1]:3000
* Listening on http://127.0.0.1:3000
Use Ctrl-C to stop

参考资料

The Rails Initialization Process

标签:end,启动,Rails,server,rails,config,options
From: https://www.cnblogs.com/NormalLLer/p/16808102.html

相关文章