diff options
Diffstat (limited to 'actionpack/lib/action_controller')
19 files changed, 158 insertions, 146 deletions
diff --git a/actionpack/lib/action_controller/api.rb b/actionpack/lib/action_controller/api.rb index b4594bf302..1a46d49a49 100644 --- a/actionpack/lib/action_controller/api.rb +++ b/actionpack/lib/action_controller/api.rb @@ -115,7 +115,6 @@ module ActionController Rendering, Renderers::All, ConditionalGet, - RackDelegation, BasicImplicitRender, StrongParameters, diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index c0cd4ed2c6..0727bb8369 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -213,7 +213,6 @@ module ActionController Renderers::All, ConditionalGet, EtagWithTemplateDigest, - RackDelegation, Caching, MimeResponds, ImplicitRender, @@ -252,7 +251,7 @@ module ActionController # Define some internal variables that should not be propagated to the view. PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [ - :@_status, :@_headers, :@_params, :@_response, :@_request, + :@_params, :@_response, :@_request, :@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout ] def _protected_ivars # :nodoc: diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index a4e4992cfe..0b8fa2ea09 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -1,6 +1,5 @@ require 'fileutils' require 'uri' -require 'set' module ActionController # \Caching is a cheap way of speeding up slow applications by keeping the result of @@ -46,7 +45,6 @@ module ActionController end end - include RackDelegation include AbstractController::Callbacks include ConfigMethods diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index 914b0d4b30..030a1f3478 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -1,6 +1,8 @@ require 'active_support/core_ext/array/extract_options' require 'action_dispatch/middleware/stack' require 'active_support/deprecation' +require 'action_dispatch/http/request' +require 'action_dispatch/http/response' module ActionController # Extend ActionDispatch middleware stack to make it aware of options @@ -132,23 +134,30 @@ module ActionController @controller_name ||= name.demodulize.sub(/Controller$/, '').underscore end + def self.make_response!(request) + ActionDispatch::Response.new.tap do |res| + res.request = request + end + end + + def self.build_with_env(env = {}) #:nodoc: + new.tap { |c| + c.set_request! ActionDispatch::Request.new(env) + c.set_response! make_response!(c.request) + } + end + # Delegates to the class' <tt>controller_name</tt> def controller_name self.class.controller_name end - # The details below can be overridden to support a specific - # Request and Response object. The default ActionController::Base - # implementation includes RackDelegation, which makes a request - # and response object available. You might wish to control the - # environment and response manually for performance reasons. - - attr_internal :headers, :response, :request + attr_internal :response, :request delegate :session, :to => "@_request" + delegate :headers, :status=, :location=, :content_type=, + :status, :location, :content_type, :to => "@_response" def initialize - @_headers = {"Content-Type" => "text/html"} - @_status = 200 @_request = nil @_response = nil @_routes = nil @@ -163,63 +172,46 @@ module ActionController @_params = val end - # Basic implementations for content_type=, location=, and headers are - # provided to reduce the dependency on the RackDelegation module - # in Renderer and Redirector. - - def content_type=(type) - headers["Content-Type"] = type.to_s - end - - def content_type - headers["Content-Type"] - end - - def location - headers["Location"] - end - - def location=(url) - headers["Location"] = url - end + alias :response_code :status # :nodoc: # Basic url_for that can be overridden for more robust functionality def url_for(string) string end - def status - @_status - end - alias :response_code :status # :nodoc: - - def status=(status) - @_status = Rack::Utils.status_code(status) - end - def response_body=(body) body = [body] unless body.nil? || body.respond_to?(:each) + response.body = body super end # Tests if render or redirect has already happened. def performed? - response_body || (response && response.committed?) + response_body || response.committed? end - def dispatch(name, request) #:nodoc: + def dispatch(name, request, response) #:nodoc: set_request!(request) + set_response!(response) process(name) to_a end + def set_response!(response) # :nodoc: + @_response = response + end + def set_request!(request) #:nodoc: @_request = request @_request.controller_instance = self end def to_a #:nodoc: - response ? response.to_a : [status, headers, response_body] + response.to_a + end + + def reset_session + @_request.reset_session end class_attribute :middleware_stack @@ -247,15 +239,32 @@ module ActionController req = ActionDispatch::Request.new env action(req.path_parameters[:action]).call(env) end + class << self; deprecate :call; end # Returns a Rack endpoint for the given action name. def self.action(name) if middleware_stack.any? middleware_stack.build(name) do |env| - new.dispatch(name, ActionDispatch::Request.new(env)) + req = ActionDispatch::Request.new(env) + res = make_response! req + new.dispatch(name, req, res) end else - lambda { |env| new.dispatch(name, ActionDispatch::Request.new(env)) } + lambda { |env| + req = ActionDispatch::Request.new(env) + res = make_response! req + new.dispatch(name, req, res) + } + end + end + + # Direct dispatch to the controller. Instantiates the controller, then + # executes the action named +name+. + def self.dispatch(name, req, res) + if middleware_stack.any? + middleware_stack.build(name) { |env| new.dispatch(name, req, res) }.call req.env + else + new.dispatch(name, req, res) end end end diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb index bb3ad9b850..89d589c486 100644 --- a/actionpack/lib/action_controller/metal/conditional_get.rb +++ b/actionpack/lib/action_controller/metal/conditional_get.rb @@ -4,7 +4,6 @@ module ActionController module ConditionalGet extend ActiveSupport::Concern - include RackDelegation include Head included do diff --git a/actionpack/lib/action_controller/metal/cookies.rb b/actionpack/lib/action_controller/metal/cookies.rb index d787f014cd..f8efb2b076 100644 --- a/actionpack/lib/action_controller/metal/cookies.rb +++ b/actionpack/lib/action_controller/metal/cookies.rb @@ -2,8 +2,6 @@ module ActionController #:nodoc: module Cookies extend ActiveSupport::Concern - include RackDelegation - included do helper_method :cookies end diff --git a/actionpack/lib/action_controller/metal/etag_with_template_digest.rb b/actionpack/lib/action_controller/metal/etag_with_template_digest.rb index f9303efe6c..669cf55bca 100644 --- a/actionpack/lib/action_controller/metal/etag_with_template_digest.rb +++ b/actionpack/lib/action_controller/metal/etag_with_template_digest.rb @@ -25,7 +25,7 @@ module ActionController class_attribute :etag_with_template_digest self.etag_with_template_digest = true - ActiveSupport.on_load :action_view, yield: true do |action_view_base| + ActiveSupport.on_load :action_view, yield: true do etag do |options| determine_template_etag(options) if etag_with_template_digest end diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb index f445094bdc..b2110bf946 100644 --- a/actionpack/lib/action_controller/metal/head.rb +++ b/actionpack/lib/action_controller/metal/head.rb @@ -28,7 +28,7 @@ module ActionController end status ||= :ok - + location = options.delete(:location) content_type = options.delete(:content_type) @@ -43,12 +43,9 @@ module ActionController if include_content?(self.response_code) self.content_type = content_type || (Mime[formats.first] if formats) - self.response.charset = false if self.response - else - headers.delete('Content-Type') - headers.delete('Content-Length') + self.response.charset = false end - + true end diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index bbb38cf8fc..15d4562abb 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -203,7 +203,7 @@ module ActionController password = password_procedure.call(credentials[:username]) return false unless password - method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD'] + method = request.get_header('rack.methodoverride.original_method') || request.get_header('REQUEST_METHOD') uri = credentials[:uri] [true, false].any? do |trailing_question_mark| @@ -260,8 +260,8 @@ module ActionController end def secret_token(request) - key_generator = request.env["action_dispatch.key_generator"] - http_auth_salt = request.env["action_dispatch.http_auth_salt"] + key_generator = request.key_generator + http_auth_salt = request.http_auth_salt key_generator.generate_key(http_auth_salt) end diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb index a3e1a71b0a..3dbf34eb2a 100644 --- a/actionpack/lib/action_controller/metal/instrumentation.rb +++ b/actionpack/lib/action_controller/metal/instrumentation.rb @@ -11,7 +11,6 @@ module ActionController extend ActiveSupport::Concern include AbstractController::Logger - include ActionController::RackDelegation attr_internal :view_runtime diff --git a/actionpack/lib/action_controller/metal/live.rb b/actionpack/lib/action_controller/metal/live.rb index 58150cd9a9..69583f8ab4 100644 --- a/actionpack/lib/action_controller/metal/live.rb +++ b/actionpack/lib/action_controller/metal/live.rb @@ -33,6 +33,20 @@ module ActionController # the main thread. Make sure your actions are thread safe, and this shouldn't # be a problem (don't share state across threads, etc). module Live + extend ActiveSupport::Concern + + module ClassMethods + def make_response!(request) + if request.env["HTTP_VERSION"] == "HTTP/1.0" + super + else + Live::Response.new.tap do |res| + res.request = request + end + end + end + end + # This class provides the ability to write an SSE (Server Sent Event) # to an IO stream. The class is initialized with a stream and can be used # to either write a JSON string or an object which can be converted to JSON. @@ -311,12 +325,7 @@ module ActionController end def set_response!(request) - if request.env["HTTP_VERSION"] == "HTTP/1.0" - super - else - @_response = Live::Response.new - @_response.request = request - end + @_response = self.class.make_response! request end end end diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index 1db68db20f..e62da0fa70 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -191,6 +191,7 @@ module ActionController #:nodoc: if format = collector.negotiate_format(request) _process_format(format) + _set_content_type _get_content_type format response = collector.response response ? response.call : render({}) else diff --git a/actionpack/lib/action_controller/metal/rack_delegation.rb b/actionpack/lib/action_controller/metal/rack_delegation.rb deleted file mode 100644 index ae9d89cc8c..0000000000 --- a/actionpack/lib/action_controller/metal/rack_delegation.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'action_dispatch/http/request' -require 'action_dispatch/http/response' - -module ActionController - module RackDelegation - extend ActiveSupport::Concern - - delegate :headers, :status=, :location=, :content_type=, - :status, :location, :content_type, :response_code, :to => "@_response" - - module ClassMethods - def build_with_env(env = {}) #:nodoc: - new.tap { |c| c.set_request! ActionDispatch::Request.new(env) } - end - end - - def set_request!(request) #:nodoc: - super - set_response!(request) - end - - def response_body=(body) - response.body = body if response - super - end - - def reset_session - @_request.reset_session - end - - private - - def set_response!(request) - @_response = ActionDispatch::Response.new - @_response.request = request - end - end -end diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb index acaa8227c9..0febc905f1 100644 --- a/actionpack/lib/action_controller/metal/redirecting.rb +++ b/actionpack/lib/action_controller/metal/redirecting.rb @@ -11,7 +11,6 @@ module ActionController extend ActiveSupport::Concern include AbstractController::Logger - include ActionController::RackDelegation include ActionController::UrlFor # Redirects the browser to the target specified in +options+. This parameter can be any one of: diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb index a3b0645dc0..c8934b367f 100644 --- a/actionpack/lib/action_controller/metal/rendering.rb +++ b/actionpack/lib/action_controller/metal/rendering.rb @@ -56,14 +56,12 @@ module ActionController nil end - def _process_format(format, options = {}) - super + def _get_content_type(rendered_format) + self.content_type || super + end - if options[:plain] - self.content_type = Mime::TEXT - else - self.content_type ||= format.to_s - end + def _set_content_type(format) + self.content_type = format end # Normalize arguments by catching blocks and setting them on :update. diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index d21a778d8d..e5f3cb8e8d 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -136,7 +136,7 @@ module ActionController #:nodoc: # This is the method that defines the application behavior when a request is found to be unverified. def handle_unverified_request request = @controller.request - request.session = NullSessionHash.new(request.env) + request.session = NullSessionHash.new(request) request.env['action_dispatch.request.flash_hash'] = nil request.env['rack.session.options'] = { skip: true } request.cookie_jar = NullCookieJar.build(request, {}) @@ -145,8 +145,8 @@ module ActionController #:nodoc: protected class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc: - def initialize(env) - super(nil, env) + def initialize(req) + super(nil, req) @data = {} @loaded = true end diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb index fc8e345d43..bf5c7003ff 100644 --- a/actionpack/lib/action_controller/metal/strong_parameters.rb +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -240,19 +240,58 @@ module ActionController self end - # Ensures that a parameter is present. If it's present, returns - # the parameter at the given +key+, otherwise raises an - # <tt>ActionController::ParameterMissing</tt> error. + # This method accepts both a single key and an array of keys. + # + # When passed a single key, if it exists and its associated value is + # either present or the singleton +false+, returns said value: # # ActionController::Parameters.new(person: { name: 'Francesco' }).require(:person) # # => {"name"=>"Francesco"} # + # Otherwise raises <tt>ActionController::ParameterMissing</tt>: + # + # ActionController::Parameters.new.require(:person) + # # ActionController::ParameterMissing: param is missing or the value is empty: person + # # ActionController::Parameters.new(person: nil).require(:person) - # # => ActionController::ParameterMissing: param is missing or the value is empty: person + # # ActionController::ParameterMissing: param is missing or the value is empty: person + # + # ActionController::Parameters.new(person: "\t").require(:person) + # # ActionController::ParameterMissing: param is missing or the value is empty: person # # ActionController::Parameters.new(person: {}).require(:person) - # # => ActionController::ParameterMissing: param is missing or the value is empty: person + # # ActionController::ParameterMissing: param is missing or the value is empty: person + # + # When given an array of keys, the method tries to require each one of them + # in order. If it succeeds, an array with the respective return values is + # returned: + # + # params = ActionController::Parameters.new(user: { ... }, profile: { ... }) + # user_params, profile_params = params.require(:user, :profile) + # + # Otherwise, the method reraises the first exception found: + # + # params = ActionController::Parameters.new(user: {}, profile: {}) + # user_params, profile_params = params.require(:user, :profile) + # # ActionController::ParameterMissing: param is missing or the value is empty: user + # + # Technically this method can be used to fetch terminal values: + # + # # CAREFUL + # params = ActionController::Parameters.new(person: { name: 'Finn' }) + # name = params.require(:person).require(:name) # CAREFUL + # + # but take into account that at some point those ones have to be permitted: + # + # def person_params + # params.require(:person).permit(:name).tap do |person_params| + # person_params.require(:name) # SAFER + # end + # end + # + # for example. def require(key) + return key.map { |k| require(k) } if key.is_a?(Array) value = self[key] if value.present? || value == false value diff --git a/actionpack/lib/action_controller/metal/testing.rb b/actionpack/lib/action_controller/metal/testing.rb index d01927b7cb..47d940f692 100644 --- a/actionpack/lib/action_controller/metal/testing.rb +++ b/actionpack/lib/action_controller/metal/testing.rb @@ -2,8 +2,6 @@ module ActionController module Testing extend ActiveSupport::Concern - include RackDelegation - # TODO : Rewrite tests using controller.headers= to use Rack env def headers=(new_headers) @_response ||= ActionDispatch::Response.new diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index e012fa617e..39cbc0cd70 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -36,11 +36,11 @@ module ActionController end def query_string=(string) - @env[Rack::QUERY_STRING] = string + set_header Rack::QUERY_STRING, string end - def request_parameters=(params) - @env["action_dispatch.request.request_parameters"] = params + def content_type=(type) + set_header 'CONTENT_TYPE', type end def assign_parameters(routes, controller_path, action, parameters, generated_path, query_string_keys) @@ -67,10 +67,12 @@ module ActionController end else if ENCODER.should_multipart?(non_path_parameters) - @env['CONTENT_TYPE'] = ENCODER.content_type + self.content_type = ENCODER.content_type data = ENCODER.build_multipart non_path_parameters else - @env['CONTENT_TYPE'] ||= 'application/x-www-form-urlencoded' + get_header('CONTENT_TYPE') do |k| + set_header k, 'application/x-www-form-urlencoded' + end # FIXME: setting `request_parametes` is normally handled by the # params parser middleware, and we should remove this roundtripping @@ -92,11 +94,13 @@ module ActionController end end - @env['CONTENT_LENGTH'] = data.length.to_s - @env['rack.input'] = StringIO.new(data) + set_header 'CONTENT_LENGTH', data.length.to_s + set_header 'rack.input', StringIO.new(data) end - @env["PATH_INFO"] ||= generated_path + get_header("PATH_INFO") do |k| + set_header k, generated_path + end path_parameters[:controller] = controller_path path_parameters[:action] = action @@ -170,8 +174,8 @@ module ActionController clear end - def fetch(*args, &block) - @data.fetch(*args, &block) + def fetch(key, *args, &block) + @data.fetch(key.to_s, *args, &block) end private @@ -450,7 +454,7 @@ module ActionController end if body.present? - @request.env['RAW_POST_DATA'] = body + @request.set_header 'RAW_POST_DATA', body end if http_method.present? @@ -472,15 +476,15 @@ module ActionController end self.cookies.update @request.cookies - @request.env['HTTP_COOKIE'] = cookies.to_header - @request.env['action_dispatch.cookies'] = nil + @request.set_header 'HTTP_COOKIE', cookies.to_header + @request.delete_header 'action_dispatch.cookies' @request = TestRequest.new scrub_env!(@request.env), @request.session @response = build_response @response_klass @response.request = @request @controller.recycle! - @request.env['REQUEST_METHOD'] = http_method + @request.set_header 'REQUEST_METHOD', http_method parameters = parameters.symbolize_keys @@ -494,24 +498,28 @@ module ActionController @request.flash.update(flash || {}) if xhr - @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' - @request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ') + @request.set_header 'HTTP_X_REQUESTED_WITH', 'XMLHttpRequest' + @request.get_header('HTTP_ACCEPT') do |k| + @request.set_header k, [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ') + end end @controller.request = @request @controller.response = @response - @request.env["SCRIPT_NAME"] ||= @controller.config.relative_url_root + @request.get_header("SCRIPT_NAME") do |k| + @request.set_header k, @controller.config.relative_url_root + end @controller.recycle! @controller.process(action) - @request.env.delete 'HTTP_COOKIE' + @request.delete_header 'HTTP_COOKIE' - if cookies = @request.env['action_dispatch.cookies'] + if @request.have_cookie_jar? unless @response.committed? - cookies.write(@response) - self.cookies.update(cookies.instance_variable_get(:@cookies)) + @request.cookie_jar.write(@response) + self.cookies.update(@request.cookie_jar.instance_variable_get(:@cookies)) end end @response.prepare! @@ -523,8 +531,8 @@ module ActionController end if xhr - @request.env.delete 'HTTP_X_REQUESTED_WITH' - @request.env.delete 'HTTP_ACCEPT' + @request.delete_header 'HTTP_X_REQUESTED_WITH' + @request.delete_header 'HTTP_ACCEPT' end @request.query_string = '' |