美文网首页
当你输入了rails server之后2

当你输入了rails server之后2

作者: will2yang | 来源:发表于2019-11-05 20:23 被阅读0次
    # bin/rails
    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'
    

    1.首先载入spring,这里不做了解. 如果这里加载了spring那么下面的代码就不会执行了。
    2.设置好APP_PATH之后的rails/commands里面会使用到.
    3.加载 ../config/boot 文件

    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.
    

    而 bundler/setup 的作用主要是激活BUNDLE_GEMFILE文件所需要的gem依赖包,这里不做了解.然后就是require 'rails/commands':

    # 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::Command.invoke 'server', ARGV
    
    module Rails
      module Command
        class << self
          # 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
    
          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
        end
      end
    end
    

    Rails寻找namespace类似于Thor的,它只有一条规则:
    命令名必须以“_command.rb”结尾。这是必需的,因为Rails根据加载路径来寻找文件,并且在在使用前才加载.
    通过这几个命名空间 :webrat, :rails, :integration

    将寻找以下命令:
    "rails:webrat", "webrat:integration", "webrat"

    autoload :Behavior
    autoload :Base
    

    首先看 behavior,找到并且require command文件:

    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
    

    之后看base, 用这个方法会将ServerCommand加入到subclassed里:

    def inherited(base) #:nodoc:
      super
    
      if base.name && base.name !~ /Base$/
        Rails::Command.subclasses << base
      end
    end
    

    最后就是我们这个 server_command的perform了

    def perform
      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)
        server.start
      end
    end
    

    将工作目录设置成rails app的根目录

    def set_application_directory!
      Dir.chdir(File.expand_path("../..", APP_PATH)) unless File.exist?(File.expand_path("config.ru"))
    end
    

    如果是重启项目那么清空pid

    def prepare_restart
      FileUtils.rm_f(options[:pid]) if options[:restart]
    end
    

    实例化Rails:Server 设置好server的options 和 environment

    # lib/rails/commands/server/server_command
    def server_options
      {
        user_supplied_options: user_supplied_options,
        server:                @server,
        log_stdout:            @log_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
    # lib/rails/commands/server/server_command
    def initialize(options = nil)
      @default_options = options || {}
      super(@default_options)
      set_environment
    end
    
    # lib/rack/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
    
    # lib/rails/commands/server/server_command
    def set_environment
      ENV["RAILS_ENV"] ||= options[:environment]
    end
    

    加载 rails/config/application

    require_relative 'boot'
    
    require 'rails/all'
    
    # Require the gems listed in Gemfile, including any gems
    # you've limited to :test, :development, or :production.
    Bundler.require(*Rails.groups)
    
    module RailsApp
      class Application < Rails::Application
        # Initialize configuration defaults for originally generated Rails version.
        config.load_defaults 5.2
    
        # Settings in config/environments/* take precedence over those specified here.
        # Application configuration can go into files in config/initializers
        # -- all .rb files in that directory are automatically loaded after loading
        # the framework and any gems in your application.
      end
    end
    

    rails/all require rails 所需要的基础组件

    # frozen_string_literal: true
    
    require "rails"
    
    %w(
      active_record/railtie
      active_storage/engine
      action_controller/railtie
      action_view/railtie
      action_mailer/railtie
      active_job/railtie
      action_cable/engine
      rails/test_unit/railtie
      sprockets/railtie
    ).each do |railtie|
      begin
        require railtie
      rescue LoadError
      end
    end
    

    Bundler.require(*Rails.groups) 根据rails运行的环境require相应的gem
    在Application继承Rails::Application的时候会调用钩子方法inherited:
    1.设置rails的app_class
    2.将rails app目录下的lib目录加入到$LOAD_PATH
    3.执行钩子before_configuration方法

    # lib/rails/application
    def inherited(base)
      super
      Rails.app_class = base
      add_lib_to_load_path!(find_root(base.called_from))
      ActiveSupport.run_load_hooks(:before_configuration, base)
    end
    

    如果base不是ABSTRACT_RAILTIES里的类,为了方便,将其加入到eager_load_namespaces里,并且根据caller_locations调用栈设置好called_from

    # lib/rails/engine
    def inherited(base)
      unless base.abstract_railtie?
        Rails::Railtie::Configuration.eager_load_namespaces << base
    
        base.called_from = begin
          call_stack = caller_locations.map { |l| l.absolute_path || l.path }
    
          File.dirname(call_stack.detect { |p| p !~ %r[railties[\w.-]*/lib/rails|rack[\w.-]*/lib/rack] })
        end
      end
    
      super
    end
    
    ABSTRACT_RAILTIES = %w(Rails::Railtie Rails::Engine Rails::Application)
    
    def abstract_railtie?
      ABSTRACT_RAILTIES.include?(name)
    end
    

    将继承railtie的类都加入到subclasses里方便后面的操作:

    # lib/rails/railtie
    def inherited(base)
      unless base.abstract_railtie?
        subclasses << base
      end
    end
    
    def application
      @application ||= (app_class.instance if app_class)
    end
    
    def config #:nodoc:
      @config ||= Application::Configuration.new(self.class.find_root(self.class.called_from))
    end
    

    相关文章

      网友评论

          本文标题:当你输入了rails server之后2

          本文链接:https://www.haomeiwen.com/subject/tbrgcqtx.html