diff options
Diffstat (limited to 'actionpack/lib/action_controller')
-rwxr-xr-x | actionpack/lib/action_controller/base.rb | 46 | ||||
-rw-r--r-- | actionpack/lib/action_controller/cgi_process.rb | 25 | ||||
-rw-r--r-- | actionpack/lib/action_controller/components.rb | 84 | ||||
-rw-r--r-- | actionpack/lib/action_controller/filters.rb | 16 | ||||
-rw-r--r-- | actionpack/lib/action_controller/flash.rb | 42 | ||||
-rwxr-xr-x | actionpack/lib/action_controller/request.rb | 51 | ||||
-rw-r--r-- | actionpack/lib/action_controller/session/active_record_store.rb | 4 | ||||
-rw-r--r-- | actionpack/lib/action_controller/session_management.rb | 8 |
8 files changed, 154 insertions, 122 deletions
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index a3f6a0326f..b92dd1ca4e 100755 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -305,8 +305,8 @@ module ActionController #:nodoc: class << self # Factory for the standard create, process loop where the controller is discarded after processing. - def process(request, response) #:nodoc: - new.process(request, response) + def process(request, response, parent_controller=nil) #:nodoc: + new(parent_controller).process(request, response) end # Converts the class name from something like "OneModule::TwoModule::NeatController" to "NeatController". @@ -359,20 +359,43 @@ module ActionController #:nodoc: end end - public + public + # If this controller was instantiated to process a component request, + # +parent_controller+ points to the instantiator of this controller. + attr_reader :parent_controller + + # Create a new controller instance. + def initialize(parent_controller=nil) #:nodoc: + @parent_controller = parent_controller + end + # Extracts the action_name from the request parameters and performs that action. def process(request, response, method = :perform_action, *arguments) #:nodoc: initialize_template_class(response) assign_shortcuts(request, response) + + my_flash = flash # calling flash creates @flash + if my_parent = @parent_controller + # only discard flash if this controller isn't a component request controller + my_flash.discard + end + initialize_current_url @action_name = params['action'] || 'index' @variables_added = nil + @before_filter_chain_aborted = false log_processing if logger send(method, *arguments) @response ensure - close_session + unless my_parent + unless @before_filter_chain_aborted + my_flash.sweep + clear_persistent_model_associations + end + close_session + end end # Returns a URL that has been rewritten according to the options hash and the defined Routes. @@ -784,7 +807,7 @@ module ActionController #:nodoc: case options when %r{^\w+://.*} raise DoubleRenderError if performed? - logger.info("Redirected to #{options}") unless logger.nil? + logger.info("Redirected to #{options}") if logger response.redirect(options) response.redirected_to = options @performed_redirect = true @@ -866,7 +889,8 @@ module ActionController #:nodoc: @session = @response.session @template = @response.template - @assigns = @response.template.assigns + @assigns = @response.template.assigns + @headers = @response.headers end @@ -929,23 +953,23 @@ module ActionController #:nodoc: if view_controller_internals [ "@assigns", "@performed_redirect", "@performed_render" ] else - [ "@assigns", "@performed_redirect", "@performed_render", "@request", "@response", "@session", "@cookies", "@template" ] + [ "@assigns", "@performed_redirect", "@performed_render", "@request", "@response", "@session", "@cookies", "@template", "@request_origin", "@parent_controller" ] end end - def request_origin - "#{@request.remote_ip} at #{Time.now.to_s(:db)}" + # this *needs* to be cached! + # otherwise you'd get different results if calling it more than once + @request_origin ||= "#{@request.remote_ip} at #{Time.now.to_s(:db)}" end def complete_request_uri - request.protocol + request.host + request.request_uri + "#{@request.protocol}#{@request.host}#{@request.request_uri}" end def close_session @session.close unless @session.nil? || Hash === @session end - def template_exists?(template_name = default_template_name) @template.file_exists?(template_name) diff --git a/actionpack/lib/action_controller/cgi_process.rb b/actionpack/lib/action_controller/cgi_process.rb index 1690f0bb58..b22921ce8b 100644 --- a/actionpack/lib/action_controller/cgi_process.rb +++ b/actionpack/lib/action_controller/cgi_process.rb @@ -43,18 +43,19 @@ module ActionController #:nodoc: def initialize(cgi, session_options = {}) @cgi = cgi @session_options = session_options + @env = @cgi.send(:env_table) super() end def query_string if (qs = @cgi.query_string) && !qs.empty? qs - elsif uri = env['REQUEST_URI'] + elsif uri = @env['REQUEST_URI'] parts = uri.split('?') parts.shift parts.join('?') else - env['QUERY_STRING'] || '' + @env['QUERY_STRING'] || '' end end @@ -64,24 +65,20 @@ module ActionController #:nodoc: def request_parameters if formatted_post? - CGIMethods.parse_formatted_request_parameters(post_format, env['RAW_POST_DATA']) + CGIMethods.parse_formatted_request_parameters(post_format, @env['RAW_POST_DATA']) else CGIMethods.parse_request_parameters(@cgi.params) end end - - def env - @cgi.send(:env_table) - end - + def cookies @cgi.cookies.freeze end def host - if env["HTTP_X_FORWARDED_HOST"] - env["HTTP_X_FORWARDED_HOST"].split(/,\s?/).last - elsif env['HTTP_HOST'] =~ /^(.*):\d+$/ + if @env["HTTP_X_FORWARDED_HOST"] + @env["HTTP_X_FORWARDED_HOST"].split(/,\s?/).last + elsif @env['HTTP_HOST'] =~ /^(.*):\d+$/ $1 else @cgi.host.to_s.split(":").first || '' @@ -89,11 +86,11 @@ module ActionController #:nodoc: end def port - env["HTTP_X_FORWARDED_HOST"] ? standard_port : (port_from_http_host || super) + @env["HTTP_X_FORWARDED_HOST"] ? standard_port : (port_from_http_host || super) end def port_from_http_host - $1.to_i if env['HTTP_HOST'] && /:(\d+)$/ =~ env['HTTP_HOST'] + $1.to_i if @env['HTTP_HOST'] && /:(\d+)$/ =~ @env['HTTP_HOST'] end def session @@ -142,7 +139,7 @@ module ActionController #:nodoc: Module.const_missing($1) rescue LoadError, NameError => const_error raise ActionController::SessionRestoreError, <<end_msg -Session contains objects whose class definition isn't available. +Session contains objects whose class definition isn\'t available. Remember to require the classes for all objects kept in the session. (Original exception: #{const_error.message} [#{const_error.class}]) end_msg diff --git a/actionpack/lib/action_controller/components.rb b/actionpack/lib/action_controller/components.rb index aeb0272cd1..7cbbe3edd2 100644 --- a/actionpack/lib/action_controller/components.rb +++ b/actionpack/lib/action_controller/components.rb @@ -20,6 +20,13 @@ module ActionController #:nodoc: # # Let's see a greeting: # <%= render_component :controller => "greeter", :action => "hello_world" %> + # + # It is also possible to specify the controller as a class constant, bypassing the inflector + # code to compute the controller class at runtime. Therefore, + # + # <%= render_component :controller => GreeterController, :action => "hello_world" %> + # + # would work as well and be slightly faster. module Components def self.append_features(base) #:nodoc: super @@ -32,16 +39,18 @@ module ActionController #:nodoc: protected # Renders the component specified as the response for the current method - def render_component(options = {}) #:doc: - component_logging(options) { render_text(component_response(options).body, response.headers["Status"]) } + def render_component(options) #:doc: + component_logging(options) do + render_text(component_response(options, true).body, response.headers["Status"]) + end end # Returns the component response as a string def render_component_as_string(options) #:doc: component_logging(options) do response = component_response(options, false) - unless response.redirected_to.nil? - render_component_as_string response.redirected_to + if redirected = response.redirected_to + render_component_as_string redirected else response.body end @@ -49,38 +58,47 @@ module ActionController #:nodoc: end private - def component_response(options, reuse_response = true) - begin - ActionController::Flash::FlashHash.avoid_sweep = true - p = component_class(options).process(request_for_component(options), reuse_response ? @response : response_for_component) - ensure - ActionController::Flash::FlashHash.avoid_sweep = false + def component_response(options, reuse_response) + c_class = component_class(options) + c_request = request_for_component(c_class.controller_name, options) + c_response = reuse_response ? @response : @response.dup + c_class.process(c_request, c_response, self) + end + + # determine the controller class for the component request + def component_class(options) + if controller = options[:controller] + if controller.is_a? Class + controller + else + "#{controller.camelize}Controller".constantize + end + else + self.class + end + end + + # Create a new request object based on the current request. + # The new request inherits the session from the current request, + # bypassing any session options set for the component controller's class + def request_for_component(controller_name, options) + sub_request = @request.dup + sub_request.session = @request.session + sub_request.instance_variable_set(:@parameters, + (options[:params] || {}).with_indifferent_access.regular_update( + "controller" => controller_name, "action" => options[:action], "id" => options[:id]) + ) + sub_request end - p - end - - def component_class(options) - options[:controller] ? (options[:controller].camelize + "Controller").constantize : self.class - end - - def request_for_component(options) - request_for_component = @request.dup - request_for_component.send( - :instance_variable_set, :@parameters, - (options[:params] || {}).merge({ "controller" => options[:controller], "action" => options[:action], "id" => options[:id] }).with_indifferent_access - ) - return request_for_component - end - - def response_for_component - @response.dup - end + def component_logging(options) - logger.info("Start rendering component (#{options.inspect}): ") unless logger.nil? - result = yield - logger.info("\n\nEnd of component rendering") unless logger.nil? - return result + unless logger then yield else + logger.info("Start rendering component (#{options.inspect}): ") + result = yield + logger.info("\n\nEnd of component rendering") + result + end end end end diff --git a/actionpack/lib/action_controller/filters.rb b/actionpack/lib/action_controller/filters.rb index 9bfe3ee1da..2e15f67867 100644 --- a/actionpack/lib/action_controller/filters.rb +++ b/actionpack/lib/action_controller/filters.rb @@ -284,12 +284,12 @@ module ActionController #:nodoc: # Returns all the before filters for this class and all its ancestors. def before_filters #:nodoc: - read_inheritable_attribute("before_filters") + read_inheritable_attribute("before_filters") || [] end # Returns all the after filters for this class and all its ancestors. def after_filters #:nodoc: - read_inheritable_attribute("after_filters") + read_inheritable_attribute("after_filters") || [] end # Returns a mapping between filters and the actions that may run them. @@ -308,7 +308,8 @@ module ActionController #:nodoc: end def prepend_filter_to_chain(condition, filters) - write_inheritable_attribute("#{condition}_filters", filters + read_inheritable_attribute("#{condition}_filters")) + old_filters = read_inheritable_attribute("#{condition}_filters") || [] + write_inheritable_attribute("#{condition}_filters", filters + old_filters) end def ensure_filter_responds_to_before_and_after(filter) @@ -344,9 +345,12 @@ module ActionController #:nodoc: end def perform_action_with_filters - return if before_action == false || performed? - perform_action_without_filters - after_action + before_action_result = before_action + unless before_action_result == false || performed? + perform_action_without_filters + after_action + end + @before_filter_chain_aborted = (before_action_result == false) end # Calls all the defined before-filter filters, which are added by using "before_filter :method". diff --git a/actionpack/lib/action_controller/flash.rb b/actionpack/lib/action_controller/flash.rb index 9eac5784fd..6a41aa97a6 100644 --- a/actionpack/lib/action_controller/flash.rb +++ b/actionpack/lib/action_controller/flash.rb @@ -24,11 +24,6 @@ module ActionController #:nodoc: # # See docs on the FlashHash class for more details about the flash. module Flash - def self.append_features(base) #:nodoc: - super - base.before_filter(:fire_flash) - base.after_filter(:sweep_flash) - end class FlashNow #:nodoc: def initialize flash @@ -47,9 +42,6 @@ module ActionController #:nodoc: end class FlashHash < Hash - @@avoid_sweep = false - cattr_accessor :avoid_sweep - def initialize #:nodoc: super @used = {} @@ -106,7 +98,6 @@ module ActionController #:nodoc: # # This method is called automatically by filters, so you generally don't need to care about it. def sweep #:nodoc: - return if @@avoid_sweep keys.each do |k| unless @used[k] use(k) @@ -139,14 +130,18 @@ module ActionController #:nodoc: # <tt>flash["notice"] = "hello"</tt> to put a new one. # Note that if sessions are disabled only flash.now will work. def flash #:doc: - # @session = Hash.new if sessions are disabled - if @session.is_a?(Hash) - @__flash ||= FlashHash.new - - # otherwise, @session is a CGI::Session or a TestSession - else - @session['flash'] ||= FlashHash.new - end + @flash ||= + if @parent_controller + @parent_controller.flash + elsif @session.is_a?(Hash) + # @session is a Hash, if sessions are disabled + # we don't put the flash in the session in this case + FlashHash.new + else + # otherwise, @session is a CGI::Session or a TestSession + # so make sure it gets retrieved from/saved to session storage after request processing + @session["flash"] ||= FlashHash.new + end end # deprecated. use <tt>flash.keep</tt> instead @@ -155,18 +150,5 @@ module ActionController #:nodoc: flash.keep end - - private - - # marks flash entries as used and expose the flash to the view - def fire_flash - flash.discard - @assigns["flash"] = flash - end - - # deletes the flash entries that were not marked for keeping - def sweep_flash - flash.sweep - end end end diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index 8b5c480626..0daa229448 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -3,14 +3,18 @@ module ActionController class AbstractRequest cattr_accessor :relative_url_root + # Returns the hash of environment variables for this request, + # such as { 'RAILS_ENV' => 'production' }. + attr_reader :env + # Returns both GET and POST parameters in a single hash. def parameters - @parameters ||= request_parameters.merge(query_parameters).merge(path_parameters).with_indifferent_access + @parameters ||= request_parameters.update(query_parameters).update(path_parameters).with_indifferent_access end # Returns the HTTP request method as a lowercase symbol (:get, for example) def method - env['REQUEST_METHOD'].downcase.to_sym + @request_method ||= @env['REQUEST_METHOD'].downcase.to_sym end # Is this a GET request? Equivalent to request.method == :get @@ -52,10 +56,10 @@ module ActionController # X-Post-Data-Format HTTP header if present. def post_format @post_format ||= - if env['HTTP_X_POST_DATA_FORMAT'] - env['HTTP_X_POST_DATA_FORMAT'].downcase.to_sym + if @env['HTTP_X_POST_DATA_FORMAT'] + @env['HTTP_X_POST_DATA_FORMAT'].downcase.to_sym else - case env['CONTENT_TYPE'].to_s.downcase + case @env['CONTENT_TYPE'].to_s.downcase when 'application/xml', 'text/xml' then :xml when 'application/x-yaml', 'text/x-yaml' then :yaml else :url_encoded @@ -82,7 +86,7 @@ module ActionController # "XMLHttpRequest". (The Prototype Javascript library sends this header with # every Ajax request.) def xml_http_request? - not /XMLHttpRequest/i.match(env['HTTP_X_REQUESTED_WITH']).nil? + not /XMLHttpRequest/i.match(@env['HTTP_X_REQUESTED_WITH']).nil? end alias xhr? :xml_http_request? @@ -93,17 +97,17 @@ module ActionController # delimited list in the case of multiple chained proxies; the first is # the originating IP. def remote_ip - return env['HTTP_CLIENT_IP'] if env.include? 'HTTP_CLIENT_IP' + return @env['HTTP_CLIENT_IP'] if @env.include? 'HTTP_CLIENT_IP' - if env.include? 'HTTP_X_FORWARDED_FOR' then - remote_ips = env['HTTP_X_FORWARDED_FOR'].split(',').reject do |ip| + if @env.include? 'HTTP_X_FORWARDED_FOR' then + remote_ips = @env['HTTP_X_FORWARDED_FOR'].split(',').reject do |ip| ip =~ /^unknown$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i end return remote_ips.first.strip unless remote_ips.empty? end - env['REMOTE_ADDR'] + @env['REMOTE_ADDR'] end # Returns the domain part of a host, such as rubyonrails.org in "www.rubyonrails.org". You can specify @@ -127,19 +131,19 @@ module ActionController # This is useful for services such as REST, XMLRPC and SOAP # which communicate over HTTP POST but don't use the traditional parameter format. def raw_post - env['RAW_POST_DATA'] + @env['RAW_POST_DATA'] end # Returns the request URI correctly, taking into account the idiosyncracies # of the various servers. def request_uri - if uri = env['REQUEST_URI'] + if uri = @env['REQUEST_URI'] (%r{^\w+\://[^/]+(/.*|$)$} =~ uri) ? $1 : uri # Remove domain, which webrick puts into the request_uri. else # REQUEST_URI is blank under IIS - get this from PATH_INFO and SCRIPT_NAME - script_filename = env['SCRIPT_NAME'].to_s.match(%r{[^/]+$}) - uri = env['PATH_INFO'] + script_filename = @env['SCRIPT_NAME'].to_s.match(%r{[^/]+$}) + uri = @env['PATH_INFO'] uri = uri.sub(/#{script_filename}\//, '') unless script_filename.nil? - unless (env_qs = env['QUERY_STRING']).nil? || env_qs.empty? + unless (env_qs = @env['QUERY_STRING']).nil? || env_qs.empty? uri << '?' << env_qs end uri @@ -153,7 +157,7 @@ module ActionController # Is this an SSL request? def ssl? - env['HTTPS'] == 'on' || env['HTTP_X_FORWARDED_PROTO'] == 'https' + @env['HTTPS'] == 'on' || @env['HTTP_X_FORWARDED_PROTO'] == 'https' end # Returns the interpreted path to requested resource after all the installation directory of this application was taken into account @@ -169,13 +173,13 @@ module ActionController # Returns the path minus the web server relative installation directory. # This method returns nil unless the web server is apache. def relative_url_root - @@relative_url_root ||= server_software == 'apache' ? env["SCRIPT_NAME"].to_s.sub(/\/dispatch\.(fcgi|rb|cgi)$/, '') : '' + @@relative_url_root ||= server_software == 'apache' ? @env["SCRIPT_NAME"].to_s.sub(/\/dispatch\.(fcgi|rb|cgi)$/, '') : '' end # Returns the port number of this request as an integer. def port - @port_as_int ||= env['SERVER_PORT'].to_i + @port_as_int ||= @env['SERVER_PORT'].to_i end # Returns the standard port number for this request's protocol @@ -213,7 +217,7 @@ module ActionController # Returns the lowercase name of the HTTP server software. def server_software - (env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ env['SERVER_SOFTWARE']) ? $1.downcase : nil + (@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil end #-- @@ -225,11 +229,6 @@ module ActionController def request_parameters #:nodoc: end - # Returns the hash of environment variables for this request, - # such as { 'RAILS_ENV' => 'production' }. - def env - end - # Returns the host for this request, such as example.com. def host end @@ -240,6 +239,10 @@ module ActionController def session #:nodoc: end + def session=(session) #:nodoc: + @session = session + end + def reset_session #:nodoc: end end diff --git a/actionpack/lib/action_controller/session/active_record_store.rb b/actionpack/lib/action_controller/session/active_record_store.rb index 7642320747..3d84d85ac0 100644 --- a/actionpack/lib/action_controller/session/active_record_store.rb +++ b/actionpack/lib/action_controller/session/active_record_store.rb @@ -278,7 +278,9 @@ class CGI raise CGI::Session::NoSession, 'uninitialized session' end @session = @@session_class.new(:session_id => session_id, :data => {}) - @session.save + # session saving can be lazy again, because of improved component implementation + # therefore next line gets commented out: + # @session.save end end diff --git a/actionpack/lib/action_controller/session_management.rb b/actionpack/lib/action_controller/session_management.rb index 827f8ebe46..950f48abf4 100644 --- a/actionpack/lib/action_controller/session_management.rb +++ b/actionpack/lib/action_controller/session_management.rb @@ -11,7 +11,6 @@ module ActionController #:nodoc: base.extend(ClassMethods) base.send(:alias_method, :process_without_session_management_support, :process) base.send(:alias_method, :process, :process_with_session_management_support) - base.after_filter(:clear_persistent_model_associations) end module ClassMethods @@ -111,8 +110,11 @@ module ActionController #:nodoc: end def process_with_session_management_support(request, response, method = :perform_action, *arguments) #:nodoc: - action = request.parameters["action"] || "index" - request.session_options = self.class.session_options_for(request, action) + unless @parent_controller + # only determine session options if this isn't a controller created for component request processing + action = request.parameters["action"] || "index" + request.session_options = self.class.session_options_for(request, action) + end process_without_session_management_support(request, response, method, *arguments) end |