Railsコードリーディング config.ru編
code:shell
$ cat config.ru
code:ruby
# This file is used by Rack-based servers to start the application.
require_relative 'config/environment'
run Rails.application
code:shell
$ cat config/environment.rb
code:ruby
# Load the Rails application.
require_relative 'application'
# Initialize the Rails application.
Rails.application.initialize!
code:shell
$ cat config/application.rb
code:ruby
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 Codereadingrails
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
require_relative 'boot'
bundler/setupとbootsnap/setupをやってる
require 'rails/all'
code:shell
$ cat vendor/bundle/ruby/2.6.0/gems/railties-5.2.4.4/lib/rails/all.rb
code:ruby
# 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
railsをrequireしている
Rails内にある各componentのRailtieをrequireしている
後述するようにEngineもRailtieである
code:shell
$ cat railties/lib/rails.rb
code:ruby
(以下は抜粋)
require "rails/application"
require "active_support/railtie"
require "action_dispatch/railtie"
module Rails
class << self
@application = @app_class = nil
attr_writer :application
attr_accessor :app_class, :cache, :logger
def application
@application ||= (app_class.instance if app_class)
end
delegate :initialize!, :initialized?, to: :application
# The Configuration instance used to configure the Rails environment
def configuration
application.config
end
# Returns a Pathname object of the current Rails project,
# otherwise it returns +nil+ if there is no project:
#
# Rails.root
def root
application && application.config.root
end
end
end
Rails.applicationやRails.configuration、Rails.rootなどが定義されている
code:shell
$ cat active_record/lib/active_record/railtie.rb
code:ruby
# frozen_string_literal: true
require "active_record"
require "rails"
require "active_model/railtie"
# For now, action_controller must always be present with
# Rails, so let's make sure that it gets required before
# here. This is needed for correctly setting up the middleware.
# In the future, this might become an optional require.
require "action_controller/railtie"
module ActiveRecord
# = Active Record Railtie
class Railtie < Rails::Railtie # :nodoc:
(略)
end
end
まず、Rails::Railtieのドキュメントを読む
code:shell
$ cat railtie/rails/railtie.rb
code:ruby
# <tt>Rails::Railtie</tt> is the core of the Rails framework and provides
# several hooks to extend Rails and/or modify the initialization process.
#
# Every major component of Rails (Action Mailer, Action Controller, Active
# Record, etc.) implements a railtie. Each of them is responsible for their
# own initialization. This makes Rails itself absent of any component hooks,
# allowing other components to be used in place of any of the Rails defaults.
#
# Developing a Rails extension does _not_ require implementing a railtie, but
# if you need to interact with the Rails framework during or after boot, then
# a railtie is needed.
#
# For example, an extension doing any of the following would need a railtie:
#
# * creating initializers
# * configuring a Rails framework for the application, like setting a generator
# * adding <tt>config.*</tt> keys to the environment
# * setting up a subscriber with <tt>ActiveSupport::Notifications</tt>
# * adding Rake tasks
#
# == Creating a Railtie
#
# To extend Rails using a railtie, create a subclass of <tt>Rails::Railtie</tt>.
# This class must be loaded during the Rails boot process, and is conventionally
# called <tt>MyNamespace::Railtie</tt>.
#
# The following example demonstrates an extension which can be used with or
# without Rails.
#
# # lib/my_gem/railtie.rb
# module MyGem
# class Railtie < Rails::Railtie
# end
# end
#
# # lib/my_gem.rb
# require 'my_gem/railtie' if defined?(Rails)
#
# == Initializers
#
# To add an initialization step to the Rails boot process from your railtie, just
# define the initialization code with the +initializer+ macro:
#
# class MyRailtie < Rails::Railtie
# initializer "my_railtie.configure_rails_initialization" do
# # some initialization behavior
# end
# end
#
# If specified, the block can also receive the application object, in case you
# need to access some application-specific configuration, like middleware:
#
# class MyRailtie < Rails::Railtie
# initializer "my_railtie.configure_rails_initialization" do |app|
# app.middleware.use MyRailtie::Middleware
# end
# end
#
# Finally, you can also pass <tt>:before</tt> and <tt>:after</tt> as options to
# +initializer+, in case you want to couple it with a specific step in the
# initialization process.
#
# == Configuration
#
# Railties can access a config object which contains configuration shared by all
# railties and the application:
#
# class MyRailtie < Rails::Railtie
# # Customize the ORM
# config.app_generators.orm :my_railtie_orm
#
# # Add a to_prepare block which is executed once in production
# # and before each request in development.
# config.to_prepare do
# MyRailtie.setup!
# end
# end
#
# == Loading Rake Tasks and Generators
#
# If your railtie has Rake tasks, you can tell Rails to load them through the method
# +rake_tasks+:
#
# class MyRailtie < Rails::Railtie
# rake_tasks do
# load 'path/to/my_railtie.tasks'
# end
# end
#
# By default, Rails loads generators from your load path. However, if you want to place
# your generators at a different location, you can specify in your railtie a block which
# will load them during normal generators lookup:
#
# class MyRailtie < Rails::Railtie
# generators do
# require 'path/to/my_railtie_generator'
# end
# end
#
# Since filenames on the load path are shared across gems, be sure that files you load
# through a railtie have unique names.
#
# == Application and Engine
#
# An engine is nothing more than a railtie with some initializers already set. And since
# <tt>Rails::Application</tt> is an engine, the same configuration described here can be
# used in both.
#
# Be sure to look at the documentation of those specific classes for more information.
railtie/lib/rails.rbの中で railtie/rails/applicationがrequire されてるのでそれを読む
code:shell
$ cat railtie/rails/application
code:ruby
# frozen_string_literal: true
require "yaml"
require "active_support/core_ext/hash/keys"
require "active_support/core_ext/object/blank"
require "active_support/key_generator"
require "active_support/message_verifier"
require "active_support/encrypted_configuration"
require "active_support/deprecation"
require "rails/engine"
require "rails/secrets"
module Rails
# An Engine with the responsibility of coordinating the whole boot process.
#
# == Initialization
#
# Rails::Application is responsible for executing all railties and engines
# initializers. It also executes some bootstrap initializers (check
# Rails::Application::Bootstrap) and finishing initializers, after all the others
# are executed (check Rails::Application::Finisher).
#
# == Configuration
#
# Besides providing the same configuration as Rails::Engine and Rails::Railtie,
# the application object has several specific configurations, for example
# "cache_classes", "consider_all_requests_local", "filter_parameters",
# "logger" and so forth.
#
# Check Rails::Application::Configuration to see them all.
#
# == Routes
#
# The application object is also responsible for holding the routes and reloading routes
# whenever the files change in development.
#
# == Middlewares
#
# The Application is also responsible for building the middleware stack.
#
# == Booting process
#
# The application is also responsible for setting up and executing the booting
# process. From the moment you require "config/application.rb" in your app,
# the booting process goes like this:
#
# 1) require "config/boot.rb" to setup load paths
# 2) require railties and engines
# 3) Define Rails.application as "class MyApp::Application < Rails::Application"
# 4) Run config.before_configuration callbacks
# 5) Load config/environments/ENV.rb
# 6) Run config.before_initialize callbacks
# 7) Run Railtie#initializer defined by railties, engines and application.
# One by one, each engine sets up its load paths, routes and runs its config/initializers/* files.
# 8) Custom Railtie#initializers added by railties, engines and applications are executed
# 9) Build the middleware stack and run to_prepare callbacks
# 10) Run config.before_eager_load and eager_load! if eager_load is true
# 11) Run config.after_initialize callbacks
#
# == Multiple Applications
#
# If you decide to define multiple applications, then the first application
# that is initialized will be set to +Rails.application+, unless you override
# it with a different application.
#
# To create a new application, you can instantiate a new instance of a class
# that has already been created:
#
# class Application < Rails::Application
# end
#
# first_application = Application.new
# second_application = Application.new(config: first_application.config)
#
# In the above example, the configuration from the first application was used
# to initialize the second application. You can also use the +initialize_copy+
# on one of the applications to create a copy of the application which shares
# the configuration.
#
# If you decide to define Rake tasks, runners, or initializers in an
# application other than +Rails.application+, then you must run them manually.
class Application < Engine
(略)
end
end
Rails::Engineとはなんぞやということで見てみてると、Railtieでした。
略した部分を読んでみると、以下のところが大事そうだった
code:ruby
module Rails
class Application < Engine
class << self
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
end
end
end
config/application.rbで MyApp::Application がdefineされるときに、上のinheritedが実行され、Rails.app_classがMyApp::Applicationになる
code:shell
$ cat railtie/rails/engine
code:ruby
# frozen_string_literal: true
require "rails/railtie"
require "rails/engine/railties"
require "active_support/core_ext/module/delegation"
require "pathname"
require "thread"
module Rails
# <tt>Rails::Engine</tt> allows you to wrap a specific Rails application or subset of
# functionality and share it with other applications or within a larger packaged application.
# Every <tt>Rails::Application</tt> is just an engine, which allows for simple
# feature and application sharing.
#
# Any <tt>Rails::Engine</tt> is also a <tt>Rails::Railtie</tt>, so the same
# methods (like <tt>rake_tasks</tt> and +generators+) and configuration
# options that are available in railties can also be used in engines.
#
# == Creating an Engine
#
# If you want a gem to behave as an engine, you have to specify an +Engine+
# for it somewhere inside your plugin's +lib+ folder (similar to how we
# specify a +Railtie+):
#
# # lib/my_engine.rb
# module MyEngine
# class Engine < Rails::Engine
# end
# end
#
# Then ensure that this file is loaded at the top of your <tt>config/application.rb</tt>
# (or in your +Gemfile+) and it will automatically load models, controllers and helpers
# inside +app+, load routes at <tt>config/routes.rb</tt>, load locales at
# <tt>config/locales/*</tt>, and load tasks at <tt>lib/tasks/*</tt>.
#
# == Configuration
#
# Besides the +Railtie+ configuration which is shared across the application, in a
# <tt>Rails::Engine</tt> you can access <tt>autoload_paths</tt>, <tt>eager_load_paths</tt>
# and <tt>autoload_once_paths</tt>, which, differently from a <tt>Railtie</tt>, are scoped to
# the current engine.
#
# class MyEngine < Rails::Engine
# # Add a load path for this specific Engine
# config.autoload_paths << File.expand_path("lib/some/path", __dir__)
#
# initializer "my_engine.add_middleware" do |app|
# app.middleware.use MyEngine::Middleware
# end
# end
#
# == Generators
#
# You can set up generators for engines with <tt>config.generators</tt> method:
#
# class MyEngine < Rails::Engine
# config.generators do |g|
# g.orm :active_record
# g.template_engine :erb
# g.test_framework :test_unit
# end
# end
#
# You can also set generators for an application by using <tt>config.app_generators</tt>:
#
# class MyEngine < Rails::Engine
# # note that you can also pass block to app_generators in the same way you
# # can pass it to generators method
# config.app_generators.orm :datamapper
# end
#
# == Paths
#
# Applications and engines have flexible path configuration, meaning that you
# are not required to place your controllers at <tt>app/controllers</tt>, but
# in any place which you find convenient.
#
# For example, let's suppose you want to place your controllers in <tt>lib/controllers</tt>.
# You can set that as an option:
#
# class MyEngine < Rails::Engine
# end
#
# You can also have your controllers loaded from both <tt>app/controllers</tt> and
# <tt>lib/controllers</tt>:
#
# class MyEngine < Rails::Engine
# end
#
# The available paths in an engine are:
#
# class MyEngine < Rails::Engine
# end
#
# The <tt>Application</tt> class adds a couple more paths to this set. And as in your
# <tt>Application</tt>, all folders under +app+ are automatically added to the load path.
# If you have an <tt>app/services</tt> folder for example, it will be added by default.
#
# == Endpoint
#
# An engine can also be a Rack application. It can be useful if you have a Rack application that
# you would like to wrap with +Engine+ and provide with some of the +Engine+'s features.
#
# To do that, use the +endpoint+ method:
#
# module MyEngine
# class Engine < Rails::Engine
# endpoint MyRackApplication
# end
# end
#
# Now you can mount your engine in application's routes just like that:
#
# Rails.application.routes.draw do
# mount MyEngine::Engine => "/engine"
# end
#
# == Middleware stack
#
# As an engine can now be a Rack endpoint, it can also have a middleware
# stack. The usage is exactly the same as in <tt>Application</tt>:
#
# module MyEngine
# class Engine < Rails::Engine
# middleware.use SomeMiddleware
# end
# end
#
# == Routes
#
# If you don't specify an endpoint, routes will be used as the default
# endpoint. You can use them just like you use an application's routes:
#
# # ENGINE/config/routes.rb
# MyEngine::Engine.routes.draw do
# get "/" => "posts#index"
# end
#
# == Mount priority
#
# Note that now there can be more than one router in your application, and it's better to avoid
# passing requests through many routers. Consider this situation:
#
# Rails.application.routes.draw do
# mount MyEngine::Engine => "/blog"
# get "/blog/omg" => "main#omg"
# end
#
# +MyEngine+ is mounted at <tt>/blog</tt>, and <tt>/blog/omg</tt> points to application's
# controller. In such a situation, requests to <tt>/blog/omg</tt> will go through +MyEngine+,
# and if there is no such route in +Engine+'s routes, it will be dispatched to <tt>main#omg</tt>.
# It's much better to swap that:
#
# Rails.application.routes.draw do
# get "/blog/omg" => "main#omg"
# mount MyEngine::Engine => "/blog"
# end
#
# Now, +Engine+ will get only requests that were not handled by +Application+.
#
# == Engine name
#
# There are some places where an Engine's name is used:
#
# * routes: when you mount an Engine with <tt>mount(MyEngine::Engine => '/my_engine')</tt>,
# it's used as default <tt>:as</tt> option
# * rake task for installing migrations <tt>my_engine:install:migrations</tt>
#
# Engine name is set by default based on class name. For <tt>MyEngine::Engine</tt> it will be
# <tt>my_engine_engine</tt>. You can change it manually using the <tt>engine_name</tt> method:
#
# module MyEngine
# class Engine < Rails::Engine
# engine_name "my_engine"
# end
# end
#
# == Isolated Engine
#
# Normally when you create controllers, helpers and models inside an engine, they are treated
# as if they were created inside the application itself. This means that all helpers and
# named routes from the application will be available to your engine's controllers as well.
#
# However, sometimes you want to isolate your engine from the application, especially if your engine
# has its own router. To do that, you simply need to call +isolate_namespace+. This method requires
# you to pass a module where all your controllers, helpers and models should be nested to:
#
# module MyEngine
# class Engine < Rails::Engine
# isolate_namespace MyEngine
# end
# end
#
# With such an engine, everything that is inside the +MyEngine+ module will be isolated from
# the application.
#
# Consider this controller:
#
# module MyEngine
# class FooController < ActionController::Base
# end
# end
#
# If the +MyEngine+ engine is marked as isolated, +FooController+ only has
# access to helpers from +MyEngine+, and <tt>url_helpers</tt> from
# <tt>MyEngine::Engine.routes</tt>.
#
# The next thing that changes in isolated engines is the behavior of routes.
# Normally, when you namespace your controllers, you also need to namespace
# the related routes. With an isolated engine, the engine's namespace is
# automatically applied, so you don't need to specify it explicitly in your
# routes:
#
# MyEngine::Engine.routes.draw do
# resources :articles
# end
#
# If +MyEngine+ is isolated, The routes above will point to
# <tt>MyEngine::ArticlesController</tt>. You also don't need to use longer
# url helpers like +my_engine_articles_path+. Instead, you should simply use
# +articles_path+, like you would do with your main application.
#
# To make this behavior consistent with other parts of the framework,
# isolated engines also have an effect on <tt>ActiveModel::Naming</tt>. In a
# normal Rails app, when you use a namespaced model such as
# <tt>Namespace::Article</tt>, <tt>ActiveModel::Naming</tt> will generate
# names with the prefix "namespace". In an isolated engine, the prefix will
# be omitted in url helpers and form fields, for convenience.
#
# polymorphic_url(MyEngine::Article.new)
# # => "articles_path" # not "my_engine_articles_path"
#
# form_for(MyEngine::Article.new) do
# text_field :title # => <input type="text" name="articletitle" id="article_title" /> # end
#
# Additionally, an isolated engine will set its own name according to its
# namespace, so <tt>MyEngine::Engine.engine_name</tt> will return
# "my_engine". It will also set +MyEngine.table_name_prefix+ to "my_engine_",
# meaning for example that <tt>MyEngine::Article</tt> will use the
# +my_engine_articles+ database table by default.
#
# == Using Engine's routes outside Engine
#
# Since you can now mount an engine inside application's routes, you do not have direct access to +Engine+'s
# <tt>url_helpers</tt> inside +Application+. When you mount an engine in an application's routes, a special helper is
# created to allow you to do that. Consider such a scenario:
#
# # config/routes.rb
# Rails.application.routes.draw do
# mount MyEngine::Engine => "/my_engine", as: "my_engine"
# get "/foo" => "foo#index"
# end
#
# Now, you can use the <tt>my_engine</tt> helper inside your application:
#
# class FooController < ApplicationController
# def index
# my_engine.root_url # => /my_engine/
# end
# end
#
# There is also a <tt>main_app</tt> helper that gives you access to application's routes inside Engine:
#
# module MyEngine
# class BarController
# def index
# main_app.foo_path # => /foo
# end
# end
# end
#
# Note that the <tt>:as</tt> option given to mount takes the <tt>engine_name</tt> as default, so most of the time
# you can simply omit it.
#
# Finally, if you want to generate a url to an engine's route using
# <tt>polymorphic_url</tt>, you also need to pass the engine helper. Let's
# say that you want to create a form pointing to one of the engine's routes.
# All you need to do is pass the helper as the first element in array with
# attributes for url:
#
#
# This code will use <tt>my_engine.user_path(@user)</tt> to generate the proper route.
#
# == Isolated engine's helpers
#
# Sometimes you may want to isolate engine, but use helpers that are defined for it.
# If you want to share just a few specific helpers you can add them to application's
# helpers in ApplicationController:
#
# class ApplicationController < ActionController::Base
# helper MyEngine::SharedEngineHelper
# end
#
# If you want to include all of the engine's helpers, you can use the #helper method on an engine's # instance:
#
# class ApplicationController < ActionController::Base
# helper MyEngine::Engine.helpers
# end
#
# It will include all of the helpers from engine's directory. Take into account that this does
# not include helpers defined in controllers with helper_method or other similar solutions,
# only helpers defined in the helpers directory will be included.
#
# == Migrations & seed data
#
# Engines can have their own migrations. The default path for migrations is exactly the same
# as in application: <tt>db/migrate</tt>
#
# To use engine's migrations in application you can use the rake task below, which copies them to
# application's dir:
#
# rake ENGINE_NAME:install:migrations
#
# Note that some of the migrations may be skipped if a migration with the same name already exists
# in application. In such a situation you must decide whether to leave that migration or rename the
# migration in the application and rerun copying migrations.
#
# If your engine has migrations, you may also want to prepare data for the database in
# the <tt>db/seeds.rb</tt> file. You can load that data using the <tt>load_seed</tt> method, e.g.
#
# MyEngine::Engine.load_seed
#
# == Loading priority
#
# In order to change engine's priority you can use +config.railties_order+ in the main application.
# It will affect the priority of loading views, helpers, assets, and all the other files
# related to engine or application.
#
# # load Blog::Engine with highest priority, followed by application and other railties
class Engine < Railtie
(略)
end
end
再掲
code:shell
$ cat config/application.rb
code:ruby
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 Codereadingrails
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
config.ru
require config/environment
config/application.rb
require config/boot
require rails/all
require rails
require rails/application
require rails/engine
define Rails::Application
define Rails.application, Rails.configuration, Rails.root
require railties
define MyApp::Application
set Rails.app_class = MyApp::Application
run Rails.application.initialize!
MyApp::Application.instance.initialize!
== Rails::Application.instance.initialize!
というわけで、Rails::Applicationを読んでいく
code:shell
$ cat railties/lib/rails/application.rb
code:ruby
module Rails
class Application < Engine
class << self
def instance
super.run_load_hooks! # superはRailtie#instanceを参照している。中身はnewしてるだけ。
end
def run_load_hooks! # :nodoc:
return self if @ran_load_hooks
@ran_load_hooks = true
# @initial_variable_values == {}なので何も起こらない
@initial_variable_values.each do |variable_name, value|
if INITIAL_VARIABLES.include?(variable_name)
instance_variable_set("@#{variable_name}", value)
end
end
# @blockなしなので何も起こらない
instance_eval(&@block) if @block
self
end
# Initialize the application passing the given group. By default, the
# group is :default
def initialize!(group = :default) #:nodoc: raise "Application has been already initialized." if @initialized
run_initializers(group, self) # Initializableのrun_initializersを参照
@initialized = true
self
end
end
end
end
code:shell
$ cat railties/lib/rails/railtie.rb
code:ruby
module Rails
class Railtie
include Initializable
def instance
@instance ||= new
end
end
end
code:shell
$ cat railties/lib/rails/initializable.rb
code:ruby
# frozen_string_literal: true
require "tsort"
module Rails
module Initializable
base.extend ClassMethods
end
def run_initializers(group = :default, *args)
return if instance_variable_defined?(:@ran)
# initializerをトポロジカルソートして、beforeとかafterの設定に矛盾しないようにinitializerを実行する
# initializersは、このモジュールのinclude先(このコンテキストではRails::Application)の祖先全部のinitializersメソッドの返り値を結合したコレクション
# つまり、Rails::Application、Rails::Engine、Rails::Railtieのinitializersの返り値全てのinitializerが実行される
initializers.tsort_each do |initializer|
initializer.run(*args) if initializer.belongs_to?(group)
end
@ran = true
end
def initializers
@initializers ||= self.class.initializers_for(self)
end
module ClassMethods
def initializers
@initializers ||= Collection.new # トポロジカルソートを実装したコレクション
end
def initializer(name, opts = {}, &blk)
raise ArgumentError, "A block must be passed when defining an initializer" unless blk
opts:after ||= initializers.last.name unless initializers.empty? || initializers.find { |i| i.name == opts:before } initializers << Initializer.new(name, nil, opts, &blk)
end
end
end
end
というわけで、Rails::Application、Rails::Engine、Rails::Railtieのinitializersを見ていく
code:ruby
module Rails
class Application < Engine
Bootstrap.initializers_for(self) +
railties_initializers(super) +
Finisher.initializers_for(self)
end
def railties_initializers(current) #:nodoc: initializers = []
ordered_railties.reverse.flatten.each do |r|
if r == self
initializers += current
else
initializers += r.initializers
end
end
initializers
end
# Returns the ordered railties for this application considering railties_order.
@ordered_railties ||= begin
order = config.railties_order.map do |railtie|
if railtie == :main_app
self
elsif railtie.respond_to?(:instance)
railtie.instance
else
railtie
end
end
# この railties が肝。これはRails::Engineで定義されているメソッドで、Rails::Engine::Railtiesのインスタンスが入っている。
# Railtiesは、Rails::RailtieとRails::Engineの全てのサブクラスを含んでいる。
# そのため、あちらこちらで require 'hogehoge/railtie' を実行して、Railtieのサブクラスを定義しつつ、initializer を書いておけば、それがすべてここで取得される。
all = (railties - order)
all.push(self) unless (all + order).include?(self)
order.push(:all) unless order.include?(:all)
index = order.index(:all)
order
end
end
end
end
ここまでで config/environment.rbの中身が理解できたので、 config.ruの続きに戻る
code:ruby
# config.ru
# This file is used by Rack-based servers to start the application.
require_relative 'config/environment'
run Rails.application
Rails.application はRails::MyAppのインスタンスであり、ほぼそのままRails::Applicationのインスタンスである
Rails::Application の call あたりを見てみる
Rails::Applicationのcallは存在しなかったのでRails::Engineを見る
code:ruby
# railties/lib/rails/engine.rb
module Rails
class Engine < Railtie
# Define the Rack API for this engine.
def call(env)
req = build_request env
app.call req.env
end
# Returns the underlying Rack application for this engine.
def app
@app || @app_build_lock.synchronize {
@app ||= begin
stack = default_middleware_stack
config.middleware = build_middleware.merge_into(stack)
config.middleware.build(endpoint)
end
}
end
# Returns the endpoint for this engine. If none is registered,
# defaults to an ActionDispatch::Routing::RouteSet.
def endpoint
self.class.endpoint || routes
end
# Defines the routes for this engine. If a block is given to
# routes, it is appended to the engine.
def routes(&block)
@routes ||= ActionDispatch::Routing::RouteSet.new_with_config(config)
@routes.append(&block) if block_given?
@routes
end
class << self
def endpoint(endpoint = nil)
@endpoint ||= nil
@endpoint = endpoint if endpoint
@endpoint
end
end
private
def default_middleware_stack
ActionDispatch::MiddlewareStack.new
end
def build_middleware
config.middleware
end
end
end
middlewareを組み立てて、routesを覆ってrack applicationとする
起動シーケンスはだいたいわかった気がする。これ以上深入りするには、各コンポーネントのコードを読んでいく必要がありそう