diff options
Diffstat (limited to 'actionpack')
35 files changed, 352 insertions, 301 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index e0076225ba..4e26a2a60e 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,4 +1,16 @@ -* Fix rake routes not showing the right format when +* `FileHandler` and `Static` middleware initializers accept `index` argument + to configure the directory index file name. Defaults to `index` (as in + `index.html`). + + See #20017. + + *Eliot Sykes* + +* Deprecate `:nothing` option for `render` method. + + *Mehmet Emin İNAÇ* + +* Fix `rake routes` not showing the right format when nesting multiple routes. See #18373. @@ -281,7 +293,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: @@ -312,7 +324,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..601263bfac 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -1,5 +1,4 @@ require 'rake/testtask' -require 'rubygems/package_task' test_files = Dir.glob('test/**/*_test.rb') @@ -24,19 +23,6 @@ namespace :test do end end -spec = eval(File.read('actionpack.gemspec')) - -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/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/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb index 2d15c39d88..b9ae8dd5ea 100644 --- a/actionpack/lib/action_controller/metal/rendering.rb +++ b/actionpack/lib/action_controller/metal/rendering.rb @@ -79,6 +79,7 @@ module ActionController end if options.delete(:nothing) + ActiveSupport::Deprecation.warn("`:nothing` option is deprecated and will be removed in Rails 5.1. Use `head` method to respond with empty response body.") options[:body] = nil end diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index 31c8856437..a7bd3622ee 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -19,7 +19,7 @@ module ActionController #:nodoc: # # 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 @@ -44,7 +44,7 @@ module ActionController #:nodoc: # 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 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 ca7ba90c40..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' @@ -205,9 +18,10 @@ module ActionController end def assign_parameters(routes, controller_path, action, parameters = {}) - parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action) - extra_keys = routes.extra_keys(parameters) + parameters = parameters.symbolize_keys + extra_keys = routes.extra_keys(parameters.merge(:controller => controller_path, :action => action)) non_path_parameters = get? ? query_parameters : request_parameters + parameters.each do |key, value| if value.is_a?(Array) && (value.frozen? || value.any?(&:frozen?)) value = value.map{ |v| v.duplicable? ? v.dup : v } @@ -217,7 +31,7 @@ module ActionController value = value.dup end - if extra_keys.include?(key) + if extra_keys.include?(key) || key == :action || key == :controller non_path_parameters[key] = value else if value.is_a?(Array) @@ -230,19 +44,16 @@ module ActionController end end + path_parameters[:controller] = controller_path + path_parameters[:action] = action + # Clear the combined params hash in case it was already referenced. @env.delete("action_dispatch.request.parameters") # Clear the filter cache variables so they're not stale @filtered_parameters = @filtered_env = @filtered_path = nil - params = self.request_parameters.dup - %w(controller action only_path).each do |k| - params.delete(k) - params.delete(k.to_sym) - end - data = params.to_query - + data = request_parameters.to_query @env['CONTENT_LENGTH'] = data.length.to_s @env['rack.input'] = StringIO.new(data) end @@ -669,12 +480,10 @@ module ActionController @controller.request = @request @controller.response = @response - build_request_uri(action, parameters) - - name = @request.parameters[:action] + build_request_uri(controller_class_name, action, parameters) @controller.recycle! - @controller.process(name) + @controller.process(action) if cookies = @request.env['action_dispatch.cookies'] unless @response.committed? @@ -790,10 +599,11 @@ module ActionController end end - def build_request_uri(action, parameters) + def build_request_uri(controller_class_name, action, parameters) unless @request.env["PATH_INFO"] options = @controller.respond_to?(:url_options) ? @controller.__send__(:url_options).merge(parameters) : parameters options.update( + :controller => controller_class_name, :action => action, :relative_url_root => nil, :_recall => @request.path_parameters) diff --git a/actionpack/lib/action_dispatch/http/mime_types.rb b/actionpack/lib/action_dispatch/http/mime_types.rb index 0e4da36038..01a10c693b 100644 --- a/actionpack/lib/action_dispatch/http/mime_types.rb +++ b/actionpack/lib/action_dispatch/http/mime_types.rb @@ -27,7 +27,7 @@ Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form # http://www.ietf.org/rfc/rfc4627.txt # http://www.json.org/JSONRequest.html -Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest ) +Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest application/vnd.api+json ) Mime::Type.register "application/pdf", :pdf, [], %w(pdf) Mime::Type.register "application/zip", :zip, [], %w(zip) diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index a1f84e5ace..fe83c01562 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! @@ -140,47 +141,11 @@ module ActionDispatch HTTP_METHOD_LOOKUP[method] end - # Is this a GET (or HEAD) request? - # Equivalent to <tt>request.request_method_symbol == :get</tt>. - def get? - HTTP_METHOD_LOOKUP[request_method] == :get - end - - # Is this a POST request? - # Equivalent to <tt>request.request_method_symbol == :post</tt>. - def post? - HTTP_METHOD_LOOKUP[request_method] == :post - end - - # Is this a PATCH request? - # Equivalent to <tt>request.request_method == :patch</tt>. - def patch? - HTTP_METHOD_LOOKUP[request_method] == :patch - end - - # Is this a PUT request? - # Equivalent to <tt>request.request_method_symbol == :put</tt>. - def put? - HTTP_METHOD_LOOKUP[request_method] == :put - end - - # Is this a DELETE request? - # Equivalent to <tt>request.request_method_symbol == :delete</tt>. - def delete? - HTTP_METHOD_LOOKUP[request_method] == :delete - end - - # Is this a HEAD request? - # Equivalent to <tt>request.request_method_symbol == :head</tt>. - def head? - HTTP_METHOD_LOOKUP[request_method] == :head - end - # Provides access to the request's HTTP headers, for example: # # 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 +199,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 +219,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 +259,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/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/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 c47e5d5245..f20f6ca865 100644 --- a/actionpack/lib/action_dispatch/middleware/static.rb +++ b/actionpack/lib/action_dispatch/middleware/static.rb @@ -13,11 +13,12 @@ module ActionDispatch # 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. class FileHandler - def initialize(root, cache_control) + def initialize(root, cache_control, index) @root = root.chomp('/') @compiled_root = /^#{Regexp.escape(root)}/ headers = cache_control && { 'Cache-Control' => cache_control } @file_server = ::Rack::File.new(@root, headers) + @index = index end @@ -26,13 +27,13 @@ module ActionDispatch # 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) + # in the server's `public/` directory (see Static#call). 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}"] + paths = [path, "#{path}#{ext}", "#{path}/#{@index}#{ext}"] if match = paths.detect { |p| path = File.join(@root, p.force_encoding('UTF-8')) @@ -104,9 +105,9 @@ module ActionDispatch # produce a directory traversal using this middleware. Only 'GET' and 'HEAD' # requests will result in a file being returned. class Static - def initialize(app, path, cache_control=nil) + def initialize(app, path, cache_control=nil, index="index") @app = app - @file_handler = FileHandler.new(path, cache_control) + @file_handler = FileHandler.new(path, cache_control, index) end def call(env) diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index b1bd6ae6d5..f953429e96 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -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/abstract_unit.rb b/actionpack/test/abstract_unit.rb index c1be2c9afe..1690bdd542 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -380,7 +380,7 @@ module RoutingTestHelpers end class ResourcesController < ActionController::Base - def index() render :nothing => true end + def index() head :ok end alias_method :show, :index end diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index 21586e2193..cbf333c772 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -123,7 +123,7 @@ end module Admin class InnerModuleController < ActionController::Base def index - render :nothing => true + head :ok end def redirect_to_index diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb index f7ad8e5158..3240185414 100644 --- a/actionpack/test/controller/base_test.rb +++ b/actionpack/test/controller/base_test.rb @@ -13,7 +13,7 @@ end class NonEmptyController < ActionController::Base def public_action - render :nothing => true + head :ok end end @@ -29,7 +29,7 @@ end class OptionalDefaultUrlOptionsController < ActionController::Base def show - render nothing: true + head :ok end def default_url_options diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb index 0ff0a1ef61..60a000c160 100644 --- a/actionpack/test/controller/flash_test.rb +++ b/actionpack/test/controller/flash_test.rb @@ -57,7 +57,7 @@ class FlashTest < ActionController::TestCase def std_action @flash_copy = {}.update(flash) - render :nothing => true + head :ok end def filter_halting_action diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb index 03a4ad7823..4ae1b20d43 100644 --- a/actionpack/test/controller/log_subscriber_test.rb +++ b/actionpack/test/controller/log_subscriber_test.rb @@ -19,7 +19,7 @@ module Another end def show - render :nothing => true + head :ok end def redirector @@ -170,7 +170,7 @@ class ACLogSubscriberTest < ActionController::TestCase def test_process_action_with_view_runtime get :show wait - assert_match(/\(Views: [\d.]+ms\)/, logs[1]) + assert_match /Completed 200 OK in [\d]ms/, logs[1] end def test_append_info_to_payload_is_called_even_with_exception diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 79e2104789..cabacad940 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -120,6 +120,10 @@ class TestController < ActionController::Base render :action => 'hello_world' end + def respond_with_empty_body + render nothing: true + end + def conditional_hello_with_bangs render :action => 'hello_world' end @@ -270,6 +274,12 @@ class ExpiresInRenderTest < ActionController::TestCase assert_match(/no-transform/, @response.headers["Cache-Control"]) end + def test_render_nothing_deprecated + assert_deprecated do + get :respond_with_empty_body + end + end + def test_date_header_when_expires_in time = Time.mktime(2011,10,30) Time.stubs(:now).returns(time) diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index f8cf79a257..82c808754c 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -72,17 +72,17 @@ class RequestForgeryProtectionControllerUsingNullSession < ActionController::Bas def signed cookies.signed[:foo] = 'bar' - render :nothing => true + head :ok end def encrypted cookies.encrypted[:foo] = 'bar' - render :nothing => true + head :ok end def try_to_reset_session reset_session - render :nothing => true + head :ok end end diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb index e348749f78..fbfc891d19 100644 --- a/actionpack/test/controller/test_case_test.rb +++ b/actionpack/test/controller/test_case_test.rb @@ -48,6 +48,14 @@ class TestCaseTest < ActionController::TestCase render text: params.inspect end + def test_query_parameters + render text: request.query_parameters.inspect + end + + def test_request_parameters + render text: request.request_parameters.inspect + end + def test_uri render text: request.fullpath end @@ -129,13 +137,13 @@ XML def delete_cookie cookies.delete("foo") - render nothing: true + head :ok end def test_assigns @foo = "foo" @foo_hash = { foo: :bar } - render nothing: true + head :ok end def test_without_body @@ -169,7 +177,7 @@ XML class ViewAssignsController < ActionController::Base def test_assigns @foo = "foo" - render nothing: true + head :ok end def view_assigns @@ -547,6 +555,18 @@ XML ) end + def test_query_param_named_action + get :test_query_parameters, params: {action: 'foobar'} + parsed_params = eval(@response.body) + assert_equal({action: 'foobar'}, parsed_params) + end + + def test_request_param_named_action + post :test_request_parameters, params: {action: 'foobar'} + parsed_params = eval(@response.body) + assert_equal({'action' => 'foobar'}, parsed_params) + end + def test_kwarg_params_passing_with_session_and_flash get :test_params, params: { page: { @@ -908,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/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/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb index 93e5c85a97..e729cc44f9 100644 --- a/actionpack/test/dispatch/static_test.rb +++ b/actionpack/test/dispatch/static_test.rb @@ -57,6 +57,7 @@ module StaticTests def test_serves_static_index_file_in_directory assert_html "/foo/index.html", get("/foo/index.html") + assert_html "/foo/index.html", get("/foo/index") assert_html "/foo/index.html", get("/foo/") assert_html "/foo/index.html", get("/foo") end @@ -260,6 +261,19 @@ class StaticTest < ActiveSupport::TestCase } assert_equal(DummyApp.call(nil), @app.call(env)) end + + def test_non_default_static_index + @app = ActionDispatch::Static.new(DummyApp, @root, "public, max-age=60", "other-index") + assert_html "/other-index.html", get("/other-index.html") + assert_html "/other-index.html", get("/other-index") + assert_html "/other-index.html", get("/") + assert_html "/other-index.html", get("") + assert_html "/foo/other-index.html", get("/foo/other-index.html") + assert_html "/foo/other-index.html", get("/foo/other-index") + assert_html "/foo/other-index.html", get("/foo/") + assert_html "/foo/other-index.html", get("/foo") + end + end class StaticEncodingTest < StaticTest diff --git a/actionpack/test/fixtures/public/foo/other-index.html b/actionpack/test/fixtures/public/foo/other-index.html new file mode 100644 index 0000000000..51c90c26ea --- /dev/null +++ b/actionpack/test/fixtures/public/foo/other-index.html @@ -0,0 +1 @@ +/foo/other-index.html
\ No newline at end of file diff --git a/actionpack/test/fixtures/public/other-index.html b/actionpack/test/fixtures/public/other-index.html new file mode 100644 index 0000000000..0820dfcb6e --- /dev/null +++ b/actionpack/test/fixtures/public/other-index.html @@ -0,0 +1 @@ +/other-index.html
\ No newline at end of file diff --git a/actionpack/test/fixtures/公共/foo/other-index.html b/actionpack/test/fixtures/公共/foo/other-index.html new file mode 100644 index 0000000000..51c90c26ea --- /dev/null +++ b/actionpack/test/fixtures/公共/foo/other-index.html @@ -0,0 +1 @@ +/foo/other-index.html
\ No newline at end of file diff --git a/actionpack/test/fixtures/公共/other-index.html b/actionpack/test/fixtures/公共/other-index.html new file mode 100644 index 0000000000..0820dfcb6e --- /dev/null +++ b/actionpack/test/fixtures/公共/other-index.html @@ -0,0 +1 @@ +/other-index.html
\ No newline at end of file diff --git a/actionpack/test/journey/route_test.rb b/actionpack/test/journey/route_test.rb index 9616f036b3..0980df06e8 100644 --- a/actionpack/test/journey/route_test.rb +++ b/actionpack/test/journey/route_test.rb @@ -30,7 +30,7 @@ module ActionDispatch path = Path::Pattern.new strexp defaults = { name: 'tender' } route = Route.new('name', nil, path, nil, defaults) - assert_equal /love/, route.requirements[:name] + assert_equal(/love/, route.requirements[:name]) end def test_ip_address 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 |