diff options
Diffstat (limited to 'actionpack')
60 files changed, 708 insertions, 180 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 9fb914ac40..102be1bdca 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,6 +1,133 @@ +* Fix generating a path for engine inside a resources block. + + Fixes #8533. + + *Piotr Sarnacki* + +* Add `Mime::Type.register "text/vcard", :vcf` to the default list of mime types. + + *DHH* + +* Remove deprecated `ActionController::RecordIdentifier`, use + `ActionView::RecordIdentifier` instead. + + *kennyj* + +* Fix regression when using `ActionView::Helpers::TranslationHelper#translate` with + `options[:raise]`. + + This regression was introduced at ec16ba75a5493b9da972eea08bae630eba35b62f. + + *Shota Fukumori (sora_h)* + +* Introducing Variants + + We often want to render different html/json/xml templates for phones, + tablets, and desktop browsers. Variants make it easy. + + The request variant is a specialization of the request format, like `:tablet`, + `:phone`, or `:desktop`. + + You can set the variant in a `before_action`: + + request.variant = :tablet if request.user_agent =~ /iPad/ + + Respond to variants in the action just like you respond to formats: + + respond_to do |format| + format.html do |html| + html.tablet # renders app/views/projects/show.html+tablet.erb + html.phone { extra_setup; render ... } + end + end + + Provide separate templates for each format and variant: + + app/views/projects/show.html.erb + app/views/projects/show.html+tablet.erb + app/views/projects/show.html+phone.erb + + *Łukasz Strzałkowski* + +* Fix header `Content-Type: #<Mime::NullType:...>` in localized template. + + When localized template has no format in the template name, + the response now has the default and correct `content-type`. + + Fixes #13064. + + *Angelo Capilleri* + +* Try to escape each part of a url correctly when using a redirect route. + + Fixes #13110. + + *Andrew White* + +* Better error message for typos in assert_response argument. + + When the response type argument to `assert_response` is not a known + response type, `assert_response` now throws an ArgumentError with a clear + message. This is intended to help debug typos in the response type. + + *Victor Costan* + +* Fix formatting for `rake routes` when a section is shorter than a header. + + *Sıtkı Bağdat* + +* Take a hash with options inside array in `#url_for`. + + Example: + + url_for [:new, :admin, :post, { param: 'value' }] + # => http://example.com/admin/posts/new?param=value + + *Andrey Ognevsky* + +* Add `session#fetch` method + + fetch behaves like [Hash#fetch](http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-fetch). + It returns a value from the hash for the given key. + If the key can’t be found, there are several options: + + * With no other arguments, it will raise an KeyError exception. + * If a default value is given, then that will be returned. + * If the optional code block is specified, then that will be run and its result returned. + + *Damien Mathieu* + +* Don't let strong parameters mutate the given hash via `fetch` + + Create a new instance if the given parameter is a `Hash` instead of + passing it to the `convert_hashes_to_parameters` method since it is + overriding its default value. + + *Brendon Murphy*, *Doug Cole* + +* Add `params` option to `button_to` form helper, which renders the given hash + as hidden form fields. + + *Andy Waite* + +* Make assets helpers work in the controllers like it works in the views. + + Example: + + # config/application.rb + config.asset_host = 'http://mycdn.com' + + ActionController::Base.helpers.asset_path('fallback.png') + # => http://mycdn.com/assets/fallback.png + + Fixes #10051. + + *Tima Maslyuchenko* + * Respect `SCRIPT_NAME` when using `redirect` with a relative path Example: + # application routes.rb mount BlogEngine => '/blog' @@ -12,7 +139,7 @@ the path. It also allows redirects to work where the application is deployed to a subdirectory of a website. - Fixes #7977 + Fixes #7977. *Andrew White* @@ -67,10 +194,6 @@ *Vasiliy Ermolovich* -* Separate Action View completely from Action Pack. - - *Łukasz Strzałkowski* - * Development mode exceptions are rendered in text format in case of XHR request. *Kir Shatrov* @@ -171,11 +294,13 @@ *Yves Senn*, *Andrew White* -* ActionView extracted from ActionPack +* Action View extracted from Action Pack. *Piotr Sarnacki*, *Łukasz Strzałkowski* -* Fix removing trailing slash for mounted apps #3215 +* Fix removing trailing slash for mounted apps. + + Fixes #3215. *Piotr Sarnacki* diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index 8a85bf346a..1d6009bab8 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -23,7 +23,7 @@ Gem::Specification.new do |s| s.add_dependency 'rack', '~> 1.5.2' s.add_dependency 'rack-test', '~> 0.6.2' + s.add_dependency 'actionview', version - s.add_development_dependency 'actionview', version s.add_development_dependency 'activemodel', version end diff --git a/actionpack/lib/abstract_controller/collector.rb b/actionpack/lib/abstract_controller/collector.rb index 09b9e7ddf0..ddd56b354a 100644 --- a/actionpack/lib/abstract_controller/collector.rb +++ b/actionpack/lib/abstract_controller/collector.rb @@ -23,7 +23,17 @@ module AbstractController protected def method_missing(symbol, &block) - mime_constant = Mime.const_get(symbol.upcase) + const_name = symbol.upcase + + unless Mime.const_defined?(const_name) + raise NoMethodError, "To respond to a custom format, register it as a MIME type first: " \ + "http://guides.rubyonrails.org/action_controller_overview.html#restful-downloads. " \ + "If you meant to respond to a variant like :tablet or :phone, not a custom format, " \ + "be sure to nest your variant response within a format response: " \ + "format.html { |html| html.tablet { ... } }" + end + + mime_constant = Mime.const_get(const_name) if Mime::SET.include?(mime_constant) AbstractController::Collector.generate_method_for_mime(mime_constant) diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index 6f6079d3c5..2580a78c7d 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -1,5 +1,7 @@ require 'active_support/concern' require 'active_support/core_ext/class/attribute' +require 'action_view/view_paths' +require 'set' module AbstractController class DoubleRenderError < Error @@ -12,11 +14,7 @@ module AbstractController module Rendering extend ActiveSupport::Concern - - included do - class_attribute :protected_instance_variables - self.protected_instance_variables = [] - end + include ActionView::ViewPaths # Normalize arguments, options and then delegates render_to_body and # sticks the result in self.response_body. @@ -55,21 +53,23 @@ module AbstractController Mime::TEXT end - DEFAULT_PROTECTED_INSTANCE_VARIABLES = %w( + DEFAULT_PROTECTED_INSTANCE_VARIABLES = Set.new %w( @_action_name @_response_body @_formats @_prefixes @_config @_view_context_class @_view_renderer @_lookup_context - ) + @_routes @_db_runtime + ).map(&:to_sym) # This method should return a hash with assigns. # You can overwrite this configuration per controller. # :api: public def view_assigns - hash = {} - variables = instance_variables - variables -= protected_instance_variables - variables -= DEFAULT_PROTECTED_INSTANCE_VARIABLES - variables.each { |name| hash[name[1..-1]] = instance_variable_get(name) } - hash + protected_vars = _protected_ivars + variables = instance_variables + + variables.reject! { |s| protected_vars.include? s } + variables.each_with_object({}) { |name, hash| + hash[name.slice(1, name.length)] = instance_variable_get(name) + } end # Normalize args by converting render "foo" to render :action => "foo" and @@ -104,8 +104,14 @@ module AbstractController # :api: private def _normalize_render(*args, &block) options = _normalize_args(*args, &block) + #TODO: remove defined? when we restore AP <=> AV dependency + options[:variant] = request.variant if defined?(request) && request.variant.present? _normalize_options(options) options end + + def _protected_ivars # :nodoc: + DEFAULT_PROTECTED_INSTANCE_VARIABLES + end end end diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 417d2efec2..50bc26a80f 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -50,7 +50,7 @@ module ActionController end # Common Active Support usage in Action Controller -require 'active_support/core_ext/class/attribute_accessors' +require 'active_support/core_ext/module/attribute_accessors' require 'active_support/core_ext/load_error' require 'active_support/core_ext/module/attr_internal' require 'active_support/core_ext/name_error' diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 3b0d094f4f..c0f10da23a 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -1,21 +1,8 @@ +require 'action_view' 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 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. @@ -99,7 +86,7 @@ module ActionController # or you can remove the entire session with +reset_session+. # # Sessions are stored by default in a browser cookie that's cryptographically signed, but unencrypted. - # This prevents the user from tampering with the session but also allows him to see its contents. + # This prevents the user from tampering with the session but also allows them to see its contents. # # Do not put secret information in cookie-based sessions! # @@ -174,7 +161,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 @@ -214,6 +201,7 @@ module ActionController end MODULES = [ + AbstractController::Rendering, AbstractController::Translation, AbstractController::AssetPaths, @@ -221,6 +209,7 @@ module ActionController HideActions, UrlFor, Redirecting, + ActionView::Layouts, Rendering, Renderers::All, ConditionalGet, @@ -261,10 +250,17 @@ module ActionController end # Define some internal variables that should not be propagated to the view. - self.protected_instance_variables = [ + PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [ :@_status, :@_headers, :@_params, :@_env, :@_response, :@_request, - :@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout - ] + :@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout ] + + def _protected_ivars # :nodoc: + PROTECTED_IVARS + end + + def self.protected_instance_variables + PROTECTED_IVARS + end ActiveSupport.run_load_hooks(:action_controller, self) end diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb index b53ae7f29f..a9c3e438fb 100644 --- a/actionpack/lib/action_controller/metal/helpers.rb +++ b/actionpack/lib/action_controller/metal/helpers.rb @@ -73,7 +73,11 @@ module ActionController # Provides a proxy to access helpers methods from outside the view. def helpers - @helper_proxy ||= ActionView::Base.new.extend(_helpers) + @helper_proxy ||= begin + proxy = ActionView::Base.new + proxy.config = config.inheritable_copy + proxy.extend(_helpers) + end end # Overwrite modules_for_helpers to accept :all as argument, which loads diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb index d3aa8f90c5..b0e164bc57 100644 --- a/actionpack/lib/action_controller/metal/instrumentation.rb +++ b/actionpack/lib/action_controller/metal/instrumentation.rb @@ -67,7 +67,7 @@ module ActionController private - # A hook invoked everytime a before callback is halted. + # A hook invoked every time a before callback is halted. def halted_callback_hook(filter) ActiveSupport::Notifications.instrument("halted_callback.action_controller", :filter => filter) end diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index a072fce1a1..dedff9f476 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -181,13 +181,41 @@ module ActionController #:nodoc: # end # end # + # Formats can have different variants. + # + # The request variant is a specialization of the request format, like <tt>:tablet</tt>, + # <tt>:phone</tt>, or <tt>:desktop<tt>. + # + # We often want to render different html/json/xml templates for phones, + # tablets, and desktop browsers. Variants make it easy. + # + # You can set the variant in a +before_action+: + # + # request.variant = :tablet if request.user_agent =~ /iPad/ + # + # Respond to variants in the action just like you respond to formats: + # + # respond_to do |format| + # format.html do |variant| + # variant.tablet # renders app/views/projects/show.html+tablet.erb + # variant.phone { extra_setup; render ... } + # variant.none { special_setup } # executed only if there is no variant set + # end + # end + # + # Provide separate templates for each format and variant: + # + # app/views/projects/show.html.erb + # app/views/projects/show.html+tablet.erb + # app/views/projects/show.html+phone.erb + # # Be sure to check the documentation of +respond_with+ and # <tt>ActionController::MimeResponds.respond_to</tt> for more examples. def respond_to(*mimes, &block) raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given? if collector = retrieve_collector_from_mimes(mimes, &block) - response = collector.response + response = collector.response(request.variant) response ? response.call : render({}) end end @@ -260,7 +288,7 @@ module ActionController #:nodoc: # * for other requests - i.e. data formats such as xml, json, csv etc, if # the resource passed to +respond_with+ responds to <code>to_<format></code>, # the method attempts to render the resource in the requested format - # directly, e.g. for an xml request, the response is equivalent to calling + # directly, e.g. for an xml request, the response is equivalent to calling # <code>render xml: resource</code>. # # === Nested resources @@ -321,13 +349,15 @@ module ActionController #:nodoc: # 2. <tt>:action</tt> - overwrites the default render action used after an # unsuccessful html +post+ request. def respond_with(*resources, &block) - raise "In order to use respond_with, first you need to declare the formats your " \ - "controller responds to in the class level" if self.class.mimes_for_respond_to.empty? + if self.class.mimes_for_respond_to.empty? + raise "In order to use respond_with, first you need to declare the " \ + "formats your controller responds to in the class level." + end if collector = retrieve_collector_from_mimes(&block) options = resources.size == 1 ? {} : resources.extract_options! options = options.clone - options[:default_response] = collector.response + options[:default_response] = collector.response(request.variant) (options.delete(:responder) || self.class.responder).call(self, resources, options) end end @@ -396,11 +426,12 @@ module ActionController #:nodoc: # request, with this response then being accessible by calling #response. class Collector include AbstractController::Collector - attr_accessor :order, :format + attr_accessor :format def initialize(mimes) - @order, @responses = [], {} - mimes.each { |mime| send(mime) } + @responses = {} + + mimes.each { |mime| @responses["Mime::#{mime.upcase}".constantize] = nil } end def any(*args, &block) @@ -414,16 +445,54 @@ module ActionController #:nodoc: def custom(mime_type, &block) mime_type = Mime::Type.lookup(mime_type.to_s) unless mime_type.is_a?(Mime::Type) - @order << mime_type - @responses[mime_type] ||= block + @responses[mime_type] ||= if block_given? + block + else + VariantCollector.new + end end - def response - @responses.fetch(format, @responses[Mime::ALL]) + def response(variant) + response = @responses.fetch(format, @responses[Mime::ALL]) + if response.is_a?(VariantCollector) + response.variant(variant) + elsif response.nil? || response.arity == 0 + response + else + lambda { response.call VariantFilter.new(variant) } + end end def negotiate_format(request) - @format = request.negotiate_mime(order) + @format = request.negotiate_mime(@responses.keys) + end + + #Used for inline syntax + class VariantCollector #:nodoc: + def initialize + @variants = {} + end + + def method_missing(name, *args, &block) + @variants[name] = block if block_given? + end + + def variant(name) + @variants[name.nil? ? :none : name] + end + end + + #Used for nested block syntax + class VariantFilter #:nodoc: + def initialize(variant) + @variant = variant + end + + def method_missing(name) + if block_given? + yield if name == @variant || (name == :none && @variant.nil?) + end + end end end end diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb index 90f0ef0b1c..66d34f3b67 100644 --- a/actionpack/lib/action_controller/metal/rendering.rb +++ b/actionpack/lib/action_controller/metal/rendering.rb @@ -27,18 +27,15 @@ module ActionController end def render_to_body(options = {}) - super || if options[:text].present? - options[:text] - else - " " - end + super || options[:text].presence || ' ' end private def _process_format(format) super - self.content_type ||= format.to_s + # format is a Mime::NullType instance here then this condition can't be changed to `if format` + self.content_type ||= format.to_s unless format.nil? end # Normalize arguments by catching blocks and setting them on :update. diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb index 66ff34a794..e24b56fa91 100644 --- a/actionpack/lib/action_controller/metal/responder.rb +++ b/actionpack/lib/action_controller/metal/responder.rb @@ -144,7 +144,7 @@ module ActionController #:nodoc: undef_method(:to_json) if method_defined?(:to_json) undef_method(:to_yaml) if method_defined?(:to_yaml) - # Initializes a new responder an invoke the proper format. If the format is + # Initializes a new responder and invokes the proper format. If the format is # not defined, call to_format. # def self.call(*args) @@ -270,7 +270,7 @@ module ActionController #:nodoc: resource.respond_to?(:errors) && !resource.errors.empty? end - # Check whether the neceessary Renderer is available + # Check whether the necessary Renderer is available def has_renderer? Renderers::RENDERERS.include?(format) end diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb index 66403d533c..b4948d99a8 100644 --- a/actionpack/lib/action_controller/metal/strong_parameters.rb +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -17,7 +17,7 @@ module ActionController def initialize(param) # :nodoc: @param = param - super("param not found: #{param}") + super("param is missing or the value is empty: #{param}") end end @@ -284,7 +284,14 @@ module ActionController # params.fetch(:none, 'Francesco') # => "Francesco" # params.fetch(:none) { 'Francesco' } # => "Francesco" def fetch(key, *args) - convert_hashes_to_parameters(key, super) + value = super + # Don't rely on +convert_hashes_to_parameters+ + # so as to not mutate via a +fetch+ + if value.is_a?(Hash) + value = self.class.new(value) + value.permit! if permitted? + end + value rescue KeyError raise ActionController::ParameterMissing.new(key) end diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb index 40bb060d52..346598b6de 100644 --- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb +++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb @@ -10,6 +10,8 @@ module ActionDispatch self.ignore_accept_header = false end + attr_reader :variant + # The MIME type of the HTTP request, such as Mime::XML. # # For backward compatibility, the post \format is extracted from the @@ -64,6 +66,18 @@ module ActionDispatch end end + # Sets the \variant for template. + def variant=(variant) + if variant.is_a? Symbol + @variant = variant + else + raise ArgumentError, "request.variant must be set to a Symbol, not a #{variant.class}. " \ + "For security reasons, never directly set the variant to a user-provided value, " \ + "like params[:variant].to_sym. Check user-provided value against a whitelist first, " \ + "then set the variant: request.variant = :tablet if params[:variant] == 'tablet'" + end + end + # Sets the \format by string extension, which can be used to force custom formats # that are not controlled by the extension. # diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index ef144c3c76..2a8ff0a5d2 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -1,5 +1,6 @@ require 'set' -require 'active_support/core_ext/class/attribute_accessors' +require 'singleton' +require 'active_support/core_ext/module/attribute_accessors' require 'active_support/core_ext/string/starts_ends_with' module Mime @@ -27,7 +28,7 @@ module Mime class << self def [](type) return type if type.is_a?(Type) - Type.lookup_by_extension(type) || NullType.new + Type.lookup_by_extension(type) || NullType.instance end def fetch(type) @@ -292,13 +293,13 @@ module Mime end class NullType + include Singleton + def nil? true end - def ref - nil - end + def ref; end def respond_to_missing?(method, include_private = false) method.to_s.ends_with? '?' diff --git a/actionpack/lib/action_dispatch/http/mime_types.rb b/actionpack/lib/action_dispatch/http/mime_types.rb index a6b3aee5e7..0e4da36038 100644 --- a/actionpack/lib/action_dispatch/http/mime_types.rb +++ b/actionpack/lib/action_dispatch/http/mime_types.rb @@ -7,6 +7,7 @@ Mime::Type.register "text/javascript", :js, %w( application/javascript applicati Mime::Type.register "text/css", :css Mime::Type.register "text/calendar", :ics Mime::Type.register "text/csv", :csv +Mime::Type.register "text/vcard", :vcf Mime::Type.register "image/png", :png, [], %w(png) Mime::Type.register "image/jpeg", :jpeg, [], %w(jpg jpeg jpe pjpeg) diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index aba8f66118..1318c62fbe 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -271,7 +271,7 @@ module ActionDispatch # Override Rack's GET method to support indifferent access def GET - @env["action_dispatch.request.query_parameters"] ||= (normalize_encode_params(super) || {}) + @env["action_dispatch.request.query_parameters"] ||= Utils.deep_munge((normalize_encode_params(super) || {})) rescue TypeError => e raise ActionController::BadRequest.new(:query, e) end @@ -279,7 +279,7 @@ module ActionDispatch # Override Rack's POST method to support indifferent access def POST - @env["action_dispatch.request.request_parameters"] ||= (normalize_encode_params(super) || {}) + @env["action_dispatch.request.request_parameters"] ||= Utils.deep_munge((normalize_encode_params(super) || {})) rescue TypeError => e raise ActionController::BadRequest.new(:request, e) end @@ -299,17 +299,24 @@ module ActionDispatch LOCALHOST =~ remote_addr && LOCALHOST =~ remote_ip end - protected + # Extracted into ActionDispatch::Request::Utils.deep_munge, but kept here for backwards compatibility. + def deep_munge(hash) + ActiveSupport::Deprecation.warn( + "This method has been extracted into ActionDispatch::Request::Utils.deep_munge. Please start using that instead." + ) - def parse_query(qs) - Utils.deep_munge(super) + Utils.deep_munge(hash) end - private + protected + def parse_query(qs) + Utils.deep_munge(super) + end - def check_method(name) - HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}") - name - end + private + def check_method(name) + HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}") + name + end end end diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 5247e61a23..7b2655b2d8 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -1,4 +1,4 @@ -require 'active_support/core_ext/class/attribute_accessors' +require 'active_support/core_ext/module/attribute_accessors' require 'monitor' module ActionDispatch # :nodoc: diff --git a/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb b/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb index 971cb3447f..5a79059ed6 100644 --- a/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb +++ b/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb @@ -43,9 +43,7 @@ module ActionDispatch move_string(t, a).concat(move_regexp(t, a)) end - def to_json - require 'json' - + def as_json(options = nil) simple_regexp = Hash.new { |h,k| h[k] = {} } @regexp_states.each do |from, hash| @@ -54,11 +52,11 @@ module ActionDispatch end end - JSON.dump({ + { regexp_states: simple_regexp, string_states: @string_states, accepting: @accepting - }) + } end def to_svg diff --git a/actionpack/lib/action_dispatch/journey/router/utils.rb b/actionpack/lib/action_dispatch/journey/router/utils.rb index 1edf86cd88..d1a004af50 100644 --- a/actionpack/lib/action_dispatch/journey/router/utils.rb +++ b/actionpack/lib/action_dispatch/journey/router/utils.rb @@ -18,7 +18,7 @@ module ActionDispatch path = "/#{path}" path.squeeze!('/') path.sub!(%r{/+\Z}, '') - path.gsub!(/(%[a-f0-9]{2}+)/) { $1.upcase } + path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase } path = '/' if path == '' path end diff --git a/actionpack/lib/action_dispatch/middleware/callbacks.rb b/actionpack/lib/action_dispatch/middleware/callbacks.rb index 852f1cf6f5..baf9d5779e 100644 --- a/actionpack/lib/action_dispatch/middleware/callbacks.rb +++ b/actionpack/lib/action_dispatch/middleware/callbacks.rb @@ -8,14 +8,14 @@ module ActionDispatch class << self delegate :to_prepare, :to_cleanup, :to => "ActionDispatch::Reloader" - end - def self.before(*args, &block) - set_callback(:call, :before, *args, &block) - end + def before(*args, &block) + set_callback(:call, :before, *args, &block) + end - def self.after(*args, &block) - set_callback(:call, :after, *args, &block) + def after(*args, &block) + set_callback(:call, :after, *args, &block) + end end def initialize(app) diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb index 1de3d14530..377f05c982 100644 --- a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb +++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb @@ -1,5 +1,5 @@ require 'action_controller/metal/exceptions' -require 'active_support/core_ext/class/attribute_accessors' +require 'active_support/core_ext/module/attribute_accessors' module ActionDispatch class ExceptionWrapper @@ -96,7 +96,7 @@ module ActionDispatch def source_fragment(path, line) return unless Rails.respond_to?(:root) && Rails.root full_path = Rails.root.join(path) - if File.exists?(full_path) + if File.exist?(full_path) File.open(full_path, "r") do |file| start = [line - 3, 0].max lines = file.each_line.drop(start).take(6) diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb index b9eb8036e9..11b42ee5be 100644 --- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb @@ -15,8 +15,8 @@ module ActionDispatch # best possible option given your application's configuration. # # If you only have secret_token set, your cookies will be signed, but - # not encrypted. This means a user cannot alter his +user_id+ without - # knowing your app's secret key, but can easily read his +user_id+. This + # not encrypted. This means a user cannot alter their +user_id+ without + # knowing your app's secret key, but can easily read their +user_id+. This # was the default for Rails 3 apps. # # If you have secret_key_base set, your cookies will be encrypted. This diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index fcc5bc12c4..1d4f0f89a6 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -29,8 +29,11 @@ module ActionDispatch def call(env) @app.call(env) rescue Exception => exception - raise exception if env['action_dispatch.show_exceptions'] == false - render_exception(env, exception) + if env['action_dispatch.show_exceptions'] == false + raise exception + else + render_exception(env, exception) + end end private diff --git a/actionpack/lib/action_dispatch/request/session.rb b/actionpack/lib/action_dispatch/request/session.rb index 7bc812fd22..973627f106 100644 --- a/actionpack/lib/action_dispatch/request/session.rb +++ b/actionpack/lib/action_dispatch/request/session.rb @@ -7,6 +7,9 @@ module ActionDispatch ENV_SESSION_KEY = Rack::Session::Abstract::ENV_SESSION_KEY # :nodoc: ENV_SESSION_OPTIONS_KEY = Rack::Session::Abstract::ENV_SESSION_OPTIONS_KEY # :nodoc: + # Singleton object used to determine if an optional param wasn't specified + Unspecified = Object.new + def self.create(store, env, default_options) session_was = find env session = Request::Session.new(store, env) @@ -127,6 +130,15 @@ module ActionDispatch @delegate.delete key.to_s end + def fetch(key, default=Unspecified, &block) + load_for_read! + if default == Unspecified + @delegate.fetch(key.to_s, &block) + else + @delegate.fetch(key.to_s, default, &block) + end + end + def inspect if loaded? super diff --git a/actionpack/lib/action_dispatch/routing/inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb index cffb814e1e..120bc54333 100644 --- a/actionpack/lib/action_dispatch/routing/inspector.rb +++ b/actionpack/lib/action_dispatch/routing/inspector.rb @@ -179,7 +179,8 @@ module ActionDispatch private def draw_section(routes) - name_width, verb_width, path_width = widths(routes) + header_lengths = ['Prefix', 'Verb', 'URI Pattern'].map(&:length) + name_width, verb_width, path_width = widths(routes).zip(header_lengths).map(&:max) routes.map do |r| "#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs]}" diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index db9c993590..bfba8d143d 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -147,14 +147,16 @@ module ActionDispatch @defaults.merge!(options[:defaults]) if options[:defaults] options.each do |key, default| - next if Regexp === default || IGNORE_OPTIONS.include?(key) - @defaults[key] = default + unless Regexp === default || IGNORE_OPTIONS.include?(key) + @defaults[key] = default + end end if options[:constraints].is_a?(Hash) options[:constraints].each do |key, default| - next unless URL_OPTIONS.include?(key) && (String === default || Fixnum === default) - @defaults[key] ||= default + if URL_OPTIONS.include?(key) && (String === default || Fixnum === default) + @defaults[key] ||= default + end end end @@ -166,19 +168,21 @@ module ActionDispatch end def normalize_conditions! - @conditions.merge!(:path_info => path) + @conditions[:path_info] = path constraints.each do |key, condition| - next if segment_keys.include?(key) || key == :controller - @conditions[key] = condition + unless segment_keys.include?(key) || key == :controller + @conditions[key] = condition + end end - @conditions[:required_defaults] = [] + required_defaults = [] options.each do |key, required_default| - next if segment_keys.include?(key) || IGNORE_OPTIONS.include?(key) - next if Regexp === required_default - @conditions[:required_defaults] << key + unless segment_keys.include?(key) || IGNORE_OPTIONS.include?(key) || Regexp === required_default + required_defaults << key + end end + @conditions[:required_defaults] = required_defaults via_all = options.delete(:via) if options[:via] == :all @@ -192,8 +196,7 @@ module ActionDispatch end if via = options[:via] - list = Array(via).map { |m| m.to_s.dasherize.upcase } - @conditions.merge!(:request_method => list) + @conditions[:request_method] = Array(via).map { |m| m.to_s.dasherize.upcase } end end @@ -226,11 +229,13 @@ module ActionDispatch action = action.to_s unless action.is_a?(Regexp) if controller.blank? && segment_keys.exclude?(:controller) - raise ArgumentError, "missing :controller" + message = "Missing :controller key on routes definition, please check your routes." + raise ArgumentError, message end if action.blank? && segment_keys.exclude?(:action) - raise ArgumentError, "missing :action" + message = "Missing :action key on routes definition, please check your routes." + raise ArgumentError, message end if controller.is_a?(String) && controller !~ /\A[a-z_0-9\/]*\z/ @@ -384,7 +389,7 @@ module ActionDispatch # The namespace for :controller. # # match 'path', to: 'c#a', module: 'sekret', controller: 'posts' - # #=> Sekret::PostsController + # # => Sekret::PostsController # # See <tt>Scoping#namespace</tt> for its scope equivalent. # @@ -497,11 +502,12 @@ module ActionDispatch raise "A rack application must be specified" unless path options[:as] ||= app_name(app) + target_as = name_for_action(options[:as], path) options[:via] ||= :all match(path, options.merge(:to => app, :anchor => false, :format => false)) - define_generate_prefix(app, options[:as]) + define_generate_prefix(app, target_as) self end diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb index 3e54c7e71c..cbf4c5aa8b 100644 --- a/actionpack/lib/action_dispatch/routing/redirection.rb +++ b/actionpack/lib/action_dispatch/routing/redirection.rb @@ -57,11 +57,33 @@ module ActionDispatch def relative_path?(path) path && !path.empty? && path[0] != '/' end + + def escape(params) + Hash[params.map{ |k,v| [k, Rack::Utils.escape(v)] }] + end + + def escape_fragment(params) + Hash[params.map{ |k,v| [k, Journey::Router::Utils.escape_fragment(v)] }] + end + + def escape_path(params) + Hash[params.map{ |k,v| [k, Journey::Router::Utils.escape_path(v)] }] + end end class PathRedirect < Redirect + URL_PARTS = /\A([^?]+)?(\?[^#]+)?(#.+)?\z/ + def path(params, request) - (params.empty? || !block.match(/%\{\w*\}/)) ? block : (block % escape(params)) + if block.match(URL_PARTS) + path = interpolation_required?($1, params) ? $1 % escape_path(params) : $1 + query = interpolation_required?($2, params) ? $2 % escape(params) : $2 + fragment = interpolation_required?($3, params) ? $3 % escape_fragment(params) : $3 + + "#{path}#{query}#{fragment}" + else + interpolation_required?(block, params) ? block % escape(params) : block + end end def inspect @@ -69,8 +91,8 @@ module ActionDispatch end private - def escape(params) - Hash[params.map{ |k,v| [k, Rack::Utils.escape(v)] }] + def interpolation_required?(string, params) + !params.empty? && string && string.match(/%\{\w*\}/) end end @@ -101,11 +123,6 @@ module ActionDispatch 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)] }] - end end module Redirection diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb index bcebe532bf..4a0ef40873 100644 --- a/actionpack/lib/action_dispatch/routing/url_for.rb +++ b/actionpack/lib/action_dispatch/routing/url_for.rb @@ -155,6 +155,8 @@ module ActionDispatch _routes.url_for(options.symbolize_keys.reverse_merge!(url_options)) when String options + when Array + polymorphic_url(options, options.extract_options!) else polymorphic_url(options) end diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb index 93f9fab9c2..68feb26936 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/response.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb @@ -27,6 +27,9 @@ module ActionDispatch assert @response.send("#{type}?"), message else code = Rack::Utils::SYMBOL_TO_STATUS_CODE[type] + if code.nil? + raise ArgumentError, "Invalid response type :#{type}" + end assert_equal code, @response.response_code, message end else diff --git a/actionpack/test/abstract/collector_test.rb b/actionpack/test/abstract/collector_test.rb index 5709ad0378..b1a5044399 100644 --- a/actionpack/test/abstract/collector_test.rb +++ b/actionpack/test/abstract/collector_test.rb @@ -37,7 +37,7 @@ module AbstractController test "does not register unknown mime types" do collector = MyCollector.new - assert_raise NameError do + assert_raise NoMethodError do collector.unknown end end diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index a0d90f7eee..0e163a93eb 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -246,8 +246,6 @@ class Rack::TestCase < ActionDispatch::IntegrationTest end end -ActionController::Base.superclass.send(:include, ActionView::Layouts) - module ActionController class Base include ActionController::Testing diff --git a/actionpack/test/assertions/response_assertions_test.rb b/actionpack/test/assertions/response_assertions_test.rb index ca1d58765d..8eec98e916 100644 --- a/actionpack/test/assertions/response_assertions_test.rb +++ b/actionpack/test/assertions/response_assertions_test.rb @@ -50,6 +50,14 @@ module ActionDispatch assert_response :success } end + + def test_assert_response_sym_typo + @response = FakeResponse.new 200 + + assert_raises(ArgumentError) { + assert_response :succezz + } + end end end end diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb index c64ffef654..9ceab91e42 100644 --- a/actionpack/test/controller/flash_test.rb +++ b/actionpack/test/controller/flash_test.rb @@ -174,12 +174,12 @@ class FlashTest < ActionController::TestCase flash.update(:foo => :foo_indeed, :bar => :bar_indeed) assert_equal(:foo_indeed, flash.discard(:foo)) # valid key passed - assert_nil flash.discard(:unknown) # non existant key passed + assert_nil flash.discard(:unknown) # non existent key passed assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.discard().to_hash) # nothing passed assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.discard(nil).to_hash) # nothing passed assert_equal(:foo_indeed, flash.keep(:foo)) # valid key passed - assert_nil flash.keep(:unknown) # non existant key passed + assert_nil flash.keep(:unknown) # non existent key passed assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep().to_hash) # nothing passed assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep(nil).to_hash) # nothing passed end diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb index 248c81193e..20f99f19ee 100644 --- a/actionpack/test/controller/helper_test.rb +++ b/actionpack/test/controller/helper_test.rb @@ -201,6 +201,12 @@ class HelperTest < ActiveSupport::TestCase # fun/pdf_helper.rb assert methods.include?(:foobar) end + + def test_helper_proxy_config + AllHelpersController.config.my_var = 'smth' + + assert_equal 'smth', AllHelpersController.helpers.config.my_var + end private def expected_helper_methods diff --git a/actionpack/test/controller/localized_templates_test.rb b/actionpack/test/controller/localized_templates_test.rb index 6b02eedaed..c95ef8a0c7 100644 --- a/actionpack/test/controller/localized_templates_test.rb +++ b/actionpack/test/controller/localized_templates_test.rb @@ -34,4 +34,15 @@ class LocalizedTemplatesTest < ActionController::TestCase get :hello_world assert_equal "Gutten Tag", @response.body end + + def test_localized_template_has_correct_header_with_no_format_in_template_name + old_locale = I18n.locale + I18n.locale = :it + + get :hello_world + assert_equal "Ciao Mondo", @response.body + assert_equal "text/html", @response.content_type + ensure + I18n.locale = old_locale + end end diff --git a/actionpack/test/controller/mime/respond_to_test.rb b/actionpack/test/controller/mime/respond_to_test.rb index 774dabe105..d84eb5790d 100644 --- a/actionpack/test/controller/mime/respond_to_test.rb +++ b/actionpack/test/controller/mime/respond_to_test.rb @@ -146,6 +146,51 @@ class RespondToController < ActionController::Base end end + def variant_with_implicit_rendering + end + + def variant_with_format_and_custom_render + request.variant = :mobile + + respond_to do |type| + type.html { render text: "mobile" } + end + end + + def multiple_variants_for_format + respond_to do |type| + type.html do |html| + html.tablet { render text: "tablet" } + html.phone { render text: "phone" } + end + end + end + + def variant_plus_none_for_format + respond_to do |format| + format.html do |variant| + variant.phone { render text: "phone" } + variant.none + end + end + end + + def variant_inline_syntax + respond_to do |format| + format.js { render text: "js" } + format.html.none { render text: "none" } + format.html.phone { render text: "phone" } + end + end + + def variant_inline_syntax_without_block + respond_to do |format| + format.js + format.html.none + format.html.phone + end + end + protected def set_layout case action_name @@ -490,4 +535,66 @@ class RespondToControllerTest < ActionController::TestCase get :using_defaults, :format => "invalidformat" end end + + def test_invalid_variant + @request.variant = :invalid + assert_raises(ActionView::MissingTemplate) do + get :variant_with_implicit_rendering + end + end + + def test_variant_not_set_regular_template_missing + assert_raises(ActionView::MissingTemplate) do + get :variant_with_implicit_rendering + end + end + + def test_variant_with_implicit_rendering + @request.variant = :mobile + get :variant_with_implicit_rendering + assert_equal "text/html", @response.content_type + assert_equal "mobile", @response.body + end + + def test_variant_with_format_and_custom_render + @request.variant = :phone + get :variant_with_format_and_custom_render + assert_equal "text/html", @response.content_type + assert_equal "mobile", @response.body + end + + def test_multiple_variants_for_format + @request.variant = :tablet + get :multiple_variants_for_format + assert_equal "text/html", @response.content_type + assert_equal "tablet", @response.body + end + + def test_no_variant_in_variant_setup + get :variant_plus_none_for_format + assert_equal "text/html", @response.content_type + assert_equal "none", @response.body + end + + def test_variant_inline_syntax + get :variant_inline_syntax, format: :js + assert_equal "text/javascript", @response.content_type + assert_equal "js", @response.body + + get :variant_inline_syntax + assert_equal "text/html", @response.content_type + assert_equal "none", @response.body + + @request.variant = :phone + get :variant_inline_syntax + assert_equal "text/html", @response.content_type + assert_equal "phone", @response.body + end + + def test_variant_inline_syntax_without_block + @request.variant = :phone + get :variant_inline_syntax_without_block + assert_equal "text/html", @response.content_type + assert_equal "phone", @response.body + end end diff --git a/actionpack/test/controller/new_base/render_streaming_test.rb b/actionpack/test/controller/new_base/render_streaming_test.rb index 2b36a399bb..4c9126ca8c 100644 --- a/actionpack/test/controller/new_base/render_streaming_test.rb +++ b/actionpack/test/controller/new_base/render_streaming_test.rb @@ -4,7 +4,7 @@ module RenderStreaming class BasicController < ActionController::Base self.view_paths = [ActionView::FixtureResolver.new( "render_streaming/basic/hello_world.html.erb" => "Hello world", - "render_streaming/basic/boom.html.erb" => "<%= nil.invalid! %>", + "render_streaming/basic/boom.html.erb" => "<%= raise 'Ruby was here!' %>", "layouts/application.html.erb" => "<%= yield %>, I'm here!", "layouts/boom.html.erb" => "<body class=\"<%= nil.invalid! %>\"<%= yield %></body>" )] @@ -90,7 +90,7 @@ module RenderStreaming begin get "/render_streaming/basic/template_exception" io.rewind - assert_match "(undefined method `invalid!' for nil:NilClass)", io.read + assert_match "Ruby was here!", io.read ensure ActionView::Base.logger = _old end diff --git a/actionpack/test/controller/new_base/render_template_test.rb b/actionpack/test/controller/new_base/render_template_test.rb index 6b2ae2b2a9..b7a9cf92f2 100644 --- a/actionpack/test/controller/new_base/render_template_test.rb +++ b/actionpack/test/controller/new_base/render_template_test.rb @@ -14,7 +14,7 @@ module RenderTemplate "test/with_json.html.erb" => "<%= render :template => 'test/with_json', :formats => [:json] %>", "test/with_json.json.erb" => "<%= render :template => 'test/final', :formats => [:json] %>", "test/final.json.erb" => "{ final: json }", - "test/with_error.html.erb" => "<%= idontexist %>" + "test/with_error.html.erb" => "<%= raise 'i do not exist' %>" )] def index @@ -132,7 +132,7 @@ module RenderTemplate test "rendering a template with error properly excerts the code" do get :with_error assert_status 500 - assert_match "undefined local variable or method `idontexist", response.body + assert_match "i do not exist", response.body end end diff --git a/actionpack/test/controller/parameters/parameters_permit_test.rb b/actionpack/test/controller/parameters/parameters_permit_test.rb index 84e007b5d0..b60c5f058d 100644 --- a/actionpack/test/controller/parameters/parameters_permit_test.rb +++ b/actionpack/test/controller/parameters/parameters_permit_test.rb @@ -147,6 +147,12 @@ class ParametersPermitTest < ActiveSupport::TestCase assert_equal :foo, e.param end + test "fetch with a default value of a hash does not mutate the object" do + params = ActionController::Parameters.new({}) + params.fetch :foo, {} + assert_equal nil, params[:foo] + end + test "fetch doesnt raise ParameterMissing exception if there is a default" do assert_equal "monkey", @params.fetch(:foo, "monkey") assert_equal "monkey", @params.fetch(:foo) { "monkey" } diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index f41287381a..26806fb03f 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -529,4 +529,4 @@ class HeadRenderTest < ActionController::TestCase assert_equal "something", @response.headers["X-Custom-Header"] assert_response :forbidden end -end
\ No newline at end of file +end diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb index 9aea7e860a..a5f43c4b6b 100644 --- a/actionpack/test/controller/resources_test.rb +++ b/actionpack/test/controller/resources_test.rb @@ -4,6 +4,7 @@ require 'active_support/core_ext/object/with_options' require 'active_support/core_ext/array/extract_options' class ResourcesTest < ActionController::TestCase + def test_default_restful_routes with_restful_routing :messages do assert_simply_restful_for :messages @@ -1004,7 +1005,7 @@ class ResourcesTest < ActionController::TestCase end end - assert_resource_allowed_routes('images', { :product_id => '1' }, { :id => '2' }, [:index, :new, :create, :show, :edit, :update, :destory], [], 'products/1/images') + assert_resource_allowed_routes('images', { :product_id => '1' }, { :id => '2' }, [:index, :new, :create, :show, :edit, :update, :destroy], [], 'products/1/images') assert_resource_allowed_routes('images', { :product_id => '1', :format => 'xml' }, { :id => '2' }, [:index, :new, :create, :show, :edit, :update, :destroy], [], 'products/1/images') end end @@ -1320,6 +1321,8 @@ class ResourcesTest < ActionController::TestCase assert_recognizes options, path_options elsif Array(not_allowed).include?(action) assert_not_recognizes options, path_options + else + raise Assertion, 'Invalid Action has passed' end end diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 46df1a7bd5..2c84e95c6e 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -2,6 +2,7 @@ require 'abstract_unit' require 'controller/fake_controllers' require 'active_support/core_ext/object/with_options' +require 'active_support/core_ext/object/json' class MilestonesController < ActionController::Base def index() head :ok end @@ -86,36 +87,36 @@ class LegacyRouteSetTests < ActiveSupport::TestCase def test_symbols_with_dashes rs.draw do get '/:artist/:song-omg', :to => lambda { |env| - resp = JSON.dump env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] + resp = ActiveSupport::JSON.encode env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] [200, {}, [resp]] } end - hash = JSON.load get(URI('http://example.org/journey/faithfully-omg')) + hash = ActiveSupport::JSON.decode get(URI('http://example.org/journey/faithfully-omg')) assert_equal({"artist"=>"journey", "song"=>"faithfully"}, hash) end def test_id_with_dash rs.draw do get '/journey/:id', :to => lambda { |env| - resp = JSON.dump env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] + resp = ActiveSupport::JSON.encode env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] [200, {}, [resp]] } end - hash = JSON.load get(URI('http://example.org/journey/faithfully-omg')) + hash = ActiveSupport::JSON.decode get(URI('http://example.org/journey/faithfully-omg')) assert_equal({"id"=>"faithfully-omg"}, hash) end def test_dash_with_custom_regexp rs.draw do get '/:artist/:song-omg', :constraints => { :song => /\d+/ }, :to => lambda { |env| - resp = JSON.dump env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] + resp = ActiveSupport::JSON.encode env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] [200, {}, [resp]] } end - hash = JSON.load get(URI('http://example.org/journey/123-omg')) + hash = ActiveSupport::JSON.decode get(URI('http://example.org/journey/123-omg')) assert_equal({"artist"=>"journey", "song"=>"123"}, hash) assert_equal 'Not Found', get(URI('http://example.org/journey/faithfully-omg')) end @@ -123,24 +124,24 @@ class LegacyRouteSetTests < ActiveSupport::TestCase def test_pre_dash rs.draw do get '/:artist/omg-:song', :to => lambda { |env| - resp = JSON.dump env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] + resp = ActiveSupport::JSON.encode env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] [200, {}, [resp]] } end - hash = JSON.load get(URI('http://example.org/journey/omg-faithfully')) + hash = ActiveSupport::JSON.decode get(URI('http://example.org/journey/omg-faithfully')) assert_equal({"artist"=>"journey", "song"=>"faithfully"}, hash) end def test_pre_dash_with_custom_regexp rs.draw do get '/:artist/omg-:song', :constraints => { :song => /\d+/ }, :to => lambda { |env| - resp = JSON.dump env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] + resp = ActiveSupport::JSON.encode env[ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] [200, {}, [resp]] } end - hash = JSON.load get(URI('http://example.org/journey/omg-123')) + hash = ActiveSupport::JSON.decode get(URI('http://example.org/journey/omg-123')) assert_equal({"artist"=>"journey", "song"=>"123"}, hash) assert_equal 'Not Found', get(URI('http://example.org/journey/omg-faithfully')) end @@ -160,14 +161,14 @@ class LegacyRouteSetTests < ActiveSupport::TestCase def test_star_paths_are_greedy_but_not_too_much rs.draw do get "/*path", :to => lambda { |env| - x = JSON.dump env["action_dispatch.request.path_parameters"] + x = ActiveSupport::JSON.encode env["action_dispatch.request.path_parameters"] [200, {}, [x]] } end expected = { "path" => "foo/bar", "format" => "html" } u = URI('http://example.org/foo/bar.html') - assert_equal expected, JSON.parse(get(u)) + assert_equal expected, ActiveSupport::JSON.decode(get(u)) end def test_optional_star_paths_are_greedy @@ -185,7 +186,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase def test_optional_star_paths_are_greedy_but_not_too_much rs.draw do get "/(*filters)", :to => lambda { |env| - x = JSON.dump env["action_dispatch.request.path_parameters"] + x = ActiveSupport::JSON.encode env["action_dispatch.request.path_parameters"] [200, {}, [x]] } end @@ -193,7 +194,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase expected = { "filters" => "ne_27.065938,-80.6092/sw_25.489856,-82", "format" => "542794" } u = URI('http://example.org/ne_27.065938,-80.6092/sw_25.489856,-82.542794') - assert_equal expected, JSON.parse(get(u)) + assert_equal expected, ActiveSupport::JSON.decode(get(u)) end def test_regexp_precidence diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb index f75c604277..de0476dbde 100644 --- a/actionpack/test/controller/test_case_test.rb +++ b/actionpack/test/controller/test_case_test.rb @@ -1,5 +1,6 @@ require 'abstract_unit' require 'controller/fake_controllers' +require 'active_support/json/decoding' class TestCaseTest < ActionController::TestCase class TestController < ActionController::Base @@ -622,7 +623,7 @@ XML @request.headers['Referer'] = "http://nohost.com/home" @request.headers['Content-Type'] = "application/rss+xml" get :test_headers - parsed_env = JSON.parse(@response.body) + parsed_env = ActiveSupport::JSON.decode(@response.body) assert_equal "http://nohost.com/home", parsed_env["HTTP_REFERER"] assert_equal "application/rss+xml", parsed_env["CONTENT_TYPE"] end @@ -631,7 +632,7 @@ XML @request.headers['HTTP_REFERER'] = "http://example.com/about" @request.headers['CONTENT_TYPE'] = "application/json" get :test_headers - parsed_env = JSON.parse(@response.body) + parsed_env = ActiveSupport::JSON.decode(@response.body) assert_equal "http://example.com/about", parsed_env["HTTP_REFERER"] assert_equal "application/json", parsed_env["CONTENT_TYPE"] end diff --git a/actionpack/test/controller/url_for_test.rb b/actionpack/test/controller/url_for_test.rb index 088ad73f2f..d2b4952759 100644 --- a/actionpack/test/controller/url_for_test.rb +++ b/actionpack/test/controller/url_for_test.rb @@ -370,6 +370,24 @@ module AbstractController assert_equal("/c/a?show=false", W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :show => false)) end + def test_url_generation_with_array_and_hash + with_routing do |set| + set.draw do + namespace :admin do + resources :posts + end + end + + kls = Class.new { include set.url_helpers } + kls.default_url_options[:host] = 'www.basecamphq.com' + + controller = kls.new + assert_equal("http://www.basecamphq.com/admin/posts/new?param=value", + controller.send(:url_for, [:new, :admin, :post, { param: 'value' }]) + ) + end + end + private def extract_params(url) url.split('?', 2).last.split('&').sort diff --git a/actionpack/test/controller/webservice_test.rb b/actionpack/test/controller/webservice_test.rb index b2dfd96606..d80b0e2da0 100644 --- a/actionpack/test/controller/webservice_test.rb +++ b/actionpack/test/controller/webservice_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'active_support/json/decoding' class WebServiceTest < ActionDispatch::IntegrationTest class TestController < ActionController::Base @@ -54,7 +55,7 @@ class WebServiceTest < ActionDispatch::IntegrationTest def test_register_and_use_json_simple with_test_route_set do - with_params_parsers Mime::JSON => Proc.new { |data| JSON.parse(data)['request'].with_indifferent_access } do + with_params_parsers Mime::JSON => Proc.new { |data| ActiveSupport::JSON.decode(data)['request'].with_indifferent_access } do post "/", '{"request":{"summary":"content...","title":"JSON"}}', 'CONTENT_TYPE' => 'application/json' diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb index 8a19129695..981cf2426e 100644 --- a/actionpack/test/dispatch/mime_type_test.rb +++ b/actionpack/test/dispatch/mime_type_test.rb @@ -31,21 +31,21 @@ class MimeTypeTest < ActiveSupport::TestCase test "parse text with trailing star at the beginning" do accept = "text/*, text/html, application/json, multipart/form-data" - expect = [Mime::HTML, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::XML, Mime::YAML, Mime::JSON, Mime::MULTIPART_FORM] + expect = [Mime::HTML, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::VCF, Mime::XML, Mime::YAML, Mime::JSON, Mime::MULTIPART_FORM] parsed = Mime::Type.parse(accept) assert_equal expect, parsed end test "parse text with trailing star in the end" do accept = "text/html, application/json, multipart/form-data, text/*" - expect = [Mime::HTML, Mime::JSON, Mime::MULTIPART_FORM, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::XML, Mime::YAML] + expect = [Mime::HTML, Mime::JSON, Mime::MULTIPART_FORM, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::VCF, Mime::XML, Mime::YAML] parsed = Mime::Type.parse(accept) assert_equal expect, parsed end test "parse text with trailing star" do accept = "text/*" - expect = [Mime::HTML, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::XML, Mime::YAML, Mime::JSON] + expect = [Mime::HTML, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::VCF, Mime::XML, Mime::YAML, Mime::JSON] parsed = Mime::Type.parse(accept) assert_equal expect, parsed end diff --git a/actionpack/test/dispatch/mount_test.rb b/actionpack/test/dispatch/mount_test.rb index 30e95a0b75..683a4f01e2 100644 --- a/actionpack/test/dispatch/mount_test.rb +++ b/actionpack/test/dispatch/mount_test.rb @@ -5,7 +5,7 @@ class TestRoutingMount < ActionDispatch::IntegrationTest class FakeEngine def self.routes - Object.new + @routes ||= ActionDispatch::Routing::RouteSet.new end def self.call(env) @@ -27,12 +27,23 @@ class TestRoutingMount < ActionDispatch::IntegrationTest scope "/its_a" do mount SprocketsApp, :at => "/sprocket" end + + resources :users do + mount FakeEngine, :at => "/fakeengine", :as => :fake_mounted_at_resource + end end def app Router end + def test_app_name_is_properly_generated_when_engine_is_mounted_in_resources + assert Router.mounted_helpers.method_defined?(:user_fake_mounted_at_resource), + "A mounted helper should be defined with a parent's prefix" + assert Router.named_routes.routes[:user_fake_mounted_at_resource], + "A named route should be defined with a parent's prefix" + end + def test_trailing_slash_is_not_removed_from_path_info get "/sprockets/omg/" assert_equal "/sprockets -- /omg/", response.body diff --git a/actionpack/test/dispatch/request/query_string_parsing_test.rb b/actionpack/test/dispatch/request/query_string_parsing_test.rb index f072a9f717..0ad0dbc958 100644 --- a/actionpack/test/dispatch/request/query_string_parsing_test.rb +++ b/actionpack/test/dispatch/request/query_string_parsing_test.rb @@ -11,6 +11,17 @@ class QueryStringParsingTest < ActionDispatch::IntegrationTest head :ok end end + class EarlyParse + def initialize(app) + @app = app + end + + def call(env) + # Trigger a Rack parse so that env caches the query params + Rack::Request.new(env).params + @app.call(env) + end + end def teardown TestController.last_query_parameters = nil @@ -131,6 +142,10 @@ class QueryStringParsingTest < ActionDispatch::IntegrationTest set.draw do get ':action', :to => ::QueryStringParsingTest::TestController end + @app = self.class.build_app(set) do |middleware| + middleware.use(EarlyParse) + end + get "/parse", actual assert_response :ok diff --git a/actionpack/test/dispatch/request/session_test.rb b/actionpack/test/dispatch/request/session_test.rb index 1517f96fdc..df55fcc8bc 100644 --- a/actionpack/test/dispatch/request/session_test.rb +++ b/actionpack/test/dispatch/request/session_test.rb @@ -61,6 +61,22 @@ module ActionDispatch assert_equal([], s.values) end + def test_fetch + session = Session.create(store, {}, {}) + + session['one'] = '1' + assert_equal '1', session.fetch(:one) + + assert_equal '2', session.fetch(:two, '2') + assert_nil session.fetch(:two, nil) + + assert_equal 'three', session.fetch(:three) {|el| el.to_s } + + assert_raise KeyError do + session.fetch(:three) + end + end + private def store Class.new { diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index f6de9748ca..b308c5749a 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -93,6 +93,14 @@ class RequestTest < ActiveSupport::TestCase assert_equal '1.1.1.1', request.remote_ip end + test "remote ip spoof protection ignores private addresses" do + request = stub_request 'HTTP_X_FORWARDED_FOR' => '172.17.19.51', + 'HTTP_CLIENT_IP' => '172.17.19.51', + 'REMOTE_ADDR' => '1.1.1.1', + 'HTTP_X_BLUECOAT_VIA' => 'de462e07a2db325e' + assert_equal '1.1.1.1', request.remote_ip + end + test "remote ip v6" do request = stub_request 'REMOTE_ADDR' => '2001:0db8:85a3:0000:0000:8a2e:0370:7334' assert_equal '2001:0db8:85a3:0000:0000:8a2e:0370:7334', request.remote_ip @@ -836,6 +844,19 @@ class RequestTest < ActiveSupport::TestCase end end + test "setting variant" do + request = stub_request + request.variant = :mobile + assert_equal :mobile, request.variant + end + + test "setting variant with non symbol value" do + request = stub_request + assert_raise ArgumentError do + request.variant = "mobile" + end + end + protected def stub_request(env = {}) diff --git a/actionpack/test/dispatch/routing/inspector_test.rb b/actionpack/test/dispatch/routing/inspector_test.rb index 4f97d28d2b..c8038bbd7c 100644 --- a/actionpack/test/dispatch/routing/inspector_test.rb +++ b/actionpack/test/dispatch/routing/inspector_test.rb @@ -46,11 +46,11 @@ module ActionDispatch assert_equal [ " Prefix Verb URI Pattern Controller#Action", - "custom_assets GET /custom/assets(.:format) custom_assets#show", - " blog /blog Blog::Engine", + "custom_assets GET /custom/assets(.:format) custom_assets#show", + " blog /blog Blog::Engine", "", "Routes for Blog::Engine:", - "cart GET /cart(.:format) cart#show" + " cart GET /cart(.:format) cart#show" ], output end @@ -61,7 +61,7 @@ module ActionDispatch assert_equal [ "Prefix Verb URI Pattern Controller#Action", - "cart GET /cart(.:format) cart#show" + " cart GET /cart(.:format) cart#show" ], output end @@ -72,7 +72,7 @@ module ActionDispatch assert_equal [ " Prefix Verb URI Pattern Controller#Action", - "custom_assets GET /custom/assets(.:format) custom_assets#show" + "custom_assets GET /custom/assets(.:format) custom_assets#show" ], output end @@ -101,7 +101,7 @@ module ActionDispatch assert_equal [ "Prefix Verb URI Pattern Controller#Action", - "root GET / pages#main" + " root GET / pages#main" ], output end @@ -112,7 +112,7 @@ module ActionDispatch assert_equal [ "Prefix Verb URI Pattern Controller#Action", - " GET /api/:action(.:format) api#:action" + " GET /api/:action(.:format) api#:action" ], output end @@ -123,7 +123,7 @@ module ActionDispatch assert_equal [ "Prefix Verb URI Pattern Controller#Action", - " GET /:controller/:action(.:format) :controller#:action" + " GET /:controller/:action(.:format) :controller#:action" ], output end @@ -134,7 +134,7 @@ module ActionDispatch assert_equal [ "Prefix Verb URI Pattern Controller#Action", - " GET /:controller(/:action(/:id))(.:format) :controller#:action {:id=>/\\d+/}" + " GET /:controller(/:action(/:id))(.:format) :controller#:action {:id=>/\\d+/}" ], output end @@ -145,7 +145,7 @@ module ActionDispatch assert_equal [ "Prefix Verb URI Pattern Controller#Action", - %Q[ GET /photos/:id(.:format) photos#show {:format=>"jpg"}] + %Q[ GET /photos/:id(.:format) photos#show {:format=>"jpg"}] ], output end @@ -156,7 +156,7 @@ module ActionDispatch assert_equal [ "Prefix Verb URI Pattern Controller#Action", - " GET /photos/:id(.:format) photos#show {:id=>/[A-Z]\\d{5}/}" + " GET /photos/:id(.:format) photos#show {:id=>/[A-Z]\\d{5}/}" ], output end @@ -172,7 +172,7 @@ module ActionDispatch assert_equal [ "Prefix Verb URI Pattern Controller#Action", - " GET /foo/:id(.:format) #{RackApp.name} {:id=>/[A-Z]\\d{5}/}" + " GET /foo/:id(.:format) #{RackApp.name} {:id=>/[A-Z]\\d{5}/}" ], output end @@ -191,7 +191,7 @@ module ActionDispatch assert_equal [ "Prefix Verb URI Pattern Controller#Action", - " /foo #{RackApp.name} {:constraint=>( my custom constraint )}" + " /foo #{RackApp.name} {:constraint=>( my custom constraint )}" ], output end @@ -212,9 +212,9 @@ module ActionDispatch assert_equal [ "Prefix Verb URI Pattern Controller#Action", - " foo GET /foo(.:format) redirect(301, /foo/bar) {:subdomain=>\"admin\"}", - " bar GET /bar(.:format) redirect(307, path: /foo/bar)", - "foobar GET /foobar(.:format) redirect(301)" + " foo GET /foo(.:format) redirect(301, /foo/bar) {:subdomain=>\"admin\"}", + " bar GET /bar(.:format) redirect(307, path: /foo/bar)", + "foobar GET /foobar(.:format) redirect(301)" ], output end @@ -241,7 +241,7 @@ module ActionDispatch end assert_equal ["Prefix Verb URI Pattern Controller#Action", - " GET /:controller(/:action) (?-mix:api\\/[^\\/]+)#:action"], output + " GET /:controller(/:action) (?-mix:api\\/[^\\/]+)#:action"], output end end end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 3e9e90a950..aac808afda 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -3235,7 +3235,9 @@ class TestRedirectInterpolation < ActionDispatch::IntegrationTest get "/foo/:id" => redirect("/foo/bar/%{id}") get "/bar/:id" => redirect(:path => "/foo/bar/%{id}") + get "/baz/:id" => redirect("/baz?id=%{id}&foo=?&bar=1#id-%{id}") get "/foo/bar/:id" => ok + get "/baz" => ok end end @@ -3251,6 +3253,14 @@ class TestRedirectInterpolation < ActionDispatch::IntegrationTest verify_redirect "http://www.example.com/foo/bar/1%3E" end + test "path redirect escapes interpolated parameters correctly" do + get "/foo/1%201" + verify_redirect "http://www.example.com/foo/bar/1%201" + + get "/baz/1%201" + verify_redirect "http://www.example.com/baz?id=1+1&foo=?&bar=1#id-1%201" + end + private def verify_redirect(url, status=301) assert_equal status, @response.status diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb index 112f470786..acccbcb2e6 100644 --- a/actionpack/test/dispatch/static_test.rb +++ b/actionpack/test/dispatch/static_test.rb @@ -37,6 +37,10 @@ module StaticTests end def test_served_static_file_with_non_english_filename + if RUBY_ENGINE == 'jruby ' + skip "Stop skipping if following bug gets fixed: " \ + "http://jira.codehaus.org/browse/JRUBY-7192" + end assert_html "means hello in Japanese\n", get("/foo/#{Rack::Utils.escape("こんにちは.html")}") end diff --git a/actionpack/test/fixtures/localized/hello_world.it.erb b/actionpack/test/fixtures/localized/hello_world.it.erb new file mode 100644 index 0000000000..9191fdc187 --- /dev/null +++ b/actionpack/test/fixtures/localized/hello_world.it.erb @@ -0,0 +1 @@ +Ciao Mondo
\ No newline at end of file diff --git a/actionpack/test/fixtures/respond_to/variant_inline_syntax_without_block.html+phone.erb b/actionpack/test/fixtures/respond_to/variant_inline_syntax_without_block.html+phone.erb new file mode 100644 index 0000000000..cd222a4a49 --- /dev/null +++ b/actionpack/test/fixtures/respond_to/variant_inline_syntax_without_block.html+phone.erb @@ -0,0 +1 @@ +phone
\ No newline at end of file diff --git a/actionpack/test/fixtures/respond_to/variant_plus_none_for_format.html.erb b/actionpack/test/fixtures/respond_to/variant_plus_none_for_format.html.erb new file mode 100644 index 0000000000..c86c3f3551 --- /dev/null +++ b/actionpack/test/fixtures/respond_to/variant_plus_none_for_format.html.erb @@ -0,0 +1 @@ +none
\ No newline at end of file diff --git a/actionpack/test/fixtures/respond_to/variant_with_implicit_rendering.html+mobile.erb b/actionpack/test/fixtures/respond_to/variant_with_implicit_rendering.html+mobile.erb new file mode 100644 index 0000000000..317801ad30 --- /dev/null +++ b/actionpack/test/fixtures/respond_to/variant_with_implicit_rendering.html+mobile.erb @@ -0,0 +1 @@ +mobile
\ No newline at end of file diff --git a/actionpack/test/journey/gtg/transition_table_test.rb b/actionpack/test/journey/gtg/transition_table_test.rb index 33acba8b65..b968780d8d 100644 --- a/actionpack/test/journey/gtg/transition_table_test.rb +++ b/actionpack/test/journey/gtg/transition_table_test.rb @@ -1,5 +1,5 @@ require 'abstract_unit' -require 'json' +require 'active_support/json/decoding' module ActionDispatch module Journey @@ -13,7 +13,7 @@ module ActionDispatch /articles/:id(.:format) } - json = JSON.load table.to_json + json = ActiveSupport::JSON.decode table.to_json assert json['regexp_states'] assert json['string_states'] assert json['accepting'] diff --git a/actionpack/test/journey/router/utils_test.rb b/actionpack/test/journey/router/utils_test.rb index 057dc40cca..93348f4647 100644 --- a/actionpack/test/journey/router/utils_test.rb +++ b/actionpack/test/journey/router/utils_test.rb @@ -15,6 +15,14 @@ module ActionDispatch def test_uri_unescape assert_equal "a/b c+d", Utils.unescape_uri("a%2Fb%20c+d") end + + def test_normalize_path_not_greedy + assert_equal "/foo%20bar%20baz", Utils.normalize_path("/foo%20bar%20baz") + end + + def test_normalize_path_uppercase + assert_equal "/foo%AAbar%AAbaz", Utils.normalize_path("/foo%aabar%aabaz") + end end end end diff --git a/actionpack/test/lib/controller/fake_models.rb b/actionpack/test/lib/controller/fake_models.rb index 08af187311..b8b51d86c2 100644 --- a/actionpack/test/lib/controller/fake_models.rb +++ b/actionpack/test/lib/controller/fake_models.rb @@ -112,7 +112,7 @@ module Blog end class RenderJsonTestException < Exception - def to_json(options = nil) - return { :error => self.class.name, :message => self.to_s }.to_json + def as_json(options = nil) + { :error => self.class.name, :message => self.to_s } end end |
