diff options
Diffstat (limited to 'actionpack')
35 files changed, 459 insertions, 282 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 5123713c6b..be1f53faf5 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,22 @@ +* Fix 'Stack level too deep' when rendering `head :ok` in an action method + called 'status' in a controller. + + Fixes #13905. + + *Christiaan Van den Poel* + +* Add MKCALENDAR HTTP method (RFC 4791). + + *Sergey Karpesh* + +* Instrument fragment cache metrics. + + Adds `:controller`: and `:action` keys to the instrumentation payload + for the `*_fragment.action_controller` notifications. This allows tracking + e.g. the fragment cache hit rates for each controller action. + + *Daniel Schierbeck* + * Always use the provided port if the protocol is relative. Fixes #15043. diff --git a/actionpack/lib/abstract_controller/url_for.rb b/actionpack/lib/abstract_controller/url_for.rb index 4a95e1f276..72d07b0927 100644 --- a/actionpack/lib/abstract_controller/url_for.rb +++ b/actionpack/lib/abstract_controller/url_for.rb @@ -11,7 +11,7 @@ module AbstractController def _routes raise "In order to use #url_for, you must include routing helpers explicitly. " \ - "For instance, `include Rails.application.routes.url_helpers" + "For instance, `include Rails.application.routes.url_helpers`." end module ClassMethods diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb index 879d5fdd94..2694d4c12f 100644 --- a/actionpack/lib/action_controller/caching/fragments.rb +++ b/actionpack/lib/action_controller/caching/fragments.rb @@ -90,7 +90,13 @@ module ActionController end def instrument_fragment_cache(name, key) # :nodoc: - ActiveSupport::Notifications.instrument("#{name}.action_controller", :key => key){ yield } + payload = { + controller: controller_name, + action: action_name, + key: key + } + + ActiveSupport::Notifications.instrument("#{name}.action_controller", payload) { yield } end end end diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index 0f4cc7a8f5..696fbf6e09 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -232,5 +232,9 @@ module ActionController new.dispatch(name, klass.new(env)) end end + + def _status_code + @_status + end end end diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb index 43407f5b78..84a9112144 100644 --- a/actionpack/lib/action_controller/metal/head.rb +++ b/actionpack/lib/action_controller/metal/head.rb @@ -27,7 +27,7 @@ module ActionController self.status = status self.location = url_for(location) if location - if include_content?(self.status) + if include_content?(self._status_code) self.content_type = content_type || (Mime[formats.first] if formats) self.response.charset = false if self.response self.response_body = " " diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index 2eb7853aa6..3111992f82 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -90,17 +90,29 @@ module ActionController end def authenticate(request, &login_procedure) - unless request.authorization.blank? + if has_basic_credentials?(request) login_procedure.call(*user_name_and_password(request)) end end + def has_basic_credentials?(request) + request.authorization.present? && (auth_scheme(request) == 'Basic') + end + def user_name_and_password(request) decode_credentials(request).split(':', 2) end def decode_credentials(request) - ::Base64.decode64(request.authorization.split(' ', 2).last || '') + ::Base64.decode64(auth_param(request) || '') + end + + def auth_scheme(request) + request.authorization.split(' ', 2).first + end + + def auth_param(request) + request.authorization.split(' ', 2).second end def encode_credentials(user_name, password) diff --git a/actionpack/lib/action_controller/metal/live.rb b/actionpack/lib/action_controller/metal/live.rb index acf40b2e16..4c0554d27b 100644 --- a/actionpack/lib/action_controller/metal/live.rb +++ b/actionpack/lib/action_controller/metal/live.rb @@ -102,7 +102,8 @@ module ActionController end end - @stream.write "data: #{json}\n\n" + message = json.gsub("\n", "\ndata: ") + @stream.write "data: #{message}\n\n" end end diff --git a/actionpack/lib/action_controller/metal/rack_delegation.rb b/actionpack/lib/action_controller/metal/rack_delegation.rb index bdf6e88699..6921834044 100644 --- a/actionpack/lib/action_controller/metal/rack_delegation.rb +++ b/actionpack/lib/action_controller/metal/rack_delegation.rb @@ -6,7 +6,7 @@ module ActionController extend ActiveSupport::Concern delegate :headers, :status=, :location=, :content_type=, - :status, :location, :content_type, :to => "@_response" + :status, :location, :content_type, :_status_code, :to => "@_response" def dispatch(action, request) set_response!(request) diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb index 2812038938..136e086d0d 100644 --- a/actionpack/lib/action_controller/metal/redirecting.rb +++ b/actionpack/lib/action_controller/metal/redirecting.rb @@ -14,7 +14,7 @@ module ActionController include ActionController::RackDelegation include ActionController::UrlFor - # Redirects the browser to the target specified in +options+. This parameter can take one of three forms: + # Redirects the browser to the target specified in +options+. This parameter can be any one of: # # * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+. # * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record. @@ -24,6 +24,8 @@ module ActionController # * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places. # Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt> # + # === Examples: + # # redirect_to action: "show", id: 5 # redirect_to post # redirect_to "http://www.rubyonrails.org" @@ -32,7 +34,7 @@ module ActionController # redirect_to :back # redirect_to proc { edit_post_url(@post) } # - # The redirection happens as a "302 Found" header unless otherwise specified. + # The redirection happens as a "302 Found" header unless otherwise specified using the <tt>:status</tt> option: # # redirect_to post_url(@post), status: :found # redirect_to action: 'atom', status: :moved_permanently @@ -60,8 +62,10 @@ module ActionController # redirect_to post_url(@post), status: 301, flash: { updated_post_id: @post.id } # redirect_to({ action: 'atom' }, alert: "Something serious happened") # - # When using <tt>redirect_to :back</tt>, if there is no referrer, ActionController::RedirectBackError will be raised. You may specify some fallback - # behavior for this case by rescuing ActionController::RedirectBackError. + # When using <tt>redirect_to :back</tt>, if there is no referrer, + # <tt>ActionController::RedirectBackError</tt> will be raised. You + # may specify some fallback behavior for this case by rescuing + # <tt>ActionController::RedirectBackError</tt>. def redirect_to(options = {}, response_status = {}) #:doc: raise ActionControllerError.new("Cannot redirect to nil!") unless options raise AbstractController::DoubleRenderError if response_body diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb index 0443b73953..29ce5abd55 100644 --- a/actionpack/lib/action_controller/metal/renderers.rb +++ b/actionpack/lib/action_controller/metal/renderers.rb @@ -6,6 +6,11 @@ module ActionController Renderers.add(key, &block) end + # See <tt>Renderers.remove</tt> + def self.remove_renderer(key) + Renderers.remove(key) + end + class MissingRenderer < LoadError def initialize(format) super "No renderer defined for format: #{format}" @@ -83,6 +88,17 @@ module ActionController RENDERERS << key.to_sym end + # This method is the opposite of add method. + # + # Usage: + # + # ActionController::Renderers.remove(:csv) + def self.remove(key) + RENDERERS.delete(key.to_sym) + method = "_render_option_#{key}" + remove_method(method) if method_defined?(method) + end + module All extend ActiveSupport::Concern include Renderers diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb index 37d4a96ee1..89c3545323 100644 --- a/actionpack/lib/action_controller/metal/url_for.rb +++ b/actionpack/lib/action_controller/metal/url_for.rb @@ -23,12 +23,12 @@ module ActionController include AbstractController::UrlFor def url_options - @_url_options ||= super.reverse_merge( + @_url_options ||= { :host => request.host, :port => request.optional_port, :protocol => request.protocol, :_recall => request.symbolized_path_parameters - ).freeze + }.merge(super).freeze if (same_origin = _routes.equal?(env["action_dispatch.routes".freeze])) || (script_name = env["ROUTES_#{_routes.object_id}_SCRIPT_NAME"]) || diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb index dcb299ed03..378d7393ff 100644 --- a/actionpack/lib/action_dispatch/http/parameters.rb +++ b/actionpack/lib/action_dispatch/http/parameters.rb @@ -25,8 +25,8 @@ module ActionDispatch def path_parameters=(parameters) #:nodoc: @symbolized_path_params = nil - @env.delete("action_dispatch.request.parameters") - @env["action_dispatch.request.path_parameters"] = parameters + @env.delete(Routing::RouteSet::PARAMETERS_KEY) + @env[Routing::RouteSet::PARAMETERS_KEY] = parameters end # The same as <tt>path_parameters</tt> with explicitly symbolized keys. @@ -41,7 +41,7 @@ module ActionDispatch # # See <tt>symbolized_path_parameters</tt> for symbolized keys. def path_parameters - @env["action_dispatch.request.path_parameters"] ||= {} + @env[Routing::RouteSet::PARAMETERS_KEY] ||= {} end def reset_parameters #:nodoc: diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index daa06e96e6..cdb3e44b3a 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -64,6 +64,7 @@ module ActionDispatch # Ordered Collections Protocol (WebDAV) (http://www.ietf.org/rfc/rfc3648.txt) # Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol (http://www.ietf.org/rfc/rfc3744.txt) # Web Distributed Authoring and Versioning (WebDAV) SEARCH (http://www.ietf.org/rfc/rfc5323.txt) + # Calendar Extensions to WebDAV (http://www.ietf.org/rfc/rfc4791.txt) # PATCH Method for HTTP (http://www.ietf.org/rfc/rfc5789.txt) RFC2616 = %w(OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT) RFC2518 = %w(PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK) @@ -71,9 +72,10 @@ module ActionDispatch RFC3648 = %w(ORDERPATCH) RFC3744 = %w(ACL) RFC5323 = %w(SEARCH) + RFC4791 = %w(MKCALENDAR) RFC5789 = %w(PATCH) - HTTP_METHODS = RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC5789 + HTTP_METHODS = RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC4791 + RFC5789 HTTP_METHOD_LOOKUP = {} diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 3d27ff2b24..eaea93b730 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -296,6 +296,9 @@ module ActionDispatch # :nodoc: cookies end + def _status_code + @status + end private def before_committed diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb index c9860af909..a6c17f50a5 100644 --- a/actionpack/lib/action_dispatch/http/url.rb +++ b/actionpack/lib/action_dispatch/http/url.rb @@ -30,19 +30,25 @@ module ActionDispatch end def url_for(options) + unless options[:host] || options[:only_path] + raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true' + end + path = options[:script_name].to_s.chomp("/") path << options[:path].to_s - result = build_host_url(options) - if options[:trailing_slash] if path.include?('?') - result << path.sub(/\?/, '/\&') + path.sub!(/\?/, '/\&') else - result << path.sub(/[^\/]\z|\A\z/, '\&/') + path.sub!(/[^\/]\z|\A\z/, '\&/') end - else - result << path + end + + result = path + + unless options[:only_path] + result.prepend build_host_url(options) end if options.key? :params @@ -61,28 +67,25 @@ module ActionDispatch private def build_host_url(options) - unless options[:host] || options[:only_path] - raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true' + if match = options[:host].match(HOST_REGEXP) + options[:protocol] ||= match[1] unless options[:protocol] == false + options[:host] = match[2] + options[:port] = match[3] unless options.key?(:port) end - result = "" + options[:protocol] = normalize_protocol(options) + options[:host] = normalize_host(options) + options[:port] = normalize_port(options) - unless options[:only_path] - if match = options[:host].match(HOST_REGEXP) - options[:protocol] ||= match[1] unless options[:protocol] == false - options[:host] = match[2] - options[:port] = match[3] unless options.key?(:port) - end - - options[:protocol] = normalize_protocol(options) - options[:host] = normalize_host(options) - options[:port] = normalize_port(options) + result = options[:protocol] - result << options[:protocol] - result << rewrite_authentication(options) - result << options[:host] - result << ":#{options[:port]}" if options[:port] + if options[:user] && options[:password] + result << "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@" end + + result << options[:host] + result << ":#{options[:port]}" if options[:port] + result end @@ -94,14 +97,6 @@ module ActionDispatch (options[:subdomain] == true || !options.key?(:subdomain)) && options[:domain].nil? end - def rewrite_authentication(options) - if options[:user] && options[:password] - "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@" - else - "" - end - end - def normalize_protocol(options) case options[:protocol] when nil diff --git a/actionpack/lib/action_dispatch/journey/formatter.rb b/actionpack/lib/action_dispatch/journey/formatter.rb index 57f0963731..7eaf8e49ce 100644 --- a/actionpack/lib/action_dispatch/journey/formatter.rb +++ b/actionpack/lib/action_dispatch/journey/formatter.rb @@ -12,7 +12,7 @@ module ActionDispatch @cache = nil end - def generate(type, name, options, recall = {}, parameterize = nil) + def generate(name, options, recall = {}, parameterize = nil) constraints = recall.merge(options) missing_keys = [] @@ -30,6 +30,12 @@ module ActionDispatch parameterized_parts.key?(key) || route.defaults.key?(key) end + defaults = route.defaults + required_parts = route.required_parts + parameterized_parts.delete_if do |key, value| + value.to_s == defaults[key].to_s && !required_parts.include?(key) + end + return [route.format(parameterized_parts), params] end diff --git a/actionpack/lib/action_dispatch/journey/parser.rb b/actionpack/lib/action_dispatch/journey/parser.rb index 430812fafe..d129ba7e16 100644 --- a/actionpack/lib/action_dispatch/journey/parser.rb +++ b/actionpack/lib/action_dispatch/journey/parser.rb @@ -1,7 +1,7 @@ # # DO NOT MODIFY!!!! -# This file is automatically generated by Racc 1.4.9 -# from Racc grammar file "". +# This file is automatically generated by Racc 1.4.11 +# from Racc grammer file "". # require 'racc/parser.rb' @@ -9,42 +9,38 @@ require 'racc/parser.rb' require 'action_dispatch/journey/parser_extras' module ActionDispatch - module Journey # :nodoc: - class Parser < Racc::Parser # :nodoc: + module Journey + class Parser < Racc::Parser ##### State transition tables begin ### racc_action_table = [ - 17, 21, 13, 15, 14, 7, nil, 16, 8, 19, - 13, 15, 14, 7, 23, 16, 8, 19, 13, 15, - 14, 7, nil, 16, 8, 13, 15, 14, 7, nil, - 16, 8, 13, 15, 14, 7, nil, 16, 8 ] + 13, 15, 14, 7, 21, 16, 8, 19, 13, 15, + 14, 7, 17, 16, 8, 13, 15, 14, 7, 24, + 16, 8, 13, 15, 14, 7, 19, 16, 8 ] racc_action_check = [ - 1, 17, 1, 1, 1, 1, nil, 1, 1, 1, - 20, 20, 20, 20, 20, 20, 20, 20, 7, 7, - 7, 7, nil, 7, 7, 19, 19, 19, 19, nil, - 19, 19, 0, 0, 0, 0, nil, 0, 0 ] + 2, 2, 2, 2, 17, 2, 2, 2, 0, 0, + 0, 0, 1, 0, 0, 19, 19, 19, 19, 20, + 19, 19, 7, 7, 7, 7, 22, 7, 7 ] racc_action_pointer = [ - 30, 0, nil, nil, nil, nil, nil, 16, nil, nil, - nil, nil, nil, nil, nil, nil, nil, 1, nil, 23, - 8, nil, nil, nil ] + 6, 12, -2, nil, nil, nil, nil, 20, nil, nil, + nil, nil, nil, nil, nil, nil, nil, 4, nil, 13, + 13, nil, 17, nil, nil ] racc_action_default = [ - -18, -18, -2, -3, -4, -5, -6, -18, -9, -10, - -11, -12, -13, -14, -15, -16, -17, -18, -1, -18, - -18, 24, -8, -7 ] + -19, -19, -2, -3, -4, -5, -6, -19, -10, -11, + -12, -13, -14, -15, -16, -17, -18, -19, -1, -19, + -19, 25, -8, -9, -7 ] racc_goto_table = [ - 18, 1, nil, nil, nil, nil, nil, nil, 20, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 22, 18 ] + 1, 22, 18, 23, nil, nil, nil, 20 ] racc_goto_check = [ - 2, 1, nil, nil, nil, nil, nil, nil, 1, nil, - nil, nil, nil, nil, nil, nil, nil, nil, 2, 2 ] + 1, 2, 1, 3, nil, nil, nil, 1 ] racc_goto_pointer = [ - nil, 1, -1, nil, nil, nil, nil, nil, nil, nil, + nil, 0, -18, -16, nil, nil, nil, nil, nil, nil, nil ] racc_goto_default = [ @@ -61,19 +57,20 @@ racc_reduce_table = [ 1, 12, :_reduce_none, 3, 15, :_reduce_7, 3, 13, :_reduce_8, - 1, 16, :_reduce_9, + 3, 13, :_reduce_9, + 1, 16, :_reduce_10, 1, 14, :_reduce_none, 1, 14, :_reduce_none, 1, 14, :_reduce_none, 1, 14, :_reduce_none, - 1, 19, :_reduce_14, - 1, 17, :_reduce_15, - 1, 18, :_reduce_16, - 1, 20, :_reduce_17 ] + 1, 19, :_reduce_15, + 1, 17, :_reduce_16, + 1, 18, :_reduce_17, + 1, 20, :_reduce_18 ] -racc_reduce_n = 18 +racc_reduce_n = 19 -racc_shift_n = 24 +racc_shift_n = 25 racc_token_table = { false => 0, @@ -137,12 +134,12 @@ Racc_debug_parser = false # reduce 0 omitted def _reduce_1(val, _values, result) - result = Cat.new(val.first, val.last) + result = Cat.new(val.first, val.last) result end def _reduce_2(val, _values, result) - result = val.first + result = val.first result end @@ -155,21 +152,24 @@ end # reduce 6 omitted def _reduce_7(val, _values, result) - result = Group.new(val[1]) + result = Group.new(val[1]) result end def _reduce_8(val, _values, result) - result = Or.new([val.first, val.last]) + result = Or.new([val.first, val.last]) result end def _reduce_9(val, _values, result) - result = Star.new(Symbol.new(val.last)) + result = Or.new([val.first, val.last]) result end -# reduce 10 omitted +def _reduce_10(val, _values, result) + result = Star.new(Symbol.new(val.last)) + result +end # reduce 11 omitted @@ -177,23 +177,25 @@ end # reduce 13 omitted -def _reduce_14(val, _values, result) - result = Slash.new('/') - result -end +# reduce 14 omitted def _reduce_15(val, _values, result) - result = Symbol.new(val.first) + result = Slash.new('/') result end def _reduce_16(val, _values, result) - result = Literal.new(val.first) + result = Symbol.new(val.first) result end def _reduce_17(val, _values, result) - result = Dot.new(val.first) + result = Literal.new(val.first) + result +end + +def _reduce_18(val, _values, result) + result = Dot.new(val.first) result end diff --git a/actionpack/lib/action_dispatch/journey/parser.y b/actionpack/lib/action_dispatch/journey/parser.y index 040f8d5922..0ead222551 100644 --- a/actionpack/lib/action_dispatch/journey/parser.y +++ b/actionpack/lib/action_dispatch/journey/parser.y @@ -4,7 +4,7 @@ token SLASH LITERAL SYMBOL LPAREN RPAREN DOT STAR OR rule expressions - : expressions expression { result = Cat.new(val.first, val.last) } + : expression expressions { result = Cat.new(val.first, val.last) } | expression { result = val.first } | or ; @@ -17,7 +17,8 @@ rule : LPAREN expressions RPAREN { result = Group.new(val[1]) } ; or - : expressions OR expression { result = Or.new([val.first, val.last]) } + : expression OR expression { result = Or.new([val.first, val.last]) } + | expression OR or { result = Or.new([val.first, val.last]) } ; star : STAR { result = Star.new(Symbol.new(val.last)) } diff --git a/actionpack/lib/action_dispatch/journey/path/pattern.rb b/actionpack/lib/action_dispatch/journey/path/pattern.rb index fb155e516f..cb0a02c298 100644 --- a/actionpack/lib/action_dispatch/journey/path/pattern.rb +++ b/actionpack/lib/action_dispatch/journey/path/pattern.rb @@ -30,6 +30,10 @@ module ActionDispatch @offsets = nil end + def build_formatter + Visitors::FormatBuilder.new.accept(spec) + end + def ast @spec.grep(Nodes::Symbol).each do |node| re = @requirements[node.to_sym] diff --git a/actionpack/lib/action_dispatch/journey/route.rb b/actionpack/lib/action_dispatch/journey/route.rb index 2b399d3ee3..cc3c7f20cb 100644 --- a/actionpack/lib/action_dispatch/journey/route.rb +++ b/actionpack/lib/action_dispatch/journey/route.rb @@ -31,6 +31,7 @@ module ActionDispatch @parts = nil @decorated_ast = nil @precedence = 0 + @path_formatter = @path.build_formatter end def ast @@ -72,15 +73,7 @@ module ActionDispatch alias :segment_keys :parts def format(path_options) - path_options.delete_if do |key, value| - value.to_s == defaults[key].to_s && !required_parts.include?(key) - end - - Visitors::Formatter.new(path_options).accept(path.spec) - end - - def optimized_path - Visitors::OptimizedPath.new.accept(path.spec) + @path_formatter.evaluate path_options end def optional_parts diff --git a/actionpack/lib/action_dispatch/journey/router.rb b/actionpack/lib/action_dispatch/journey/router.rb index 36561c71a1..3bfa03713d 100644 --- a/actionpack/lib/action_dispatch/journey/router.rb +++ b/actionpack/lib/action_dispatch/journey/router.rb @@ -90,7 +90,7 @@ module ActionDispatch req.env['PATH_INFO'] = match.post_match.sub(/^([^\/])/, '/\1') end - yield(route, nil, parameters) + yield(route, parameters) end end @@ -136,11 +136,11 @@ module ActionDispatch routes.map! { |r| match_data = r.path.match(req.path_info) - match_names = match_data.names.map { |n| n.to_sym } - match_values = match_data.captures.map { |v| v && Utils.unescape_uri(v) } - info = Hash[match_names.zip(match_values).find_all { |_, y| y }] - - [match_data, r.defaults.merge(info), r] + path_parameters = r.defaults.dup + match_data.names.zip(match_data.captures) { |name,val| + path_parameters[name.to_sym] = Utils.unescape_uri(val) if val + } + [match_data, path_parameters, r] } end diff --git a/actionpack/lib/action_dispatch/journey/visitors.rb b/actionpack/lib/action_dispatch/journey/visitors.rb index d9f634623d..52b4c8b489 100644 --- a/actionpack/lib/action_dispatch/journey/visitors.rb +++ b/actionpack/lib/action_dispatch/journey/visitors.rb @@ -1,14 +1,57 @@ # encoding: utf-8 -require 'thread_safe' - module ActionDispatch module Journey # :nodoc: + class Format + ESCAPE_PATH = ->(value) { Router::Utils.escape_path(value) } + ESCAPE_SEGMENT = ->(value) { Router::Utils.escape_segment(value) } + + class Parameter < Struct.new(:name, :escaper) + def escape(value); escaper.call value; end + end + + def self.required_path(symbol) + Parameter.new symbol, ESCAPE_PATH + end + + def self.required_segment(symbol) + Parameter.new symbol, ESCAPE_SEGMENT + end + + def initialize(parts) + @parts = parts + @children = [] + @parameters = [] + + parts.each_with_index do |object,i| + case object + when Journey::Format + @children << i + when Parameter + @parameters << i + end + end + end + + def evaluate(hash) + parts = @parts.dup + + @parameters.each do |index| + param = parts[index] + value = hash[param.name] + return ''.freeze unless value + parts[index] = param.escape value + end + + @children.each { |index| parts[index] = parts[index].evaluate(hash) } + + parts.join + end + end + module Visitors # :nodoc: class Visitor # :nodoc: - DISPATCH_CACHE = ThreadSafe::Cache.new { |h,k| - h[k] = :"visit_#{k}" - } + DISPATCH_CACHE = {} def accept(node) visit(node) @@ -38,11 +81,41 @@ module ActionDispatch def visit_STAR(n); unary(n); end def terminal(node); end - %w{ LITERAL SYMBOL SLASH DOT }.each do |t| - class_eval %{ def visit_#{t}(n); terminal(n); end }, __FILE__, __LINE__ + def visit_LITERAL(n); terminal(n); end + def visit_SYMBOL(n); terminal(n); end + def visit_SLASH(n); terminal(n); end + def visit_DOT(n); terminal(n); end + + private_instance_methods(false).each do |pim| + next unless pim =~ /^visit_(.*)$/ + DISPATCH_CACHE[$1.to_sym] = pim end end + class FormatBuilder < Visitor # :nodoc: + def accept(node); Journey::Format.new(super); end + def terminal(node); [node.left]; end + + def binary(node) + visit(node.left) + visit(node.right) + end + + def visit_GROUP(n); [Journey::Format.new(unary(n))]; end + + def visit_STAR(n) + [Journey::Format.required_path(n.left.to_sym)] + end + + def visit_SYMBOL(n) + symbol = n.to_sym + if symbol == :controller + [Journey::Format.required_path(symbol)] + else + [Journey::Format.required_segment(symbol)] + end + end + end + # Loop through the requirements AST class Each < Visitor # :nodoc: attr_reader :block @@ -52,8 +125,8 @@ module ActionDispatch end def visit(node) - super block.call(node) + super end end @@ -77,90 +150,6 @@ module ActionDispatch end end - class OptimizedPath < Visitor # :nodoc: - def accept(node) - Array(visit(node)) - end - - private - - def visit_CAT(node) - [visit(node.left), visit(node.right)].flatten - end - - def visit_SYMBOL(node) - node.left[1..-1].to_sym - end - - def visit_STAR(node) - visit(node.left) - end - - def visit_GROUP(node) - [] - end - - %w{ LITERAL SLASH DOT }.each do |t| - class_eval %{ def visit_#{t}(n); n.left; end }, __FILE__, __LINE__ - end - end - - # Used for formatting urls (url_for) - class Formatter < Visitor # :nodoc: - attr_reader :options - - def initialize(options) - @options = options - end - - private - def escape_path(value) - Router::Utils.escape_path(value) - end - - def escape_segment(value) - Router::Utils.escape_segment(value) - end - - def visit(node, optional = false) - case node.type - when :LITERAL, :SLASH, :DOT - node.left - when :STAR - visit_STAR(node.left) - when :GROUP - visit(node.left, true) - when :CAT - visit_CAT(node, optional) - when :SYMBOL - visit_SYMBOL(node, node.to_sym) - end - end - - def visit_CAT(node, optional) - left = visit(node.left, optional) - right = visit(node.right, optional) - - if optional && !(right && left) - "" - else - [left, right].join - end - end - - def visit_STAR(node) - if value = options[node.to_sym] - escape_path(value) - end - end - - def visit_SYMBOL(node, name) - if value = options[name] - name == :controller ? escape_path(value) : escape_segment(value) - end - end - end - class Dot < Visitor # :nodoc: def initialize @nodes = [] diff --git a/actionpack/lib/action_dispatch/middleware/public_exceptions.rb b/actionpack/lib/action_dispatch/middleware/public_exceptions.rb index cbb2d475b1..6c8944e067 100644 --- a/actionpack/lib/action_dispatch/middleware/public_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/public_exceptions.rb @@ -32,9 +32,8 @@ module ActionDispatch end def render_html(status) - found = false - path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale - path = "#{public_path}/#{status}.html" unless path && (found = File.exist?(path)) + path = "#{public_path}/#{status}.#{I18n.locale}.html" + path = "#{public_path}/#{status}.html" unless (found = File.exist?(path)) if found || File.exist?(path) render_format(status, 'text/html', File.read(path)) diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb index 9cd884daa3..ce03164ca9 100644 --- a/actionpack/lib/action_dispatch/routing.rb +++ b/actionpack/lib/action_dispatch/routing.rb @@ -1,6 +1,7 @@ # encoding: UTF-8 require 'active_support/core_ext/object/to_param' require 'active_support/core_ext/regexp' +require 'active_support/dependencies/autoload' module ActionDispatch # The routing module provides URL rewriting in native Ruby. It's a way to diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index fd163a47f4..8c2e821573 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -1,11 +1,13 @@ require 'action_dispatch/journey' require 'forwardable' require 'thread_safe' +require 'active_support/concern' require 'active_support/core_ext/object/to_query' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/module/remove_method' require 'active_support/core_ext/array/extract_options' require 'action_controller/metal/exceptions' +require 'action_dispatch/http/request' module ActionDispatch module Routing @@ -155,7 +157,7 @@ module ActionDispatch end def self.optimize_helper?(route) - !route.glob? && route.requirements.except(:controller, :action, :host).empty? + !route.glob? && route.path.requirements.empty? end class OptimizedUrlHelper < UrlHelper # :nodoc: @@ -163,10 +165,8 @@ module ActionDispatch def initialize(route, options) super - @klass = Journey::Router::Utils @required_parts = @route.required_parts @arg_size = @required_parts.size - @optimized_path = @route.optimized_path end def call(t, args) @@ -182,18 +182,14 @@ module ActionDispatch private def optimized_helper(args) - params = Hash[parameterize_args(args)] + params = parameterize_args(args) missing_keys = missing_keys(params) unless missing_keys.empty? raise_generation_error(params, missing_keys) end - @optimized_path.map{ |segment| replace_segment(params, segment) }.join - end - - def replace_segment(params, segment) - Symbol === segment ? @klass.escape_segment(params[segment]) : segment + @route.format params end def optimize_routes_generation?(t) @@ -201,7 +197,9 @@ module ActionDispatch end def parameterize_args(args) - @required_parts.zip(args.map(&:to_param)) + params = {} + @required_parts.zip(args.map(&:to_param)) { |k,v| params[k] = v } + params end def missing_keys(args) @@ -224,21 +222,23 @@ module ActionDispatch end def call(t, args) - options = t.url_options.merge @options - hash = handle_positional_args(t, args, options, @segment_keys) + controller_options = t.url_options + options = controller_options.merge @options + hash = handle_positional_args(controller_options, args, options, @segment_keys) t._routes.url_for(hash) end - def handle_positional_args(t, args, result, keys) + def handle_positional_args(controller_options, args, result, path_params) inner_options = args.extract_options! if args.size > 0 - if args.size < keys.size - 1 # take format into account - keys -= t.url_options.keys - keys -= result.keys + if args.size < path_params.size - 1 # take format into account + path_params -= controller_options.keys + path_params -= result.keys end - keys -= inner_options.keys - result.merge!(Hash[keys.zip(args)]) + path_params.each { |param| + result[param] = inner_options[param] || args.shift + } end result.merge!(inner_options) @@ -596,7 +596,7 @@ module ActionDispatch # Generates a path from routes, returns [path, params]. # If no route is generated the formatter will raise ActionController::UrlGenerationError def generate - @set.formatter.generate(:path_info, named_route, options, recall, PARAMETERIZE) + @set.formatter.generate(named_route, options, recall, PARAMETERIZE) end def different_controller? @@ -669,15 +669,18 @@ module ActionDispatch RESERVED_OPTIONS.each { |ro| path_options.delete ro } path, params = generate(path_options, recall) - params.merge!(options[:params] || {}) - - ActionDispatch::Http::URL.url_for(options.merge!({ - :path => path, - :script_name => script_name, - :params => params, - :user => user, - :password => password - })) + + if options.key? :params + params.merge! options[:params] + end + + options[:path] = path + options[:script_name] = script_name + options[:params] = params + options[:user] = user + options[:password] = password + + ActionDispatch::Http::URL.url_for(options) end def call(env) @@ -696,7 +699,7 @@ module ActionDispatch end req = @request_class.new(env) - @router.recognize(req) do |route, _matches, params| + @router.recognize(req) do |route, params| params.merge!(extras) params.each do |key, value| if value.is_a?(String) diff --git a/actionpack/test/abstract/collector_test.rb b/actionpack/test/abstract/collector_test.rb index b1a5044399..fc59bf19c4 100644 --- a/actionpack/test/abstract/collector_test.rb +++ b/actionpack/test/abstract/collector_test.rb @@ -24,15 +24,21 @@ module AbstractController test "does not respond to unknown mime types" do collector = MyCollector.new - assert !collector.respond_to?(:unknown) + assert_not_respond_to collector, :unknown end test "register mime types on method missing" do AbstractController::Collector.send(:remove_method, :js) - collector = MyCollector.new - assert !collector.respond_to?(:js) - collector.js - assert_respond_to collector, :js + begin + collector = MyCollector.new + assert_not_respond_to collector, :js + collector.js + assert_respond_to collector, :js + ensure + unless AbstractController::Collector.method_defined? :js + AbstractController::Collector.generate_method_for_mime :js + end + end end test "does not register unknown mime types" do diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 58a86ce9af..c0e6a2ebd1 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -227,6 +227,22 @@ CACHED @store.read("views/test.host/functional_caching/inline_fragment_cached/#{template_digest("functional_caching/inline_fragment_cached")}")) end + def test_fragment_cache_instrumentation + payload = nil + + subscriber = proc do |*args| + event = ActiveSupport::Notifications::Event.new(*args) + payload = event.payload + end + + ActiveSupport::Notifications.subscribed(subscriber, "read_fragment.action_controller") do + get :inline_fragment_cached + end + + assert_equal "functional_caching", payload[:controller] + assert_equal "inline_fragment_cached", payload[:action] + end + def test_html_formatted_fragment_caching get :formatted_fragment_cached, :format => "html" assert_response :success diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb index 90548d4294..9052fc6962 100644 --- a/actionpack/test/controller/http_basic_authentication_test.rb +++ b/actionpack/test/controller/http_basic_authentication_test.rb @@ -129,6 +129,13 @@ class HttpBasicAuthenticationTest < ActionController::TestCase assert_response :unauthorized end + test "authentication request with wrong scheme" do + header = 'Bearer ' + encode_credentials('David', 'Goliath').split(' ', 2)[1] + @request.env['HTTP_AUTHORIZATION'] = header + get :search + assert_response :unauthorized + end + private def encode_credentials(username, password) diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb index 200a2dcc47..214eab2f0d 100644 --- a/actionpack/test/controller/integration_test.rb +++ b/actionpack/test/controller/integration_test.rb @@ -775,3 +775,34 @@ class UrlOptionsIntegrationTest < ActionDispatch::IntegrationTest assert_equal "/foo/1/edit", url_for(:action => 'edit', :only_path => true) end end + +class HeadWithStatusActionIntegrationTest < ActionDispatch::IntegrationTest + class FooController < ActionController::Base + def status + head :ok + end + end + + def self.routes + @routes ||= ActionDispatch::Routing::RouteSet.new + end + + def self.call(env) + routes.call(env) + end + + def app + self.class + end + + routes.draw do + get "/foo/status" => 'head_with_status_action_integration_test/foo#status' + end + + test "get /foo/status with head result does not cause stack overflow error" do + assert_nothing_raised do + get '/foo/status' + end + assert_response :ok + end +end diff --git a/actionpack/test/controller/live_stream_test.rb b/actionpack/test/controller/live_stream_test.rb index 947f64176b..1dca36374a 100644 --- a/actionpack/test/controller/live_stream_test.rb +++ b/actionpack/test/controller/live_stream_test.rb @@ -39,6 +39,13 @@ module ActionController ensure sse.close end + + def sse_with_multiple_line_message + sse = SSE.new(response.stream) + sse.write("first line.\nsecond line.") + ensure + sse.close + end end tests SSETestController @@ -87,6 +94,15 @@ module ActionController assert_match(/data: {\"name\":\"Ryan\"}/, second_response) assert_match(/id: 2/, second_response) end + + def test_sse_with_multiple_line_message + get :sse_with_multiple_line_message + + wait_for_response_stream_close + first_response, second_response = response.body.split("\n") + assert_match(/data: first line/, first_response) + assert_match(/data: second line/, second_response) + end end class LiveStreamTest < ActionController::TestCase diff --git a/actionpack/test/controller/mime/respond_with_test.rb b/actionpack/test/controller/mime/respond_with_test.rb index 416b3b81a5..115f3b2f41 100644 --- a/actionpack/test/controller/mime/respond_with_test.rb +++ b/actionpack/test/controller/mime/respond_with_test.rb @@ -2,6 +2,10 @@ require 'abstract_unit' require 'controller/fake_models' class RespondWithController < ActionController::Base + class CustomerWithJson < Customer + def to_json; super; end + end + respond_to :html, :json, :touch respond_to :xml, :except => :using_resource_with_block respond_to :js, :only => [ :using_resource_with_block, :using_resource, 'using_hash_resource' ] @@ -38,6 +42,10 @@ class RespondWithController < ActionController::Base respond_with(resource, :location => "http://test.host/", :status => :created) end + def using_resource_with_json + respond_with(CustomerWithJson.new("david", request.delete? ? nil : 13)) + end + def using_invalid_resource_with_template respond_with(resource) end @@ -380,9 +388,8 @@ class RespondWithControllerTest < ActionController::TestCase end def test_using_resource_for_put_with_json_yields_no_content_on_success - Customer.any_instance.stubs(:to_json).returns('{"name": "David"}') @request.accept = "application/json" - put :using_resource + put :using_resource_with_json assert_equal "application/json", @response.content_type assert_equal 204, @response.status assert_equal "", @response.body @@ -431,10 +438,9 @@ class RespondWithControllerTest < ActionController::TestCase end def test_using_resource_for_delete_with_json_yields_no_content_on_success - Customer.any_instance.stubs(:to_json).returns('{"name": "David"}') Customer.any_instance.stubs(:destroyed?).returns(true) @request.accept = "application/json" - delete :using_resource + delete :using_resource_with_json assert_equal "application/json", @response.content_type assert_equal 204, @response.status assert_equal "", @response.body @@ -643,6 +649,8 @@ class RespondWithControllerTest < ActionController::TestCase get :index, format: 'csv' assert_equal Mime::CSV, @response.content_type assert_equal "c,s,v", @response.body + ensure + ActionController::Renderers.remove :csv end def test_raises_missing_renderer_if_an_api_behavior_with_no_renderer @@ -652,6 +660,23 @@ class RespondWithControllerTest < ActionController::TestCase end end + def test_removing_renderers + ActionController::Renderers.add :csv do |obj, options| + send_data obj.to_csv, type: Mime::CSV + end + @controller = CsvRespondWithController.new + @request.accept = "text/csv" + get :index, format: 'csv' + assert_equal Mime::CSV, @response.content_type + + ActionController::Renderers.remove :csv + assert_raise ActionController::MissingRenderer do + get :index, format: 'csv' + end + ensure + ActionController::Renderers.remove :csv + end + def test_error_is_raised_if_no_respond_to_is_declared_and_respond_with_is_called @controller = EmptyRespondWithController.new @request.accept = "*/*" diff --git a/actionpack/test/controller/url_for_test.rb b/actionpack/test/controller/url_for_test.rb index 0c6df16325..f52f8be101 100644 --- a/actionpack/test/controller/url_for_test.rb +++ b/actionpack/test/controller/url_for_test.rb @@ -11,6 +11,26 @@ module AbstractController W.default_url_options.clear end + def test_nested_optional + klass = Class.new { + include ActionDispatch::Routing::RouteSet.new.tap { |r| + r.draw { + get "/foo/(:bar/(:baz))/:zot", :as => 'fun', + :controller => :articles, + :action => :index + } + }.url_helpers + self.default_url_options[:host] = 'example.com' + } + + path = klass.new.fun_path({:controller => :articles, + :baz => "baz", + :zot => "zot", + :only_path => true }) + # :bar key isn't provided + assert_equal '/foo/zot', path + end + def add_host! W.default_url_options[:host] = 'www.basecamphq.com' end diff --git a/actionpack/test/dispatch/routing/route_set_test.rb b/actionpack/test/dispatch/routing/route_set_test.rb index 0e488d2b88..c465d56bde 100644 --- a/actionpack/test/dispatch/routing/route_set_test.rb +++ b/actionpack/test/dispatch/routing/route_set_test.rb @@ -81,10 +81,6 @@ module ActionDispatch end private - def clear! - @set.clear! - end - def draw(&block) @set.draw(&block) end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 0a13dcfad4..cae6b312b6 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -3553,6 +3553,7 @@ class TestHttpMethods < ActionDispatch::IntegrationTest RFC3648 = %w(ORDERPATCH) RFC3744 = %w(ACL) RFC5323 = %w(SEARCH) + RFC4791 = %w(MKCALENDAR) RFC5789 = %w(PATCH) def simple_app(response) @@ -3564,13 +3565,13 @@ class TestHttpMethods < ActionDispatch::IntegrationTest @app = ActionDispatch::Routing::RouteSet.new @app.draw do - (RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC5789).each do |method| + (RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC4791 + RFC5789).each do |method| match '/' => s.simple_app(method), :via => method.underscore.to_sym end end end - (RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC5789).each do |method| + (RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC4791 + RFC5789).each do |method| test "request method #{method.underscore} can be matched" do get '/', nil, 'REQUEST_METHOD' => method assert_equal method, @response.body diff --git a/actionpack/test/journey/router_test.rb b/actionpack/test/journey/router_test.rb index e54b64e0f3..9a8d644f7b 100644 --- a/actionpack/test/journey/router_test.rb +++ b/actionpack/test/journey/router_test.rb @@ -48,7 +48,7 @@ module ActionDispatch env = rails_env 'PATH_INFO' => '/foo-bar-baz' called = false - router.recognize(env) do |r, _, params| + router.recognize(env) do |r, params| called = true end assert called @@ -65,7 +65,7 @@ module ActionDispatch env = rails_env 'PATH_INFO' => '/%E3%81%BB%E3%81%92' called = false - router.recognize(env) do |r, _, params| + router.recognize(env) do |r, params| called = true end assert called @@ -83,7 +83,7 @@ module ActionDispatch routes.add_route nil, path, requirements, {:id => nil}, {} env = rails_env 'PATH_INFO' => '/foo/10' - router.recognize(env) do |r, _, params| + router.recognize(env) do |r, params| assert_equal({:id => '10'}, params) end @@ -103,7 +103,7 @@ module ActionDispatch router.routes.add_route nil, path, requirements, {:id => nil}, {} env = rails_env 'PATH_INFO' => '/foo/10' - router.recognize(env) do |r, _, params| + router.recognize(env) do |r, params| flunk 'route should not be found' end @@ -128,7 +128,7 @@ module ActionDispatch env = rails_env 'PATH_INFO' => '/foo', 'custom.path_info' => '/bar' recognized = false - router.recognize(env) do |r, _, params| + router.recognize(env) do |r, params| recognized = true end @@ -144,7 +144,7 @@ module ActionDispatch env = rails_env 'PATH_INFO' => '/whois/example.com' list = [] - @router.recognize(env) do |r, _, params| + @router.recognize(env) do |r, params| list << r end assert_equal 2, list.length @@ -160,7 +160,7 @@ module ActionDispatch ] assert_raises(ActionController::UrlGenerationError) do - @formatter.generate(:path_info, nil, { :id => '10' }, { }) + @formatter.generate(nil, { :id => '10' }, { }) end end @@ -169,11 +169,11 @@ module ActionDispatch Router::Strexp.new("/foo/:id", { :id => /\d+/ }, ['/', '.', '?'], false) ] - path, _ = @formatter.generate(:path_info, nil, { :id => '10' }, { }) + path, _ = @formatter.generate(nil, { :id => '10' }, { }) assert_equal '/foo/10', path assert_raises(ActionController::UrlGenerationError) do - @formatter.generate(:path_info, nil, { :id => 'aa' }, { }) + @formatter.generate(nil, { :id => 'aa' }, { }) end end @@ -182,13 +182,13 @@ module ActionDispatch Router::Strexp.new("/foo(/:id)", {:id => /\d/}, ['/', '.', '?'], false) ] - path, _ = @formatter.generate(:path_info, nil, { :id => '10' }, { }) + path, _ = @formatter.generate(nil, { :id => '10' }, { }) assert_equal '/foo/10', path - path, _ = @formatter.generate(:path_info, nil, { }, { }) + path, _ = @formatter.generate(nil, { }, { }) assert_equal '/foo', path - path, _ = @formatter.generate(:path_info, nil, { :id => 'aa' }, { }) + path, _ = @formatter.generate(nil, { :id => 'aa' }, { }) assert_equal '/foo/aa', path end @@ -199,7 +199,7 @@ module ActionDispatch @router.routes.add_route nil, path, {}, {}, route_name error = assert_raises(ActionController::UrlGenerationError) do - @formatter.generate(:path_info, route_name, { }, { }) + @formatter.generate(route_name, { }, { }) end assert_match(/missing required keys: \[:id\]/, error.message) @@ -230,12 +230,12 @@ module ActionDispatch @router.routes.add_route nil, path, {}, {:id => nil}, {} env = rails_env 'PATH_INFO' => '/foo/10' - @router.recognize(env) do |r, _, params| + @router.recognize(env) do |r, params| assert_equal({:id => '10'}, params) end env = rails_env 'PATH_INFO' => '/foo' - @router.recognize(env) do |r, _, params| + @router.recognize(env) do |r, params| assert_equal({:id => nil}, params) end end @@ -287,14 +287,14 @@ module ActionDispatch def test_required_part_in_recall add_routes @router, [ "/messages/:a/:b" ] - path, _ = @formatter.generate(:path_info, nil, { :a => 'a' }, { :b => 'b' }) + path, _ = @formatter.generate(nil, { :a => 'a' }, { :b => 'b' }) assert_equal "/messages/a/b", path end def test_splat_in_recall add_routes @router, [ "/*path" ] - path, _ = @formatter.generate(:path_info, nil, { }, { :path => 'b' }) + path, _ = @formatter.generate(nil, { }, { :path => 'b' }) assert_equal "/b", path end @@ -304,7 +304,7 @@ module ActionDispatch "/messages/:id(.:format)" ] - path, _ = @formatter.generate(:path_info, nil, { :id => 10 }, { :action => 'index' }) + path, _ = @formatter.generate(nil, { :id => 10 }, { :action => 'index' }) assert_equal "/messages/index/10", path end @@ -315,7 +315,7 @@ module ActionDispatch params = { :controller => "tasks", :format => nil } extras = { :action => 'lol' } - path, _ = @formatter.generate(:path_info, nil, params, extras) + path, _ = @formatter.generate(nil, params, extras) assert_equal '/tasks', path end @@ -327,7 +327,7 @@ module ActionDispatch @router.routes.add_route @app, path, {}, {}, {} - path, _ = @formatter.generate(:path_info, nil, Hash[params], {}) + path, _ = @formatter.generate(nil, Hash[params], {}) assert_equal '/', path end @@ -340,7 +340,6 @@ module ActionDispatch [:action, "show"] ] @formatter.generate( - :path_info, nil, Hash[params], {}, @@ -354,7 +353,7 @@ module ActionDispatch @router.routes.add_route @app, path, {}, {}, {} path, params = @formatter.generate( - :path_info, nil, {:id=>1, :controller=>"tasks", :action=>"show"}, {}) + nil, {:id=>1, :controller=>"tasks", :action=>"show"}, {}) assert_equal '/tasks/show', path assert_equal({:id => 1}, params) end @@ -363,8 +362,8 @@ module ActionDispatch path = Path::Pattern.new '/:controller(/:action)' @router.routes.add_route @app, path, {}, {}, {} - path, _ = @formatter.generate(:path_info, - nil, { :controller => "tasks", + path, _ = @formatter.generate(nil, + { :controller => "tasks", :action => "a/b c+d", }, {}) assert_equal '/tasks/a%2Fb%20c+d', path @@ -374,7 +373,7 @@ module ActionDispatch path = Path::Pattern.new '/:controller(/:action)' @router.routes.add_route @app, path, {}, {}, {} - path, _ = @formatter.generate(:path_info, + path, _ = @formatter.generate( nil, { :controller => "admin/tasks", :action => "a/b c+d", }, {}) @@ -385,7 +384,7 @@ module ActionDispatch path = Path::Pattern.new '/:controller(/:action)' @router.routes.add_route @app, path, {}, {}, {} - path, params = @formatter.generate(:path_info, + path, params = @formatter.generate( nil, { :id => 1, :controller => "tasks", :action => "show", @@ -399,7 +398,7 @@ module ActionDispatch path = Path::Pattern.new '/:controller(/:action(/:id))' @router.routes.add_route @app, path, {}, {}, {} - path, params = @formatter.generate(:path_info, + path, params = @formatter.generate( nil, {:controller =>"tasks", :id => 10}, {:action =>"index"}) @@ -411,7 +410,7 @@ module ActionDispatch path = Path::Pattern.new '/:controller(/:action)' @router.routes.add_route @app, path, {}, {}, {} - path, params = @formatter.generate(:path_info, + path, params = @formatter.generate( "tasks", {:controller=>"tasks"}, {:controller=>"tasks", :action=>"index"}) @@ -432,7 +431,7 @@ module ActionDispatch env = rails_env 'PATH_INFO' => request_path called = false - @router.recognize(env) do |r, _, params| + @router.recognize(env) do |r, params| assert_equal route, r assert_equal(expected, params) called = true @@ -454,7 +453,7 @@ module ActionDispatch env = rails_env 'PATH_INFO' => request_path called = false - @router.recognize(env) do |r, _, params| + @router.recognize(env) do |r, params| assert_equal route, r assert_equal(expected, params) called = true @@ -482,7 +481,7 @@ module ActionDispatch :id => '10' } - @router.recognize(env) do |r, _, params| + @router.recognize(env) do |r, params| assert_equal route, r assert_equal(expected, params) called = true @@ -498,7 +497,7 @@ module ActionDispatch env = rails_env 'PATH_INFO' => '/books/list.rss' expected = { :controller => 'books', :action => 'list', :format => 'rss' } called = false - @router.recognize(env) do |r, _, params| + @router.recognize(env) do |r, params| assert_equal route, r assert_equal(expected, params) called = true @@ -519,7 +518,7 @@ module ActionDispatch "REQUEST_METHOD" => "HEAD" called = false - @router.recognize(env) do |r, _, params| + @router.recognize(env) do |r, params| called = true end @@ -543,7 +542,7 @@ module ActionDispatch "REQUEST_METHOD" => "POST" called = false - @router.recognize(env) do |r, _, params| + @router.recognize(env) do |r, params| assert_equal post, r called = true end |