diff options
Diffstat (limited to 'actionpack/lib/action_controller')
27 files changed, 265 insertions, 183 deletions
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 98bfe72fef..3b82231b15 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -116,12 +116,12 @@ module ActionController # # Title: <%= @post.title %> # - # You don't have to rely on the automated rendering. For example, actions that could result in the rendering of different templates + # You don't have to rely on the automated rendering. For example, actions that could result in the rendering of different templates # will use the manual rendering methods: # # def search # @results = Search.find(params[:query]) - # case @results + # case @results.count # when 0 then render :action => "no_results" # when 1 then render :action => "show" # when 2..10 then render :action => "show_many" @@ -133,7 +133,7 @@ module ActionController # == Redirects # # Redirects are used to move from one action to another. For example, after a <tt>create</tt> action, which stores a blog entry to the - # database, we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're + # database, we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're # going to reuse (and redirect to) a <tt>show</tt> action that we'll assume has already been created. The code might look like this: # # def create @@ -228,8 +228,11 @@ module ActionController include mod end - # Rails 2.x compatibility - include ActionController::Compatibility + # Define some internal variables that should not be propagated to the view. + self.protected_instance_variables = [ + :@_status, :@_headers, :@_params, :@_env, :@_response, :@_request, + :@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout + ] ActiveSupport.run_load_hooks(:action_controller, self) end diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb index f988de39dd..e76a79f710 100644 --- a/actionpack/lib/action_controller/caching/actions.rb +++ b/actionpack/lib/action_controller/caching/actions.rb @@ -56,14 +56,14 @@ module ActionController #:nodoc: # # caches_page :public # - # caches_action :index, :if => proc do + # caches_action :index, :if => Proc.new do # !request.format.json? # cache if is not a JSON request # end # # caches_action :show, :cache_path => { :project => 1 }, # :expires_in => 1.hour # - # caches_action :feed, :cache_path => proc do + # caches_action :feed, :cache_path => Proc.new do # if params[:user_id] # user_list_url(params[:user_id, params[:id]) # else @@ -116,9 +116,8 @@ module ActionController #:nodoc: def expire_action(options = {}) return unless cache_configured? - actions = options[:action] - if actions.is_a?(Array) - actions.each {|action| expire_action(options.merge(:action => action)) } + if options.is_a?(Hash) && options[:action].is_a?(Array) + options[:action].each {|action| expire_action(options.merge(:action => action)) } else expire_fragment(ActionCachePath.new(self, options, false).path) end diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb index 957bb7de6b..159f718029 100644 --- a/actionpack/lib/action_controller/caching/pages.rb +++ b/actionpack/lib/action_controller/caching/pages.rb @@ -16,7 +16,7 @@ module ActionController #:nodoc: # caches_page :show, :new # end # - # This will generate cache files such as <tt>weblog/show/5.html</tt> and <tt>weblog/new.html</tt>, which match the URLs used + # This will generate cache files such as <tt>weblog/show/5.html</tt> and <tt>weblog/new.html</tt>, which match the URLs used # that would normally trigger dynamic page generation. Page caching works by configuring a web server to first check for the # existence of files on disk, and to serve them directly when found, without passing the request through to Action Pack. # This is much faster than handling the full dynamic request in the usual way. @@ -38,23 +38,25 @@ module ActionController #:nodoc: extend ActiveSupport::Concern included do - ## - # :singleton-method: # The cache directory should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>. # For Rails, this directory has already been set to Rails.public_path (which is usually set to <tt>Rails.root + "/public"</tt>). Changing # this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your # web server to look in the new location for cached files. - config_accessor :page_cache_directory + class_attribute :page_cache_directory self.page_cache_directory ||= '' - ## - # :singleton-method: # Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>. In these cases, the page caching mechanism will add one in # order to make it easy for the cached files to be picked up properly by the web server. By default, this cache extension is <tt>.html</tt>. # If you want something else, like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension. In cases where a request already has an # extension, such as <tt>.xml</tt> or <tt>.rss</tt>, page caching will not add an extension. This allows it to work well with RESTful apps. - config_accessor :page_cache_extension + class_attribute :page_cache_extension self.page_cache_extension ||= '.html' + + # The compression used for gzip. If false (default), the page is not compressed. + # If can be a symbol showing the ZLib compression method, for example, :best_compression + # or :best_speed or an integer configuring the compression level. + class_attribute :page_cache_compression + self.page_cache_compression ||= false end module ClassMethods @@ -66,24 +68,31 @@ module ActionController #:nodoc: instrument_page_cache :expire_page, path do File.delete(path) if File.exist?(path) + File.delete(path + '.gz') if File.exist?(path + '.gz') end end # Manually cache the +content+ in the key determined by +path+. Example: # cache_page "I'm the cached content", "/lists/show" - def cache_page(content, path, extension = nil) + def cache_page(content, path, extension = nil, gzip = Zlib::BEST_COMPRESSION) return unless perform_caching path = page_cache_path(path, extension) instrument_page_cache :write_page, path do FileUtils.makedirs(File.dirname(path)) File.open(path, "wb+") { |f| f.write(content) } + if gzip + Zlib::GzipWriter.open(path + '.gz', gzip) { |f| f.write(content) } + end end end - # Caches the +actions+ using the page-caching approach that'll store the cache in a path within the page_cache_directory that + # Caches the +actions+ using the page-caching approach that'll store + # the cache in a path within the page_cache_directory that # matches the triggering url. # + # You can also pass a :gzip option to override the class configuration one. + # # Usage: # # # cache the index action @@ -91,10 +100,28 @@ module ActionController #:nodoc: # # # cache the index action except for JSON requests # caches_page :index, :if => Proc.new { |c| !c.request.format.json? } + # + # # don't gzip images + # caches_page :image, :gzip => false def caches_page(*actions) return unless perform_caching options = actions.extract_options! - after_filter({:only => actions}.merge(options)) { |c| c.cache_page } + + gzip_level = options.fetch(:gzip, page_cache_compression) + gzip_level = case gzip_level + when Symbol + Zlib.const_get(gzip_level.to_s.upcase) + when Fixnum + gzip_level + when false + nil + else + Zlib::BEST_COMPRESSION + end + + after_filter({:only => actions}.merge(options)) do |c| + c.cache_page(nil, nil, gzip_level) + end end private @@ -136,7 +163,7 @@ module ActionController #:nodoc: # Manually cache the +content+ in the key determined by +options+. If no content is provided, the contents of response.body is used. # If no options are provided, the url of the current request being handled is used. Example: # cache_page "I'm the cached content", :controller => "lists", :action => "show" - def cache_page(content = nil, options = nil) + def cache_page(content = nil, options = nil, gzip = Zlib::BEST_COMPRESSION) return unless self.class.perform_caching && caching_allowed? path = case options @@ -152,7 +179,7 @@ module ActionController #:nodoc: extension = ".#{type_symbol}" end - self.class.cache_page(content || response.body, path, extension) + self.class.cache_page(content || response.body, path, extension, gzip) end end diff --git a/actionpack/lib/action_controller/deprecated.rb b/actionpack/lib/action_controller/deprecated.rb index aa0cfc9395..2405bebb97 100644 --- a/actionpack/lib/action_controller/deprecated.rb +++ b/actionpack/lib/action_controller/deprecated.rb @@ -1,3 +1,7 @@ ActionController::AbstractRequest = ActionController::Request = ActionDispatch::Request ActionController::AbstractResponse = ActionController::Response = ActionDispatch::Response -ActionController::Routing = ActionDispatch::Routing
\ No newline at end of file +ActionController::Routing = ActionDispatch::Routing + +ActiveSupport::Deprecation.warn 'ActionController::AbstractRequest and ActionController::Request are deprecated and will be removed, use ActionDispatch::Request instead.' +ActiveSupport::Deprecation.warn 'ActionController::AbstractResponse and ActionController::Response are deprecated and will be removed, use ActionDispatch::Response instead.' +ActiveSupport::Deprecation.warn 'ActionController::Routing is deprecated and will be removed, use ActionDispatch::Routing instead.'
\ No newline at end of file diff --git a/actionpack/lib/action_controller/deprecated/integration_test.rb b/actionpack/lib/action_controller/deprecated/integration_test.rb index 86336b6bc4..54eae48f47 100644 --- a/actionpack/lib/action_controller/deprecated/integration_test.rb +++ b/actionpack/lib/action_controller/deprecated/integration_test.rb @@ -1,2 +1,5 @@ ActionController::Integration = ActionDispatch::Integration ActionController::IntegrationTest = ActionDispatch::IntegrationTest + +ActiveSupport::Deprecation.warn 'ActionController::Integration is deprecated and will be removed, use ActionDispatch::Integration instead.' +ActiveSupport::Deprecation.warn 'ActionController::IntegrationTest is deprecated and will be removed, use ActionDispatch::IntegrationTest instead.' diff --git a/actionpack/lib/action_controller/deprecated/performance_test.rb b/actionpack/lib/action_controller/deprecated/performance_test.rb index fcf47d31a7..c7ba5a2fe7 100644 --- a/actionpack/lib/action_controller/deprecated/performance_test.rb +++ b/actionpack/lib/action_controller/deprecated/performance_test.rb @@ -1 +1,3 @@ ActionController::PerformanceTest = ActionDispatch::PerformanceTest + +ActiveSupport::Deprecation.warn 'ActionController::PerformanceTest is deprecated and will be removed, use ActionDispatch::PerformanceTest instead.' diff --git a/actionpack/lib/action_controller/log_subscriber.rb b/actionpack/lib/action_controller/log_subscriber.rb index 35e29398e6..4c76f4c43b 100644 --- a/actionpack/lib/action_controller/log_subscriber.rb +++ b/actionpack/lib/action_controller/log_subscriber.rb @@ -20,15 +20,18 @@ module ActionController status = payload[:status] if status.nil? && payload[:exception].present? - status = Rack::Utils.status_code(ActionDispatch::ShowExceptions.rescue_responses[payload[:exception].first]) rescue nil + status = Rack::Utils.status_code(ActionDispatch::ExceptionWrapper.new({}, payload[:exception]).status_code) end message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in %.0fms" % event.duration message << " (#{additions.join(" | ")})" unless additions.blank? - message << "\n" info(message) end + def halted_callback(event) + info "Filter chain halted as #{event.payload[:filter]} rendered or redirected" + end + def send_file(event) message = "Sent file %s" message << " (%.1fms)" diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index 125dbf6bb5..92433ab462 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -181,15 +181,13 @@ module ActionController @_status = Rack::Utils.status_code(status) end - def response_body=(val) - body = if val.is_a?(String) - [val] - elsif val.nil? || val.respond_to?(:each) - val - else - [val] - end - super body + def response_body=(body) + body = [body] unless body.nil? || body.respond_to?(:each) + super + end + + def performed? + !!response_body end def dispatch(name, request) #:nodoc: diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb deleted file mode 100644 index 05dca445a4..0000000000 --- a/actionpack/lib/action_controller/metal/compatibility.rb +++ /dev/null @@ -1,58 +0,0 @@ -module ActionController - module Compatibility - extend ActiveSupport::Concern - - class ::ActionController::ActionControllerError < StandardError #:nodoc: - end - - # Temporary hax - included do - ::ActionController::UnknownAction = ::AbstractController::ActionNotFound - ::ActionController::DoubleRenderError = ::AbstractController::DoubleRenderError - - # ROUTES TODO: This should be handled by a middleware and route generation - # should be able to handle SCRIPT_NAME - self.config.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT'] - - class << self - delegate :default_charset=, :to => "ActionDispatch::Response" - end - - self.protected_instance_variables = %w( - @_status @_headers @_params @_env @_response @_request - @_view_runtime @_stream @_url_options @_action_has_layout - ) - - def rescue_action(env) - raise env["action_dispatch.rescue.exception"] - end - end - - # For old tests - def initialize_template_class(*) end - def assign_shortcuts(*) end - - def _normalize_options(options) - options[:text] = nil if options.delete(:nothing) == true - options[:text] = " " if options.key?(:text) && options[:text].nil? - super - end - - def render_to_body(options) - options[:template].sub!(/^\//, '') if options.key?(:template) - super || " " - end - - def _handle_method_missing - method_missing(@_action_name.to_sym) - end - - def method_for_action(action_name) - super || (respond_to?(:method_missing) && "_handle_method_missing") - end - - def performed? - response_body - end - end -end diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb index a5e37172c9..1645400693 100644 --- a/actionpack/lib/action_controller/metal/conditional_get.rb +++ b/actionpack/lib/action_controller/metal/conditional_get.rb @@ -23,8 +23,27 @@ module ActionController # This will render the show template if the request isn't sending a matching etag or # If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match. # - def fresh_when(options) - options.assert_valid_keys(:etag, :last_modified, :public) + # You can also just pass a record where last_modified will be set by calling updated_at and the etag by passing the object itself. Example: + # + # def show + # @article = Article.find(params[:id]) + # fresh_when(@article) + # end + # + # When passing a record, you can still set whether the public header: + # + # def show + # @article = Article.find(params[:id]) + # fresh_when(@article, :public => true) + # end + def fresh_when(record_or_options, additional_options = {}) + if record_or_options.is_a? Hash + options = record_or_options + options.assert_valid_keys(:etag, :last_modified, :public) + else + record = record_or_options + options = { :etag => record, :last_modified => record.try(:updated_at) }.merge(additional_options) + end response.etag = options[:etag] if options[:etag] response.last_modified = options[:last_modified] if options[:last_modified] @@ -55,8 +74,34 @@ module ActionController # end # end # end - def stale?(options) - fresh_when(options) + # + # You can also just pass a record where last_modified will be set by calling updated_at and the etag by passing the object itself. Example: + # + # def show + # @article = Article.find(params[:id]) + # + # if stale?(@article) + # @statistics = @article.really_expensive_call + # respond_to do |format| + # # all the supported formats + # end + # end + # end + # + # When passing a record, you can still set whether the public header: + # + # def show + # @article = Article.find(params[:id]) + # + # if stale?(@article, :public => true) + # @statistics = @article.really_expensive_call + # respond_to do |format| + # # all the supported formats + # end + # end + # end + def stale?(record_or_options, additional_options = {}) + fresh_when(record_or_options, additional_options) !request.fresh?(response) end diff --git a/actionpack/lib/action_controller/metal/data_streaming.rb b/actionpack/lib/action_controller/metal/data_streaming.rb index 0670a58d97..30ddf6c16e 100644 --- a/actionpack/lib/action_controller/metal/data_streaming.rb +++ b/actionpack/lib/action_controller/metal/data_streaming.rb @@ -1,4 +1,3 @@ -require 'active_support/core_ext/file/path' require 'action_controller/metal/exceptions' module ActionController #:nodoc: @@ -115,7 +114,7 @@ module ActionController #:nodoc: private def send_file_headers!(options) type_provided = options.has_key?(:type) - + options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options)) [:type, :disposition].each do |arg| raise ArgumentError, ":#{arg} option required" if options[arg].nil? diff --git a/actionpack/lib/action_controller/metal/exceptions.rb b/actionpack/lib/action_controller/metal/exceptions.rb index 07024d0a9a..ece9ba3725 100644 --- a/actionpack/lib/action_controller/metal/exceptions.rb +++ b/actionpack/lib/action_controller/metal/exceptions.rb @@ -43,4 +43,4 @@ module ActionController class UnknownHttpMethod < ActionControllerError #:nodoc: end -end
\ No newline at end of file +end diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb index 0fd42f9d8a..b45f211e83 100644 --- a/actionpack/lib/action_controller/metal/force_ssl.rb +++ b/actionpack/lib/action_controller/metal/force_ssl.rb @@ -29,6 +29,7 @@ module ActionController if !request.ssl? && !Rails.env.development? redirect_options = {:protocol => 'https://', :status => :moved_permanently} redirect_options.merge!(:host => host) if host + flash.keep redirect_to redirect_options end end diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb index bd515bba82..d070eaae5d 100644 --- a/actionpack/lib/action_controller/metal/helpers.rb +++ b/actionpack/lib/action_controller/metal/helpers.rb @@ -1,4 +1,3 @@ -require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/class/attribute' module ActionController @@ -56,7 +55,7 @@ module ActionController include AbstractController::Helpers included do - config_accessor :helpers_path, :include_all_helpers + class_attribute :helpers_path, :include_all_helpers self.helpers_path ||= [] self.include_all_helpers = true end @@ -94,7 +93,7 @@ module ActionController def all_helpers_from_path(path) helpers = [] - Array.wrap(path).each do |_path| + Array(path).each do |_path| extract = /^#{Regexp.quote(_path.to_s)}\/?(.*)_helper.rb$/ helpers += Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1') } end diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index 264806cd36..4972c6bede 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -1,4 +1,4 @@ -require 'active_support/base64' +require 'base64' require 'active_support/core_ext/object/blank' module ActionController @@ -141,11 +141,11 @@ module ActionController end def decode_credentials(request) - ActiveSupport::Base64.decode64(request.authorization.split(' ', 2).last || '') + ::Base64.decode64(request.authorization.split(' ', 2).last || '') end def encode_credentials(user_name, password) - "Basic #{ActiveSupport::Base64.encode64s("#{user_name}:#{password}")}" + "Basic #{::Base64.strict_encode64("#{user_name}:#{password}")}" end def authentication_request(controller, realm) @@ -192,12 +192,15 @@ module ActionController return false unless password method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD'] - uri = credentials[:uri][0,1] == '/' ? request.fullpath : request.url + uri = credentials[:uri][0,1] == '/' ? request.original_fullpath : request.original_url - [true, false].any? do |password_is_ha1| - expected = expected_response(method, uri, credentials, password, password_is_ha1) - expected == credentials[:response] - end + [true, false].any? do |trailing_question_mark| + [true, false].any? do |password_is_ha1| + _uri = trailing_question_mark ? uri + "?" : uri + expected = expected_response(method, _uri, credentials, password, password_is_ha1) + expected == credentials[:response] + end + end end end @@ -286,7 +289,7 @@ module ActionController t = time.to_i hashed = [t, secret_key] digest = ::Digest::MD5.hexdigest(hashed.join(":")) - ActiveSupport::Base64.encode64("#{t}:#{digest}").gsub("\n", '') + ::Base64.strict_encode64("#{t}:#{digest}") end # Might want a shorter timeout depending on whether the request @@ -295,7 +298,7 @@ module ActionController # allow a user to use new nonce without prompting user again for their # username and password. def validate_nonce(secret_key, request, value, seconds_to_timeout=5*60) - t = ActiveSupport::Base64.decode64(value).split(":").first.to_i + t = ::Base64.decode64(value).split(":").first.to_i nonce(secret_key, t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout end diff --git a/actionpack/lib/action_controller/metal/implicit_render.rb b/actionpack/lib/action_controller/metal/implicit_render.rb index e8e465d3ba..ae04b53825 100644 --- a/actionpack/lib/action_controller/metal/implicit_render.rb +++ b/actionpack/lib/action_controller/metal/implicit_render.rb @@ -2,7 +2,7 @@ module ActionController module ImplicitRender def send_action(method, *args) ret = super - default_render unless response_body + default_render unless performed? ret end diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb index 777a0ab343..640ebf5f00 100644 --- a/actionpack/lib/action_controller/metal/instrumentation.rb +++ b/actionpack/lib/action_controller/metal/instrumentation.rb @@ -64,7 +64,12 @@ module ActionController end end - protected + private + + # A hook invoked everytime a before callback is halted. + def halted_callback_hook(filter) + ActiveSupport::Notifications.instrument("halted_callback.action_controller", :filter => filter) + end # A hook which allows you to clean up any time taken into account in # views wrongly, like database querying time. diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index e0d8e1c992..fa760f2658 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -1,7 +1,6 @@ require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/hash/except' -require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/module/anonymous' require 'action_dispatch/http/mime_types' @@ -43,6 +42,11 @@ module ActionController # wrap_parameters :person, :include => [:username, :password] # end # + # On ActiveRecord models with no +:include+ or +:exclude+ option set, + # if attr_accessible is set on that model, it will only wrap the accessible + # parameters, else it will only wrap the parameters returned by the class + # method attribute_names. + # # If you're going to pass the parameters to an +ActiveModel+ object (such as # +User.new(params[:user])+), you might consider passing the model class to # the method instead. The +ParamsWrapper+ will actually try to determine the @@ -141,7 +145,7 @@ module ActionController # try to find Foo::Bar::User, Foo::User and finally User. def _default_wrap_model #:nodoc: return nil if self.anonymous? - model_name = self.name.sub(/Controller$/, '').singularize + model_name = self.name.sub(/Controller$/, '').classify begin if model_klass = model_name.safe_constantize @@ -162,7 +166,9 @@ module ActionController unless options[:include] || options[:exclude] model ||= _default_wrap_model - if model.respond_to?(:attribute_names) && model.attribute_names.present? + if model.respond_to?(:accessible_attributes) && model.accessible_attributes.present? + options[:include] = model.accessible_attributes.to_a + elsif model.respond_to?(:attribute_names) && model.attribute_names.present? options[:include] = model.attribute_names end end @@ -173,9 +179,9 @@ module ActionController controller_name.singularize end - options[:include] = Array.wrap(options[:include]).collect(&:to_s) if options[:include] - options[:exclude] = Array.wrap(options[:exclude]).collect(&:to_s) if options[:exclude] - options[:format] = Array.wrap(options[:format]) + options[:include] = Array(options[:include]).collect(&:to_s) if options[:include] + options[:exclude] = Array(options[:exclude]).collect(&:to_s) if options[:exclude] + options[:format] = Array(options[:format]) self._wrapper_options = options end diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb index 0355c9f458..b07742e0e1 100644 --- a/actionpack/lib/action_controller/metal/redirecting.rb +++ b/actionpack/lib/action_controller/metal/redirecting.rb @@ -18,7 +18,7 @@ module ActionController # # * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+. # * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record. - # * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) - Is passed straight through as the target for redirection. + # * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) or a protocol relative reference (like <tt>//</tt>) - Is passed straight through as the target for redirection. # * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string. # * <tt>Proc</tt> - A block that will be executed in the controller's context. Should return any option accepted by +redirect_to+. # * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places. @@ -81,7 +81,8 @@ module ActionController # The scheme name consist of a letter followed by any combination of # letters, digits, and the plus ("+"), period ("."), or hyphen ("-") # characters; and is terminated by a colon (":"). - when %r{^\w[\w+.-]*:.*} + # The protocol relative scheme starts with a double slash "//" + when %r{^(\w[\w+.-]*:|//).*} options when String request.protocol + request.host_with_port + options diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb index 70fd79bb8b..a677cdf15d 100644 --- a/actionpack/lib/action_controller/metal/rendering.rb +++ b/actionpack/lib/action_controller/metal/rendering.rb @@ -29,6 +29,10 @@ module ActionController self.response_body = nil end + def render_to_body(*) + super || " " + end + private # Normalize arguments by catching blocks and setting them on :update. @@ -44,6 +48,10 @@ module ActionController options[:text] = options[:text].to_text end + if options.delete(:nothing) || (options.key?(:text) && options[:text].nil?) + options[:text] = " " + end + if options[:status] options[:status] = Rack::Utils.status_code(options[:status]) end diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index bc22e39efb..afa9243f02 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -74,7 +74,7 @@ module ActionController #:nodoc: # The actual before_filter that is used. Modify this to change how you handle unverified requests. def verify_authenticity_token unless verified_request? - logger.warn "WARNING: Can't verify CSRF token authenticity" if logger + logger.warn "Can't verify CSRF token authenticity" if logger handle_unverified_request end end diff --git a/actionpack/lib/action_controller/metal/rescue.rb b/actionpack/lib/action_controller/metal/rescue.rb index 736ff5b31c..68cc9a9c9b 100644 --- a/actionpack/lib/action_controller/metal/rescue.rb +++ b/actionpack/lib/action_controller/metal/rescue.rb @@ -1,13 +1,11 @@ module ActionController #:nodoc: + # This module is responsible to provide `rescue_from` helpers + # to controllers and configure when detailed exceptions must be + # shown. module Rescue extend ActiveSupport::Concern include ActiveSupport::Rescuable - included do - config_accessor :consider_all_requests_local - self.consider_all_requests_local = false if consider_all_requests_local.nil? - end - def rescue_with_handler(exception) if (exception.respond_to?(:original_exception) && (orig_exception = exception.original_exception) && @@ -17,15 +15,20 @@ module ActionController #:nodoc: super(exception) end + # Override this method if you want to customize when detailed + # exceptions must be shown. This method is only called when + # consider_all_requests_local is false. By default, it returns + # false, but someone may set it to `request.local?` so local + # requests in production still shows the detailed exception pages. def show_detailed_exceptions? - consider_all_requests_local || request.local? + false end private def process_action(*args) super rescue Exception => exception - request.env['action_dispatch.show_detailed_exceptions'] = show_detailed_exceptions? + request.env['action_dispatch.show_detailed_exceptions'] ||= show_detailed_exceptions? rescue_with_handler(exception) || raise(exception) end end diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb index 5fe5334458..e9783e6919 100644 --- a/actionpack/lib/action_controller/metal/streaming.rb +++ b/actionpack/lib/action_controller/metal/streaming.rb @@ -1,4 +1,3 @@ -require 'active_support/core_ext/file/path' require 'rack/chunked' module ActionController #:nodoc: @@ -195,7 +194,7 @@ module ActionController #:nodoc: # ==== Passenger # # To be described. - # + # module Streaming extend ActiveSupport::Concern @@ -217,7 +216,7 @@ module ActionController #:nodoc: end end - # Call render_to_body if we are streaming instead of usual +render+. + # Call render_body if we are streaming instead of usual +render+. def _render_template(options) #:nodoc: if options.delete(:stream) Rack::Chunked::Body.new view_renderer.render_body(view_context, options) diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb index de7b837ecc..a288e69649 100644 --- a/actionpack/lib/action_controller/railtie.rb +++ b/actionpack/lib/action_controller/railtie.rb @@ -14,16 +14,17 @@ module ActionController end initializer "action_controller.initialize_framework_caches" do - ActiveSupport.on_load(:action_controller) { self.cache_store ||= RAILS_CACHE } + ActiveSupport.on_load(:action_controller) { self.cache_store ||= Rails.cache } + end + + initializer "action_controller.assets_config", :group => :all do |app| + app.config.action_controller.assets_dir ||= app.config.paths["public"].first end initializer "action_controller.set_configs" do |app| paths = app.config.paths options = app.config.action_controller - options.consider_all_requests_local ||= app.config.consider_all_requests_local - - options.assets_dir ||= paths["public"].first options.javascripts_dir ||= paths["public/javascripts"].first options.stylesheets_dir ||= paths["public/stylesheets"].first options.page_cache_directory ||= paths["public"].first @@ -31,6 +32,7 @@ module ActionController # make sure readers methods get compiled options.asset_path ||= app.config.asset_path options.asset_host ||= app.config.asset_host + options.relative_url_root ||= app.config.relative_url_root ActiveSupport.on_load(:action_controller) do include app.routes.mounted_helpers diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 6913c1ef4a..fce6e29d5f 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -69,15 +69,12 @@ module ActionController # assert_template :partial => '_customer', :locals => { :customer => @customer } # def assert_template(options = {}, message = nil) - validate_request! - case options when NilClass, String, Symbol options = options.to_s if Symbol === options rendered = @templates - msg = build_message(message, - "expecting <?> but rendering with <?>", - options, rendered.keys.join(', ')) + msg = message || sprintf("expecting <%s> but rendering with <%s>", + options, rendered.keys) assert_block(msg) do if options rendered.any? { |t,num| t.match(options) } @@ -86,6 +83,20 @@ module ActionController end end when Hash + if expected_layout = options[:layout] + msg = message || sprintf("expecting layout <%s> but action rendered <%s>", + expected_layout, @layouts.keys) + + case expected_layout + when String + assert_includes @layouts.keys, expected_layout, msg + when Regexp + assert(@layouts.keys.any? {|l| l =~ expected_layout }, msg) + when nil + assert(@layouts.empty?, msg) + end + end + if expected_partial = options[:partial] if expected_locals = options[:locals] actual_locals = @locals[expected_partial.to_s.sub(/^_/,'')] @@ -94,28 +105,13 @@ module ActionController end elsif expected_count = options[:count] actual_count = @partials[expected_partial] - msg = build_message(message, - "expecting ? to be rendered ? time(s) but rendered ? time(s)", + 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) - elsif options.key?(:layout) - msg = build_message(message, - "expecting layout <?> but action rendered <?>", - expected_layout, @layouts.keys) - - case layout = options[:layout] - when String - assert(@layouts.include?(expected_layout), msg) - when Regexp - assert(@layouts.any? {|l| l =~ layout }, msg) - when nil - assert(@layouts.empty?, msg) - end else - msg = build_message(message, - "expecting partial <?> but action rendered <?>", + msg = message || sprintf("expecting partial <%s> but action rendered <%s>", options[:partial], @partials.keys) - assert(@partials.include?(expected_partial), msg) + assert_includes @partials, expected_partial, msg end else assert @partials.empty?, @@ -250,6 +246,13 @@ module ActionController # end # end # + # You can also send a real document in the simulated HTTP request. + # + # def test_create + # json = {:book => { :title => "Love Hina" }}.to_json + # post :create, json + # end + # # == Special instance variables # # ActionController::TestCase will also automatically provide the following instance @@ -296,11 +299,11 @@ module ActionController # assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave" # assert flash.empty? # makes sure that there's nothing in the flash # - # For historic reasons, the assigns hash uses string-based keys. So assigns[:person] won't work, but assigns["person"] will. To + # For historic reasons, the assigns hash uses string-based keys. So <tt>assigns[:person]</tt> won't work, but <tt>assigns["person"]</tt> will. To # appease our yearning for symbols, though, an alternative accessor has been devised using a method call instead of index referencing. - # So assigns(:person) will work just like assigns["person"], but again, assigns[:person] will not work. + # So <tt>assigns(:person)</tt> will work just like <tt>assigns["person"]</tt>, but again, <tt>assigns[:person]</tt> will not work. # - # On top of the collections, you have the complete url that a given action redirected to available in redirect_to_url. + # On top of the collections, you have the complete url that a given action redirected to available in <tt>redirect_to_url</tt>. # # For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another # action call which can then be asserted against. @@ -324,6 +327,12 @@ module ActionController # # assert_redirected_to page_url(:title => 'foo') class TestCase < ActiveSupport::TestCase + + # Use AS::TestCase for the base class when describing a model + register_spec_type(self) do |desc| + desc < ActionController::Base + end + module Behavior extend ActiveSupport::Concern include ActionDispatch::TestProcess @@ -374,28 +383,28 @@ module ActionController end # Executes a request simulating GET HTTP method and set/volley the response - def get(action, parameters = nil, session = nil, flash = nil) - process(action, parameters, session, flash, "GET") + def get(action, *args) + process(action, "GET", *args) end # Executes a request simulating POST HTTP method and set/volley the response - def post(action, parameters = nil, session = nil, flash = nil) - process(action, parameters, session, flash, "POST") + def post(action, *args) + process(action, "POST", *args) end # Executes a request simulating PUT HTTP method and set/volley the response - def put(action, parameters = nil, session = nil, flash = nil) - process(action, parameters, session, flash, "PUT") + def put(action, *args) + process(action, "PUT", *args) end # Executes a request simulating DELETE HTTP method and set/volley the response - def delete(action, parameters = nil, session = nil, flash = nil) - process(action, parameters, session, flash, "DELETE") + def delete(action, *args) + process(action, "DELETE", *args) end # Executes a request simulating HEAD HTTP method and set/volley the response def head(action, parameters = nil, session = nil, flash = nil) - process(action, parameters, session, flash, "HEAD") + process(action, "HEAD", parameters, session, flash) end def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil) @@ -421,19 +430,20 @@ module ActionController end end - def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET') + def process(action, http_method = 'GET', *args) + check_required_ivars + http_method, args = handle_old_process_api(http_method, args) + + if args.first.is_a?(String) && http_method != 'HEAD' + @request.env['RAW_POST_DATA'] = args.shift + end + + parameters, session, flash = args + # Ensure that numbers and symbols passed as params are converted to # proper params, as is the case when engaging rack. parameters = paramify_values(parameters) - # Sanity check for required instance variables so we can give an - # understandable error message. - %w(@routes @controller @request @response).each do |iv_name| - if !(instance_variable_names.include?(iv_name) || instance_variable_names.include?(iv_name.to_sym)) || instance_variable_get(iv_name).nil? - raise "#{iv_name} is nil: make sure you set it in your test's setup method." - end - end - @request.recycle! @response.recycle! @controller.response_body = nil @@ -494,6 +504,26 @@ module ActionController end private + def check_required_ivars + # Sanity check for required instance variables so we can give an + # understandable error message. + [:@routes, :@controller, :@request, :@response].each do |iv_name| + if !instance_variable_defined?(iv_name) || instance_variable_get(iv_name).nil? + raise "#{iv_name} is nil: make sure you set it in your test's setup method." + end + end + end + + def handle_old_process_api(http_method, args) + # 4.0: Remove this method. + if http_method.is_a?(Hash) + ActiveSupport::Deprecation.warn("TestCase#process now expects the HTTP method as second argument: process(action, http_method, params, session, flash)") + args.unshift(http_method) + http_method = args.last.is_a?(String) ? args.last : "GET" + end + + [http_method, args] + end def build_request_uri(action, parameters) unless @request.env["PATH_INFO"] diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb index af06bffa16..24ffc28710 100644 --- a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +++ b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb @@ -171,7 +171,7 @@ module HTML def contains_bad_protocols?(attr_name, value) uri_attributes.include?(attr_name) && - (value =~ /(^[^\/:]*):|(�*58)|(p)|(%|%)3A/ && !allowed_protocols.include?(value.split(protocol_separator).first.downcase)) + (value =~ /(^[^\/:]*):|(�*58)|(p)|(%|%)3A/ && !allowed_protocols.include?(value.split(protocol_separator).first.downcase.strip)) end end end diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb index c252e01cf5..8ac8d34430 100644 --- a/actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +++ b/actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb @@ -23,7 +23,7 @@ module HTML #:nodoc: # Create a new Tokenizer for the given text. def initialize(text) - text.encode! if text.encoding_aware? + text.encode! @scanner = StringScanner.new(text) @position = 0 @line = 0 |