diff options
Diffstat (limited to 'actionpack')
35 files changed, 343 insertions, 103 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 41af29573e..1e6e84ea4a 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,5 +1,16 @@ ## Rails 4.0.0 (unreleased) ## +* `assert_generates`, `assert_recognizes`, and `assert_routing` all raise + `Assertion` instead of `RoutingError` *David Chelimsky* + +* URL path parameters with invalid encoding now raise ActionController::BadRequest. *Andrew White* + +* Malformed query and request parameter hashes now raise ActionController::BadRequest. *Andrew White* + +* Add `divider` option to `grouped_options_for_select` to generate a separator + `optgroup` automatically, and deprecate `prompt` as third argument, in favor + of using an options hash. *Nicholas Greenfield* + * Add `time_field` and `time_field_tag` helpers which render an `input[type="time"]` tag. *Alex Soulim* * Removed old text_helper apis for highlight, excerpt and word_wrap *Jeremy Walker* diff --git a/actionpack/examples/performance.rb b/actionpack/examples/performance.rb index 994e745bb0..8ea4758961 100644 --- a/actionpack/examples/performance.rb +++ b/actionpack/examples/performance.rb @@ -166,15 +166,7 @@ def run_all!(times, verbose) Runner.run(:diff_100, times, verbose) end -unless ENV["PROFILE"] - run_all!(1, false) - - (ENV["M"] || 1).to_i.times do - $ran = [] - run_all!(N, true) - Runner.done - end -else +if ENV["PROFILE"] Runner.run(ENV["PROFILE"].to_sym, 1, false) require "ruby-prof" RubyProf.start @@ -182,4 +174,12 @@ else result = RubyProf.stop printer = RubyProf::CallStackPrinter.new(result) printer.print(File.open("output.html", "w")) +else + run_all!(1, false) + + (ENV["M"] || 1).to_i.times do + $ran = [] + run_all!(N, true) + Runner.done + end end
\ No newline at end of file diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb index bc9f6fc3e8..c1b3994035 100644 --- a/actionpack/lib/abstract_controller/layouts.rb +++ b/actionpack/lib/abstract_controller/layouts.rb @@ -203,8 +203,7 @@ module AbstractController include Rendering included do - class_attribute :_layout, :_layout_conditions, - :instance_reader => false, :instance_writer => false + class_attribute :_layout, :_layout_conditions, :instance_accessor => false self._layout = nil self._layout_conditions = {} _write_layout_method diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb index 80901b8bf3..0238135bc1 100644 --- a/actionpack/lib/action_controller/caching/actions.rb +++ b/actionpack/lib/action_controller/caching/actions.rb @@ -47,7 +47,7 @@ module ActionController #:nodoc: # And you can also use <tt>:if</tt> (or <tt>:unless</tt>) to pass a # proc that specifies when the action should be cached. # - # As of Rails 3.0, you can also pass <tt>:expires_in</tt> with a time + # As of Rails 3.0, you can also pass <tt>:expires_in</tt> with a time # interval (in seconds) to schedule expiration of the cached item. # # The following example depicts some of the points made above: @@ -178,8 +178,9 @@ module ActionController #:nodoc: private def normalize!(path) + ext = URI.parser.escape(extension) if extension path << 'index' if path[-1] == ?/ - path << ".#{extension}" if extension and !path.split('?', 2).first.ends_with?(".#{extension}") + path << ".#{ext}" if extension and !path.split('?', 2).first.ends_with?(".#{ext}") URI.parser.unescape(path) end end diff --git a/actionpack/lib/action_controller/log_subscriber.rb b/actionpack/lib/action_controller/log_subscriber.rb index 11aa393bf9..0fb419f941 100644 --- a/actionpack/lib/action_controller/log_subscriber.rb +++ b/actionpack/lib/action_controller/log_subscriber.rb @@ -33,9 +33,7 @@ module ActionController end def send_file(event) - message = "Sent file %s" - message << " (%.1fms)" - info(message % [event.payload[:path], event.duration]) + info("Sent file %s (%.1fms)" % [event.payload[:path], event.duration]) end def redirect_to(event) diff --git a/actionpack/lib/action_controller/metal/exceptions.rb b/actionpack/lib/action_controller/metal/exceptions.rb index 90648c37ad..8fd8f4797c 100644 --- a/actionpack/lib/action_controller/metal/exceptions.rb +++ b/actionpack/lib/action_controller/metal/exceptions.rb @@ -2,6 +2,9 @@ module ActionController class ActionControllerError < StandardError #:nodoc: end + class BadRequest < ActionControllerError #:nodoc: + end + class RenderError < ActionControllerError #:nodoc: end @@ -38,7 +41,7 @@ module ActionController class UnknownHttpMethod < ActionControllerError #:nodoc: end - + class UnknownFormat < ActionControllerError #:nodoc: end end diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index 1f52c164de..aa67fa7f23 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -193,7 +193,8 @@ module ActionController def process_action(*args) if _wrapper_enabled? wrapped_hash = _wrap_parameters request.request_parameters - wrapped_filtered_hash = _wrap_parameters request.filtered_parameters + wrapped_keys = request.request_parameters.keys + wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys) # This will make the wrapped hash accessible from controller and view request.parameters.merge! wrapped_hash diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 56908b5794..aa5ba3e8a5 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -231,17 +231,24 @@ module ActionDispatch # Override Rack's GET method to support indifferent access def GET - @env["action_dispatch.request.query_parameters"] ||= (normalize_parameters(super) || {}) + begin + @env["action_dispatch.request.query_parameters"] ||= (normalize_parameters(super) || {}) + rescue TypeError => e + raise ActionController::BadRequest, "Invalid query parameters: #{e.message}" + end end alias :query_parameters :GET # Override Rack's POST method to support indifferent access def POST - @env["action_dispatch.request.request_parameters"] ||= (normalize_parameters(super) || {}) + begin + @env["action_dispatch.request.request_parameters"] ||= (normalize_parameters(super) || {}) + rescue TypeError => e + raise ActionController::BadRequest, "Invalid request parameters: #{e.message}" + end end alias :request_parameters :POST - # Returns the authorization header regardless of whether it was specified directly or through one of the # proxy alternatives. def authorization diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb index a8f49bd3bd..7349b578d2 100644 --- a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb +++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb @@ -12,7 +12,8 @@ module ActionDispatch 'ActionController::MethodNotAllowed' => :method_not_allowed, 'ActionController::NotImplemented' => :not_implemented, 'ActionController::UnknownFormat' => :not_acceptable, - 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity + 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity, + 'ActionController::BadRequest' => :bad_request ) cattr_accessor :rescue_templates diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb index 95c588c00a..205ff44b1c 100644 --- a/actionpack/lib/action_dispatch/routing/redirection.rb +++ b/actionpack/lib/action_dispatch/routing/redirection.rb @@ -2,6 +2,7 @@ require 'action_dispatch/http/request' require 'active_support/core_ext/uri' require 'active_support/core_ext/array/extract_options' require 'rack/utils' +require 'action_controller/metal/exceptions' module ActionDispatch module Routing @@ -16,6 +17,14 @@ module ActionDispatch def call(env) req = Request.new(env) + # If any of the path parameters has a invalid encoding then + # raise since it's likely to trigger errors further on. + req.symbolized_path_parameters.each do |key, value| + unless value.valid_encoding? + raise ActionController::BadRequest, "Invalid parameter: #{key} => #{value}" + end + end + uri = URI.parse(path(req.symbolized_path_parameters, req)) uri.scheme ||= req.scheme uri.host ||= req.host @@ -35,6 +44,25 @@ module ActionDispatch def path(params, request) block.call params, request end + + def inspect + "redirect(#{status})" + end + end + + class PathRedirect < Redirect + def path(params, request) + (params.empty? || !block.match(/%\{\w*\}/)) ? block : (block % escape(params)) + end + + def inspect + "redirect(#{status}, #{block})" + end + + private + def escape(params) + Hash[params.map{ |k,v| [k, Rack::Utils.escape(v)] }] + end end class OptionRedirect < Redirect # :nodoc: @@ -56,6 +84,10 @@ module ActionDispatch ActionDispatch::Http::URL.url_for url_options end + def inspect + "redirect(#{status}, #{options.map{ |k,v| "#{k}: #{v}" }.join(', ')})" + end + private def escape_path(params) Hash[params.map{ |k,v| [k, URI.parser.escape(v)] }] @@ -102,24 +134,15 @@ module ActionDispatch def redirect(*args, &block) options = args.extract_options! status = options.delete(:status) || 301 + path = args.shift return OptionRedirect.new(status, options) if options.any? - - path = args.shift - - block = lambda { |params, request| - (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % escape(params)) - } if String === path + return PathRedirect.new(status, path) if String === path block = path if path.respond_to? :call raise ArgumentError, "redirection argument not supported" unless block Redirect.new status, block end - - private - def escape(params) - Hash[params.map{ |k,v| [k, Rack::Utils.escape(v)] }] - end end end end diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 0ae668d42a..7872f4007e 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -26,6 +26,15 @@ module ActionDispatch def call(env) params = env[PARAMETERS_KEY] + + # If any of the path parameters has a invalid encoding then + # raise since it's likely to trigger errors further on. + params.each do |key, value| + unless value.valid_encoding? + raise ActionController::BadRequest, "Invalid parameter: #{key} => #{value}" + end + end + prepare_params!(params) # Just raise undefined constant errors if a controller was specified as default. @@ -654,9 +663,13 @@ module ActionDispatch dispatcher = dispatcher.app end - if dispatcher.is_a?(Dispatcher) && dispatcher.controller(params, false) - dispatcher.prepare_params!(params) - return params + if dispatcher.is_a?(Dispatcher) + if dispatcher.controller(params, false) + dispatcher.prepare_params!(params) + return params + else + raise ActionController::RoutingError, "A route matches #{path.inspect}, but references missing controller: #{params[:controller].camelize}Controller" + end end end diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb index 3d121b6b9c..b4c8f839ac 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/response.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb @@ -28,7 +28,7 @@ module ActionDispatch assert @response.send("#{type}?"), message else code = Rack::Utils::SYMBOL_TO_STATUS_CODE[type] - assert_equal @response.response_code, code, message + assert_equal code, @response.response_code, message end else assert_equal type, @response.response_code, message diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb index 567ca0c392..41fa3a4b95 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb @@ -69,11 +69,9 @@ module ActionDispatch # assert_generates "changesets/12", { :controller => 'scm', :action => 'show_diff', :revision => "12" } def assert_generates(expected_path, options, defaults={}, extras = {}, message=nil) if expected_path =~ %r{://} - begin + fail_on(URI::InvalidURIError) do uri = URI.parse(expected_path) expected_path = uri.path.to_s.empty? ? "/" : uri.path - rescue URI::InvalidURIError => e - raise ActionController::RoutingError, e.message end else expected_path = "/#{expected_path}" unless expected_path.first == '/' @@ -189,14 +187,12 @@ module ActionDispatch request = ActionController::TestRequest.new if path =~ %r{://} - begin + fail_on(URI::InvalidURIError) do uri = URI.parse(path) request.env["rack.url_scheme"] = uri.scheme || "http" request.host = uri.host if uri.host request.port = uri.port if uri.port request.path = uri.path.to_s.empty? ? "/" : uri.path - rescue URI::InvalidURIError => e - raise ActionController::RoutingError, e.message end else path = "/#{path}" unless path.first == "/" @@ -205,11 +201,21 @@ module ActionDispatch request.request_method = method if method - params = @routes.recognize_path(path, { :method => method, :extras => extras }) + params = fail_on(ActionController::RoutingError) do + @routes.recognize_path(path, { :method => method, :extras => extras }) + end request.path_parameters = params.with_indifferent_access request end + + def fail_on(exception_class) + begin + yield + rescue exception_class => e + raise MiniTest::Assertion, e.message + end + end end end end diff --git a/actionpack/lib/action_dispatch/testing/test_request.rb b/actionpack/lib/action_dispatch/testing/test_request.rb index d04be2099c..a86b510719 100644 --- a/actionpack/lib/action_dispatch/testing/test_request.rb +++ b/actionpack/lib/action_dispatch/testing/test_request.rb @@ -11,7 +11,7 @@ module ActionDispatch end def initialize(env = {}) - env = Rails.application.env_config.merge(env) if defined?(Rails.application) + env = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application super(DEFAULT_ENV.merge(env)) self.host = 'test.host' diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index 52eb1aa447..eef426703d 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -477,8 +477,8 @@ module ActionView # # Sample usage (Hash): # grouped_options = { - # 'North America' => [['United States','US'], 'Canada'], - # 'Europe' => ['Denmark','Germany','France'] + # 'North America' => [['United States','US'], 'Canada'], + # 'Europe' => ['Denmark','Germany','France'] # } # grouped_options_for_select(grouped_options) # @@ -495,10 +495,10 @@ module ActionView # # Sample usage (divider): # grouped_options = [ - # [['United States','US'], 'Canada'], - # ['Denmark','Germany','France'] + # [['United States','US'], 'Canada'], + # ['Denmark','Germany','France'] # ] - # grouped_options_for_select(grouped_options, divider: '---------') + # grouped_options_for_select(grouped_options, nil, divider: '---------') # # Possible output: # <optgroup label="---------"> @@ -513,15 +513,14 @@ module ActionView # # <b>Note:</b> Only the <tt><optgroup></tt> and <tt><option></tt> tags are returned, so you still have to # wrap the output in an appropriate <tt><select></tt> tag. - def grouped_options_for_select(*args) - grouped_options = args.shift - options = args.extract_options! - selected_key = args.shift - if prompt = args.shift - ActiveSupport::Deprecation.warn 'Passing the prompt to grouped_options_for_select as an argument is deprecated. Please pass it in an options hash.' - else - prompt = options[:prompt] + def grouped_options_for_select(grouped_options, selected_key = nil, options = {}) + if options.is_a?(Hash) + prompt = options[:prompt] divider = options[:divider] + else + prompt = options + options = {} + ActiveSupport::Deprecation.warn "Passing the prompt to grouped_options_for_select as an argument is deprecated. Please use an options hash like `{ prompt: #{prompt.inspect} }`." end body = "".html_safe @@ -534,7 +533,7 @@ module ActionView grouped_options.each do |container| if divider - label, container = divider, container + label = divider else label, container = container end diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index 62455b97f9..dfc26acfad 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -254,7 +254,7 @@ module ActionView parts = number.to_s.to_str.split('.') parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}") - parts.join(options[:separator]).html_safe + safe_join(parts, options[:separator]) end # Formats a +number+ with the specified level of diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb index 498be596ad..9572f1c192 100644 --- a/actionpack/lib/action_view/helpers/tag_helper.rb +++ b/actionpack/lib/action_view/helpers/tag_helper.rb @@ -41,7 +41,7 @@ module ActionView # thus accessed as <tt>dataset.userId</tt>. # # Values are encoded to JSON, with the exception of strings and symbols. - # This may come in handy when using jQuery's HTML5-aware <tt>.data()<tt> + # This may come in handy when using jQuery's HTML5-aware <tt>.data()</tt> # from 1.4.3. # # ==== Examples diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index da8c0d1de6..8cd7cf0052 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -81,8 +81,7 @@ module ActionView # truncate("<p>Once upon a time in a world far far away</p>") # # => "<p>Once upon a time in a wo..." def truncate(text, options = {}) - options.reverse_merge!(:length => 30) - text.truncate(options.delete(:length), options) if text + text.truncate(options.fetch(:length, 30), options) if text end # Highlights one or more +phrases+ everywhere in +text+ by inserting it into @@ -102,14 +101,14 @@ module ActionView # highlight('You searched for: rails', 'rails', :highlighter => '<a href="search?q=\1">\1</a>') # # => You searched for: <a href="search?q=rails">rails</a> def highlight(text, phrases, options = {}) - options[:highlighter] ||= '<mark>\1</mark>' - - text = sanitize(text) unless options[:sanitize] == false + highlighter = options.fetch(:highlighter, '<mark>\1</mark>') + + text = sanitize(text) if options.fetch(:sanitize, true) if text.blank? || phrases.blank? text else match = Array(phrases).map { |p| Regexp.escape(p) }.join('|') - text.gsub(/(#{match})(?![^<]*?>)/i, options[:highlighter]) + text.gsub(/(#{match})(?![^<]*?>)/i, highlighter) end.html_safe end @@ -135,8 +134,8 @@ module ActionView # # => <chop> is also an example def excerpt(text, phrase, options = {}) return unless text && phrase - radius = options[:radius] || 100 - omission = options[:omission] || "..." + radius = options.fetch(:radius, 100) + omission = options.fetch(:omission, "...") phrase = Regexp.escape(phrase) return unless found_pos = text =~ /(#{phrase})/i @@ -152,7 +151,7 @@ module ActionView # Attempts to pluralize the +singular+ word unless +count+ is 1. If # +plural+ is supplied, it will use that when count is > 1, otherwise - # it will use the Inflector to determine the plural form + # it will use the Inflector to determine the plural form. # # pluralize(1, 'person') # # => 1 person @@ -166,7 +165,13 @@ module ActionView # pluralize(0, 'person') # # => 0 people def pluralize(count, singular, plural = nil) - "#{count || 0} " + ((count == 1 || count =~ /^1(\.0+)?$/) ? singular : (plural || singular.pluralize)) + word = if (count == 1 || count =~ /^1(\.0+)?$/) + singular + else + plural || singular.pluralize + end + + "#{count || 0} #{word}" end # Wraps the +text+ into lines no longer than +line_width+ width. This method @@ -185,7 +190,7 @@ module ActionView # word_wrap('Once upon a time', :line_width => 1) # # => Once\nupon\na\ntime def word_wrap(text, options = {}) - line_width = options[:line_width] || 80 + line_width = options.fetch(:line_width, 80) text.split("\n").collect do |line| line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line @@ -203,13 +208,16 @@ module ActionView # # ==== Options # * <tt>:sanitize</tt> - If +false+, does not sanitize +text+. - # * <tt>:wrapper_tag</tt> - String representing the tag wrapper, defaults to <tt>"p"</tt> + # * <tt>:wrapper_tag</tt> - String representing the wrapper tag, defaults to <tt>"p"</tt> # # ==== Examples # my_text = "Here is some basic text...\n...with a line break." # # simple_format(my_text) # # => "<p>Here is some basic text...\n<br />...with a line break.</p>" + # + # simple_format(my_text, {}, :wrapper_tag => "div") + # # => "<div>Here is some basic text...\n<br />...with a line break.</div>" # # more_text = "We want to put a paragraph...\n\n...right there." # @@ -221,9 +229,10 @@ module ActionView # # simple_format("<span>I'm allowed!</span> It's true.", {}, :sanitize => false) # # => "<p><span>I'm allowed!</span> It's true.</p>" - def simple_format(text, html_options={}, options={}) - text = sanitize(text) unless options[:sanitize] == false + def simple_format(text, html_options = {}, options = {}) wrapper_tag = options.fetch(:wrapper_tag, :p) + + text = sanitize(text) if options.fetch(:sanitize, true) paragraphs = split_paragraphs(text) if paragraphs.empty? @@ -274,7 +283,7 @@ module ActionView # <% end %> def cycle(first_value, *values) options = values.extract_options! - name = options.fetch(:name, "default") + name = options.fetch(:name, 'default') values.unshift(first_value) diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index edb3d427d5..cd79468502 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -1,6 +1,7 @@ require 'active_support/core_ext/object/blank' require 'active_support/core_ext/object/try' require 'active_support/core_ext/kernel/singleton_class' +require 'thread' module ActionView # = Action View Template @@ -122,6 +123,7 @@ module ActionView @virtual_path = details[:virtual_path] @updated_at = details[:updated_at] || Time.now @formats = Array(format).map { |f| f.is_a?(Mime::Type) ? f.ref : f } + @compile_mutex = Mutex.new end # Returns if the underlying handler supports streaming. If so, @@ -223,18 +225,28 @@ module ActionView def compile!(view) #:nodoc: return if @compiled - if view.is_a?(ActionView::CompiledTemplates) - mod = ActionView::CompiledTemplates - else - mod = view.singleton_class - end + # Templates can be used concurrently in threaded environments + # so compilation and any instance variable modification must + # be synchronized + @compile_mutex.synchronize do + # Any thread holding this lock will be compiling the template needed + # by the threads waiting. So re-check the @compiled flag to avoid + # re-compilation + return if @compiled + + if view.is_a?(ActionView::CompiledTemplates) + mod = ActionView::CompiledTemplates + else + mod = view.singleton_class + end - compile(view, mod) + compile(view, mod) - # Just discard the source if we have a virtual path. This - # means we can get the template back. - @source = nil if @virtual_path - @compiled = true + # Just discard the source if we have a virtual path. This + # means we can get the template back. + @source = nil if @virtual_path + @compiled = true + end end # Among other things, this method is responsible for properly setting diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 9efe328d62..d5afef9086 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -223,6 +223,7 @@ end class ActionCachingTestController < CachingController rescue_from(Exception) { head 500 } + rescue_from(ActionController::UnknownFormat) { head :not_acceptable } if defined? ActiveRecord rescue_from(ActiveRecord::RecordNotFound) { head :not_found } end @@ -230,7 +231,7 @@ class ActionCachingTestController < CachingController # Eliminate uninitialized ivar warning before_filter { @title = nil } - caches_action :index, :redirected, :forbidden, :if => Proc.new { |c| !c.request.format.json? }, :expires_in => 1.hour + caches_action :index, :redirected, :forbidden, :if => Proc.new { |c| c.request.format && !c.request.format.json? }, :expires_in => 1.hour caches_action :show, :cache_path => 'http://test.host/custom/show' caches_action :edit, :cache_path => Proc.new { |c| c.params[:id] ? "http://test.host/#{c.params[:id]};edit" : "http://test.host/edit" } caches_action :with_layout @@ -239,6 +240,7 @@ class ActionCachingTestController < CachingController caches_action :with_layout_proc_param, :layout => Proc.new { |c| c.params[:layout] } caches_action :record_not_found, :four_oh_four, :simple_runtime_error caches_action :streaming + caches_action :invalid layout 'talk_from_action' @@ -303,6 +305,14 @@ class ActionCachingTestController < CachingController def streaming render :text => "streaming", :stream => true end + + def invalid + @cache_this = MockTime.now.to_f.to_s + + respond_to do |format| + format.json{ render :json => @cache_this } + end + end end class MockTime < Time @@ -690,6 +700,25 @@ class ActionCacheTest < ActionController::TestCase assert fragment_exist?('hostname.com/action_caching_test/streaming') end + def test_invalid_format_returns_not_acceptable + get :invalid, :format => "json" + assert_response :success + cached_time = content_to_cache + assert_equal cached_time, @response.body + + assert fragment_exist?("hostname.com/action_caching_test/invalid.json") + + get :invalid, :format => "json" + assert_response :success + assert_equal cached_time, @response.body + + get :invalid, :format => "xml" + assert_response :not_acceptable + + get :invalid, :format => "\xC3\x83" + assert_response :not_acceptable + end + private def content_to_cache assigns(:cache_this) diff --git a/actionpack/test/controller/params_wrapper_test.rb b/actionpack/test/controller/params_wrapper_test.rb index fa1608b9df..5b05f77045 100644 --- a/actionpack/test/controller/params_wrapper_test.rb +++ b/actionpack/test/controller/params_wrapper_test.rb @@ -37,6 +37,14 @@ class ParamsWrapperTest < ActionController::TestCase UsersController.last_parameters = nil end + def test_filtered_parameters + with_default_wrapper_options do + @request.env['CONTENT_TYPE'] = 'application/json' + post :parse, { 'username' => 'sikachu' } + assert_equal @request.filtered_parameters, { 'controller' => 'params_wrapper_test/users', 'action' => 'parse', 'username' => 'sikachu', 'user' => { 'username' => 'sikachu' } } + end + end + def test_derived_name_from_controller with_default_wrapper_options do @request.env['CONTENT_TYPE'] = 'application/json' diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb index 9fc875014c..de1bff17eb 100644 --- a/actionpack/test/controller/resources_test.rb +++ b/actionpack/test/controller/resources_test.rb @@ -109,7 +109,7 @@ class ResourcesTest < ActionController::TestCase expected_options = {:controller => 'messages', :action => 'show', :id => '1.1.1'} with_restful_routing :messages do - assert_raise(ActionController::RoutingError) do + assert_raise(Assertion) do assert_recognizes(expected_options, :path => 'messages/1.1.1', :method => :get) end end @@ -660,15 +660,15 @@ class ResourcesTest < ActionController::TestCase options = { :controller => controller_name.to_s } collection_path = "/#{controller_name}" - assert_raise(ActionController::RoutingError) do + assert_raise(Assertion) do assert_recognizes(options.merge(:action => 'update'), :path => collection_path, :method => :patch) end - assert_raise(ActionController::RoutingError) do + assert_raise(Assertion) do assert_recognizes(options.merge(:action => 'update'), :path => collection_path, :method => :put) end - assert_raise(ActionController::RoutingError) do + assert_raise(Assertion) do assert_recognizes(options.merge(:action => 'destroy'), :path => collection_path, :method => :delete) end end @@ -1353,7 +1353,7 @@ class ResourcesTest < ActionController::TestCase end def assert_not_recognizes(expected_options, path) - assert_raise ActionController::RoutingError, Assertion do + assert_raise Assertion do assert_recognizes(expected_options, path) end end diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index cd91064ab8..2f552c3a5a 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -1037,6 +1037,16 @@ class RouteSetTest < ActiveSupport::TestCase end end + def test_route_error_with_missing_controller + set.draw do + get "/people" => "missing#index" + end + + assert_raise(ActionController::RoutingError) { + set.recognize_path("/people", :method => :get) + } + end + def test_recognize_with_encoded_id_and_regex set.draw do get 'page/:id' => 'pages#show', :id => /[a-zA-Z0-9\+]+/ diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb index 11c292d61a..6ff651ad52 100644 --- a/actionpack/test/dispatch/debug_exceptions_test.rb +++ b/actionpack/test/dispatch/debug_exceptions_test.rb @@ -35,6 +35,8 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest raise ActionController::InvalidAuthenticityToken when "/not_found_original_exception" raise ActionView::Template::Error.new('template', AbstractController::ActionNotFound.new) + when "/bad_request" + raise ActionController::BadRequest else raise "puke!" end @@ -88,6 +90,10 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest get "/method_not_allowed", {}, {'action_dispatch.show_exceptions' => true} assert_response 405 assert_match(/ActionController::MethodNotAllowed/, body) + + get "/bad_request", {}, {'action_dispatch.show_exceptions' => true} + assert_response 400 + assert_match(/ActionController::BadRequest/, body) end test "does not show filtered parameters" do diff --git a/actionpack/test/dispatch/request/query_string_parsing_test.rb b/actionpack/test/dispatch/request/query_string_parsing_test.rb index d14f188e30..c3f009ab15 100644 --- a/actionpack/test/dispatch/request/query_string_parsing_test.rb +++ b/actionpack/test/dispatch/request/query_string_parsing_test.rb @@ -105,6 +105,17 @@ class QueryStringParsingTest < ActionDispatch::IntegrationTest ) end + test "ambiguous query string returns a bad request" do + with_routing do |set| + set.draw do + get ':action', :to => ::QueryStringParsingTest::TestController + end + + get "/parse", nil, "QUERY_STRING" => "foo[]=bar&foo[4]=bar" + assert_response :bad_request + end + end + private def assert_parses(expected, actual) with_routing do |set| diff --git a/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb index 568e220b15..e9b59f55a7 100644 --- a/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb @@ -126,6 +126,17 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest assert_parses expected, query end + test "ambiguous params returns a bad request" do + with_routing do |set| + set.draw do + post ':action', :to => ::UrlEncodedParamsParsingTest::TestController + end + + post "/parse", "foo[]=bar&foo[4]=bar" + assert_response :bad_request + end + end + private def with_test_routing with_routing do |set| diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index 94d0e09842..54fc1b208d 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -561,7 +561,7 @@ class RequestTest < ActiveSupport::TestCase begin request = stub_request(mock_rack_env) request.parameters - rescue TypeError + rescue ActionController::BadRequest # rack will raise a TypeError when parsing this query string end assert_equal({}, request.parameters) diff --git a/actionpack/test/dispatch/routing_assertions_test.rb b/actionpack/test/dispatch/routing_assertions_test.rb index 517354ae58..aea4489852 100644 --- a/actionpack/test/dispatch/routing_assertions_test.rb +++ b/actionpack/test/dispatch/routing_assertions_test.rb @@ -54,21 +54,21 @@ class RoutingAssertionsTest < ActionController::TestCase end def test_assert_recognizes_with_hash_constraint - assert_raise(ActionController::RoutingError) do + assert_raise(Assertion) do assert_recognizes({ :controller => 'secure_articles', :action => 'index' }, 'http://test.host/secure/articles') end assert_recognizes({ :controller => 'secure_articles', :action => 'index', :protocol => 'https://' }, 'https://test.host/secure/articles') end def test_assert_recognizes_with_block_constraint - assert_raise(ActionController::RoutingError) do + assert_raise(Assertion) do assert_recognizes({ :controller => 'block_articles', :action => 'index' }, 'http://test.host/block/articles') end assert_recognizes({ :controller => 'block_articles', :action => 'index' }, 'https://test.host/block/articles') end def test_assert_recognizes_with_query_constraint - assert_raise(ActionController::RoutingError) do + assert_raise(Assertion) do assert_recognizes({ :controller => 'query_articles', :action => 'index', :use_query => 'false' }, '/query/articles', { :use_query => 'false' }) end assert_recognizes({ :controller => 'query_articles', :action => 'index', :use_query => 'true' }, '/query/articles', { :use_query => 'true' }) @@ -87,14 +87,14 @@ class RoutingAssertionsTest < ActionController::TestCase end def test_assert_routing_with_hash_constraint - assert_raise(ActionController::RoutingError) do + assert_raise(Assertion) do assert_routing('http://test.host/secure/articles', { :controller => 'secure_articles', :action => 'index' }) end assert_routing('https://test.host/secure/articles', { :controller => 'secure_articles', :action => 'index', :protocol => 'https://' }) end def test_assert_routing_with_block_constraint - assert_raise(ActionController::RoutingError) do + assert_raise(Assertion) do assert_routing('http://test.host/block/articles', { :controller => 'block_articles', :action => 'index' }) end assert_routing('https://test.host/block/articles', { :controller => 'block_articles', :action => 'index' }) @@ -107,7 +107,7 @@ class RoutingAssertionsTest < ActionController::TestCase end assert_routing('/artikel', :controller => 'articles', :action => 'index') - assert_raise(ActionController::RoutingError) do + assert_raise(Assertion) do assert_routing('/articles', { :controller => 'articles', :action => 'index' }) end end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 1a8f40037f..00d09282ca 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -2697,3 +2697,34 @@ class TestUrlConstraints < ActionDispatch::IntegrationTest assert_response :success end end + +class TestInvalidUrls < ActionDispatch::IntegrationTest + class FooController < ActionController::Base + def show + render :text => "foo#show" + end + end + + test "invalid UTF-8 encoding returns a 400 Bad Request" do + with_routing do |set| + set.draw do + get "/bar/:id", :to => redirect("/foo/show/%{id}") + get "/foo/show(/:id)", :to => "test_invalid_urls/foo#show" + get "/foo(/:action(/:id))", :to => "test_invalid_urls/foo" + get "/:controller(/:action(/:id))" + end + + get "/%E2%EF%BF%BD%A6" + assert_response :bad_request + + get "/foo/%E2%EF%BF%BD%A6" + assert_response :bad_request + + get "/foo/show/%E2%EF%BF%BD%A6" + assert_response :bad_request + + get "/bar/%E2%EF%BF%BD%A6" + assert_response :bad_request + end + end +end
\ No newline at end of file diff --git a/actionpack/test/dispatch/test_request_test.rb b/actionpack/test/dispatch/test_request_test.rb index 4ee1d61146..6047631ba3 100644 --- a/actionpack/test/dispatch/test_request_test.rb +++ b/actionpack/test/dispatch/test_request_test.rb @@ -55,6 +55,13 @@ class TestRequestTest < ActiveSupport::TestCase assert_cookies({"user_name" => "david"}, req.cookie_jar) end + test "does not complain when Rails.application is nil" do + Rails.stubs(:application).returns(nil) + req = ActionDispatch::TestRequest.new + + assert_equal false, req.env.empty? + end + private def assert_cookies(expected, cookie_jar) assert_equal(expected, cookie_jar.instance_variable_get("@cookies")) diff --git a/actionpack/test/template/benchmark_helper_test.rb b/actionpack/test/template/benchmark_helper_test.rb index 1bdda22959..8c198d2562 100644 --- a/actionpack/test/template/benchmark_helper_test.rb +++ b/actionpack/test/template/benchmark_helper_test.rb @@ -19,6 +19,6 @@ class BenchmarkHelperTest < ActionView::TestCase log = StringIO.new self.stubs(:logger).returns(Logger.new(log)) benchmark {} - assert_match(log.rewind && log.read, /Benchmarking \(\d+.\d+ms\)/) + assert_match(/Benchmarking \(\d+.\d+ms\)/, log.rewind && log.read) end end diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb index 9b64bc9d81..2322fb0406 100644 --- a/actionpack/test/template/form_options_helper_test.rb +++ b/actionpack/test/template/form_options_helper_test.rb @@ -300,12 +300,12 @@ class FormOptionsHelperTest < ActionView::TestCase assert_dom_equal( "<optgroup label=\"----------\"><option value=\"US\">US</option>\n<option value=\"Canada\">Canada</option></optgroup><optgroup label=\"----------\"><option value=\"GB\">GB</option>\n<option value=\"Germany\">Germany</option></optgroup>", - grouped_options_for_select([['US',"Canada"] , ["GB", "Germany"]], divider: "----------") + grouped_options_for_select([['US',"Canada"] , ["GB", "Germany"]], nil, divider: "----------") ) end def test_grouped_options_for_select_with_selected_and_prompt_deprecated - assert_deprecated 'Passing the prompt to grouped_options_for_select as an argument is deprecated. Please pass it in an options hash.' do + assert_deprecated 'Passing the prompt to grouped_options_for_select as an argument is deprecated. Please use an options hash like `{ prompt: "Choose a product..." }`.' do assert_dom_equal( "<option value=\"\">Choose a product...</option><optgroup label=\"Hats\"><option value=\"Baseball Cap\">Baseball Cap</option>\n<option selected=\"selected\" value=\"Cowboy Hat\">Cowboy Hat</option></optgroup>", grouped_options_for_select([["Hats", ["Baseball Cap","Cowboy Hat"]]], "Cowboy Hat", "Choose a product...") diff --git a/actionpack/test/template/number_helper_test.rb b/actionpack/test/template/number_helper_test.rb index 5c6f23d70b..14ca6d9879 100644 --- a/actionpack/test/template/number_helper_test.rb +++ b/actionpack/test/template/number_helper_test.rb @@ -78,6 +78,8 @@ class NumberHelperTest < ActionView::TestCase assert_equal '12,345,678-05', number_with_delimiter(12345678.05, :separator => '-') assert_equal '12.345.678,05', number_with_delimiter(12345678.05, :separator => ',', :delimiter => '.') assert_equal '12.345.678,05', number_with_delimiter(12345678.05, :delimiter => '.', :separator => ',') + assert_equal '1<script></script>01', number_with_delimiter(1.01, :separator => "<script></script>") + assert_equal '1<script></script>000', number_with_delimiter(1000, :delimiter => "<script></script>") end def test_number_with_precision diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index 66fab20710..f58e474759 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -60,6 +60,20 @@ class TextHelperTest < ActionView::TestCase simple_format(text) assert_equal text_clone, text end + + def test_simple_format_does_not_modify_the_html_options_hash + options = { :class => "foobar"} + passed_options = options.dup + simple_format("some text", passed_options) + assert_equal options, passed_options + end + + def test_simple_format_does_not_modify_the_options_hash + options = { :wrapper_tag => :div, :sanitize => false } + passed_options = options.dup + simple_format("some text", {}, passed_options) + assert_equal options, passed_options + end def test_truncate_should_not_be_html_safe assert !truncate("Hello World!", :length => 12).html_safe? @@ -92,6 +106,13 @@ class TextHelperTest < ActionView::TestCase assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding('UTF-8'), truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding('UTF-8'), :length => 10) end + + def test_truncate_does_not_modify_the_options_hash + options = { :length => 10 } + passed_options = options.dup + truncate("some text", passed_options) + assert_equal options, passed_options + end def test_highlight_should_be_html_safe assert highlight("This is a beautiful morning", "beautiful").html_safe? @@ -182,6 +203,13 @@ class TextHelperTest < ActionView::TestCase highlight("<div>abc div</div>", "div", :highlighter => '<b>\1</b>') ) end + + def test_highlight_does_not_modify_the_options_hash + options = { :highlighter => '<b>\1</b>', :sanitize => false } + passed_options = options.dup + highlight("<div>abc div</div>", "div", passed_options) + assert_equal options, passed_options + end def test_excerpt assert_equal("...is a beautiful morn...", excerpt("This is a beautiful morning", "beautiful", :radius => 5)) @@ -228,6 +256,13 @@ class TextHelperTest < ActionView::TestCase def test_excerpt_with_utf8 assert_equal("...\357\254\203ciency could not be...".force_encoding('UTF-8'), excerpt("That's why e\357\254\203ciency could not be helped".force_encoding('UTF-8'), 'could', :radius => 8)) end + + def test_excerpt_does_not_modify_the_options_hash + options = { :omission => "[...]",:radius => 5 } + passed_options = options.dup + excerpt("This is a beautiful morning", "beautiful", passed_options) + assert_equal options, passed_options + end def test_word_wrap assert_equal("my very very\nvery long\nstring", word_wrap("my very very very long string", :line_width => 15)) @@ -236,6 +271,13 @@ class TextHelperTest < ActionView::TestCase def test_word_wrap_with_extra_newlines assert_equal("my very very\nvery long\nstring\n\nwith another\nline", word_wrap("my very very very long string\n\nwith another line", :line_width => 15)) end + + def test_word_wrap_does_not_modify_the_options_hash + options = { :line_width => 15 } + passed_options = options.dup + word_wrap("some text", passed_options) + assert_equal options, passed_options + end def test_pluralization assert_equal("1 count", pluralize(1, "count")) diff --git a/actionpack/test/ts_isolated.rb b/actionpack/test/ts_isolated.rb index 595b4018e9..c44c5d8968 100644 --- a/actionpack/test/ts_isolated.rb +++ b/actionpack/test/ts_isolated.rb @@ -9,7 +9,7 @@ class TestIsolated < ActiveSupport::TestCase define_method("test #{file}") do command = "#{ruby} -Ilib:test #{file}" result = silence_stderr { `#{command}` } - assert_block("#{command}\n#{result}") { $?.to_i.zero? } + assert $?.to_i.zero?, "#{command}\n#{result}" end end end |