diff options
Diffstat (limited to 'actionpack/lib/action_controller')
-rw-r--r-- | actionpack/lib/action_controller/assertions/response_assertions.rb | 4 | ||||
-rw-r--r-- | actionpack/lib/action_controller/base.rb | 20 | ||||
-rw-r--r-- | actionpack/lib/action_controller/cgi_process.rb | 45 | ||||
-rw-r--r-- | actionpack/lib/action_controller/filters.rb | 19 | ||||
-rw-r--r-- | actionpack/lib/action_controller/headers.rb | 30 | ||||
-rw-r--r-- | actionpack/lib/action_controller/rack_process.rb | 52 | ||||
-rw-r--r-- | actionpack/lib/action_controller/request.rb | 165 | ||||
-rw-r--r-- | actionpack/lib/action_controller/resources.rb | 42 | ||||
-rw-r--r-- | actionpack/lib/action_controller/response.rb | 54 | ||||
-rw-r--r-- | actionpack/lib/action_controller/routing/segments.rb | 3 | ||||
-rw-r--r-- | actionpack/lib/action_controller/session/cookie_store.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_controller/test_process.rb | 44 |
12 files changed, 250 insertions, 230 deletions
diff --git a/actionpack/lib/action_controller/assertions/response_assertions.rb b/actionpack/lib/action_controller/assertions/response_assertions.rb index 765225ae24..e2e8bbdc71 100644 --- a/actionpack/lib/action_controller/assertions/response_assertions.rb +++ b/actionpack/lib/action_controller/assertions/response_assertions.rb @@ -87,11 +87,11 @@ module ActionController # def assert_template(expected = nil, message=nil) clean_backtrace do - rendered = @response.rendered_template + rendered = @response.rendered_template.to_s msg = build_message(message, "expecting <?> but rendering with <?>", expected, rendered) assert_block(msg) do if expected.nil? - @response.rendered_template.nil? + @response.rendered_template.blank? else rendered.to_s.match(expected) end diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 5689a9825e..0fdbcbd26f 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -428,11 +428,7 @@ module ActionController #:nodoc: # By default, all methods defined in ActionController::Base and included modules are hidden. # More methods can be hidden using <tt>hide_actions</tt>. def hidden_actions - unless read_inheritable_attribute(:hidden_actions) - write_inheritable_attribute(:hidden_actions, ActionController::Base.public_instance_methods.map { |m| m.to_s }) - end - - read_inheritable_attribute(:hidden_actions) + read_inheritable_attribute(:hidden_actions) || write_inheritable_attribute(:hidden_actions, []) end # Hide each of the given methods from being callable as actions. @@ -1199,7 +1195,7 @@ module ActionController #:nodoc: end def perform_action - if self.class.action_methods.include?(action_name) + if action_methods.include?(action_name) send(action_name) default_render unless performed? elsif respond_to? :method_missing @@ -1208,7 +1204,7 @@ module ActionController #:nodoc: elsif template_exists? && template_public? default_render else - raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.to_a.sort.to_sentence}", caller + raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.sort.to_sentence}", caller end end @@ -1234,7 +1230,15 @@ module ActionController #:nodoc: end def self.action_methods - @action_methods ||= Set.new(public_instance_methods.map { |m| m.to_s }) - hidden_actions + @action_methods ||= + # All public instance methods of this class, including ancestors + public_instance_methods(true).map { |m| m.to_s }.to_set - + # Except for public instance methods of Base and its ancestors + Base.public_instance_methods(true).map { |m| m.to_s } + + # Be sure to include shadowed public instance methods of this class + public_instance_methods(false).map { |m| m.to_s } - + # And always exclude explicitly hidden actions + hidden_actions end def add_variables_to_assigns diff --git a/actionpack/lib/action_controller/cgi_process.rb b/actionpack/lib/action_controller/cgi_process.rb index 8bc5e4c3a7..0ca27b30db 100644 --- a/actionpack/lib/action_controller/cgi_process.rb +++ b/actionpack/lib/action_controller/cgi_process.rb @@ -43,7 +43,7 @@ module ActionController #:nodoc: :session_path => "/", # available to all paths in app :session_key => "_session_id", :cookie_only => true - } unless const_defined?(:DEFAULT_SESSION_OPTIONS) + } def initialize(cgi, session_options = {}) @cgi = cgi @@ -61,53 +61,14 @@ module ActionController #:nodoc: end end - # The request body is an IO input stream. If the RAW_POST_DATA environment - # variable is already set, wrap it in a StringIO. - def body - if raw_post = env['RAW_POST_DATA'] - raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding) - StringIO.new(raw_post) - else - @cgi.stdinput - end - end - - def query_parameters - @query_parameters ||= self.class.parse_query_parameters(query_string) - end - - def request_parameters - @request_parameters ||= parse_formatted_request_parameters + def body_stream #:nodoc: + @cgi.stdinput end def cookies @cgi.cookies.freeze end - def host_with_port_without_standard_port_handling - if forwarded = env["HTTP_X_FORWARDED_HOST"] - forwarded.split(/,\s?/).last - elsif http_host = env['HTTP_HOST'] - http_host - elsif server_name = env['SERVER_NAME'] - server_name - else - "#{env['SERVER_ADDR']}:#{env['SERVER_PORT']}" - end - end - - def host - host_with_port_without_standard_port_handling.sub(/:\d+$/, '') - end - - def port - if host_with_port_without_standard_port_handling =~ /:(\d+)$/ - $1.to_i - else - standard_port - end - end - def session unless defined?(@session) if @session_options == false diff --git a/actionpack/lib/action_controller/filters.rb b/actionpack/lib/action_controller/filters.rb index 10dc0cc45b..1d7236f18a 100644 --- a/actionpack/lib/action_controller/filters.rb +++ b/actionpack/lib/action_controller/filters.rb @@ -109,16 +109,17 @@ module ActionController #:nodoc: update_options! options end + # override these to return true in appropriate subclass def before? - self.class == BeforeFilter + false end def after? - self.class == AfterFilter + false end def around? - self.class == AroundFilter + false end # Make sets of strings from :only/:except options @@ -170,6 +171,10 @@ module ActionController #:nodoc: :around end + def around? + true + end + def call(controller, &block) if should_run_callback?(controller) method = filter_responds_to_before_and_after? ? around_proc : self.method @@ -212,6 +217,10 @@ module ActionController #:nodoc: :before end + def before? + true + end + def call(controller, &block) super if controller.send!(:performed?) @@ -224,6 +233,10 @@ module ActionController #:nodoc: def type :after end + + def after? + true + end end # Filters enable controllers to run shared pre- and post-processing code for its actions. These filters can be used to do diff --git a/actionpack/lib/action_controller/headers.rb b/actionpack/lib/action_controller/headers.rb index 7239438c49..139669c66f 100644 --- a/actionpack/lib/action_controller/headers.rb +++ b/actionpack/lib/action_controller/headers.rb @@ -1,31 +1,33 @@ +require 'active_support/memoizable' + module ActionController module Http class Headers < ::Hash - - def initialize(constructor = {}) - if constructor.is_a?(Hash) + extend ActiveSupport::Memoizable + + def initialize(*args) + if args.size == 1 && args[0].is_a?(Hash) super() - update(constructor) + update(args[0]) else - super(constructor) + super end end - + def [](header_name) if include?(header_name) - super + super else - super(normalize_header(header_name)) + super(env_name(header_name)) end end - - + private - # Takes an HTTP header name and returns it in the - # format - def normalize_header(header_name) + # Converts a HTTP header name to an environment variable name. + def env_name(header_name) "HTTP_#{header_name.upcase.gsub(/-/, '_')}" end + memoize :env_name end end -end
\ No newline at end of file +end diff --git a/actionpack/lib/action_controller/rack_process.rb b/actionpack/lib/action_controller/rack_process.rb index 7e0a6b091e..dcbcf8bc1d 100644 --- a/actionpack/lib/action_controller/rack_process.rb +++ b/actionpack/lib/action_controller/rack_process.rb @@ -3,7 +3,7 @@ require 'action_controller/session/cookie_store' module ActionController #:nodoc: class RackRequest < AbstractRequest #:nodoc: - attr_accessor :env, :session_options + attr_accessor :session_options attr_reader :cgi class SessionFixationAttempt < StandardError #:nodoc: @@ -15,7 +15,7 @@ module ActionController #:nodoc: :session_path => "/", # available to all paths in app :session_key => "_session_id", :cookie_only => true - } unless const_defined?(:DEFAULT_SESSION_OPTIONS) + } def initialize(env, session_options = DEFAULT_SESSION_OPTIONS) @session_options = session_options @@ -30,35 +30,21 @@ module ActionController #:nodoc: SERVER_NAME SERVER_PROTOCOL HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING - HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM HTTP_HOST + HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env| define_method(env.sub(/^HTTP_/n, '').downcase) do @env[env] end end - # The request body is an IO input stream. If the RAW_POST_DATA environment - # variable is already set, wrap it in a StringIO. - def body - if raw_post = env['RAW_POST_DATA'] - StringIO.new(raw_post) - else - @env['rack.input'] - end + def body_stream #:nodoc: + @env['rack.input'] end def key?(key) @env.key?(key) end - def query_parameters - @query_parameters ||= self.class.parse_query_parameters(query_string) - end - - def request_parameters - @request_parameters ||= parse_formatted_request_parameters - end - def cookies return {} unless @env["HTTP_COOKIE"] @@ -70,34 +56,6 @@ module ActionController #:nodoc: @env["rack.request.cookie_hash"] end - def host_with_port_without_standard_port_handling - if forwarded = @env["HTTP_X_FORWARDED_HOST"] - forwarded.split(/,\s?/).last - elsif http_host = @env['HTTP_HOST'] - http_host - elsif server_name = @env['SERVER_NAME'] - server_name - else - "#{env['SERVER_ADDR']}:#{env['SERVER_PORT']}" - end - end - - def host - host_with_port_without_standard_port_handling.sub(/:\d+$/, '') - end - - def port - if host_with_port_without_standard_port_handling =~ /:(\d+)$/ - $1.to_i - else - standard_port - end - end - - def remote_addr - @env['REMOTE_ADDR'] - end - def server_port @env['SERVER_PORT'].to_i end diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index 60ff75fe2c..185518761d 100644 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -2,35 +2,35 @@ require 'tempfile' require 'stringio' require 'strscan' -module ActionController - # HTTP methods which are accepted by default. - ACCEPTED_HTTP_METHODS = Set.new(%w( get head put post delete options )) +require 'active_support/memoizable' +module ActionController # CgiRequest and TestRequest provide concrete implementations. class AbstractRequest + extend ActiveSupport::Memoizable + def self.relative_url_root=(*args) ActiveSupport::Deprecation.warn( "ActionController::AbstractRequest.relative_url_root= has been renamed." + "You can now set it with config.action_controller.relative_url_root=", caller) end - # The hash of CGI-like environment variables for this request, such as - # - # { 'SERVER_PROTOCOL' => 'HTTP/1.1', 'HTTP_ACCEPT_LANGUAGE' => 'en-us', ... } + HTTP_METHODS = %w(get head put post delete options) + HTTP_METHOD_LOOKUP = HTTP_METHODS.inject({}) { |h, m| h[m] = h[m.upcase] = m.to_sym; h } + + # The hash of environment variables for this request, + # such as { 'RAILS_ENV' => 'production' }. attr_reader :env # The true HTTP request \method as a lowercase symbol, such as <tt>:get</tt>. # UnknownHttpMethod is raised for invalid methods not listed in ACCEPTED_HTTP_METHODS. def request_method - @request_method ||= begin - method = ((@env['REQUEST_METHOD'] == 'POST' && !parameters[:_method].blank?) ? parameters[:_method].to_s : @env['REQUEST_METHOD']).downcase - if ACCEPTED_HTTP_METHODS.include?(method) - method.to_sym - else - raise UnknownHttpMethod, "#{method}, accepted HTTP methods are #{ACCEPTED_HTTP_METHODS.to_a.to_sentence}" - end - end + method = @env['REQUEST_METHOD'] + method = parameters[:_method] if method == 'POST' && !parameters[:_method].blank? + + HTTP_METHOD_LOOKUP[method] || raise(UnknownHttpMethod, "#{method}, accepted HTTP methods are #{HTTP_METHODS.to_sentence}") end + memoize :request_method # The HTTP request \method as a lowercase symbol, such as <tt>:get</tt>. # Note, HEAD is returned as <tt>:get</tt> since the two are functionally @@ -69,34 +69,60 @@ module ActionController # # request.headers["Content-Type"] # => "text/plain" def headers - @headers ||= ActionController::Http::Headers.new(@env) + ActionController::Http::Headers.new(@env) end + memoize :headers # Returns the content length of the request as an integer. def content_length - @content_length ||= env['CONTENT_LENGTH'].to_i + @env['CONTENT_LENGTH'].to_i end + memoize :content_length # The MIME type of the HTTP request, such as Mime::XML. # # For backward compatibility, the post \format is extracted from the # X-Post-Data-Format HTTP header if present. def content_type - @content_type ||= Mime::Type.lookup(content_type_without_parameters) + Mime::Type.lookup(content_type_without_parameters) end + memoize :content_type # Returns the accepted MIME type for the request. def accepts - @accepts ||= - begin - header = @env['HTTP_ACCEPT'].to_s.strip + header = @env['HTTP_ACCEPT'].to_s.strip - if header.empty? - [content_type, Mime::ALL].compact - else - Mime::Type.parse(header) - end - end + if header.empty? + [content_type, Mime::ALL].compact + else + Mime::Type.parse(header) + end + end + memoize :accepts + + def if_modified_since + if since = env['HTTP_IF_MODIFIED_SINCE'] + Time.rfc2822(since) + end + end + memoize :if_modified_since + + def if_none_match + env['HTTP_IF_NONE_MATCH'] + end + + def not_modified?(modified_at) + if_modified_since && modified_at && if_modified_since >= modified_at + end + + def etag_matches?(etag) + if_none_match && if_none_match == etag + end + + # Check response freshness (Last-Modified and ETag) against request + # If-Modified-Since and If-None-Match conditions. + def fresh?(response) + not_modified?(response.last_modified) || etag_matches?(response.etag) end # Returns the Mime type for the \format used in the request. @@ -105,7 +131,7 @@ module ActionController # GET /posts/5.xhtml | request.format => Mime::HTML # GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first depending on the value of <tt>ActionController::Base.use_accept_header</tt> def format - @format ||= begin + @format ||= if parameters[:format] Mime::Type.lookup_by_extension(parameters[:format]) elsif ActionController::Base.use_accept_header @@ -115,7 +141,6 @@ module ActionController else Mime::Type.lookup_by_extension("html") end - end end @@ -203,22 +228,26 @@ EOM @env['REMOTE_ADDR'] end + memoize :remote_ip # Returns the lowercase name of the HTTP server software. def server_software (@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil end + memoize :server_software # Returns the complete URL used for this request. def url protocol + host_with_port + request_uri end + memoize :url # Returns 'https://' if this is an SSL request and 'http://' otherwise. def protocol ssl? ? 'https://' : 'http://' end + memoize :protocol # Is this an SSL request? def ssl? @@ -226,19 +255,36 @@ EOM end # Returns the \host for this request, such as "example.com". + def raw_host_with_port + if forwarded = env["HTTP_X_FORWARDED_HOST"] + forwarded.split(/,\s?/).last + else + env['HTTP_HOST'] || env['SERVER_NAME'] || "#{env['SERVER_ADDR']}:#{env['SERVER_PORT']}" + end + end + + # Returns the host for this request, such as example.com. def host + raw_host_with_port.sub(/:\d+$/, '') end + memoize :host # Returns a \host:\port string for this request, such as "example.com" or # "example.com:8080". def host_with_port - @host_with_port ||= host + port_string + "#{host}#{port_string}" end + memoize :host_with_port # Returns the port number of this request as an integer. def port - @port_as_int ||= @env['SERVER_PORT'].to_i + if raw_host_with_port =~ /:(\d+)$/ + $1.to_i + else + standard_port + end end + memoize :port # Returns the standard \port number for this request's protocol. def standard_port @@ -251,7 +297,7 @@ EOM # Returns a \port suffix like ":8080" if the \port number of this request # is not the default HTTP \port 80 or HTTPS \port 443. def port_string - (port == standard_port) ? '' : ":#{port}" + port == standard_port ? '' : ":#{port}" end # Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify @@ -280,6 +326,7 @@ EOM @env['QUERY_STRING'] || '' end end + memoize :query_string # Returns the request URI, accounting for server idiosyncrasies. # WEBrick includes the full URL. IIS leaves REQUEST_URI blank. @@ -289,21 +336,23 @@ EOM (%r{^\w+\://[^/]+(/.*|$)$} =~ uri) ? $1 : uri else # Construct IIS missing REQUEST_URI from SCRIPT_NAME and 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? - uri << '?' << env_qs + uri = @env['PATH_INFO'].to_s + + if script_filename = @env['SCRIPT_NAME'].to_s.match(%r{[^/]+$}) + uri = uri.sub(/#{script_filename}\//, '') end - if uri.nil? + env_qs = @env['QUERY_STRING'].to_s + uri += "?#{env_qs}" unless env_qs.empty? + + if uri.blank? @env.delete('REQUEST_URI') - uri else @env['REQUEST_URI'] = uri end end end + memoize :request_uri # Returns the interpreted \path to requested resource after all the installation # directory of this application was taken into account. @@ -314,6 +363,7 @@ EOM path.sub!(%r/^#{ActionController::Base.relative_url_root}/, '') path || '' end + memoize :path # Read the request \body. This is useful for web services that need to # work with raw requests directly. @@ -350,19 +400,41 @@ EOM @path_parameters ||= {} end + # The request body is an IO input stream. If the RAW_POST_DATA environment + # variable is already set, wrap it in a StringIO. + def body + if raw_post = env['RAW_POST_DATA'] + raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding) + StringIO.new(raw_post) + else + body_stream + end + end - #-- - # Must be implemented in the concrete request - #++ + def remote_addr + @env['REMOTE_ADDR'] + end - # The request \body as an IO input stream. - def body + def referrer + @env['HTTP_REFERER'] end + alias referer referrer + - def query_parameters #:nodoc: + def query_parameters + @query_parameters ||= self.class.parse_query_parameters(query_string) end - def request_parameters #:nodoc: + def request_parameters + @request_parameters ||= parse_formatted_request_parameters + end + + + #-- + # Must be implemented in the concrete request + #++ + + def body_stream #:nodoc: end def cookies #:nodoc: @@ -389,8 +461,9 @@ EOM # The raw content type string with its parameters stripped off. def content_type_without_parameters - @content_type_without_parameters ||= self.class.extract_content_type_without_parameters(content_type_with_parameters) + self.class.extract_content_type_without_parameters(content_type_with_parameters) end + memoize :content_type_without_parameters private def content_type_from_legacy_post_data_format_header diff --git a/actionpack/lib/action_controller/resources.rb b/actionpack/lib/action_controller/resources.rb index 77b329b70e..becf6b0b63 100644 --- a/actionpack/lib/action_controller/resources.rb +++ b/actionpack/lib/action_controller/resources.rb @@ -481,8 +481,7 @@ module ActionController resource.collection_methods.each do |method, actions| actions.each do |action| action_options = action_options_for(action, resource, method) - map.named_route("#{action}_#{resource.name_prefix}#{resource.plural}", "#{resource.path}#{resource.action_separator}#{action}", action_options) - map.named_route("formatted_#{action}_#{resource.name_prefix}#{resource.plural}", "#{resource.path}#{resource.action_separator}#{action}.:format", action_options) + map_named_routes(map, "#{action}_#{resource.name_prefix}#{resource.plural}", "#{resource.path}#{resource.action_separator}#{action}", action_options) end end end @@ -495,18 +494,15 @@ module ActionController index_route_name << "_index" end - map.named_route(index_route_name, resource.path, index_action_options) - map.named_route("formatted_#{index_route_name}", "#{resource.path}.:format", index_action_options) + map_named_routes(map, index_route_name, resource.path, index_action_options) create_action_options = action_options_for("create", resource) - map.connect(resource.path, create_action_options) - map.connect("#{resource.path}.:format", create_action_options) + map_unnamed_routes(map, resource.path, create_action_options) end def map_default_singleton_actions(map, resource) create_action_options = action_options_for("create", resource) - map.connect(resource.path, create_action_options) - map.connect("#{resource.path}.:format", create_action_options) + map_unnamed_routes(map, resource.path, create_action_options) end def map_new_actions(map, resource) @@ -514,11 +510,9 @@ module ActionController actions.each do |action| action_options = action_options_for(action, resource, method) if action == :new - map.named_route("new_#{resource.name_prefix}#{resource.singular}", resource.new_path, action_options) - map.named_route("formatted_new_#{resource.name_prefix}#{resource.singular}", "#{resource.new_path}.:format", action_options) + map_named_routes(map, "new_#{resource.name_prefix}#{resource.singular}", resource.new_path, action_options) else - map.named_route("#{action}_new_#{resource.name_prefix}#{resource.singular}", "#{resource.new_path}#{resource.action_separator}#{action}", action_options) - map.named_route("formatted_#{action}_new_#{resource.name_prefix}#{resource.singular}", "#{resource.new_path}#{resource.action_separator}#{action}.:format", action_options) + map_named_routes(map, "#{action}_new_#{resource.name_prefix}#{resource.singular}", "#{resource.new_path}#{resource.action_separator}#{action}", action_options) end end end @@ -532,22 +526,28 @@ module ActionController action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash) action_path ||= Base.resources_path_names[action] || action - map.named_route("#{action}_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action_path}", action_options) - map.named_route("formatted_#{action}_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action_path}.:format",action_options) + map_named_routes(map, "#{action}_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action_path}", action_options) end end show_action_options = action_options_for("show", resource) - map.named_route("#{resource.name_prefix}#{resource.singular}", resource.member_path, show_action_options) - map.named_route("formatted_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}.:format", show_action_options) + map_named_routes(map, "#{resource.name_prefix}#{resource.singular}", resource.member_path, show_action_options) update_action_options = action_options_for("update", resource) - map.connect(resource.member_path, update_action_options) - map.connect("#{resource.member_path}.:format", update_action_options) + map_unnamed_routes(map, resource.member_path, update_action_options) destroy_action_options = action_options_for("destroy", resource) - map.connect(resource.member_path, destroy_action_options) - map.connect("#{resource.member_path}.:format", destroy_action_options) + map_unnamed_routes(map, resource.member_path, destroy_action_options) + end + + def map_unnamed_routes(map, path_without_format, options) + map.connect(path_without_format, options) + map.connect("#{path_without_format}.:format", options) + end + + def map_named_routes(map, name, path_without_format, options) + map.named_route(name, path_without_format, options) + map.named_route("formatted_#{name}", "#{path_without_format}.:format", options) end def add_conditions_for(conditions, method) @@ -574,4 +574,4 @@ end class ActionController::Routing::RouteSet::Mapper include ActionController::Resources -end +end
\ No newline at end of file diff --git a/actionpack/lib/action_controller/response.rb b/actionpack/lib/action_controller/response.rb index da352b6993..a85fad0d39 100644 --- a/actionpack/lib/action_controller/response.rb +++ b/actionpack/lib/action_controller/response.rb @@ -37,12 +37,20 @@ module ActionController # :nodoc: attr_accessor :body # The headers of the response, as a Hash. It maps header names to header values. attr_accessor :headers - attr_accessor :session, :cookies, :assigns, :template, :redirected_to, :redirected_to_method_params, :layout + attr_accessor :session, :cookies, :assigns, :template, :layout + attr_accessor :redirected_to, :redirected_to_method_params def initialize @body, @headers, @session, @assigns = "", DEFAULT_HEADERS.merge("cookie" => []), [], [] end + def status; headers['Status'] end + def status=(status) headers['Status'] = status end + + def location; headers['Location'] end + def location=(url) headers['Location'] = url end + + # Sets the HTTP response's content MIME type. For example, in the controller # you could write this: # @@ -70,35 +78,29 @@ module ActionController # :nodoc: charset.blank? ? nil : charset.strip.split("=")[1] end - def redirect(to_url, response_status) - self.headers["Status"] = response_status - self.headers["Location"] = to_url + def last_modified + Time.rfc2822(headers['Last-Modified']) + end - self.body = "<html><body>You are being <a href=\"#{to_url}\">redirected</a>.</body></html>" + def last_modified=(utc_time) + headers['Last-Modified'] = utc_time.httpdate end - def prepare! - handle_conditional_get! - convert_content_type! - set_content_length! + def etag; headers['ETag'] end + def etag=(etag) + headers['ETag'] = %("#{Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(etag))}") end - # Sets the Last-Modified response header. Returns whether it's older than - # the If-Modified-Since request header. - def last_modified!(utc_time) - headers['Last-Modified'] ||= utc_time.httpdate - if request && since = request.headers['HTTP_IF_MODIFIED_SINCE'] - utc_time <= Time.rfc2822(since) - end + def redirect(url, status) + self.status = status + self.location = url + self.body = "<html><body>You are being <a href=\"#{url}\">redirected</a>.</body></html>" end - # Sets the ETag response header. Returns whether it matches the - # If-None-Match request header. - def etag!(tag) - headers['ETag'] ||= %("#{Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(tag))}") - if request && request.headers['HTTP_IF_NONE_MATCH'] == headers['ETag'] - true - end + def prepare! + handle_conditional_get! + convert_content_type! + set_content_length! end private @@ -106,15 +108,15 @@ module ActionController # :nodoc: if nonempty_ok_response? set_conditional_cache_control! - if etag!(body) - headers['Status'] = '304 Not Modified' + self.etag ||= body + if request && request.etag_matches?(etag) + self.status = '304 Not Modified' self.body = '' end end end def nonempty_ok_response? - status = headers['Status'] ok = !status || status[0..2] == '200' ok && body.is_a?(String) && !body.empty? end diff --git a/actionpack/lib/action_controller/routing/segments.rb b/actionpack/lib/action_controller/routing/segments.rb index 75784c3b78..9d4b740a44 100644 --- a/actionpack/lib/action_controller/routing/segments.rb +++ b/actionpack/lib/action_controller/routing/segments.rb @@ -2,7 +2,8 @@ module ActionController module Routing class Segment #:nodoc: RESERVED_PCHAR = ':@&=+$,;' - UNSAFE_PCHAR = Regexp.new("[^#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}]", false, 'N').freeze + SAFE_PCHAR = "#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}" + UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false, 'N').freeze # TODO: Convert :is_optional accessor to read only attr_accessor :is_optional diff --git a/actionpack/lib/action_controller/session/cookie_store.rb b/actionpack/lib/action_controller/session/cookie_store.rb index b477c1f7da..5bf7503f04 100644 --- a/actionpack/lib/action_controller/session/cookie_store.rb +++ b/actionpack/lib/action_controller/session/cookie_store.rb @@ -129,7 +129,7 @@ class CGI::Session::CookieStore private # Marshal a session hash into safe cookie data. Include an integrity hash. def marshal(session) - data = ActiveSupport::Base64.encode64(Marshal.dump(session)).chop + data = ActiveSupport::Base64.encode64s(Marshal.dump(session)) "#{data}--#{generate_digest(data)}" end diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb index 66675aaa13..0c705207e3 100644 --- a/actionpack/lib/action_controller/test_process.rb +++ b/actionpack/lib/action_controller/test_process.rb @@ -23,7 +23,7 @@ module ActionController #:nodoc: class TestRequest < AbstractRequest #:nodoc: attr_accessor :cookies, :session_options - attr_accessor :query_parameters, :request_parameters, :path, :session, :env + attr_accessor :query_parameters, :request_parameters, :path, :session attr_accessor :host, :user_agent def initialize(query_parameters = nil, request_parameters = nil, session = nil) @@ -42,7 +42,7 @@ module ActionController #:nodoc: end # Wraps raw_post in a StringIO. - def body + def body_stream #:nodoc: StringIO.new(raw_post) end @@ -54,7 +54,7 @@ module ActionController #:nodoc: def port=(number) @env["SERVER_PORT"] = number.to_i - @port_as_int = nil + port(true) end def action=(action_name) @@ -68,6 +68,8 @@ module ActionController #:nodoc: @env["REQUEST_URI"] = value @request_uri = nil @path = nil + request_uri(true) + path(true) end def request_uri=(uri) @@ -77,21 +79,26 @@ module ActionController #:nodoc: def accept=(mime_types) @env["HTTP_ACCEPT"] = Array(mime_types).collect { |mime_types| mime_types.to_s }.join(",") + accepts(true) end - def remote_addr=(addr) - @env['REMOTE_ADDR'] = addr + def if_modified_since=(last_modified) + @env["HTTP_IF_MODIFIED_SINCE"] = last_modified end - def remote_addr - @env['REMOTE_ADDR'] + def if_none_match=(etag) + @env["HTTP_IF_NONE_MATCH"] = etag end - def request_uri + def remote_addr=(addr) + @env['REMOTE_ADDR'] = addr + end + + def request_uri(*args) @request_uri || super end - def path + def path(*args) @path || super end @@ -113,17 +120,13 @@ module ActionController #:nodoc: end end @parameters = nil # reset TestRequest#parameters to use the new path_parameters - end - + end + def recycle! self.request_parameters = {} self.query_parameters = {} self.path_parameters = {} - @request_method, @accepts, @content_type = nil, nil, nil - end - - def referer - @env["HTTP_REFERER"] + unmemoize_all end private @@ -448,10 +451,13 @@ module ActionController #:nodoc: end def method_missing(selector, *args) - return @controller.send!(selector, *args) if ActionController::Routing::Routes.named_routes.helpers.include?(selector) - return super + if ActionController::Routing::Routes.named_routes.helpers.include?(selector) + @controller.send(selector, *args) + else + super + end end - + # Shortcut for <tt>ActionController::TestUploadedFile.new(Test::Unit::TestCase.fixture_path + path, type)</tt>: # # post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png') |