diff options
Diffstat (limited to 'actionpack/lib/action_dispatch')
16 files changed, 127 insertions, 145 deletions
diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index 047a17937a..7e585aa244 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -45,7 +45,7 @@ module Mime # # respond_to do |format| # format.html - # format.ics { render text: @post.to_ics, mime_type: Mime::Type["text/calendar"] } + # format.ics { render text: @post.to_ics, mime_type: Mime::Type.lookup("text/calendar") } # format.xml { render xml: @post } # end # end diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index cadbfc88cb..07b3814ca4 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -50,7 +50,7 @@ module ActionDispatch @original_fullpath = nil @fullpath = nil @ip = nil - @uuid = nil + @request_id = nil end def check_path_parameters! @@ -249,10 +249,12 @@ module ActionDispatch # # This unique ID is useful for tracing a request from end-to-end as part of logging or debugging. # This relies on the rack variable set by the ActionDispatch::RequestId middleware. - def uuid - @uuid ||= env["action_dispatch.request_id"] + def request_id + @request_id ||= env["action_dispatch.request_id"] end + alias_method :uuid, :request_id + # Returns the lowercase name of the HTTP server software. def server_software (@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 4061ea71a3..a895d1ab18 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -113,10 +113,10 @@ module ActionDispatch # :nodoc: # The underlying body, as a streamable object. attr_reader :stream - def initialize(status = 200, header = {}, body = []) + def initialize(status = 200, header = {}, body = [], default_headers: self.class.default_headers) super() - header = merge_default_headers(header, self.class.default_headers) + header = merge_default_headers(header, default_headers) self.body, self.header, self.status = body, header, status @@ -308,9 +308,7 @@ module ActionDispatch # :nodoc: end def merge_default_headers(original, default) - return original unless default.respond_to?(:merge) - - default.merge(original) + default.respond_to?(:merge) ? default.merge(original) : original end def build_buffer(response, body) diff --git a/actionpack/lib/action_dispatch/journey/router.rb b/actionpack/lib/action_dispatch/journey/router.rb index e9df984c86..2b036796ab 100644 --- a/actionpack/lib/action_dispatch/journey/router.rb +++ b/actionpack/lib/action_dispatch/journey/router.rb @@ -121,7 +121,6 @@ module ActionDispatch end def match_head_routes(routes, req) - routes.delete_if { |route| route.verb == // } head_routes = match_routes(routes, req) if head_routes.empty? diff --git a/actionpack/lib/action_dispatch/middleware/request_id.rb b/actionpack/lib/action_dispatch/middleware/request_id.rb index 25658bac3d..b9ca524309 100644 --- a/actionpack/lib/action_dispatch/middleware/request_id.rb +++ b/actionpack/lib/action_dispatch/middleware/request_id.rb @@ -3,7 +3,7 @@ require 'active_support/core_ext/string/access' module ActionDispatch # Makes a unique request id available to the action_dispatch.request_id env variable (which is then accessible through - # ActionDispatch::Request#uuid) and sends the same id to the client via the X-Request-Id header. + # ActionDispatch::Request#uuid or the alias ActionDispatch::Request#request_id) and sends the same id to the client via the X-Request-Id header. # # The unique request id is either based on the X-Request-Id header in the request, which would typically be generated # by a firewall, load balancer, or the web server, or, if this header is not available, a random uuid. If the @@ -12,19 +12,23 @@ module ActionDispatch # The unique request id can be used to trace a request end-to-end and would typically end up being part of log files # from multiple pieces of the stack. class RequestId + X_REQUEST_ID = "X-Request-Id".freeze # :nodoc: + ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id".freeze # :nodoc: + HTTP_X_REQUEST_ID = "HTTP_X_REQUEST_ID".freeze # :nodoc: + def initialize(app) @app = app end def call(env) - env["action_dispatch.request_id"] = external_request_id(env) || internal_request_id - @app.call(env).tap { |_status, headers, _body| headers["X-Request-Id"] = env["action_dispatch.request_id"] } + env[ACTION_DISPATCH_REQUEST_ID] = external_request_id(env) || internal_request_id + @app.call(env).tap { |_status, headers, _body| headers[X_REQUEST_ID] = env[ACTION_DISPATCH_REQUEST_ID] } end private def external_request_id(env) - if request_id = env["HTTP_X_REQUEST_ID"].presence - request_id.gsub(/[^\w\-]/, "").first(255) + if request_id = env[HTTP_X_REQUEST_ID].presence + request_id.gsub(/[^\w\-]/, "".freeze).first(255) end end diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb index 002bf8b11a..2e1bd45c3d 100644 --- a/actionpack/lib/action_dispatch/middleware/static.rb +++ b/actionpack/lib/action_dispatch/middleware/static.rb @@ -23,10 +23,9 @@ module ActionDispatch def match?(path) path = URI.parser.unescape(path) return false unless path.valid_encoding? + path = Rack::Utils.clean_path_info path - paths = [path, "#{path}#{ext}", "#{path}/index#{ext}"].map { |v| - Rack::Utils.clean_path_info v - } + paths = [path, "#{path}#{ext}", "#{path}/index#{ext}"] if match = paths.detect { |p| path = File.join(@root, p) diff --git a/actionpack/lib/action_dispatch/middleware/templates/routes/_route.html.erb b/actionpack/lib/action_dispatch/middleware/templates/routes/_route.html.erb index 24e44f31ac..6e995c85c1 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/routes/_route.html.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/routes/_route.html.erb @@ -4,13 +4,13 @@ <%= route[:name] %><span class='helper'>_path</span> <% end %> </td> - <td data-route-verb='<%= route[:verb] %>'> + <td> <%= route[:verb] %> </td> - <td data-route-path='<%= route[:path] %>' data-regexp='<%= route[:regexp] %>'> + <td data-route-path='<%= route[:path] %>'> <%= route[:path] %> </td> - <td data-route-reqs='<%= route[:reqs] %>'> - <%= route[:reqs] %> + <td> + <%=simple_format route[:reqs] %> </td> </tr> diff --git a/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb b/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb index 5cee0b5932..429ea7057c 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb @@ -81,92 +81,87 @@ </table> <script type='text/javascript'> - // Iterates each element through a function - function each(elems, func) { - if (!elems instanceof Array) { elems = [elems]; } - for (var i = 0, len = elems.length; i < len; i++) { - func(elems[i]); - } - } - - // Sets innerHTML for an element - function setContent(elem, text) { - elem.innerHTML = text; - } + // support forEarch iterator on NodeList + NodeList.prototype.forEach = Array.prototype.forEach; // Enables path search functionality function setupMatchPaths() { - // Check if the user input (sanitized as a path) matches the regexp data attribute - function checkExactMatch(section, elem, value) { - var string = sanitizePath(value), - regexp = elem.getAttribute("data-regexp"); - - showMatch(string, regexp, section, elem); + // Check if there are any matched results in a section + function checkNoMatch(section, noMatchText) { + if (section.children.length <= 1) { + section.innerHTML += noMatchText; + } } - // Check if the route path data attribute contains the user input - function checkFuzzyMatch(section, elem, value) { - var string = elem.getAttribute("data-route-path"), - regexp = value; - - showMatch(string, regexp, section, elem); + // get JSON from url and invoke callback with result + function getJSON(url, success) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url); + xhr.onload = function() { + if (this.status == 200) + success(JSON.parse(this.response)); + }; + xhr.send(); } - // Display the parent <tr> element in the appropriate section when there's a match - function showMatch(string, regexp, section, elem) { - if(string.match(RegExp(regexp))) { - section.appendChild(elem.parentNode.cloneNode(true)); + function delayedKeyup(input, callback) { + var timeout; + input.onkeyup = function(){ + if (timeout) clearTimeout(timeout); + timeout = setTimeout(callback, 300); } } - // Check if there are any matched results in a section - function checkNoMatch(section, defaultText, noMatchText) { - if (section.innerHTML === defaultText) { - setContent(section, defaultText + noMatchText); - } - } - - // Ensure path always starts with a slash "/" and remove params or fragments + // remove params or fragments function sanitizePath(path) { - var path = path.charAt(0) == '/' ? path : "/" + path; - return path.replace(/\#.*|\?.*/, ''); + return path.replace(/[#?].*/, ''); } - var regexpElems = document.querySelectorAll('#route_table [data-regexp]'), - searchElem = document.querySelector('#search'), - exactMatches = document.querySelector('#exact_matches'), - fuzzyMatches = document.querySelector('#fuzzy_matches'); + var pathElements = document.querySelectorAll('#route_table [data-route-path]'), + searchElem = document.querySelector('#search'), + exactSection = document.querySelector('#exact_matches'), + fuzzySection = document.querySelector('#fuzzy_matches'); // Remove matches when no search value is present searchElem.onblur = function(e) { if (searchElem.value === "") { - setContent(exactMatches, ""); - setContent(fuzzyMatches, ""); + exactSection.innerHTML = ""; + fuzzySection.innerHTML = ""; } } // On key press perform a search for matching paths - searchElem.onkeyup = function(e){ - var userInput = searchElem.value, - defaultExactMatch = '<tr><th colspan="4">Paths Matching (' + escape(sanitizePath(userInput)) +'):</th></tr>', - defaultFuzzyMatch = '<tr><th colspan="4">Paths Containing (' + escape(userInput) +'):</th></tr>', + delayedKeyup(searchElem, function() { + var path = sanitizePath(searchElem.value), + defaultExactMatch = '<tr><th colspan="4">Paths Matching (' + path +'):</th></tr>', + defaultFuzzyMatch = '<tr><th colspan="4">Paths Containing (' + path +'):</th></tr>', noExactMatch = '<tr><th colspan="4">No Exact Matches Found</th></tr>', noFuzzyMatch = '<tr><th colspan="4">No Fuzzy Matches Found</th></tr>'; - // Clear out results section - setContent(exactMatches, defaultExactMatch); - setContent(fuzzyMatches, defaultFuzzyMatch); + if (!path) + return searchElem.onblur(); - // Display exact matches and fuzzy matches - each(regexpElems, function(elem) { - checkExactMatch(exactMatches, elem, userInput); - checkFuzzyMatch(fuzzyMatches, elem, userInput); - }) + getJSON('/rails/info/routes?path=' + path, function(matches){ + // Clear out results section + exactSection.innerHTML = defaultExactMatch; + fuzzySection.innerHTML = defaultFuzzyMatch; - // Display 'No Matches' message when no matches are found - checkNoMatch(exactMatches, defaultExactMatch, noExactMatch); - checkNoMatch(fuzzyMatches, defaultFuzzyMatch, noFuzzyMatch); - } + // Display exact matches and fuzzy matches + pathElements.forEach(function(elem) { + var elemPath = elem.getAttribute('data-route-path'); + + if (matches['exact'].indexOf(elemPath) != -1) + exactSection.appendChild(elem.parentNode.cloneNode(true)); + + if (matches['fuzzy'].indexOf(elemPath) != -1) + fuzzySection.appendChild(elem.parentNode.cloneNode(true)); + }) + + // Display 'No Matches' message when no matches are found + checkNoMatch(exactSection, noExactMatch); + checkNoMatch(fuzzySection, noFuzzyMatch); + }) + }) } // Enables functionality to toggle between `_path` and `_url` helper suffixes @@ -174,19 +169,20 @@ // Sets content for each element function setValOn(elems, val) { - each(elems, function(elem) { - setContent(elem, val); + elems.forEach(function(elem) { + elem.innerHTML = val; }); } // Sets onClick event for each element function onClick(elems, func) { - each(elems, function(elem) { + elems.forEach(function(elem) { elem.onclick = func; }); } var toggleLinks = document.querySelectorAll('#route_table [data-route-helper]'); + onClick(toggleLinks, function(){ var helperTxt = this.getAttribute("data-route-helper"), helperElems = document.querySelectorAll('[data-route-name] span.helper'); diff --git a/actionpack/lib/action_dispatch/routing/inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb index df5ebb6751..c513737fc2 100644 --- a/actionpack/lib/action_dispatch/routing/inspector.rb +++ b/actionpack/lib/action_dispatch/routing/inspector.rb @@ -28,23 +28,6 @@ module ActionDispatch super.to_s end - def regexp - __getobj__.path.to_regexp - end - - def json_regexp - str = regexp.inspect. - sub('\\A' , '^'). - sub('\\Z' , '$'). - sub('\\z' , '$'). - sub(/^\// , ''). - sub(/\/[a-z]*$/ , ''). - gsub(/\(\?#.+\)/ , ''). - gsub(/\(\?-\w+:/ , '('). - gsub(/\s/ , '') - Regexp.new(str).source - end - def reqs @reqs ||= begin reqs = endpoint @@ -117,11 +100,10 @@ module ActionDispatch end.reject(&:internal?).collect do |route| collect_engine_routes(route) - { name: route.name, - verb: route.verb, - path: route.path, - reqs: route.reqs, - regexp: route.json_regexp } + { name: route.name, + verb: route.verb, + path: route.path, + reqs: route.reqs } end end diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index f2c9e7b1a0..34b5b48f3a 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1504,7 +1504,7 @@ module ActionDispatch end def using_match_shorthand?(path, options) - path && (options[:to] || options[:action]).nil? && path =~ %r{/[\w/]+$} + path && (options[:to] || options[:action]).nil? && path =~ %r{^/?[-\w]+/[-\w/]+$} end def decomposed_match(path, options) # :nodoc: diff --git a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb index 2e116ea9cd..f2ce175cac 100644 --- a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb +++ b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb @@ -254,7 +254,7 @@ module ActionDispatch @key_strategy.call model.model_name end - named_route = prefix + "#{name}_#{suffix}" + named_route = "#{prefix}#{name}_#{suffix}" [named_route, args] end diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 65d6a5eb1e..ce04f0b08a 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -395,9 +395,11 @@ module ActionDispatch return if MountedHelpers.method_defined?(name) routes = self + helpers = routes.url_helpers + MountedHelpers.class_eval do define_method "_#{name}" do - RoutesProxy.new(routes, _routes_context) + RoutesProxy.new(routes, _routes_context, helpers) end end diff --git a/actionpack/lib/action_dispatch/routing/routes_proxy.rb b/actionpack/lib/action_dispatch/routing/routes_proxy.rb index e2393d3799..040ea04046 100644 --- a/actionpack/lib/action_dispatch/routing/routes_proxy.rb +++ b/actionpack/lib/action_dispatch/routing/routes_proxy.rb @@ -8,8 +8,9 @@ module ActionDispatch attr_accessor :scope, :routes alias :_routes :routes - def initialize(routes, scope) + def initialize(routes, scope, helpers) @routes, @scope = routes, scope + @helpers = helpers end def url_options @@ -19,16 +20,16 @@ module ActionDispatch end def respond_to?(method, include_private = false) - super || routes.url_helpers.respond_to?(method) + super || @helpers.respond_to?(method) end def method_missing(method, *args) - if routes.url_helpers.respond_to?(method) + if @helpers.respond_to?(method) self.class.class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{method}(*args) options = args.extract_options! args << url_options.merge((options || {}).symbolize_keys) - routes.url_helpers.#{method}(*args) + @helpers.#{method}(*args) end RUBY send(method, *args) diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb index a927738b42..c94eea9134 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb @@ -38,18 +38,24 @@ module ActionDispatch # # Test a custom route # assert_recognizes({controller: 'items', action: 'show', id: '1'}, 'view/item1') def assert_recognizes(expected_options, path, extras={}, msg=nil) - request = recognized_request_for(path, extras, msg) + if path.is_a?(Hash) && path[:method].to_s == "all" + [:get, :post, :put, :delete].each do |method| + assert_recognizes(expected_options, path.merge(method: method), extras, msg) + end + else + request = recognized_request_for(path, extras, msg) - expected_options = expected_options.clone + expected_options = expected_options.clone - expected_options.stringify_keys! + expected_options.stringify_keys! - msg = message(msg, "") { - sprintf("The recognized options <%s> did not match <%s>, difference:", - request.path_parameters, expected_options) - } + msg = message(msg, "") { + sprintf("The recognized options <%s> did not match <%s>, difference:", + request.path_parameters, expected_options) + } - assert_equal(expected_options, request.path_parameters, msg) + assert_equal(expected_options, request.path_parameters, msg) + end end # Asserts that the provided options can be used to generate the provided path. This is the inverse of +assert_recognizes+. diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 9b00616968..f7f898288b 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -369,7 +369,7 @@ module ActionDispatch @request_count += 1 @request = ActionDispatch::Request.new(session.last_request.env) response = _mock_session.last_response - @response = ActionDispatch::TestResponse.new(response.status, response.headers, response.body) + @response = ActionDispatch::TestResponse.from_response(response) @html_document = nil @url_options = nil @@ -388,16 +388,8 @@ module ActionDispatch APP_SESSIONS = {} - attr_reader :app - - def before_setup - super - @app = nil - @integration_session = nil - end - - def integration_session - @integration_session ||= create_session(app) + def app + @app ||= nil end # Reset the current session. This is useful for testing multiple sessions @@ -425,6 +417,8 @@ module ActionDispatch %w(get post patch put head delete cookies assigns xml_http_request xhr get_via_redirect post_via_redirect).each do |method| define_method(method) do |*args| + reset! unless integration_session + # reset the html_document variable, except for cookies/assigns calls unless method == 'cookies' || method == 'assigns' @html_document = nil @@ -456,16 +450,19 @@ module ActionDispatch # Copy the instance variables from the current session instance into the # test instance. def copy_session_variables! #:nodoc: + return unless integration_session @controller = @integration_session.controller @response = @integration_session.response @request = @integration_session.request end def default_url_options + reset! unless integration_session integration_session.default_url_options end def default_url_options=(options) + reset! unless integration_session integration_session.default_url_options = options end @@ -475,6 +472,7 @@ module ActionDispatch # Delegate unhandled messages to the current session instance. def method_missing(sym, *args, &block) + reset! unless integration_session if integration_session.respond_to?(sym) integration_session.__send__(sym, *args, &block).tap do copy_session_variables! @@ -483,6 +481,11 @@ module ActionDispatch super end end + + private + def integration_session + @integration_session ||= nil + end end end @@ -659,6 +662,7 @@ module ActionDispatch end def url_options + reset! unless integration_session integration_session.url_options end diff --git a/actionpack/lib/action_dispatch/testing/test_response.rb b/actionpack/lib/action_dispatch/testing/test_response.rb index 369ea1467c..a9b88ac5fd 100644 --- a/actionpack/lib/action_dispatch/testing/test_response.rb +++ b/actionpack/lib/action_dispatch/testing/test_response.rb @@ -7,11 +7,7 @@ module ActionDispatch # See Response for more information on controller response objects. class TestResponse < Response def self.from_response(response) - new.tap do |resp| - resp.status = response.status - resp.headers = response.headers - resp.body = response.body - end + new response.status, response.headers, response.body, default_headers: nil end # Was the response successful? @@ -25,12 +21,5 @@ module ActionDispatch # Was there a server-side error? alias_method :error?, :server_error? - - def merge_default_headers(original, *args) - # Default headers are already applied, no need to merge them a second time. - # This makes sure that default headers, removed in controller actions, will - # not be reapplied to the test response. - original - end end end |