aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_controller/dispatcher.rb
blob: 0cfd451c0437c1ee3ace779e366b365bc9123309 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
module ActionController
  # Dispatches requests to the appropriate controller and takes care of
  # reloading the app after each request when Dependencies.load? is true.
  class Dispatcher
    class << self
      def define_dispatcher_callbacks(cache_classes)
        unless cache_classes
          # Development mode callbacks
          before_dispatch :reload_application
          after_dispatch :cleanup_application
        end

        if defined?(ActiveRecord)
          after_dispatch :checkin_connections
          to_prepare(:activerecord_instantiate_observers) { ActiveRecord::Base.instantiate_observers }
        end

        after_dispatch :flush_logger if Base.logger && Base.logger.respond_to?(:flush)

        to_prepare do
          I18n.reload!
        end
      end

      # DEPRECATE: Remove CGI support
      def dispatch(cgi = nil, session_options = CgiRequest::DEFAULT_SESSION_OPTIONS, output = $stdout)
        new(output).dispatch_cgi(cgi, session_options)
      end

      # Add a preparation callback. Preparation callbacks are run before every
      # request in development mode, and before the first request in production
      # mode.
      #
      # An optional identifier may be supplied for the callback. If provided,
      # to_prepare may be called again with the same identifier to replace the
      # existing callback. Passing an identifier is a suggested practice if the
      # code adding a preparation block may be reloaded.
      def to_prepare(identifier = nil, &block)
        @prepare_dispatch_callbacks ||= ActiveSupport::Callbacks::CallbackChain.new
        callback = ActiveSupport::Callbacks::Callback.new(:prepare_dispatch, block, :identifier => identifier)
        @prepare_dispatch_callbacks.replace_or_append!(callback)
      end
    end

    cattr_accessor :middleware
    self.middleware = MiddlewareStack.new do |middleware|
      middleware.use "ActionController::Lock", :if => lambda {
        !ActionController::Base.allow_concurrency
      }
      middleware.use "ActionController::Failsafe"
      middleware.use "ActiveRecord::QueryCache" if defined?(ActiveRecord)

      ["ActionController::Session::CookieStore",
       "ActionController::Session::MemCacheStore",
       "ActiveRecord::SessionStore"].each do |store|
          middleware.use(store, ActionController::Base.session_options,
            :if => lambda {
              if session_store = ActionController::Base.session_store
                session_store.name == store
              end
            }
          )
      end
    end

    include ActiveSupport::Callbacks
    define_callbacks :prepare_dispatch, :before_dispatch, :after_dispatch

    # DEPRECATE: Remove arguments, since they are only used by CGI
    def initialize(output = $stdout, request = nil, response = nil)
      @output = output
      @app = @@middleware.build(lambda { |env| self.dup._call(env) })
    end

    def dispatch
      begin
        run_callbacks :before_dispatch
        controller = Routing::Routes.recognize(@request)
        controller.process(@request, @response).to_a
      rescue Exception => exception
        if controller ||= (::ApplicationController rescue Base)
          controller.process_with_exception(@request, @response, exception).to_a
        else
          raise exception
        end
      ensure
        run_callbacks :after_dispatch, :enumerator => :reverse_each
      end
    end

    # DEPRECATE: Remove CGI support
    def dispatch_cgi(cgi, session_options)
      CGIHandler.dispatch_cgi(self, cgi, @output)
    end

    def call(env)
      @app.call(env)
    end

    def _call(env)
      @request = RackRequest.new(env)
      @response = Response.new
      dispatch
    end

    def reload_application
      # Run prepare callbacks before every request in development mode
      run_callbacks :prepare_dispatch

      Routing::Routes.reload
      ActionView::Helpers::AssetTagHelper::AssetTag::Cache.clear
    end

    # Cleanup the application by clearing out loaded classes so they can
    # be reloaded on the next request without restarting the server.
    def cleanup_application
      ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
      ActiveSupport::Dependencies.clear
      ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
    end

    def flush_logger
      Base.logger.flush
    end

    def checkin_connections
      # Don't return connection (and peform implicit rollback) if this request is a part of integration test
      # TODO: This callback should have direct access to env
      return if @request.key?("rack.test")
      ActiveRecord::Base.clear_active_connections!
    end
  end
end