diff options
Diffstat (limited to 'actionpack')
34 files changed, 366 insertions, 301 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index a5e551c78e..a9233014e4 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,10 @@ +* Fix `rake routes` not showing the right format when + nesting multiple routes. + + See #18373. + + *Ravil Bayramgalin* + * Add ability to override default form builder for a controller. class AdminController < ApplicationController @@ -35,11 +42,11 @@ *Kohei Suzuki* -* Fix handling of empty X_FORWARDED_HOST header in raw_host_with_port +* Fix handling of empty `X_FORWARDED_HOST` header in `raw_host_with_port`. - Previously, an empty X_FORWARDED_HOST header would cause - Actiondispatch::Http:URL.raw_host_with_port to return nil, causing - Actiondispatch::Http:URL.host to raise a NoMethodError. + Previously, an empty `X_FORWARDED_HOST` header would cause + `Actiondispatch::Http:URL.raw_host_with_port` to return `nil`, causing + `Actiondispatch::Http:URL.host` to raise a `NoMethodError`. *Adam Forsyth* @@ -274,7 +281,7 @@ * Ensure `append_info_to_payload` is called even if an exception is raised. - Fixes an issue where when an exception is raised in the request the additonal + Fixes an issue where when an exception is raised in the request the additional payload data is not available. See: @@ -305,7 +312,7 @@ * Stop converting empty arrays in `params` to `nil`. - This behaviour was introduced in response to CVE-2012-2660, CVE-2012-2694 + This behavior was introduced in response to CVE-2012-2660, CVE-2012-2694 and CVE-2013-0155 ActiveRecord now issues a safe query when passing an empty array into diff --git a/actionpack/README.rdoc b/actionpack/README.rdoc index 02a24a7412..44c980b070 100644 --- a/actionpack/README.rdoc +++ b/actionpack/README.rdoc @@ -28,7 +28,7 @@ can be used outside of Rails. The latest version of Action Pack can be installed with RubyGems: - % [sudo] gem install actionpack + % gem install actionpack Source code can be downloaded as part of the Rails project on GitHub diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 3bd27f8d64..70f9763764 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -30,13 +30,6 @@ Gem::PackageTask.new(spec) do |p| p.gem_spec = spec end -desc "Release to rubygems" -task :release => :package do - require 'rake/gemcutter' - Rake::Gemcutter::Tasks.new(spec).define - Rake::Task['gem:push'].invoke -end - task :lines do load File.expand_path('..', File.dirname(__FILE__)) + '/tools/line_statistics' files = FileList["lib/**/*.rb"] diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb index 47bcfdb1e9..bb3ad9b850 100644 --- a/actionpack/lib/action_controller/metal/conditional_get.rb +++ b/actionpack/lib/action_controller/metal/conditional_get.rb @@ -40,7 +40,7 @@ module ActionController # * <tt>:etag</tt>. # * <tt>:last_modified</tt>. # * <tt>:public</tt> By default the Cache-Control header is private, set this to - # +true+ if you want your application to be cachable by other devices (proxy caches). + # +true+ if you want your application to be cacheable by other devices (proxy caches). # * <tt>:template</tt> By default, the template digest for the current # controller/action is included in ETags. If the action renders a # different template, you can include its digest instead. If the action @@ -111,7 +111,7 @@ module ActionController # * <tt>:etag</tt>. # * <tt>:last_modified</tt>. # * <tt>:public</tt> By default the Cache-Control header is private, set this to - # +true+ if you want your application to be cachable by other devices (proxy caches). + # +true+ if you want your application to be cacheable by other devices (proxy caches). # * <tt>:template</tt> By default, the template digest for the current # controller/action is included in ETags. If the action renders a # different template, you can include its digest instead. If the action diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb index 4038101fe0..b4da381d26 100644 --- a/actionpack/lib/action_controller/metal/helpers.rb +++ b/actionpack/lib/action_controller/metal/helpers.rb @@ -44,7 +44,7 @@ module ActionController # the output might look like this: # # 23 Aug 11:30 | Carolina Railhawks Soccer Match - # N/A | Carolina Railhaws Training Workshop + # N/A | Carolina Railhawks Training Workshop # module Helpers extend ActiveSupport::Concern diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index 909ed19a49..32c3c9652f 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -74,16 +74,16 @@ module ActionController end end - def authenticate_or_request_with_http_basic(realm = "Application", &login_procedure) - authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm) + def authenticate_or_request_with_http_basic(realm = "Application", message = nil, &login_procedure) + authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm, message) end def authenticate_with_http_basic(&login_procedure) HttpAuthentication::Basic.authenticate(request, &login_procedure) end - def request_http_basic_authentication(realm = "Application") - HttpAuthentication::Basic.authentication_request(self, realm) + def request_http_basic_authentication(realm = "Application", message = nil) + HttpAuthentication::Basic.authentication_request(self, realm, message) end end @@ -117,10 +117,11 @@ module ActionController "Basic #{::Base64.strict_encode64("#{user_name}:#{password}")}" end - def authentication_request(controller, realm) + def authentication_request(controller, realm, message) + message ||= "HTTP Basic: Access denied.\n" controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.tr('"'.freeze, "".freeze)}") controller.status = 401 - controller.response_body = "HTTP Basic: Access denied.\n" + controller.response_body = message end end @@ -170,8 +171,8 @@ module ActionController extend self module ControllerMethods - def authenticate_or_request_with_http_digest(realm = "Application", &password_procedure) - authenticate_with_http_digest(realm, &password_procedure) || request_http_digest_authentication(realm) + def authenticate_or_request_with_http_digest(realm = "Application", message = nil, &password_procedure) + authenticate_with_http_digest(realm, &password_procedure) || request_http_digest_authentication(realm, message) end # Authenticate with HTTP Digest, returns true or false @@ -401,16 +402,16 @@ module ActionController extend self module ControllerMethods - def authenticate_or_request_with_http_token(realm = "Application", &login_procedure) - authenticate_with_http_token(&login_procedure) || request_http_token_authentication(realm) + def authenticate_or_request_with_http_token(realm = "Application", message = nil, &login_procedure) + authenticate_with_http_token(&login_procedure) || request_http_token_authentication(realm, message) end def authenticate_with_http_token(&login_procedure) Token.authenticate(self, &login_procedure) end - def request_http_token_authentication(realm = "Application") - Token.authentication_request(self, realm) + def request_http_token_authentication(realm = "Application", message = nil) + Token.authentication_request(self, realm, message) end end @@ -498,9 +499,10 @@ module ActionController # realm - String realm to use in the header. # # Returns nothing. - def authentication_request(controller, realm) + def authentication_request(controller, realm, message = nil) + message ||= "HTTP Token: Access denied.\n" controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.tr('"'.freeze, "".freeze)}") - controller.__send__ :render, :text => "HTTP Token: Access denied.\n", :status => :unauthorized + controller.__send__ :render, :text => message, :status => :unauthorized end end end diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index 0a04848eba..8a4ea70649 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -8,8 +8,7 @@ module ActionController # POST requests without having to specify any root elements. # # This functionality is enabled in +config/initializers/wrap_parameters.rb+ - # and can be customized. If you are upgrading to \Rails 3.1, this file will - # need to be created for the functionality to be enabled. + # and can be customized. # # You could also turn it on per controller by setting the format array to # a non-empty array: @@ -251,7 +250,7 @@ module ActionController private - # Returns the wrapper key which will be used to stored wrapped parameters. + # Returns the wrapper key which will be used to store wrapped parameters. def _wrapper_key _wrapper_options.name end diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index 663a969f72..a7bd3622ee 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -15,11 +15,11 @@ module ActionController #:nodoc: # access. When a request reaches your application, \Rails verifies the received # token with the token in the session. All requests are checked except GET requests # as these should be idempotent. Keep in mind that all session-oriented requests - # should be CSRF protected, including Javascript and HTML requests. + # should be CSRF protected, including JavaScript and HTML requests. # - # Since HTML and Javascript requests are typically made from the browser, we + # Since HTML and JavaScript requests are typically made from the browser, we # need to ensure to verify request authenticity for the web browser. We can - # use session-oriented authentication for these types requests, by using + # use session-oriented authentication for these types of requests, by using # the `protect_form_forgery` method in our controllers. # # GET requests are not protected since they don't have side effects like writing @@ -40,10 +40,11 @@ module ActionController #:nodoc: # # CSRF protection is turned on with the <tt>protect_from_forgery</tt> method. # By default <tt>protect_from_forgery</tt> protects your session with - # <tt>:null_session</tt> method, which provides an empty session during request + # <tt>:null_session</tt> method, which provides an empty session + # during request. # # We may want to disable CSRF protection for APIs since they are typically - # designed to be state-less. That is, the requestion API client will handle + # designed to be state-less. That is, the request API client will handle # the session for you instead of Rails. # # The token parameter is named <tt>authenticity_token</tt> by default. The name and @@ -96,10 +97,10 @@ module ActionController #:nodoc: # Valid Options: # # * <tt>:only/:except</tt> - Only apply forgery protection to a subset of actions. Like <tt>only: [ :create, :create_all ]</tt>. - # * <tt>:if/:unless</tt> - Turn off the forgery protection entirely depending on the passed proc or method reference. + # * <tt>:if/:unless</tt> - Turn off the forgery protection entirely depending on the passed Proc or method reference. # * <tt>:prepend</tt> - By default, the verification of the authentication token is added to the front of the # callback chain. If you need to make the verification depend on other callbacks, like authentication methods - # (say cookies vs oauth), this might not work for you. Pass <tt>prepend: false</tt> to just add the + # (say cookies vs OAuth), this might not work for you. Pass <tt>prepend: false</tt> to just add the # verification callback in the position of the protect_from_forgery call. This means any callbacks added # before are run first. # * <tt>:with</tt> - Set the method to handle unverified request. diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb index 04401cad7b..af31de1f3a 100644 --- a/actionpack/lib/action_controller/metal/streaming.rb +++ b/actionpack/lib/action_controller/metal/streaming.rb @@ -110,9 +110,9 @@ module ActionController #:nodoc: # This means that, if you have <code>yield :title</code> in your layout # and you want to use streaming, you would have to render the whole template # (and eventually trigger all queries) before streaming the title and all - # assets, which kills the purpose of streaming. For this reason Rails 3.1 - # introduces a new helper called +provide+ that does the same as +content_for+ - # but tells the layout to stop searching for other entries and continue rendering. + # assets, which kills the purpose of streaming. For this purpose, you can use + # a helper called +provide+ that does the same as +content_for+ but tells the + # layout to stop searching for other entries and continue rendering. # # For instance, the template above using +provide+ would be: # diff --git a/actionpack/lib/action_controller/template_assertions.rb b/actionpack/lib/action_controller/template_assertions.rb new file mode 100644 index 0000000000..304012d24d --- /dev/null +++ b/actionpack/lib/action_controller/template_assertions.rb @@ -0,0 +1,188 @@ +module ActionController + module TemplateAssertions + extend ActiveSupport::Concern + + included do + setup :setup_subscriptions + teardown :teardown_subscriptions + end + + RENDER_TEMPLATE_INSTANCE_VARIABLES = %w{partials templates layouts files}.freeze + + def setup_subscriptions + RENDER_TEMPLATE_INSTANCE_VARIABLES.each do |instance_variable| + instance_variable_set("@_#{instance_variable}", Hash.new(0)) + end + + @_subscribers = [] + + @_subscribers << ActiveSupport::Notifications.subscribe("render_template.action_view") do |_name, _start, _finish, _id, payload| + path = payload[:layout] + if path + @_layouts[path] += 1 + if path =~ /^layouts\/(.*)/ + @_layouts[$1] += 1 + end + end + end + + @_subscribers << ActiveSupport::Notifications.subscribe("!render_template.action_view") do |_name, _start, _finish, _id, payload| + if virtual_path = payload[:virtual_path] + partial = virtual_path =~ /^.*\/_[^\/]*$/ + + if partial + @_partials[virtual_path] += 1 + @_partials[virtual_path.split("/").last] += 1 + end + + @_templates[virtual_path] += 1 + else + path = payload[:identifier] + if path + @_files[path] += 1 + @_files[path.split("/").last] += 1 + end + end + end + end + + def teardown_subscriptions + @_subscribers.each do |subscriber| + ActiveSupport::Notifications.unsubscribe(subscriber) + end + end + + def process(*args) + reset_template_assertion + super + end + + def reset_template_assertion + RENDER_TEMPLATE_INSTANCE_VARIABLES.each do |instance_variable| + ivar_name = "@_#{instance_variable}" + if instance_variable_defined?(ivar_name) + instance_variable_get(ivar_name).clear + end + end + end + + # Asserts that the request was rendered with the appropriate template file or partials. + # + # # assert that the "new" view template was rendered + # assert_template "new" + # + # # assert that the exact template "admin/posts/new" was rendered + # assert_template %r{\Aadmin/posts/new\Z} + # + # # assert that the layout 'admin' was rendered + # assert_template layout: 'admin' + # assert_template layout: 'layouts/admin' + # assert_template layout: :admin + # + # # assert that no layout was rendered + # assert_template layout: nil + # assert_template layout: false + # + # # assert that the "_customer" partial was rendered twice + # assert_template partial: '_customer', count: 2 + # + # # assert that no partials were rendered + # assert_template partial: false + # + # # assert that a file was rendered + # assert_template file: "README.rdoc" + # + # # assert that no file was rendered + # assert_template file: nil + # assert_template file: false + # + # In a view test case, you can also assert that specific locals are passed + # to partials: + # + # # assert that the "_customer" partial was rendered with a specific object + # assert_template partial: '_customer', locals: { customer: @customer } + def assert_template(options = {}, message = nil) + # Force body to be read in case the template is being streamed. + response.body + + case options + when NilClass, Regexp, String, Symbol + options = options.to_s if Symbol === options + rendered = @_templates + msg = message || sprintf("expecting <%s> but rendering with <%s>", + options.inspect, rendered.keys) + matches_template = + case options + when String + !options.empty? && rendered.any? do |t, num| + options_splited = options.split(File::SEPARATOR) + t_splited = t.split(File::SEPARATOR) + t_splited.last(options_splited.size) == options_splited + end + when Regexp + rendered.any? { |t,num| t.match(options) } + when NilClass + rendered.blank? + end + assert matches_template, msg + when Hash + options.assert_valid_keys(:layout, :partial, :locals, :count, :file) + + if options.key?(:layout) + expected_layout = options[:layout] + msg = message || sprintf("expecting layout <%s> but action rendered <%s>", + expected_layout, @_layouts.keys) + + case expected_layout + when String, Symbol + assert_includes @_layouts.keys, expected_layout.to_s, msg + when Regexp + assert(@_layouts.keys.any? {|l| l =~ expected_layout }, msg) + when nil, false + assert(@_layouts.empty?, msg) + else + raise ArgumentError, "assert_template only accepts a String, Symbol, Regexp, nil or false for :layout" + end + end + + if options[:file] + assert_includes @_files.keys, options[:file] + elsif options.key?(:file) + assert @_files.blank?, "expected no files but #{@_files.keys} was rendered" + end + + if expected_partial = options[:partial] + if expected_locals = options[:locals] + if defined?(@_rendered_views) + view = expected_partial.to_s.sub(/^_/, '').sub(/\/_(?=[^\/]+\z)/, '/') + + partial_was_not_rendered_msg = "expected %s to be rendered but it was not." % view + assert_includes @_rendered_views.rendered_views, view, partial_was_not_rendered_msg + + msg = 'expecting %s to be rendered with %s but was with %s' % [expected_partial, + expected_locals, + @_rendered_views.locals_for(view)] + assert(@_rendered_views.view_rendered?(view, options[:locals]), msg) + else + warn "the :locals option to #assert_template is only supported in a ActionView::TestCase" + end + elsif expected_count = options[:count] + actual_count = @_partials[expected_partial] + msg = message || sprintf("expecting %s to be rendered %s time(s) but rendered %s time(s)", + expected_partial, expected_count, actual_count) + assert(actual_count == expected_count.to_i, msg) + else + msg = message || sprintf("expecting partial <%s> but action rendered <%s>", + options[:partial], @_partials.keys) + assert_includes @_partials, expected_partial, msg + end + elsif options.key?(:partial) + assert @_partials.empty?, + "Expected no partials to be rendered" + end + else + raise ArgumentError, "assert_template only accepts a String, Symbol, Hash, Regexp, or nil" + end + end + end +end diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 0faf068298..b29c5b23fc 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -2,197 +2,10 @@ require 'rack/session/abstract/id' require 'active_support/core_ext/object/to_query' require 'active_support/core_ext/module/anonymous' require 'active_support/core_ext/hash/keys' - +require 'action_controller/template_assertions' require 'rails-dom-testing' module ActionController - module TemplateAssertions - extend ActiveSupport::Concern - - included do - setup :setup_subscriptions - teardown :teardown_subscriptions - end - - RENDER_TEMPLATE_INSTANCE_VARIABLES = %w{partials templates layouts files}.freeze - - def setup_subscriptions - RENDER_TEMPLATE_INSTANCE_VARIABLES.each do |instance_variable| - instance_variable_set("@_#{instance_variable}", Hash.new(0)) - end - - @_subscribers = [] - - @_subscribers << ActiveSupport::Notifications.subscribe("render_template.action_view") do |_name, _start, _finish, _id, payload| - path = payload[:layout] - if path - @_layouts[path] += 1 - if path =~ /^layouts\/(.*)/ - @_layouts[$1] += 1 - end - end - end - - @_subscribers << ActiveSupport::Notifications.subscribe("!render_template.action_view") do |_name, _start, _finish, _id, payload| - if virtual_path = payload[:virtual_path] - partial = virtual_path =~ /^.*\/_[^\/]*$/ - - if partial - @_partials[virtual_path] += 1 - @_partials[virtual_path.split("/").last] += 1 - end - - @_templates[virtual_path] += 1 - else - path = payload[:identifier] - if path - @_files[path] += 1 - @_files[path.split("/").last] += 1 - end - end - end - end - - def teardown_subscriptions - @_subscribers.each do |subscriber| - ActiveSupport::Notifications.unsubscribe(subscriber) - end - end - - def process(*args) - reset_template_assertion - super - end - - def reset_template_assertion - RENDER_TEMPLATE_INSTANCE_VARIABLES.each do |instance_variable| - ivar_name = "@_#{instance_variable}" - if instance_variable_defined?(ivar_name) - instance_variable_get(ivar_name).clear - end - end - end - - # Asserts that the request was rendered with the appropriate template file or partials. - # - # # assert that the "new" view template was rendered - # assert_template "new" - # - # # assert that the exact template "admin/posts/new" was rendered - # assert_template %r{\Aadmin/posts/new\Z} - # - # # assert that the layout 'admin' was rendered - # assert_template layout: 'admin' - # assert_template layout: 'layouts/admin' - # assert_template layout: :admin - # - # # assert that no layout was rendered - # assert_template layout: nil - # assert_template layout: false - # - # # assert that the "_customer" partial was rendered twice - # assert_template partial: '_customer', count: 2 - # - # # assert that no partials were rendered - # assert_template partial: false - # - # # assert that a file was rendered - # assert_template file: "README.rdoc" - # - # # assert that no file was rendered - # assert_template file: nil - # assert_template file: false - # - # In a view test case, you can also assert that specific locals are passed - # to partials: - # - # # assert that the "_customer" partial was rendered with a specific object - # assert_template partial: '_customer', locals: { customer: @customer } - def assert_template(options = {}, message = nil) - # Force body to be read in case the template is being streamed. - response.body - - case options - when NilClass, Regexp, String, Symbol - options = options.to_s if Symbol === options - rendered = @_templates - msg = message || sprintf("expecting <%s> but rendering with <%s>", - options.inspect, rendered.keys) - matches_template = - case options - when String - !options.empty? && rendered.any? do |t, num| - options_splited = options.split(File::SEPARATOR) - t_splited = t.split(File::SEPARATOR) - t_splited.last(options_splited.size) == options_splited - end - when Regexp - rendered.any? { |t,num| t.match(options) } - when NilClass - rendered.blank? - end - assert matches_template, msg - when Hash - options.assert_valid_keys(:layout, :partial, :locals, :count, :file) - - if options.key?(:layout) - expected_layout = options[:layout] - msg = message || sprintf("expecting layout <%s> but action rendered <%s>", - expected_layout, @_layouts.keys) - - case expected_layout - when String, Symbol - assert_includes @_layouts.keys, expected_layout.to_s, msg - when Regexp - assert(@_layouts.keys.any? {|l| l =~ expected_layout }, msg) - when nil, false - assert(@_layouts.empty?, msg) - else - raise ArgumentError, "assert_template only accepts a String, Symbol, Regexp, nil or false for :layout" - end - end - - if options[:file] - assert_includes @_files.keys, options[:file] - elsif options.key?(:file) - assert @_files.blank?, "expected no files but #{@_files.keys} was rendered" - end - - if expected_partial = options[:partial] - if expected_locals = options[:locals] - if defined?(@_rendered_views) - view = expected_partial.to_s.sub(/^_/, '').sub(/\/_(?=[^\/]+\z)/, '/') - - partial_was_not_rendered_msg = "expected %s to be rendered but it was not." % view - assert_includes @_rendered_views.rendered_views, view, partial_was_not_rendered_msg - - msg = 'expecting %s to be rendered with %s but was with %s' % [expected_partial, - expected_locals, - @_rendered_views.locals_for(view)] - assert(@_rendered_views.view_rendered?(view, options[:locals]), msg) - else - warn "the :locals option to #assert_template is only supported in a ActionView::TestCase" - end - elsif expected_count = options[:count] - actual_count = @_partials[expected_partial] - msg = message || sprintf("expecting %s to be rendered %s time(s) but rendered %s time(s)", - expected_partial, expected_count, actual_count) - assert(actual_count == expected_count.to_i, msg) - else - msg = message || sprintf("expecting partial <%s> but action rendered <%s>", - options[:partial], @_partials.keys) - assert_includes @_partials, expected_partial, msg - end - elsif options.key?(:partial) - assert @_partials.empty?, - "Expected no partials to be rendered" - end - else - raise ArgumentError, "assert_template only accepts a String, Symbol, Hash, Regexp, or nil" - end - end - end - class TestRequest < ActionDispatch::TestRequest #:nodoc: DEFAULT_ENV = ActionDispatch::TestRequest::DEFAULT_ENV.dup DEFAULT_ENV.delete 'PATH_INFO' @@ -602,7 +415,7 @@ module ActionController def process(action, *args) check_required_ivars - if kwarg_request?(*args) + if kwarg_request?(args) parameters, session, body, flash, http_method, format, xhr = args[0].values_at(:params, :session, :body, :flash, :method, :format, :xhr) else http_method, parameters, session, flash = args @@ -741,7 +554,7 @@ module ActionController private def process_with_kwargs(http_method, action, *args) - if kwarg_request?(*args) + if kwarg_request?(args) args.first.merge!(method: http_method) process(action, *args) else @@ -753,7 +566,7 @@ module ActionController end REQUEST_KWARGS = %i(params session flash method body xhr) - def kwarg_request?(*args) + def kwarg_request?(args) args[0].respond_to?(:keys) && ( (args[0].key?(:format) && args[0].keys.size == 1) || args[0].keys.any? { |k| REQUEST_KWARGS.include?(k) } diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb index 2b851cc28d..3170389b36 100644 --- a/actionpack/lib/action_dispatch/http/filter_parameters.rb +++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb @@ -16,7 +16,7 @@ module ActionDispatch # env["action_dispatch.parameter_filter"] = [:foo, "bar"] # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]" # - # env["action_dispatch.parameter_filter"] = lambda do |k,v| + # env["action_dispatch.parameter_filter"] = -> (k, v) do # v.reverse! if k =~ /secret/i # end # => reverses the value to all keys matching /secret/i diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index a1f84e5ace..f3de19db0b 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -20,6 +20,8 @@ module ActionDispatch include ActionDispatch::Http::FilterParameters include ActionDispatch::Http::URL + HTTP_X_REQUEST_ID = "HTTP_X_REQUEST_ID".freeze # :nodoc: + autoload :Session, 'action_dispatch/request/session' autoload :Utils, 'action_dispatch/request/utils' @@ -50,7 +52,6 @@ module ActionDispatch @original_fullpath = nil @fullpath = nil @ip = nil - @request_id = nil end def check_path_parameters! @@ -180,7 +181,7 @@ module ActionDispatch # # request.headers["Content-Type"] # => "text/plain" def headers - Http::Headers.new(@env) + @headers ||= Http::Headers.new(@env) end # Returns a +String+ with the last requested path including their params. @@ -234,15 +235,19 @@ module ActionDispatch end alias :xhr? :xml_http_request? + # Returns the IP address of client as a +String+. def ip @ip ||= super end - # Originating IP address, usually set by the RemoteIp middleware. + # Returns the IP address of client as a +String+, + # usually set by the RemoteIp middleware. def remote_ip @remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s end + ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id".freeze # :nodoc: + # Returns the unique request id, which is based on either the X-Request-Id header that can # be generated by a firewall, load balancer, or web server or by the RequestId middleware # (which sets the action_dispatch.request_id environment variable). @@ -250,11 +255,19 @@ 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 request_id - @request_id ||= env["action_dispatch.request_id"] + env[ACTION_DISPATCH_REQUEST_ID] + end + + def request_id=(id) # :nodoc: + env[ACTION_DISPATCH_REQUEST_ID] = id end alias_method :uuid, :request_id + def x_request_id # :nodoc + @env[HTTP_X_REQUEST_ID] + end + # Returns the lowercase name of the HTTP server software. def server_software (@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil @@ -282,6 +295,8 @@ module ActionDispatch end end + # returns true if request content mime type is + # +application/x-www-form-urlencoded+ or +multipart/form-data+. def form_data? FORM_DATA_MEDIA_TYPES.include?(content_mime_type.to_s) end diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index a895d1ab18..9e53a0f08b 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -274,7 +274,7 @@ module ActionDispatch # :nodoc: end # Turns the Response into a Rack-compatible array of the status, headers, - # and body. Allows explict splatting: + # and body. Allows explicit splatting: # # status, headers, body = *response def to_a diff --git a/actionpack/lib/action_dispatch/journey/route.rb b/actionpack/lib/action_dispatch/journey/route.rb index 4d5c18984a..4698ff8cc7 100644 --- a/actionpack/lib/action_dispatch/journey/route.rb +++ b/actionpack/lib/action_dispatch/journey/route.rb @@ -36,7 +36,7 @@ module ActionDispatch def requirements # :nodoc: # needed for rails `rake routes` - path.requirements.merge(@defaults).delete_if { |_,v| + @defaults.merge(path.requirements).delete_if { |_,v| /.+?/ == v } end diff --git a/actionpack/lib/action_dispatch/journey/routes.rb b/actionpack/lib/action_dispatch/journey/routes.rb index a6d1980db2..6325dfa3dd 100644 --- a/actionpack/lib/action_dispatch/journey/routes.rb +++ b/actionpack/lib/action_dispatch/journey/routes.rb @@ -16,6 +16,10 @@ module ActionDispatch @simulator = nil end + def empty? + routes.empty? + end + def length routes.length end diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index b7687ca100..dd1f140051 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -79,6 +79,9 @@ module ActionDispatch # domain: %w(.example.com .example.org) # Allow the cookie # # for concrete domain names. # + # * <tt>:tld_length</tt> - When using <tt>:domain => :all</tt>, this option can be used to explicitly + # set the TLD length when using a short (<= 3 character) domain that is being interpreted as part of a TLD. + # For example, to share cookies between user1.lvh.me and user2.lvh.me, set <tt>:tld_length</tt> to 1. # * <tt>:expires</tt> - The time at which this cookie expires, as a \Time object. # * <tt>:secure</tt> - Whether this cookie is only transmitted to HTTPS servers. # Default is +false+. @@ -181,7 +184,7 @@ module ActionDispatch # to the Message{Encryptor,Verifier} allows us to handle the # (de)serialization step within the cookie jar, which gives us the # opportunity to detect and migrate legacy cookies. - module VerifyAndUpgradeLegacySignedMessage + module VerifyAndUpgradeLegacySignedMessage # :nodoc: def initialize(*args) super @legacy_verifier = ActiveSupport::MessageVerifier.new(@options[:secret_token], serializer: ActiveSupport::MessageEncryptor::NullSerializer) @@ -392,7 +395,7 @@ module ActionDispatch end end - class JsonSerializer + class JsonSerializer # :nodoc: def self.load(value) ActiveSupport::JSON.decode(value) end @@ -402,7 +405,7 @@ module ActionDispatch end end - module SerializedCookieJars + module SerializedCookieJars # :nodoc: MARSHAL_SIGNATURE = "\x04\x08".freeze protected @@ -454,12 +457,16 @@ module ActionDispatch @verifier = ActiveSupport::MessageVerifier.new(secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer) end + # Returns the value of the cookie by +name+ if it is untampered, + # returns +nil+ otherwise or if no such cookie exists. def [](name) if signed_message = @parent_jar[name] deserialize name, verify(signed_message) end end + # Signs and sets the cookie named +name+. The second argument may be the cookie's + # value or a hash of options as documented above. def []=(name, options) if options.is_a?(Hash) options.symbolize_keys! @@ -482,8 +489,8 @@ module ActionDispatch # UpgradeLegacySignedCookieJar is used instead of SignedCookieJar if # secrets.secret_token and secrets.secret_key_base are both set. It reads - # legacy cookies signed with the old dummy key generator and re-saves - # them using the new key generator to provide a smooth upgrade path. + # legacy cookies signed with the old dummy key generator and signs and + # re-saves them using the new key generator to provide a smooth upgrade path. class UpgradeLegacySignedCookieJar < SignedCookieJar #:nodoc: include VerifyAndUpgradeLegacySignedMessage @@ -511,12 +518,16 @@ module ActionDispatch @encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer) end + # Returns the value of the cookie by +name+ if it is untampered, + # returns +nil+ otherwise or if no such cookie exists. def [](name) if encrypted_message = @parent_jar[name] deserialize name, decrypt_and_verify(encrypted_message) end end + # Encrypts and sets the cookie named +name+. The second argument may be the cookie's + # value or a hash of options as documented above. def []=(name, options) if options.is_a?(Hash) options.symbolize_keys! diff --git a/actionpack/lib/action_dispatch/middleware/reloader.rb b/actionpack/lib/action_dispatch/middleware/reloader.rb index 15b5a48535..6c7fba00cb 100644 --- a/actionpack/lib/action_dispatch/middleware/reloader.rb +++ b/actionpack/lib/action_dispatch/middleware/reloader.rb @@ -11,9 +11,9 @@ module ActionDispatch # the response body. This is important for streaming responses such as the # following: # - # self.response_body = lambda { |response, output| + # self.response_body = -> (response, output) do # # code here which refers to application models - # } + # end # # Cleanup callbacks will not be called until after the response_body lambda # is evaluated, ensuring that it can refer to application models and other diff --git a/actionpack/lib/action_dispatch/middleware/request_id.rb b/actionpack/lib/action_dispatch/middleware/request_id.rb index b9ca524309..1555ff72af 100644 --- a/actionpack/lib/action_dispatch/middleware/request_id.rb +++ b/actionpack/lib/action_dispatch/middleware/request_id.rb @@ -13,22 +13,23 @@ module ActionDispatch # 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] } + req = ActionDispatch::Request.new env + req.request_id = make_request_id(req.x_request_id) + @app.call(env).tap { |_status, headers, _body| headers[X_REQUEST_ID] = req.request_id } end private - def external_request_id(env) - if request_id = env[HTTP_X_REQUEST_ID].presence + def make_request_id(request_id) + if request_id.presence request_id.gsub(/[^\w\-]/, "".freeze).first(255) + else + internal_request_id end end diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb index 9a92b690c7..bc5ef1abc9 100644 --- a/actionpack/lib/action_dispatch/middleware/static.rb +++ b/actionpack/lib/action_dispatch/middleware/static.rb @@ -3,15 +3,15 @@ require 'active_support/core_ext/uri' module ActionDispatch # This middleware returns a file's contents from disk in the body response. - # When initialized it can accept an optional 'Cache-Control' header which + # When initialized, it can accept an optional 'Cache-Control' header, which # will be set when a response containing a file's contents is delivered. # # This middleware will render the file specified in `env["PATH_INFO"]` - # where the base path is in the +root+ directory. For example if the +root+ - # is set to `public/` then a request with `env["PATH_INFO"]` of - # `assets/application.js` will return a response with contents of a file + # where the base path is in the +root+ directory. For example, if the +root+ + # is set to `public/`, then a request with `env["PATH_INFO"]` of + # `assets/application.js` will return a response with the contents of a file # located at `public/assets/application.js` if the file exists. If the file - # does not exist a 404 "File not Found" response will be returned. + # does not exist, a 404 "File not Found" response will be returned. class FileHandler def initialize(root, cache_control) @root = root.chomp('/') @@ -20,6 +20,13 @@ module ActionDispatch @file_server = ::Rack::File.new(@root, headers) end + + # Takes a path to a file. If the file is found, has valid encoding, and has + # correct read permissions, the return value is a URI-escaped string + # representing the filename. Otherwise, false is returned. + # + # Used by the `Static` class to check the existence of a valid file + # in the server's `public/` directory (see Static#call). def match?(path) path = URI.parser.unescape(path) return false unless path.valid_encoding? @@ -88,7 +95,7 @@ module ActionDispatch end # This middleware will attempt to return the contents of a file's body from - # disk in the response. If a file is not found on disk, the request will be + # disk in the response. If a file is not found on disk, the request will be # delegated to the application stack. This middleware is commonly initialized # to serve assets from a server's `public/` directory. # diff --git a/actionpack/lib/action_dispatch/routing/inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb index c513737fc2..48c10a7d4c 100644 --- a/actionpack/lib/action_dispatch/routing/inspector.rb +++ b/actionpack/lib/action_dispatch/routing/inspector.rb @@ -45,7 +45,7 @@ module ActionDispatch end def internal? - controller.to_s =~ %r{\Arails/(info|mailers|welcome)} || path =~ %r{\A#{Rails.application.config.assets.prefix}\z} + controller.to_s =~ %r{\Arails/(info|mailers|welcome)} end def engine? diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 49009a45cc..0a444ddffc 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -418,7 +418,7 @@ module ActionDispatch # A pattern can also point to a +Rack+ endpoint i.e. anything that # responds to +call+: # - # match 'photos/:id', to: lambda {|hash| [200, {}, ["Coming soon"]] }, via: :get + # match 'photos/:id', to: -> (hash) { [200, {}, ["Coming soon"]] }, via: :get # match 'photos/:id', to: PhotoRackApp, via: :get # # Yes, controller actions are just rack endpoints # match 'photos/:id', to: PhotosController.action(:show), via: :get @@ -470,7 +470,7 @@ module ActionDispatch # +call+ or a string representing a controller's action. # # match 'path', to: 'controller#action', via: :get - # match 'path', to: lambda { |env| [200, {}, ["Success!"]] }, via: :get + # match 'path', to: -> (env) { [200, {}, ["Success!"]] }, via: :get # match 'path', to: RackApp, via: :get # # [:on] @@ -899,7 +899,7 @@ module ActionDispatch # # Requests to routes can be constrained based on specific criteria: # - # constraints(lambda { |req| req.env["HTTP_USER_AGENT"] =~ /iPhone/ }) do + # constraints(-> (req) { req.env["HTTP_USER_AGENT"] =~ /iPhone/ }) do # resources :iphones # end # diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 3800c61dab..f953429e96 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -81,7 +81,7 @@ module ActionDispatch # # xhr :get, '/feed', params: { since: 201501011400 } def xml_http_request(request_method, path, *args) - if kwarg_request?(*args) + if kwarg_request?(args) params, headers, env = args.first.values_at(:params, :headers, :env) else params = args[0] @@ -291,7 +291,7 @@ module ActionDispatch end def process_with_kwargs(http_method, path, *args) - if kwarg_request?(*args) + if kwarg_request?(args) process(http_method, path, *args) else non_kwarg_request_warning if args.present? @@ -300,7 +300,7 @@ module ActionDispatch end REQUEST_KWARGS = %i(params headers env xhr) - def kwarg_request?(*args) + def kwarg_request?(args) args[0].respond_to?(:keys) && args[0].keys.any? { |k| REQUEST_KWARGS.include?(k) } end @@ -584,7 +584,7 @@ module ActionDispatch # https!(false) # get "/articles/all" # assert_response :success - # assert assigns(:articles) + # assert_select 'h1', 'Articles' # end # end # @@ -623,7 +623,7 @@ module ActionDispatch # def browses_site # get "/products/all" # assert_response :success - # assert assigns(:products) + # assert_select 'h1', 'Products' # end # end # diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb index 20962a90cb..10fbee7582 100644 --- a/actionpack/test/controller/http_basic_authentication_test.rb +++ b/actionpack/test/controller/http_basic_authentication_test.rb @@ -36,7 +36,7 @@ class HttpBasicAuthenticationTest < ActionController::TestCase if authenticate_with_http_basic { |username, password| username == 'pretty' && password == 'please' } @logged_in = true else - request_http_basic_authentication("SuperSecret") + request_http_basic_authentication("SuperSecret", "Authentication Failed\n") end end @@ -104,7 +104,7 @@ class HttpBasicAuthenticationTest < ActionController::TestCase get :display assert_response :unauthorized - assert_equal "HTTP Basic: Access denied.\n", @response.body + assert_equal "Authentication Failed\n", @response.body assert_equal 'Basic realm="SuperSecret"', @response.headers['WWW-Authenticate'] end @@ -113,7 +113,7 @@ class HttpBasicAuthenticationTest < ActionController::TestCase get :display assert_response :unauthorized - assert_equal "HTTP Basic: Access denied.\n", @response.body + assert_equal "Authentication Failed\n", @response.body assert_equal 'Basic realm="SuperSecret"', @response.headers['WWW-Authenticate'] end diff --git a/actionpack/test/controller/http_token_authentication_test.rb b/actionpack/test/controller/http_token_authentication_test.rb index a758df2ec6..2521f0322c 100644 --- a/actionpack/test/controller/http_token_authentication_test.rb +++ b/actionpack/test/controller/http_token_authentication_test.rb @@ -30,7 +30,7 @@ class HttpTokenAuthenticationTest < ActionController::TestCase if authenticate_with_http_token { |token, options| token == '"quote" pretty' && options[:algorithm] == 'test' } @logged_in = true else - request_http_token_authentication("SuperSecret") + request_http_token_authentication("SuperSecret", "Authentication Failed\n") end end @@ -91,7 +91,7 @@ class HttpTokenAuthenticationTest < ActionController::TestCase get :display assert_response :unauthorized - assert_equal "HTTP Token: Access denied.\n", @response.body + assert_equal "Authentication Failed\n", @response.body assert_equal 'Token realm="SuperSecret"', @response.headers['WWW-Authenticate'] end @@ -100,7 +100,7 @@ class HttpTokenAuthenticationTest < ActionController::TestCase get :display assert_response :unauthorized - assert_equal "HTTP Token: Access denied.\n", @response.body + assert_equal "Authentication Failed\n", @response.body assert_equal 'Token realm="SuperSecret"', @response.headers['WWW-Authenticate'] end diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index efd790de63..ef30f1ea0f 100644 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -1,8 +1,5 @@ require 'abstract_unit' -class WorkshopsController < ActionController::Base -end - class RedirectController < ActionController::Base # empty method not used anywhere to ensure methods like # `status` and `location` aren't called on `redirect_to` calls diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index 8887f291cf..f8cf79a257 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -12,14 +12,6 @@ module RequestForgeryProtectionActions render :inline => "<%= button_to('New', '/') %>" end - def external_form - render :inline => "<%= form_tag('http://farfar.away/form', :authenticity_token => 'external_token') {} %>" - end - - def external_form_without_protection - render :inline => "<%= form_tag('http://farfar.away/form', :authenticity_token => false) {} %>" - end - def unsafe render :text => 'pwn' end @@ -28,14 +20,6 @@ module RequestForgeryProtectionActions render :inline => "<%= csrf_meta_tags %>" end - def external_form_for - render :inline => "<%= form_for(:some_resource, :authenticity_token => 'external_token') {} %>" - end - - def form_for_without_protection - render :inline => "<%= form_for(:some_resource, :authenticity_token => false ) {} %>" - end - def form_for_remote render :inline => "<%= form_for(:some_resource, :remote => true ) {} %>" end @@ -70,7 +54,6 @@ module RequestForgeryProtectionActions negotiate_same_origin end - def rescue_action(e) raise e end end # sample controllers diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 2d08987ca6..9bbfb74e72 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -8,8 +8,6 @@ class MilestonesController < ActionController::Base alias_method :show, :index end -ROUTING = ActionDispatch::Routing - # See RFC 3986, section 3.3 for allowed path characters. class UriReservedCharactersRoutingTest < ActiveSupport::TestCase include RoutingTestHelpers @@ -871,7 +869,7 @@ class RouteSetTest < ActiveSupport::TestCase def default_route_set @default_route_set ||= begin - set = ROUTING::RouteSet.new + set = ActionDispatch::Routing::RouteSet.new set.draw do get '/:controller(/:action(/:id))' end @@ -1748,13 +1746,13 @@ class RouteSetTest < ActiveSupport::TestCase include ActionDispatch::RoutingVerbs - class TestSet < ROUTING::RouteSet + class TestSet < ActionDispatch::Routing::RouteSet def initialize(block) @block = block super() end - class Dispatcher < ROUTING::RouteSet::Dispatcher + class Dispatcher < ActionDispatch::Routing::RouteSet::Dispatcher def initialize(defaults, set, block) super(defaults) @block = block diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb index 4bda440331..37f27c5d36 100644 --- a/actionpack/test/controller/test_case_test.rb +++ b/actionpack/test/controller/test_case_test.rb @@ -928,6 +928,11 @@ XML assert_equal File.open(path, READ_PLAIN).read, plain_file_upload.read end + def test_fixture_file_upload_should_be_able_access_to_tempfile + file = fixture_file_upload(FILES_DIR + "/mona_lisa.jpg", "image/jpg") + assert file.respond_to?(:tempfile), "expected tempfile should respond on fixture file object, got nothing" + end + def test_fixture_file_upload post :test_file_upload, params: { diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index f208cfda89..27ee8603e4 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -663,6 +663,7 @@ class RequestMethod < BaseRequestTest assert_equal 'GET', request.request_method assert_equal 'GET', request.env["REQUEST_METHOD"] + assert request.get? end test "invalid http method raises exception" do diff --git a/actionpack/test/dispatch/routing/inspector_test.rb b/actionpack/test/dispatch/routing/inspector_test.rb index 3df022c64b..4047214843 100644 --- a/actionpack/test/dispatch/routing/inspector_test.rb +++ b/actionpack/test/dispatch/routing/inspector_test.rb @@ -313,6 +313,22 @@ module ActionDispatch assert_equal ["Prefix Verb URI Pattern Controller#Action", " GET /:controller(/:action) (?-mix:api\\/[^\\/]+)#:action"], output end + + def test_inspect_routes_shows_resources_route_when_assets_disabled + @set = ActionDispatch::Routing::RouteSet.new + app = ActiveSupport::OrderedOptions.new + + Rails.stubs(:application).returns(app) + + output = draw do + get '/cart', to: 'cart#show' + end + + assert_equal [ + "Prefix Verb URI Pattern Controller#Action", + " cart GET /cart(.:format) cart#show" + ], output + end end end end diff --git a/actionpack/test/dispatch/routing/route_set_test.rb b/actionpack/test/dispatch/routing/route_set_test.rb index fe52c50336..9327fe12c6 100644 --- a/actionpack/test/dispatch/routing/route_set_test.rb +++ b/actionpack/test/dispatch/routing/route_set_test.rb @@ -17,6 +17,16 @@ module ActionDispatch @set = RouteSet.new end + test "not being empty when route is added" do + assert empty? + + draw do + get 'foo', to: SimpleApp.new('foo#index') + end + + assert_not empty? + end + test "url helpers are added when route is added" do draw do get 'foo', to: SimpleApp.new('foo#index') @@ -136,6 +146,10 @@ module ActionDispatch def url_helpers @set.url_helpers end + + def empty? + @set.empty? + end end end end diff --git a/actionpack/test/journey/route_test.rb b/actionpack/test/journey/route_test.rb index 21d867aca0..0980df06e8 100644 --- a/actionpack/test/journey/route_test.rb +++ b/actionpack/test/journey/route_test.rb @@ -25,6 +25,14 @@ module ActionDispatch end end + def test_path_requirements_override_defaults + strexp = Router::Strexp.build(':name', { name: /love/ }, ['/']) + path = Path::Pattern.new strexp + defaults = { name: 'tender' } + route = Route.new('name', nil, path, nil, defaults) + assert_equal(/love/, route.requirements[:name]) + end + def test_ip_address path = Path::Pattern.from_string '/messages/:id(.:format)' route = Route.new("name", nil, path, {:ip => '192.168.1.1'}, diff --git a/actionpack/test/journey/routes_test.rb b/actionpack/test/journey/routes_test.rb index b54d961f66..2a1c1c337e 100644 --- a/actionpack/test/journey/routes_test.rb +++ b/actionpack/test/journey/routes_test.rb @@ -14,9 +14,11 @@ module ActionDispatch requirements = { :hello => /world/ } routes.add_route nil, path, requirements, {:id => nil}, {} + assert_not routes.empty? assert_equal 1, routes.length routes.clear + assert routes.empty? assert_equal 0, routes.length end |