diff options
Diffstat (limited to 'actionpack/lib/action_dispatch')
24 files changed, 192 insertions, 181 deletions
diff --git a/actionpack/lib/action_dispatch/http/filter_redirect.rb b/actionpack/lib/action_dispatch/http/filter_redirect.rb index cd603649c3..bf79963351 100644 --- a/actionpack/lib/action_dispatch/http/filter_redirect.rb +++ b/actionpack/lib/action_dispatch/http/filter_redirect.rb @@ -4,7 +4,7 @@ module ActionDispatch FILTERED = '[FILTERED]'.freeze # :nodoc: - def filtered_location + def filtered_location # :nodoc: filters = location_filter if !filters.empty? && location_filter_match?(filters) FILTERED diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb index 53a98c5d0a..ff336b7354 100644 --- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb +++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb @@ -10,8 +10,6 @@ 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 @@ -75,18 +73,22 @@ module ActionDispatch # Sets the \variant for template. def variant=(variant) - if variant.is_a?(Symbol) - @variant = [variant] - elsif variant.nil? || variant.is_a?(Array) && variant.any? && variant.all?{ |v| v.is_a?(Symbol) } - @variant = variant + variant = Array(variant) + + if variant.all? { |v| v.is_a?(Symbol) } + @variant = ActiveSupport::ArrayInquirer.new(variant) else - raise ArgumentError, "request.variant must be set to a Symbol or an Array of Symbols, not a #{variant.class}. " \ + raise ArgumentError, "request.variant must be set to a Symbol or an Array of Symbols. " \ "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 + def variant + @variant ||= ActiveSupport::ArrayInquirer.new + 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 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..732ee67268 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! @@ -114,7 +114,7 @@ module ActionDispatch end def engine_script_name(_routes) # :nodoc: - env["ROUTES_#{_routes.object_id}_SCRIPT_NAME"] + env[_routes.env_key] end def request_method=(request_method) #:nodoc: @@ -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/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb index 001b14ec97..f5b709ccd6 100644 --- a/actionpack/lib/action_dispatch/http/url.rb +++ b/actionpack/lib/action_dispatch/http/url.rb @@ -67,7 +67,7 @@ module ActionDispatch end def path_for(options) - path = options[:script_name].to_s.chomp("/") + path = options[:script_name].to_s.chomp("/".freeze) path << options[:path] if options.key?(:path) add_trailing_slash(path) if options[:trailing_slash] @@ -229,7 +229,7 @@ module ActionDispatch # req = Request.new 'HTTP_HOST' => 'example.com:8080' # req.raw_host_with_port # => "example.com:8080" def raw_host_with_port - if forwarded = env["HTTP_X_FORWARDED_HOST"] + if forwarded = env["HTTP_X_FORWARDED_HOST"].presence forwarded.split(/,\s?/).last else env['HTTP_HOST'] || "#{env['SERVER_NAME'] || env['SERVER_ADDR']}:#{env['SERVER_PORT']}" diff --git a/actionpack/lib/action_dispatch/journey/router.rb b/actionpack/lib/action_dispatch/journey/router.rb index e9df984c86..cc4bd6105d 100644 --- a/actionpack/lib/action_dispatch/journey/router.rb +++ b/actionpack/lib/action_dispatch/journey/router.rb @@ -88,7 +88,7 @@ module ActionDispatch end def custom_routes - partitioned_routes.last + routes.custom_routes end def filter_routes(path) @@ -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/journey/routes.rb b/actionpack/lib/action_dispatch/journey/routes.rb index 80e3818ccd..a6d1980db2 100644 --- a/actionpack/lib/action_dispatch/journey/routes.rb +++ b/actionpack/lib/action_dispatch/journey/routes.rb @@ -5,13 +5,14 @@ module ActionDispatch class Routes # :nodoc: include Enumerable - attr_reader :routes, :named_routes + attr_reader :routes, :named_routes, :custom_routes, :anchored_routes def initialize @routes = [] @named_routes = {} @ast = nil - @partitioned_routes = nil + @anchored_routes = [] + @custom_routes = [] @simulator = nil end @@ -30,18 +31,22 @@ module ActionDispatch def clear routes.clear + anchored_routes.clear + custom_routes.clear named_routes.clear end - def partitioned_routes - @partitioned_routes ||= routes.partition do |r| - r.path.anchored && r.ast.grep(Nodes::Symbol).all?(&:default_regexp?) + def partition_route(route) + if route.path.anchored && route.ast.grep(Nodes::Symbol).all?(&:default_regexp?) + anchored_routes << route + else + custom_routes << route end end def ast @ast ||= begin - asts = partitioned_routes.first.map(&:ast) + asts = anchored_routes.map(&:ast) Nodes::Or.new(asts) unless asts.empty? end end @@ -60,6 +65,7 @@ module ActionDispatch route.precedence = routes.length routes << route named_routes[name] = route if name && !named_routes[name] + partition_route(route) clear_cache! route end @@ -68,7 +74,6 @@ module ActionDispatch def clear_cache! @ast = nil - @partitioned_routes = nil @simulator = nil end end diff --git a/actionpack/lib/action_dispatch/middleware/public_exceptions.rb b/actionpack/lib/action_dispatch/middleware/public_exceptions.rb index 040cb215b7..7cde76b30e 100644 --- a/actionpack/lib/action_dispatch/middleware/public_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/public_exceptions.rb @@ -17,10 +17,10 @@ module ActionDispatch end def call(env) - status = env["PATH_INFO"][1..-1] + status = env["PATH_INFO"][1..-1].to_i request = ActionDispatch::Request.new(env) content_type = request.formats.first - body = { :status => status, :error => Rack::Utils::HTTP_STATUS_CODES.fetch(status.to_i, Rack::Utils::HTTP_STATUS_CODES[500]) } + body = { :status => status, :error => Rack::Utils::HTTP_STATUS_CODES.fetch(status, Rack::Utils::HTTP_STATUS_CODES[500]) } render(status, content_type, body) end diff --git a/actionpack/lib/action_dispatch/middleware/request_id.rb b/actionpack/lib/action_dispatch/middleware/request_id.rb index cf31815c23..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 diff --git a/actionpack/lib/action_dispatch/middleware/session/cache_store.rb b/actionpack/lib/action_dispatch/middleware/session/cache_store.rb index 625050dc4b..857e49a682 100644 --- a/actionpack/lib/action_dispatch/middleware/session/cache_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/cache_store.rb @@ -2,12 +2,15 @@ require 'action_dispatch/middleware/session/abstract_store' module ActionDispatch module Session - # Session store that uses an ActiveSupport::Cache::Store to store the sessions. This store is most useful + # A session store that uses an ActiveSupport::Cache::Store to store the sessions. This store is most useful # if you don't store critical data in your sessions and you don't need them to live for extended periods # of time. + # + # ==== Options + # * <tt>cache</tt> - The cache to use. If it is not specified, <tt>Rails.cache</tt> will be used. + # * <tt>expire_after</tt> - The length of time a session will be stored before automatically expiring. + # By default, the <tt>:expires_in</tt> option of the cache is used. class CacheStore < AbstractStore - # Create a new store. The cache to use can be passed in the <tt>:cache</tt> option. If it is - # not specified, <tt>Rails.cache</tt> will be used. def initialize(app, options = {}) @cache = options[:cache] || Rails.cache options[:expire_after] ||= @cache.options[:expires_in] diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb index ed25c67ae5..d8f9614904 100644 --- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb @@ -52,6 +52,16 @@ module ActionDispatch # JavaScript before upgrading. # # Note that changing the secret key will invalidate all existing sessions! + # + # Because CookieStore extends Rack::Session::Abstract::ID, many of the + # options described there can be used to customize the session cookie that + # is generated. For example: + # + # Rails.application.config.session_store :cookie_store, expire_after: 14.days + # + # would set the session cookie to expire automatically 14 days after creation. + # Other useful options include <tt>:key</tt>, <tt>:secure</tt> and + # <tt>:httponly</tt>. class CookieStore < Rack::Session::Abstract::ID include Compatibility include StaleSessionCheck diff --git a/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb b/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb index b4d6629c35..cb19786f0b 100644 --- a/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb @@ -8,6 +8,10 @@ end module ActionDispatch module Session + # A session store that uses MemCache to implement storage. + # + # ==== Options + # * <tt>expire_after</tt> - The length of time a session will be stored before automatically expiring. class MemCacheStore < Rack::Session::Dalli include Compatibility include StaleSessionCheck diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb index 2e1bd45c3d..fdd1bc4e69 100644 --- a/actionpack/lib/action_dispatch/middleware/static.rb +++ b/actionpack/lib/action_dispatch/middleware/static.rb @@ -47,6 +47,9 @@ module ActionDispatch if gzip_path && gzip_encoding_accepted?(env) env['PATH_INFO'] = gzip_path status, headers, body = @file_server.call(env) + if status == 304 + return [status, headers, body] + end headers['Content-Encoding'] = 'gzip' headers['Content-Type'] = content_type(path) else 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..9934f5547a 100644 --- a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb +++ b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb @@ -247,14 +247,12 @@ module ActionDispatch args = [] model = record.to_model - name = if model.persisted? - args << model - model.model_name.singular_route_key - else - @key_strategy.call model.model_name - end - - named_route = prefix + "#{name}_#{suffix}" + named_route = if model.persisted? + args << model + get_method_for_string model.model_name.singular_route_key + else + get_method_for_class model + end [named_route, args] end @@ -309,11 +307,11 @@ module ActionDispatch def get_method_for_class(klass) name = @key_strategy.call klass.model_name - prefix + "#{name}_#{suffix}" + get_method_for_string name end def get_method_for_string(str) - prefix + "#{str}_#{suffix}" + "#{prefix}#{str}_#{suffix}" end [nil, 'new', 'edit'].each do |action| diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index ce04f0b08a..0f3734dd74 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -199,12 +199,9 @@ module ActionDispatch private def optimized_helper(args) - params = parameterize_args(args) - missing_keys = missing_keys(params) - - unless missing_keys.empty? - raise_generation_error(params, missing_keys) - end + params = parameterize_args(args) { |k| + raise_generation_error(args) + } @route.format params end @@ -215,16 +212,21 @@ module ActionDispatch def parameterize_args(args) params = {} - @required_parts.zip(args.map(&:to_param)) { |k,v| params[k] = v } + @arg_size.times { |i| + key = @required_parts[i] + value = args[i].to_param + yield key if value.nil? || value.empty? + params[key] = value + } params end - def missing_keys(args) - args.select{ |part, arg| arg.nil? || arg.empty? }.keys - end - - def raise_generation_error(args, missing_keys) - constraints = Hash[@route.requirements.merge(args).sort] + def raise_generation_error(args) + missing_keys = [] + params = parameterize_args(args) { |missing_key| + missing_keys << missing_key + } + constraints = Hash[@route.requirements.merge(params).sort] message = "No route matches #{constraints.inspect}" message << " missing required keys: #{missing_keys.sort.inspect}" @@ -307,7 +309,8 @@ module ActionDispatch attr_accessor :formatter, :set, :named_routes, :default_scope, :router attr_accessor :disable_clear_and_finalize, :resources_path_names - attr_accessor :default_url_options, :request_class + attr_accessor :default_url_options + attr_reader :env_key alias :routes :set @@ -315,22 +318,44 @@ module ActionDispatch { :new => 'new', :edit => 'edit' } end - def initialize(request_class = ActionDispatch::Request) + def self.new_with_config(config) + if config.respond_to? :relative_url_root + new Config.new config.relative_url_root + else + # engines apparently don't have this set + new + end + end + + Config = Struct.new :relative_url_root + + DEFAULT_CONFIG = Config.new(nil) + + def initialize(config = DEFAULT_CONFIG) self.named_routes = NamedRouteCollection.new self.resources_path_names = self.class.default_resources_path_names self.default_url_options = {} - self.request_class = request_class + @config = config @append = [] @prepend = [] @disable_clear_and_finalize = false @finalized = false + @env_key = "ROUTES_#{object_id}_SCRIPT_NAME".freeze @set = Journey::Routes.new @router = Journey::Router.new @set @formatter = Journey::Formatter.new @set end + def relative_url_root + @config.relative_url_root + end + + def request_class + ActionDispatch::Request + end + def draw(&block) clear! unless @disable_clear_and_finalize eval_block(block) @@ -539,7 +564,7 @@ module ActionDispatch conditions.keep_if do |k, _| k == :action || k == :controller || k == :required_defaults || - @request_class.public_method_defined?(k) || path_values.include?(k) + request_class.public_method_defined?(k) || path_values.include?(k) end end private :build_conditions @@ -693,7 +718,7 @@ module ActionDispatch end def find_script_name(options) - options.delete(:script_name) || '' + options.delete(:script_name) || relative_url_root || '' end def path_for(options, route_name = nil) diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb index dca86858cc..eb554ec383 100644 --- a/actionpack/lib/action_dispatch/routing/url_for.rb +++ b/actionpack/lib/action_dispatch/routing/url_for.rb @@ -184,12 +184,6 @@ module ActionDispatch def _routes_context self end - - private - - def _generate_paths_by_default - true - end end end end diff --git a/actionpack/lib/action_dispatch/testing/assertions.rb b/actionpack/lib/action_dispatch/testing/assertions.rb index f325c35b57..21b3b89d22 100644 --- a/actionpack/lib/action_dispatch/testing/assertions.rb +++ b/actionpack/lib/action_dispatch/testing/assertions.rb @@ -12,7 +12,7 @@ module ActionDispatch include Rails::Dom::Testing::Assertions def html_document - @html_document ||= if @response.content_type =~ /xml$/ + @html_document ||= if @response.content_type === Mime::XML Nokogiri::XML::Document.parse(@response.body) else Nokogiri::HTML::Document.parse(@response.body) diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 9b00616968..9390e2937a 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 @@ -391,9 +391,9 @@ module ActionDispatch attr_reader :app def before_setup - super @app = nil @integration_session = nil + super end def integration_session @@ -505,8 +505,8 @@ module ActionDispatch # assert_equal 200, status # # # post the login and follow through to the home page - # post "/login", username: people(:jamis).username, - # password: people(:jamis).password + # post "/login", params: { username: people(:jamis).username, + # password: people(:jamis).password } # follow_redirect! # assert_equal 200, status # assert_equal "/home", path @@ -545,7 +545,7 @@ module ActionDispatch # end # # def speak(room, message) - # xml_http_request "/say/#{room.id}", message: message + # post "/say/#{room.id}", xhr: true, params: { message: message } # assert(...) # ... # end @@ -555,8 +555,8 @@ module ActionDispatch # open_session do |sess| # sess.extend(CustomAssertions) # who = people(who) - # sess.post "/login", username: who.username, - # password: who.password + # sess.post "/login", params: { username: who.username, + # password: who.password } # assert(...) # end # end @@ -575,7 +575,8 @@ module ActionDispatch # get "/login" # assert_response :success # - # post_via_redirect "/login", username: users(:david).username, password: users(:david).password + # post "/login", params: { username: users(:david).username, password: users(:david).password } + # follow_redirect! # assert_equal '/welcome', path # assert_equal 'Welcome david!', flash[:notice] # @@ -630,7 +631,7 @@ module ActionDispatch # sess.extend(CustomDsl) # u = users(user) # sess.https! - # sess.post "/login", username: u.username, password: u.password + # sess.post "/login", params: { username: u.username, password: u.password } # assert_equal '/welcome', sess.path # sess.https!(false) # 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 |