developmentを実現したいのでコードを読んでみる#5

昨日ログを出力するように変更したrailsを使って、ほとんど素のrails3のアプリでサーバの起動、GETリクエストx3、サーバの停止までにActionDispatch::Reloaderの各メソッドがどのように呼びだされるのか、developmentとproductionの場合それぞれについて出力してみました。
https://github.com/akm/rails3_reloader_study/blob/master/actiondispatch_reloader_method_invocations_development.log
https://github.com/akm/rails3_reloader_study/blob/master/actiondispatch_reloader_method_invocations_production.log

production

起動時

ActionDispatch::Reloader.to_cleanup(&#)

module Rails
  class Application
    module Bootstrap
      include Initializable

    # (中略)

      initializer :set_clear_dependencies_hook, :group => :all do
        ActionDispatch::Reloader.to_cleanup do
          ActiveSupport::DescendantsTracker.clear
          ActiveSupport::Dependencies.clear
        end
      end

ActionDispatch::Reloader.to_prepare(&#)

module I18n
  class Railtie < Rails::Railtie

    # (中略)

    initializer "i18n.callbacks" do
      ActionDispatch::Reloader.to_prepare do
        I18n::Railtie.reloader.execute_if_updated
      end
    end

ActionDispatch::Reloader.prepare!
ActionDispatch::Reloader#initialize(nil)

module Rails
  class Application
    module Finisher
      include Initializable

      # (中略)

      initializer :run_prepare_callbacks do
        ActionDispatch::Reloader.prepare!
      end
module ActionDispatch
  class Reloader
    include ActiveSupport::Callbacks

    # (中略)

    # Execute all prepare callbacks.
    def self.prepare!
      self.trace_log(:prepare!)
      new(nil).run_callbacks :prepare
    end
GET /tests/foo?idx=1

ActionDispatch::Reloader.to_prepare(&#)

module Rails
  class Application
    module Finisher
      include Initializable

      # (中略)

      initializer :set_routes_reloader do |app|
        reloader = lambda { app.routes_reloader.execute_if_updated }
        reloader.call
        ActionDispatch::Reloader.to_prepare(&reloader)
      end


ActionDispatch::Reloader.to_cleanup(&#)
ActionDispatch::Reloader.to_prepare(&#)

module ActiveRecord
  # = Active Record Railtie
  class Railtie < Rails::Railtie

    # (中略)

    initializer "active_record.set_dispatch_hooks", :before => :set_clear_dependencies_hook do |app|
      ActiveSupport.on_load(:active_record) do
        ActionDispatch::Reloader.to_cleanup do
          ActiveRecord::Base.clear_reloadable_connections!
          ActiveRecord::Base.clear_cache!
        end
      end
    end

    config.after_initialize do
      ActiveSupport.on_load(:active_record) do
        instantiate_observers

        ActionDispatch::Reloader.to_prepare do
          ActiveRecord::Base.instantiate_observers
        end
      end
    end
GET /tests/foo?idx=2

なし

GET /tests/foo?idx=3

なし

サーバを停止

なし

まとめ

development

起動時

ActionDispatch::Reloader.to_cleanup(&#)
ActionDispatch::Reloader.to_prepare(&#)
これらはproductionと同じ。

ActionDispatch::Reloader#initialize(#

  /Users/akima/workspace/rails/actionpack/lib/action_dispatch/middleware/stack.rb:112:in `build'
  /Users/akima/workspace/rails/railties/lib/rails/engine.rb:447:in `app'
module ActionDispatch
  class MiddlewareStack

    # (中略)

    def build(app = nil, &block)
      app ||= block
      raise "MiddlewareStack#build requires an app" unless app
      middlewares.reverse.inject(app) { |a, e| e.build(a) }
    end
module Rails
  class Engine < Railtie

    # (中略)

    def app
      @app ||= begin
        config.middleware = config.middleware.merge_into(default_middleware_stack)
        config.middleware.build(endpoint)
      end
    end
GET /tests/foo?idx=1

ActionDispatch::Reloader.prepare!
ActionDispatch::Reloader#initialize(nil)
これらはproductionの起動時に実行されるものと同じ

ActionDispatch::Reloader.to_prepare(&#)
productionのGET /tests/foo?idx=1で実行されるものと同じ


ActionDispatch::Reloader#call({"GATEWAY_INTERFACE"=>"CGI/1.1", "PATH_INFO"=>"/tests/foo", "QUERY_STRING"=>"idx=1", ...)

module Rack

  class Sendfile

    # (中略)

    def call(env)
      status, headers, body = @app.call(env)
      if body.respond_to?(:to_path)
        case type = variation(env)

ActionDispatch::Reloader.to_cleanup(&#)
ActionDispatch::Reloader.to_prepare(&#)
productionのGET /tests/foo?idx=1で実行されるものと同じ


ActiveRecord::ConnectionAdapters::ConnectionManagement::Proxy#close
ActionDispatch::Reloader.cleanup!
ActionDispatch::Reloader#initialize(nil)

module Rack
  module Handler
      def service(req, res)

        # (中略)

        status, headers, body = @app.call(env)
        begin
          res.status = status.to_i
          headers.each { |k, vs|
            if k.downcase == "set-cookie"
              res.cookies.concat vs.split("\n")
            else
              # Since WEBrick won't accept repeated headers,
              # merge the values per RFC 1945 section 4.2.
              res[k] = vs.split("\n").join(", ")
            end
          }
          body.each { |part|
            res.body << part
          }
        ensure
          body.close  if body.respond_to? :close
        end
      end
module ActionDispatch
  class Reloader

    # (中略)

    # Execute all cleanup callbacks.
    def self.cleanup!
      self.trace_log(:cleanup!)
      new(nil).run_callbacks :cleanup
    end

    # (中略)

    module CleanupOnClose
      def close
        ActionDispatch::Reloader.trace_log_impl("#{self.class.name}#close")
        super if defined?(super)
      ensure
        ActionDispatch::Reloader.cleanup!
      end
    end
GET /tests/foo?idx=2

ActionDispatch::Reloader#call({"GATEWAY_INTERFACE"=>"CGI/1.1", "PATH_INFO"=>"/tests/foo", "QUERY_STRING"=>"idx=2", ...)

GET /tests/foo?idx=3

ActiveRecord::ConnectionAdapters::ConnectionManagement::Proxy#close
ActionDispatch::Reloader.cleanup!
ActionDispatch::Reloader#initialize(nil)
ActionDispatch::Reloader#call({"GATEWAY_INTERFACE"=>"CGI/1.1", "PATH_INFO"=>"/tests/foo", "QUERY_STRING"=>"idx=3", ...)

サーバを停止

ActiveRecord::ConnectionAdapters::ConnectionManagement::Proxy#close
ActionDispatch::Reloader.cleanup!
ActionDispatch::Reloader#initialize(nil)