diff options
Diffstat (limited to 'actionpack/lib/action_controller')
19 files changed, 191 insertions, 100 deletions
| diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index d7b09b67c0..f93f0d4404 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -2,8 +2,22 @@ require "action_controller/log_subscriber"  require "action_controller/metal/params_wrapper"  module ActionController +  # The <tt>metal</tt> anonymous class was introduced to solve issue with including modules in <tt>ActionController::Base</tt>. +  # Modules needs to be included in particluar order. First we need to have <tt>AbstractController::Rendering</tt> included, +  # next we should include actuall implementation which would be for example <tt>ActionView::Rendering</tt> and after that +  # <tt>ActionController::Rendering</tt>. This order must be preserved and as we want to have middle module included dynamicaly +  # <tt>metal</tt> class was introduced. It has <tt>AbstractController::Rendering</tt> included and is parent class of +  # <tt>ActionController::Base</tt> which includes <tt>ActionController::Rendering</tt>. If we include <tt>ActionView::Rendering</tt> +  # beetween them to perserve the required order, we can simply do this by: +  # +  #   ActionController::Base.superclass.send(:include, ActionView::Rendering) +  # +  metal = Class.new(Metal) do +    include AbstractController::Rendering +  end +    # Action Controllers are the core of a web request in \Rails. They are made up of one or more actions that are executed -  # on request and then either render a template or redirect to another action. An action is defined as a public method +  # on request and then either it renders a template or redirects to another action. An action is defined as a public method    # on the controller, which will automatically be made accessible to the web-server through \Rails Routes.    #    # By default, only the ApplicationController in a \Rails application inherits from <tt>ActionController::Base</tt>. All other @@ -160,7 +174,7 @@ module ActionController    #     render action: "overthere" # won't be called if monkeys is nil    #   end    # -  class Base < Metal +  class Base < metal      abstract!      # We document the request and response methods here because albeit they are @@ -200,7 +214,6 @@ module ActionController      end      MODULES = [ -      AbstractController::Layouts,        AbstractController::Translation,        AbstractController::AssetPaths, diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index ea33d975ef..12d798d0c1 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -9,7 +9,7 @@ module ActionController    # You can read more about each approach by clicking the modules below.    #    # Note: To turn off all caching, set -  #   config.action_controller.perform_caching = false. +  #   config.action_controller.perform_caching = false    #    # == \Caching stores    # @@ -58,16 +58,6 @@ module ActionController        config_accessor :default_static_extension        self.default_static_extension ||= '.html' -      def self.page_cache_extension=(extension) -        ActiveSupport::Deprecation.deprecation_warning(:page_cache_extension, :default_static_extension) -        self.default_static_extension = extension -      end - -      def self.page_cache_extension -        ActiveSupport::Deprecation.deprecation_warning(:page_cache_extension, :default_static_extension) -        default_static_extension -      end -        config_accessor :perform_caching        self.perform_caching = true if perform_caching.nil? diff --git a/actionpack/lib/action_controller/deprecated.rb b/actionpack/lib/action_controller/deprecated.rb deleted file mode 100644 index 2405bebb97..0000000000 --- a/actionpack/lib/action_controller/deprecated.rb +++ /dev/null @@ -1,7 +0,0 @@ -ActionController::AbstractRequest = ActionController::Request = ActionDispatch::Request -ActionController::AbstractResponse = ActionController::Response = ActionDispatch::Response -ActionController::Routing = ActionDispatch::Routing - -ActiveSupport::Deprecation.warn 'ActionController::AbstractRequest and ActionController::Request are deprecated and will be removed, use ActionDispatch::Request instead.' -ActiveSupport::Deprecation.warn 'ActionController::AbstractResponse and ActionController::Response are deprecated and will be removed, use ActionDispatch::Response instead.' -ActiveSupport::Deprecation.warn 'ActionController::Routing is deprecated and will be removed, use ActionDispatch::Routing instead.'
\ No newline at end of file diff --git a/actionpack/lib/action_controller/deprecated/integration_test.rb b/actionpack/lib/action_controller/deprecated/integration_test.rb deleted file mode 100644 index 54eae48f47..0000000000 --- a/actionpack/lib/action_controller/deprecated/integration_test.rb +++ /dev/null @@ -1,5 +0,0 @@ -ActionController::Integration = ActionDispatch::Integration -ActionController::IntegrationTest = ActionDispatch::IntegrationTest - -ActiveSupport::Deprecation.warn 'ActionController::Integration is deprecated and will be removed, use ActionDispatch::Integration instead.' -ActiveSupport::Deprecation.warn 'ActionController::IntegrationTest is deprecated and will be removed, use ActionDispatch::IntegrationTest instead.' diff --git a/actionpack/lib/action_controller/metal/flash.rb b/actionpack/lib/action_controller/metal/flash.rb index b078beb675..65351284b9 100644 --- a/actionpack/lib/action_controller/metal/flash.rb +++ b/actionpack/lib/action_controller/metal/flash.rb @@ -11,6 +11,23 @@ module ActionController #:nodoc:      end      module ClassMethods +      # Creates new flash types. You can pass as many types as you want to create +      # flash types other than the default <tt>alert</tt> and <tt>notice</tt> in +      # your controllers and views. For instance: +      # +      #   # in application_controller.rb +      #   class ApplicationController < ActionController::Base +      #     add_flash_types :warning +      #   end +      # +      #   # in your controller +      #   redirect_to user_path(@user), warning: "Incomplete profile" +      # +      #   # in your view +      #   <%= warning %> +      # +      # This method will automatically define a new method for each of the given +      # names, and it will be available in your views.        def add_flash_types(*types)          types.each do |type|            next if _flash_types.include?(type) @@ -20,7 +37,7 @@ module ActionController #:nodoc:            end            helper_method type -          _flash_types << type +          self._flash_types += [type]          end        end      end diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb index b8afce42c9..a2cb6d1e66 100644 --- a/actionpack/lib/action_controller/metal/force_ssl.rb +++ b/actionpack/lib/action_controller/metal/force_ssl.rb @@ -48,7 +48,7 @@ module ActionController        # You can pass any of the following options to affect the redirect status and response        # * <tt>status</tt>     - Redirect with a custom status (default is 301 Moved Permanently)        # * <tt>flash</tt>      - Set a flash message when redirecting -      # * <tt>alert</tt>      - Set a alert message when redirecting +      # * <tt>alert</tt>      - Set an alert message when redirecting        # * <tt>notice</tt>     - Set a notice message when redirecting        #        # ==== Action Options diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb index 8237db15ca..424473801d 100644 --- a/actionpack/lib/action_controller/metal/head.rb +++ b/actionpack/lib/action_controller/metal/head.rb @@ -1,7 +1,5 @@  module ActionController    module Head -    extend ActiveSupport::Concern -      # Return a response that has no content (merely headers). The options      # argument is interpreted to be a hash of header names and values.      # This allows you to easily return a response that consists only of diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb index 243fd40a7e..b53ae7f29f 100644 --- a/actionpack/lib/action_controller/metal/helpers.rb +++ b/actionpack/lib/action_controller/metal/helpers.rb @@ -5,7 +5,7 @@ module ActionController    #    # In addition to using the standard template helpers provided, creating custom helpers to    # extract complicated logic or reusable functionality is strongly encouraged. By default, each controller -  # will include all helpers. +  # will include all helpers. These helpers are only accessible on the controller through <tt>.helpers</tt>    #    # In previous versions of \Rails the controller will include a helper whose    # name matches that of the controller, e.g., <tt>MyController</tt> will automatically diff --git a/actionpack/lib/action_controller/metal/live.rb b/actionpack/lib/action_controller/metal/live.rb index 8092fd639f..0dd788645b 100644 --- a/actionpack/lib/action_controller/metal/live.rb +++ b/actionpack/lib/action_controller/metal/live.rb @@ -1,5 +1,6 @@  require 'action_dispatch/http/response'  require 'delegate' +require 'active_support/json'  module ActionController    # Mix this module in to your controller, and all actions in that controller @@ -32,6 +33,79 @@ 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 +    # 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. +    # +    # Writing an object will convert it into standard SSE format with whatever +    # options you have configured. You may choose to set the following options: +    # +    #   1) Event. If specified, an event with this name will be dispatched on +    #   the browser. +    #   2) Retry. The reconnection time in milliseconds used when attempting +    #   to send the event. +    #   3) Id. If the connection dies while sending an SSE to the browser, then +    #   the server will receive a +Last-Event-ID+ header with value equal to +id+. +    # +    # After setting an option in the constructor of the SSE object, all future +    # SSEs sent accross the stream will use those options unless overridden. +    # +    # Example Usage: +    # +    #   class MyController < ActionController::Base +    #     include ActionController::Live +    # +    #     def index +    #       response.headers['Content-Type'] = 'text/event-stream' +    #       sse = SSE.new(response.stream, retry: 300, event: "event-name") +    #       sse.write({ name: 'John'}) +    #       sse.write({ name: 'John'}, id: 10) +    #       sse.write({ name: 'John'}, id: 10, event: "other-event") +    #       sse.write({ name: 'John'}, id: 10, event: "other-event", retry: 500) +    #     ensure +    #       sse.close +    #     end +    #   end +    # +    # Note: SSEs are not currently supported by IE. However, they are supported +    # by Chrome, Firefox, Opera, and Safari. +    class SSE + +      WHITELISTED_OPTIONS = %w( retry event id ) + +      def initialize(stream, options = {}) +        @stream = stream +        @options = options +      end + +      def close +        @stream.close +      end + +      def write(object, options = {}) +        case object +        when String +          perform_write(object, options) +        else +          perform_write(ActiveSupport::JSON.encode(object), options) +        end +      end + +      private + +        def perform_write(json, options) +          current_options = @options.merge(options).stringify_keys + +          WHITELISTED_OPTIONS.each do |option_name| +            if (option_value = current_options[option_name]) +              @stream.write "#{option_name}: #{option_value}\n" +            end +          end + +          @stream.write "data: #{json}\n\n" +        end +    end +      class Buffer < ActionDispatch::Response::Buffer #:nodoc:        def initialize(response)          @error_callback = nil diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index 834d44f045..66dabd821f 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -364,9 +364,7 @@ module ActionController #:nodoc:        format = collector.negotiate_format(request)        if format -        self.content_type ||= format.to_s -        lookup_context.formats = [format.to_sym] -        lookup_context.rendered_format = lookup_context.formats.first +        _process_format(format)          collector        else          raise ActionController::UnknownFormat diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb index e9031f3fac..ab14a61b97 100644 --- a/actionpack/lib/action_controller/metal/redirecting.rb +++ b/actionpack/lib/action_controller/metal/redirecting.rb @@ -71,6 +71,26 @@ module ActionController        self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.h(location)}\">redirected</a>.</body></html>"      end +    def _compute_redirect_to_location(options) #:nodoc: +      case options +      # The scheme name consist of a letter followed by any combination of +      # letters, digits, and the plus ("+"), period ("."), or hyphen ("-") +      # characters; and is terminated by a colon (":"). +      # See http://tools.ietf.org/html/rfc3986#section-3.1 +      # The protocol relative scheme starts with a double slash "//". +      when /\A([a-z][a-z\d\-+\.]*:|\/\/).*/i +        options +      when String +        request.protocol + request.host_with_port + options +      when :back +        request.headers["Referer"] or raise RedirectBackError +      when Proc +        _compute_redirect_to_location options.call +      else +        url_for(options) +      end.delete("\0\r\n") +    end +      private        def _extract_redirect_to_status(options, response_status)          if options.is_a?(Hash) && options.key?(:status) @@ -81,24 +101,5 @@ module ActionController            302          end        end - -      def _compute_redirect_to_location(options) -        case options -        # The scheme name consist of a letter followed by any combination of -        # letters, digits, and the plus ("+"), period ("."), or hyphen ("-") -        # characters; and is terminated by a colon (":"). -        # The protocol relative scheme starts with a double slash "//" -        when %r{\A(\w[\w+.-]*:|//).*} -          options -        when String -          request.protocol + request.host_with_port + options -        when :back -          request.headers["Referer"] or raise RedirectBackError -        when Proc -          _compute_redirect_to_location options.call -        else -          url_for(options) -        end.delete("\0\r\n") -      end    end  end diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb index 5272dc6cdb..62a3844b04 100644 --- a/actionpack/lib/action_controller/metal/renderers.rb +++ b/actionpack/lib/action_controller/metal/renderers.rb @@ -6,6 +6,12 @@ module ActionController      Renderers.add(key, &block)    end +  class MissingRenderer < LoadError +    def initialize(format) +      super "No renderer defined for format: #{format}" +    end +  end +    module Renderers      extend ActiveSupport::Concern diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb index bea6b88f91..90f0ef0b1c 100644 --- a/actionpack/lib/action_controller/metal/rendering.rb +++ b/actionpack/lib/action_controller/metal/rendering.rb @@ -2,8 +2,6 @@ module ActionController    module Rendering      extend ActiveSupport::Concern -    include AbstractController::Rendering -      # Before processing, set the request formats in current controller formats.      def process_action(*) #:nodoc:        self.formats = request.formats.map(&:ref).compact @@ -12,29 +10,37 @@ module ActionController      # Check for double render errors and set the content_type after rendering.      def render(*args) #:nodoc: -      raise ::AbstractController::DoubleRenderError if response_body +      raise ::AbstractController::DoubleRenderError if self.response_body        super -      self.content_type ||= Mime[lookup_context.rendered_format].to_s -      response_body      end      # Overwrite render_to_string because body can now be set to a rack body.      def render_to_string(*) -      if self.response_body = super +      result = super +      if result.respond_to?(:each)          string = "" -        response_body.each { |r| string << r } +        result.each { |r| string << r }          string +      else +        result        end -    ensure -      self.response_body = nil      end -    def render_to_body(*) -      super || " " +    def render_to_body(options = {}) +      super || if options[:text].present? +        options[:text] +      else +        " " +      end      end      private +    def _process_format(format) +      super +      self.content_type ||= format.to_s +    end +      # Normalize arguments by catching blocks and setting them on :update.      def _normalize_args(action=nil, options={}, &blk) #:nodoc:        options = super diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index 573c739da4..bd64b1f812 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -124,6 +124,9 @@ module ActionController #:nodoc:              @loaded = true            end +          # no-op +          def destroy; end +            def exists?              true            end diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb index fd5b661209..66ff34a794 100644 --- a/actionpack/lib/action_controller/metal/responder.rb +++ b/actionpack/lib/action_controller/metal/responder.rb @@ -202,6 +202,7 @@ module ActionController #:nodoc:      # This is the common behavior for formats associated with APIs, such as :xml and :json.      def api_behavior(error)        raise error unless resourceful? +      raise MissingRenderer.new(format) unless has_renderer?        if get?          display resource @@ -269,6 +270,11 @@ module ActionController #:nodoc:        resource.respond_to?(:errors) && !resource.errors.empty?      end +    # Check whether the neceessary Renderer is available +    def has_renderer? +      Renderers::RENDERERS.include?(format) +    end +      # By default, render the <code>:edit</code> action for HTML requests with errors, unless      # the verb was POST.      # diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb index 73e9b5660d..62d5931b45 100644 --- a/actionpack/lib/action_controller/metal/streaming.rb +++ b/actionpack/lib/action_controller/metal/streaming.rb @@ -193,31 +193,29 @@ module ActionController #:nodoc:    module Streaming      extend ActiveSupport::Concern -    include AbstractController::Rendering -      protected -    # Set proper cache control and transfer encoding when streaming -    def _process_options(options) #:nodoc: -      super -      if options[:stream] -        if env["HTTP_VERSION"] == "HTTP/1.0" -          options.delete(:stream) -        else -          headers["Cache-Control"] ||= "no-cache" -          headers["Transfer-Encoding"] = "chunked" -          headers.delete("Content-Length") +      # Set proper cache control and transfer encoding when streaming +      def _process_options(options) #:nodoc: +        super +        if options[:stream] +          if env["HTTP_VERSION"] == "HTTP/1.0" +            options.delete(:stream) +          else +            headers["Cache-Control"] ||= "no-cache" +            headers["Transfer-Encoding"] = "chunked" +            headers.delete("Content-Length") +          end          end        end -    end -    # Call render_body if we are streaming instead of usual +render+. -    def _render_template(options) #:nodoc: -      if options.delete(:stream) -        Rack::Chunked::Body.new view_renderer.render_body(view_context, options) -      else -        super +      # Call render_body if we are streaming instead of usual +render+. +      def _render_template(options) #:nodoc: +        if options.delete(:stream) +          Rack::Chunked::Body.new view_renderer.render_body(view_context, options) +        else +          super +        end        end -    end    end  end diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb index 44703221f3..b495ab3f0f 100644 --- a/actionpack/lib/action_controller/metal/strong_parameters.rb +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -201,6 +201,7 @@ module ActionController      # You may declare that the parameter should be an array of permitted scalars      # by mapping it to an empty array:      # +    #   params = ActionController::Parameters.new(tags: ['rails', 'parameters'])      #   params.permit(tags: [])      #      # You can also use +permit+ on nested parameters, like: @@ -297,7 +298,7 @@ module ActionController      #   params.slice(:d)     # => {}      def slice(*keys)        self.class.new(super).tap do |new_instance| -        new_instance.instance_variable_set :@permitted, @permitted +        new_instance.permitted = @permitted        end      end @@ -311,10 +312,15 @@ module ActionController      #   copy_params.permitted?   # => true      def dup        super.tap do |duplicate| -        duplicate.instance_variable_set :@permitted, @permitted +        duplicate.permitted = @permitted        end      end +    protected +      def permitted=(new_permitted) +        @permitted = new_permitted +      end +      private        def convert_hashes_to_parameters(key, value)          if value.is_a?(Parameters) || !value.is_a?(Hash) @@ -415,7 +421,7 @@ module ActionController          # Slicing filters out non-declared keys.          slice(*filter.keys).each do |key, value| -          return unless value +          next unless value            if filter[key] == EMPTY_ARRAY              # Declaration { comment_ids: [] }. diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb index 5379547c57..0833e65d23 100644 --- a/actionpack/lib/action_controller/railtie.rb +++ b/actionpack/lib/action_controller/railtie.rb @@ -1,7 +1,6 @@  require "rails"  require "action_controller"  require "action_dispatch/railtie" -require "action_view/railtie"  require "abstract_controller/railties/routes_helpers"  require "action_controller/railties/helpers" diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 0cbbbbe1fd..5ed3d2ebc1 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -524,7 +524,6 @@ module ActionController        def process(action, http_method = 'GET', *args)          check_required_ivars -        http_method, args = handle_old_process_api(http_method, args, caller)          if args.first.is_a?(String) && http_method != 'HEAD'            @request.env['RAW_POST_DATA'] = args.shift @@ -628,17 +627,6 @@ module ActionController          end        end -      def handle_old_process_api(http_method, args, callstack) -        # 4.0: Remove this method. -        if http_method.is_a?(Hash) -          ActiveSupport::Deprecation.warn("TestCase#process now expects the HTTP method as second argument: process(action, http_method, params, session, flash)", callstack) -          args.unshift(http_method) -          http_method = args.last.is_a?(String) ? args.last : "GET" -        end - -        [http_method, args] -      end -        def build_request_uri(action, parameters)          unless @request.env["PATH_INFO"]            options = @controller.respond_to?(:url_options) ? @controller.__send__(:url_options).merge(parameters) : parameters | 
