diff options
author | wycats <wycats@gmail.com> | 2010-04-26 23:32:30 -0700 |
---|---|---|
committer | wycats <wycats@gmail.com> | 2010-04-26 23:32:30 -0700 |
commit | 91963e9e33eb5a28297323f1346aeb8b643e9d65 (patch) | |
tree | 0452dbaf63110fb5a19c958da50d4677dee11333 /actionpack/lib/action_controller | |
parent | d5d717161d853d8d7240da59b41a879bdac6e982 (diff) | |
parent | c1d73270717f30498f8f4d55d6695509107c2834 (diff) | |
download | rails-91963e9e33eb5a28297323f1346aeb8b643e9d65.tar.gz rails-91963e9e33eb5a28297323f1346aeb8b643e9d65.tar.bz2 rails-91963e9e33eb5a28297323f1346aeb8b643e9d65.zip |
Merge branch 'master' of github.com:rails/rails
Diffstat (limited to 'actionpack/lib/action_controller')
-rw-r--r-- | actionpack/lib/action_controller/base.rb | 5 | ||||
-rw-r--r-- | actionpack/lib/action_controller/caching.rb | 6 | ||||
-rw-r--r-- | actionpack/lib/action_controller/caching/pages.rb | 8 | ||||
-rw-r--r-- | actionpack/lib/action_controller/deprecated/base.rb | 48 | ||||
-rw-r--r-- | actionpack/lib/action_controller/metal/compatibility.rb | 4 | ||||
-rw-r--r-- | actionpack/lib/action_controller/metal/helpers.rb | 4 | ||||
-rw-r--r-- | actionpack/lib/action_controller/metal/renderers.rb | 4 | ||||
-rw-r--r-- | actionpack/lib/action_controller/metal/request_forgery_protection.rb | 118 | ||||
-rw-r--r-- | actionpack/lib/action_controller/railtie.rb | 65 | ||||
-rw-r--r-- | actionpack/lib/action_controller/test_case.rb | 260 |
10 files changed, 229 insertions, 293 deletions
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 2e94a20d9d..092fe98588 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -65,8 +65,11 @@ module ActionController @subclasses ||= [] end + # TODO Move this to the appropriate module + config_accessor :assets_dir, :asset_path, :javascripts_dir, :stylesheets_dir + ActiveSupport.run_load_hooks(:action_controller, self) end end -require "action_controller/deprecated/base" +require "action_controller/deprecated/base"
\ No newline at end of file diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index 0da0ca1893..4105f9e14f 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -63,12 +63,10 @@ module ActionController #:nodoc: included do extend ConfigMethods - delegate :perform_caching, :perform_caching=, :to => :config - singleton_class.delegate :perform_caching, :perform_caching=, :to => :config - self.perform_caching = true + config_accessor :perform_caching + self.perform_caching = true if perform_caching.nil? end - def caching_allowed? request.get? && response.status == 200 end diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb index 20df3a1a69..cefd1f48c0 100644 --- a/actionpack/lib/action_controller/caching/pages.rb +++ b/actionpack/lib/action_controller/caching/pages.rb @@ -44,8 +44,8 @@ module ActionController #:nodoc: # 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. - singleton_class.delegate :page_cache_directory, :page_cache_directory=, :to => :config - self.page_cache_directory = '' + config_accessor :page_cache_directory + self.page_cache_directory ||= '' ## # :singleton-method: @@ -53,8 +53,8 @@ module ActionController #:nodoc: # 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. - singleton_class.delegate :page_cache_extension, :page_cache_extension=, :to => :config - self.page_cache_extension = '.html' + config_accessor :page_cache_extension + self.page_cache_extension ||= '.html' end module ClassMethods diff --git a/actionpack/lib/action_controller/deprecated/base.rb b/actionpack/lib/action_controller/deprecated/base.rb index 57203ce95f..3975afcaf0 100644 --- a/actionpack/lib/action_controller/deprecated/base.rb +++ b/actionpack/lib/action_controller/deprecated/base.rb @@ -1,33 +1,16 @@ module ActionController class Base - class << self - def deprecated_config_accessor(option, message = nil) - deprecated_config_reader(option, message) - deprecated_config_writer(option, message) + # Deprecated methods. Wrap them in a module so they can be overwritten by plugins + # (like the verify method.) + module DeprecatedBehavior #:nodoc: + def relative_url_root + ActiveSupport::Deprecation.warn "ActionController::Base.relative_url_root is ineffective. " << + "Please stop using it.", caller end - def deprecated_config_reader(option, message = nil) - message ||= "Reading #{option} directly from ActionController::Base is deprecated. " \ - "Please read it from config.#{option}" - - self.class_eval <<-RUBY, __FILE__, __LINE__ + 1 - def #{option} - ActiveSupport::Deprecation.warn #{message.inspect}, caller - config.#{option} - end - RUBY - end - - def deprecated_config_writer(option, message = nil) - message ||= "Setting #{option} directly on ActionController::Base is deprecated. " \ - "Please set it on config.action_controller.#{option}" - - self.class_eval <<-RUBY, __FILE__, __LINE__ + 1 - def #{option}=(val) - ActiveSupport::Deprecation.warn #{message.inspect}, caller - config.#{option} = val - end - RUBY + def relative_url_root= + ActiveSupport::Deprecation.warn "ActionController::Base.relative_url_root= is ineffective. " << + "Please stop using it.", caller end def consider_all_requests_local @@ -125,9 +108,7 @@ module ActionController def use_accept_header=(val) use_accept_header end - end - module DeprecatedBehavior # This method has been moved to ActionDispatch::Request.filter_parameters def filter_parameter_logging(*args, &block) ActiveSupport::Deprecation.warn("Setting filter_parameter_logging in ActionController is deprecated and has no longer effect, please set 'config.filter_parameters' in config/application.rb instead", caller) @@ -146,17 +127,6 @@ module ActionController extend DeprecatedBehavior - deprecated_config_writer :session_options - deprecated_config_writer :session_store - - deprecated_config_accessor :assets_dir - deprecated_config_accessor :asset_path - deprecated_config_accessor :helpers_path - deprecated_config_accessor :javascripts_dir - deprecated_config_accessor :page_cache_directory - deprecated_config_accessor :relative_url_root, "relative_url_root is ineffective. Please stop using it" - deprecated_config_accessor :stylesheets_dir - delegate :consider_all_requests_local, :consider_all_requests_local=, :allow_concurrency, :allow_concurrency=, :to => :"self.class" end diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb index 02722360f1..d49465fa0b 100644 --- a/actionpack/lib/action_controller/metal/compatibility.rb +++ b/actionpack/lib/action_controller/metal/compatibility.rb @@ -21,8 +21,8 @@ module ActionController delegate :default_charset=, :to => "ActionDispatch::Response" end - # cattr_reader :protected_instance_variables - cattr_accessor :protected_instance_variables + # TODO: Update protected instance variables list + config_accessor :protected_instance_variables self.protected_instance_variables = %w(@assigns @performed_redirect @performed_render @variables_added @request_origin @url @parent_controller @action_name diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb index 82bedc3fad..1110d7c117 100644 --- a/actionpack/lib/action_controller/metal/helpers.rb +++ b/actionpack/lib/action_controller/metal/helpers.rb @@ -52,8 +52,8 @@ module ActionController include AbstractController::Helpers included do - class_attribute :helpers_path - self.helpers_path = [] + config_accessor :helpers_path + self.helpers_path ||= [] end module ClassMethods diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb index 47759aa2fe..0be07cd1fc 100644 --- a/actionpack/lib/action_controller/metal/renderers.rb +++ b/actionpack/lib/action_controller/metal/renderers.rb @@ -79,12 +79,12 @@ module ActionController add :js do |js, options| self.content_type ||= Mime::JS - self.response_body = js.respond_to?(:to_js) ? js.to_js : js + self.response_body = js.respond_to?(:to_js) ? js.to_js(options) : js end add :xml do |xml, options| self.content_type ||= Mime::XML - self.response_body = xml.respond_to?(:to_xml) ? xml.to_xml : xml + self.response_body = xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml end add :update do |proc, options| diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index 39a809657b..2ba0d6e5cd 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -4,6 +4,45 @@ module ActionController #:nodoc: class InvalidAuthenticityToken < ActionControllerError #:nodoc: end + # Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current + # web application, not a forged link from another site, is done by embedding a token based on a random + # string stored in the session (which an attacker wouldn't know) in all forms and Ajax requests generated + # by Rails and then verifying the authenticity of that token in the controller. Only HTML/JavaScript + # requests are checked, so this will not protect your XML API (presumably you'll have a different + # authentication scheme there anyway). Also, GET requests are not protected as these should be + # idempotent anyway. + # + # This is turned on with the <tt>protect_from_forgery</tt> method, which will check the token and raise an + # ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the + # error message in production by editing public/422.html. A call to this method in ApplicationController is + # generated by default in post-Rails 2.0 applications. + # + # The token parameter is named <tt>authenticity_token</tt> by default. If you are generating an HTML form + # manually (without the use of Rails' <tt>form_for</tt>, <tt>form_tag</tt> or other helpers), you have to + # include a hidden field named like that and set its value to what is returned by + # <tt>form_authenticity_token</tt>. + # + # Request forgery protection is disabled by default in test environment. If you are upgrading from Rails + # 1.x, add this to config/environments/test.rb: + # + # # Disable request forgery protection in test environment + # config.action_controller.allow_forgery_protection = false + # + # == Learn more about CSRF (Cross-Site Request Forgery) attacks + # + # Here are some resources: + # * http://isc.sans.org/diary.html?storyid=1750 + # * http://en.wikipedia.org/wiki/Cross-site_request_forgery + # + # Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application. + # There are a few guidelines you should follow: + # + # * Keep your GET requests safe and idempotent. More reading material: + # * http://www.xml.com/pub/a/2002/04/24/deviant.html + # * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1 + # * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look + # for "Expires: at end of session" + # module RequestForgeryProtection extend ActiveSupport::Concern @@ -12,54 +51,17 @@ module ActionController #:nodoc: included do # Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+ # sets it to <tt>:authenticity_token</tt> by default. - config.request_forgery_protection_token ||= :authenticity_token + config_accessor :request_forgery_protection_token + self.request_forgery_protection_token ||= :authenticity_token # Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode. - config.allow_forgery_protection ||= true + config_accessor :allow_forgery_protection + self.allow_forgery_protection = true if allow_forgery_protection.nil? helper_method :form_authenticity_token helper_method :protect_against_forgery? end - # Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current - # web application, not a forged link from another site, is done by embedding a token based on a random - # string stored in the session (which an attacker wouldn't know) in all forms and Ajax requests generated - # by Rails and then verifying the authenticity of that token in the controller. Only HTML/JavaScript - # requests are checked, so this will not protect your XML API (presumably you'll have a different - # authentication scheme there anyway). Also, GET requests are not protected as these should be - # idempotent anyway. - # - # This is turned on with the <tt>protect_from_forgery</tt> method, which will check the token and raise an - # ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the - # error message in production by editing public/422.html. A call to this method in ApplicationController is - # generated by default in post-Rails 2.0 applications. - # - # The token parameter is named <tt>authenticity_token</tt> by default. If you are generating an HTML form - # manually (without the use of Rails' <tt>form_for</tt>, <tt>form_tag</tt> or other helpers), you have to - # include a hidden field named like that and set its value to what is returned by - # <tt>form_authenticity_token</tt>. - # - # Request forgery protection is disabled by default in test environment. If you are upgrading from Rails - # 1.x, add this to config/environments/test.rb: - # - # # Disable request forgery protection in test environment - # config.action_controller.allow_forgery_protection = false - # - # == Learn more about CSRF (Cross-Site Request Forgery) attacks - # - # Here are some resources: - # * http://isc.sans.org/diary.html?storyid=1750 - # * http://en.wikipedia.org/wiki/Cross-site_request_forgery - # - # Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application. - # There are a few guidelines you should follow: - # - # * Keep your GET requests safe and idempotent. More reading material: - # * http://www.xml.com/pub/a/2002/04/24/deviant.html - # * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1 - # * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look - # for "Expires: at end of session" - # module ClassMethods # Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked. # @@ -79,22 +81,6 @@ module ActionController #:nodoc: self.request_forgery_protection_token ||= :authenticity_token before_filter :verify_authenticity_token, options end - - def request_forgery_protection_token - config.request_forgery_protection_token - end - - def request_forgery_protection_token=(val) - config.request_forgery_protection_token = val - end - - def allow_forgery_protection - config.allow_forgery_protection - end - - def allow_forgery_protection=(val) - config.allow_forgery_protection = val - end end protected @@ -104,22 +90,6 @@ module ActionController #:nodoc: before_filter :verify_authenticity_token, options end - def request_forgery_protection_token - config.request_forgery_protection_token - end - - def request_forgery_protection_token=(val) - config.request_forgery_protection_token = val - end - - def allow_forgery_protection - config.allow_forgery_protection - end - - def allow_forgery_protection=(val) - config.allow_forgery_protection = val - end - # The actual before_filter that is used. Modify this to change how you handle unverified requests. def verify_authenticity_token verified_request? || raise(ActionController::InvalidAuthenticityToken) @@ -146,7 +116,7 @@ module ActionController #:nodoc: end def protect_against_forgery? - config.allow_forgery_protection + allow_forgery_protection end end end diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb index b029434004..0e3cdffadc 100644 --- a/actionpack/lib/action_controller/railtie.rb +++ b/actionpack/lib/action_controller/railtie.rb @@ -13,64 +13,51 @@ module ActionController class Railtie < Rails::Railtie config.action_controller = ActiveSupport::OrderedOptions.new - ad = config.action_dispatch - config.action_controller.singleton_class.send(:define_method, :session) do - ActiveSupport::Deprecation.warn "config.action_controller.session has been " \ - "renamed to config.action_dispatch.session.", caller - ad.session - end + config.action_controller.singleton_class.tap do |d| + d.send(:define_method, :session) do + ActiveSupport::Deprecation.warn "config.action_controller.session has been deprecated. " << + "Please use Rails.application.config.session_store instead.", caller + end - config.action_controller.singleton_class.send(:define_method, :session=) do |val| - ActiveSupport::Deprecation.warn "config.action_controller.session has been " \ - "renamed to config.action_dispatch.session.", caller - ad.session = val - end + d.send(:define_method, :session=) do |val| + ActiveSupport::Deprecation.warn "config.action_controller.session= has been deprecated. " << + "Please use config.session_store(name, options) instead.", caller + end - config.action_controller.singleton_class.send(:define_method, :session_store) do - ActiveSupport::Deprecation.warn "config.action_controller.session_store has been " \ - "renamed to config.action_dispatch.session_store.", caller - ad.session_store - end + d.send(:define_method, :session_store) do + ActiveSupport::Deprecation.warn "config.action_controller.session_store has been deprecated. " << + "Please use Rails.application.config.session_store instead.", caller + end - config.action_controller.singleton_class.send(:define_method, :session_store=) do |val| - ActiveSupport::Deprecation.warn "config.action_controller.session_store has been " \ - "renamed to config.action_dispatch.session_store.", caller - ad.session_store = val + d.send(:define_method, :session_store=) do |val| + ActiveSupport::Deprecation.warn "config.action_controller.session_store= has been deprecated. " << + "Please use config.session_store(name, options) instead.", caller + end end log_subscriber :action_controller, ActionController::Railties::LogSubscriber.new - initializer "action_controller.logger" do - ActiveSupport.on_load(:action_controller) { self.logger ||= Rails.logger } - end - - initializer "action_controller.page_cache_directory" do - ActiveSupport.on_load(:action_controller) do - self.page_cache_directory = Rails.public_path - end - end - initializer "action_controller.set_configs" do |app| paths = app.config.paths ac = app.config.action_controller - ac.assets_dir = paths.public.to_a.first - ac.javascripts_dir = paths.public.javascripts.to_a.first - ac.stylesheets_dir = paths.public.stylesheets.to_a.first + ac.assets_dir ||= paths.public.to_a.first + ac.javascripts_dir ||= paths.public.javascripts.to_a.first + ac.stylesheets_dir ||= paths.public.stylesheets.to_a.first + ac.page_cache_directory ||= paths.public.to_a.first + ac.helpers_path ||= paths.app.helpers.to_a ActiveSupport.on_load(:action_controller) do self.config.merge!(ac) end end - initializer "action_controller.initialize_framework_caches" do - ActiveSupport.on_load(:action_controller) { self.cache_store ||= RAILS_CACHE } + initializer "action_controller.logger" do + ActiveSupport.on_load(:action_controller) { self.logger ||= Rails.logger } end - initializer "action_controller.set_helpers_path" do |app| - ActiveSupport.on_load(:action_controller) do - self.helpers_path = app.config.paths.app.helpers.to_a - end + initializer "action_controller.initialize_framework_caches" do + ActiveSupport.on_load(:action_controller) { self.cache_store ||= RAILS_CACHE } end initializer "action_controller.url_helpers" do |app| diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index b3758218a2..2b9b34961e 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -36,6 +36,7 @@ module ActionController end def teardown_subscriptions + ActiveSupport::Notifications.unsubscribe("action_view.render_template") ActiveSupport::Notifications.unsubscribe("action_view.render_template!") end @@ -282,165 +283,143 @@ module ActionController # # assert_redirected_to page_url(:title => 'foo') class TestCase < ActiveSupport::TestCase - include ActionDispatch::TestProcess - include ActionController::TemplateAssertions + module Behavior + extend ActiveSupport::Concern + include ActionDispatch::TestProcess - attr_reader :response, :request + attr_reader :response, :request - # 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") - end + module ClassMethods - # 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") - end + # Sets the controller class name. Useful if the name can't be inferred from test class. + # Expects +controller_class+ as a constant. Example: <tt>tests WidgetController</tt>. + def tests(controller_class) + self.controller_class = controller_class + end + + def controller_class=(new_class) + prepare_controller_class(new_class) if new_class + write_inheritable_attribute(:controller_class, new_class) + 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") - end + def controller_class + if current_controller_class = read_inheritable_attribute(:controller_class) + current_controller_class + else + self.controller_class = determine_default_controller_class(name) + end + 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") - end + def determine_default_controller_class(name) + name.sub(/Test$/, '').constantize + rescue NameError + nil + 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") - end + def prepare_controller_class(new_class) + new_class.send :include, ActionController::TestCase::RaiseActionExceptions + end - def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil) - @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' - @request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ') - returning __send__(request_method, action, parameters, session, flash) do - @request.env.delete 'HTTP_X_REQUESTED_WITH' - @request.env.delete 'HTTP_ACCEPT' end - end - alias xhr :xml_http_request - - def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET') - # 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 + + # 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") end - @request.recycle! - @response.recycle! - @controller.response_body = nil - @controller.formats = nil - @controller.params = nil - - @html_document = nil - @request.env['REQUEST_METHOD'] = http_method - - parameters ||= {} - @request.assign_parameters(@routes, @controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters) - - @request.session = ActionController::TestSession.new(session) unless session.nil? - @request.session["flash"] = @request.flash.update(flash || {}) - @request.session["flash"].sweep - - @controller.request = @request - @controller.params.merge!(parameters) - build_request_uri(action, parameters) - Base.class_eval { include Testing } - @controller.process_with_new_base_test(@request, @response) - @request.session.delete('flash') if @request.session['flash'].blank? - @response - 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") + end - include ActionDispatch::Assertions + # 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") + end - # When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline - # (bystepping the regular exception handling from rescue_action). If the request.remote_addr is anything else, the regular - # rescue_action process takes place. This means you can test your rescue_action code by setting remote_addr to something else - # than 0.0.0.0. - # - # The exception is stored in the exception accessor for further inspection. - module RaiseActionExceptions - def self.included(base) - base.class_eval do - attr_accessor :exception - protected :exception, :exception= - 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") end - protected - def rescue_action_without_handler(e) - self.exception = e + # 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") + end - if request.remote_addr == "0.0.0.0" - raise(e) - else - super(e) + def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil) + @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' + @request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ') + returning __send__(request_method, action, parameters, session, flash) do + @request.env.delete 'HTTP_X_REQUESTED_WITH' + @request.env.delete 'HTTP_ACCEPT' + end + end + alias xhr :xml_http_request + + def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET') + # 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 - end - setup :setup_controller_request_and_response + @request.recycle! + @response.recycle! + @controller.response_body = nil + @controller.formats = nil + @controller.params = nil - @@controller_class = nil + @html_document = nil + @request.env['REQUEST_METHOD'] = http_method - class << self - # Sets the controller class name. Useful if the name can't be inferred from test class. - # Expects +controller_class+ as a constant. Example: <tt>tests WidgetController</tt>. - def tests(controller_class) - self.controller_class = controller_class - end + parameters ||= {} + @request.assign_parameters(@routes, @controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters) - def controller_class=(new_class) - prepare_controller_class(new_class) if new_class - write_inheritable_attribute(:controller_class, new_class) - end + @request.session = ActionController::TestSession.new(session) unless session.nil? + @request.session["flash"] = @request.flash.update(flash || {}) + @request.session["flash"].sweep - def controller_class - if current_controller_class = read_inheritable_attribute(:controller_class) - current_controller_class - else - self.controller_class = determine_default_controller_class(name) - end + @controller.request = @request + @controller.params.merge!(parameters) + build_request_uri(action, parameters) + Base.class_eval { include Testing } + @controller.process_with_new_base_test(@request, @response) + @request.session.delete('flash') if @request.session['flash'].blank? + @response end - def determine_default_controller_class(name) - name.sub(/Test$/, '').constantize - rescue NameError - nil - end + def setup_controller_request_and_response + @request = TestRequest.new + @response = TestResponse.new - def prepare_controller_class(new_class) - new_class.send :include, RaiseActionExceptions - end - end + if klass = self.class.controller_class + @controller ||= klass.new rescue nil + end - def setup_controller_request_and_response - @request = TestRequest.new - @response = TestResponse.new + @request.env.delete('PATH_INFO') - if klass = self.class.controller_class - @controller ||= klass.new rescue nil + if @controller + @controller.request = @request + @controller.params = {} + end end - @request.env.delete('PATH_INFO') - - if @controller - @controller.request = @request - @controller.params = {} + # Cause the action to be rescued according to the regular rules for rescue_action when the visitor is not local + def rescue_action_in_public! + @request.remote_addr = '208.77.188.166' # example.com end - end - # Cause the action to be rescued according to the regular rules for rescue_action when the visitor is not local - def rescue_action_in_public! - @request.remote_addr = '208.77.188.166' # example.com - end + included do + include ActionController::TemplateAssertions + include ActionDispatch::Assertions + setup :setup_controller_request_and_response + end private + def build_request_uri(action, parameters) unless @request.env["PATH_INFO"] options = @controller.__send__(:url_options).merge(parameters) @@ -458,4 +437,33 @@ module ActionController end end end + + # When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline + # (bystepping the regular exception handling from rescue_action). If the request.remote_addr is anything else, the regular + # rescue_action process takes place. This means you can test your rescue_action code by setting remote_addr to something else + # than 0.0.0.0. + # + # The exception is stored in the exception accessor for further inspection. + module RaiseActionExceptions + def self.included(base) + base.class_eval do + attr_accessor :exception + protected :exception, :exception= + end + end + + protected + def rescue_action_without_handler(e) + self.exception = e + + if request.remote_addr == "0.0.0.0" + raise(e) + else + super(e) + end + end + end + + include Behavior + end end |