diff options
Diffstat (limited to 'actionpack')
-rw-r--r-- | actionpack/CHANGELOG | 15 | ||||
-rw-r--r-- | actionpack/lib/action_controller/metal/cookies.rb | 5 | ||||
-rw-r--r-- | actionpack/lib/action_controller/metal/responder.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_controller/metal/verification.rb | 2 | ||||
-rwxr-xr-x | actionpack/lib/action_dispatch/http/request.rb | 60 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/middleware/cookies.rb | 42 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/routing/mapper.rb | 19 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/routing/route_set.rb | 10 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/testing/integration.rb | 3 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/url_helper.rb | 7 | ||||
-rw-r--r-- | actionpack/lib/action_view/template.rb | 6 | ||||
-rw-r--r-- | actionpack/test/controller/integration_test.rb | 2 | ||||
-rw-r--r-- | actionpack/test/controller/verification_test.rb | 2 | ||||
-rw-r--r-- | actionpack/test/dispatch/rack_test.rb | 2 | ||||
-rw-r--r-- | actionpack/test/dispatch/request_test.rb | 20 | ||||
-rw-r--r-- | actionpack/test/dispatch/routing_test.rb | 68 |
16 files changed, 193 insertions, 72 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 7c2cebcff1..c4ebedf1b0 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,3 +1,18 @@ +*Rails 3.0.0 [Edge] (pending)* + +* Changed the object used in routing constraints to be an instance of + ActionDispatch::Request rather than Rack::Request + +* Changed ActionDispatch::Request#method to return a String, to be compatible + with Rack::Request. Added ActionDispatch::Request#method_symbol to + return a symbol form of the request method. + +* Changed ActionDispatch::Request#method to return the original + method and #request_method to return the overridden method in the + case of methodoverride being used (this means that #method returns + "HEAD" and #request_method returns "GET" in HEAD requests). This + is for compatibility with Rack::Request + *Rails 3.0.0 [beta 2] (April 1st, 2010)* * #concat is now deprecated in favor of using <%= %> helpers [YK] diff --git a/actionpack/lib/action_controller/metal/cookies.rb b/actionpack/lib/action_controller/metal/cookies.rb index 75e5d40a63..4aaa705203 100644 --- a/actionpack/lib/action_controller/metal/cookies.rb +++ b/actionpack/lib/action_controller/metal/cookies.rb @@ -7,10 +7,11 @@ module ActionController #:nodoc: included do helper_method :cookies end - + private def cookies - request.cookie_jar + raise "You must set config.cookie_secret in your app's config" if config.secret.blank? + request.cookie_jar(:signing_secret => config.secret) end end end diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb index 0b2cee6868..d97c10a293 100644 --- a/actionpack/lib/action_controller/metal/responder.rb +++ b/actionpack/lib/action_controller/metal/responder.rb @@ -216,7 +216,7 @@ module ActionController #:nodoc: # the verb is POST. # def default_action - @action ||= ACTIONS_FOR_VERBS[request.method] + @action ||= ACTIONS_FOR_VERBS[request.method_symbol] end end end diff --git a/actionpack/lib/action_controller/metal/verification.rb b/actionpack/lib/action_controller/metal/verification.rb index bce942b588..b7fc2b7421 100644 --- a/actionpack/lib/action_controller/metal/verification.rb +++ b/actionpack/lib/action_controller/metal/verification.rb @@ -108,7 +108,7 @@ module ActionController #:nodoc: end def verify_method(options) # :nodoc: - [*options[:method]].all? { |v| request.method != v.to_sym } if options[:method] + [*options[:method]].all? { |v| request.method_symbol != v.to_sym } if options[:method] end def verify_request_xhr_status(options) # :nodoc: diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 8b8426b5aa..8560a6fc9c 100755 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -45,47 +45,65 @@ module ActionDispatch 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 } - # Returns the true HTTP request \method as a lowercase symbol, such as - # <tt>:get</tt>. If the request \method is not listed in the HTTP_METHODS - # constant above, an UnknownHttpMethod exception is raised. + # Returns the HTTP \method that the application should see. + # In the case where the \method was overridden by a middleware + # (for instance, if a HEAD request was converted to a GET, + # or if a _method parameter was used to determine the \method + # the application should use), this \method returns the overridden + # value, not the original. def request_method - method = env["rack.methodoverride.original_method"] || env["REQUEST_METHOD"] + method = env["REQUEST_METHOD"] HTTP_METHOD_LOOKUP[method] || raise(ActionController::UnknownHttpMethod, "#{method}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}") + method + end + + # Returns a symbol form of the #request_method + def request_method_symbol + HTTP_METHOD_LOOKUP[request_method] end - # Returns the HTTP request \method used for action processing as a - # lowercase symbol, such as <tt>:post</tt>. (Unlike #request_method, this - # method returns <tt>:get</tt> for a HEAD request because the two are - # functionally equivalent from the application's perspective.) + # Returns the original value of the environment's REQUEST_METHOD, + # even if it was overridden by middleware. See #request_method for + # more information. def method - method = env["REQUEST_METHOD"] + method = env["rack.methodoverride.original_method"] || env['REQUEST_METHOD'] HTTP_METHOD_LOOKUP[method] || raise(ActionController::UnknownHttpMethod, "#{method}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}") + method + end + + # Returns a symbol form of the #method + def method_symbol + HTTP_METHOD_LOOKUP[method] end - # Is this a GET (or HEAD) request? Equivalent to <tt>request.method == :get</tt>. + # Is this a GET (or HEAD) request? + # Equivalent to <tt>request.request_method == :get</tt>. def get? - method == :get + HTTP_METHOD_LOOKUP[request_method] == :get end - # Is this a POST request? Equivalent to <tt>request.method == :post</tt>. + # Is this a POST request? + # Equivalent to <tt>request.request_method == :post</tt>. def post? - method == :post + HTTP_METHOD_LOOKUP[request_method] == :post end - # Is this a PUT request? Equivalent to <tt>request.method == :put</tt>. + # Is this a PUT request? + # Equivalent to <tt>request.request_method == :put</tt>. def put? - method == :put + HTTP_METHOD_LOOKUP[request_method] == :put end - # Is this a DELETE request? Equivalent to <tt>request.method == :delete</tt>. + # Is this a DELETE request? + # Equivalent to <tt>request.request_method == :delete</tt>. def delete? - method == :delete + HTTP_METHOD_LOOKUP[request_method] == :delete end - # Is this a HEAD request? Since <tt>request.method</tt> sees HEAD as <tt>:get</tt>, - # this \method checks the actual HTTP \method directly. + # Is this a HEAD request? + # Equivalent to <tt>request.method == :head</tt>. def head? - request_method == :head + HTTP_METHOD_LOOKUP[method] == :head end # Provides access to the request's HTTP headers, for example: @@ -96,7 +114,7 @@ module ActionDispatch end def forgery_whitelisted? - method == :get || xhr? || content_mime_type.nil? || !content_mime_type.verify_request? + get? || xhr? || content_mime_type.nil? || !content_mime_type.verify_request? end def media_type diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index cb0d12cab1..71dcac9e94 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -1,7 +1,7 @@ module ActionDispatch class Request - def cookie_jar - env['action_dispatch.cookies'] ||= Cookies::CookieJar.build(self) + def cookie_jar(config = {}) + env['action_dispatch.cookies'] ||= Cookies::CookieJar.build(self, config) end end @@ -51,17 +51,18 @@ module ActionDispatch # only HTTP. Defaults to +false+. class Cookies class CookieJar < Hash #:nodoc: - def self.build(request) - new.tap do |hash| + def self.build(request, config = {}) + new(config).tap do |hash| hash.update(request.cookies) end end - def initialize + def initialize(config = {}) + @config = config @set_cookies = {} @delete_cookies = {} - super + super() end # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists. @@ -111,7 +112,7 @@ module ActionDispatch # cookies.permanent.signed[:remember_me] = current_user.id # # => Set-Cookie: discount=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT def permanent - @permanent ||= PermanentCookieJar.new(self) + @permanent ||= PermanentCookieJar.new(self, @config) end # Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from @@ -119,7 +120,7 @@ module ActionDispatch # cookie was tampered with by the user (or a 3rd party), an ActiveSupport::MessageVerifier::InvalidSignature exception will # be raised. # - # This jar requires that you set a suitable secret for the verification on ActionController::Base.cookie_verifier_secret. + # This jar requires that you set a suitable secret for the verification on your app's config.cookie_secret. # # Example: # @@ -128,7 +129,7 @@ module ActionDispatch # # cookies.signed[:discount] # => 45 def signed - @signed ||= SignedCookieJar.new(self) + @signed ||= SignedCookieJar.new(self, @config) end def write(response) @@ -138,8 +139,9 @@ module ActionDispatch end class PermanentCookieJar < CookieJar #:nodoc: - def initialize(parent_jar) + def initialize(parent_jar, config = {}) @parent_jar = parent_jar + @config = config end def []=(key, options) @@ -154,11 +156,7 @@ module ActionDispatch end def signed - @signed ||= SignedCookieJar.new(self) - end - - def controller - @parent_jar.controller + @signed ||= SignedCookieJar.new(self, @config) end def method_missing(method, *arguments, &block) @@ -167,18 +165,16 @@ module ActionDispatch end class SignedCookieJar < CookieJar #:nodoc: - def initialize(parent_jar) - unless ActionController::Base.config.secret - raise "You must set ActionController::Base.config.secret" - end - + def initialize(parent_jar, config = {}) + raise 'Missing cookie signing secret' if config[:signing_secret].blank? @parent_jar = parent_jar - @verifier = ActiveSupport::MessageVerifier.new(ActionController::Base.config.secret) + @config = config + @verifier = ActiveSupport::MessageVerifier.new(config[:signing_secret]) end def [](name) - if value = @parent_jar[name] - @verifier.verify(value) + if signed_message = @parent_jar[name] + @verifier.verify(signed_message) end end diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 2c7ee7dad0..925e91f081 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -5,20 +5,20 @@ module ActionDispatch module Routing class Mapper class Constraints #:nodoc: - def self.new(app, constraints = []) + def self.new(app, constraints, request = Rack::Request) if constraints.any? - super(app, constraints) + super(app, constraints, request) else app end end - def initialize(app, constraints = []) - @app, @constraints = app, constraints + def initialize(app, constraints, request) + @app, @constraints, @request = app, constraints, request end def call(env) - req = Rack::Request.new(env) + req = @request.new(env) @constraints.each { |constraint| if constraint.respond_to?(:matches?) && !constraint.matches?(req) @@ -83,7 +83,8 @@ module ActionDispatch def app Constraints.new( to.respond_to?(:call) ? to : Routing::RouteSet::Dispatcher.new(:defaults => defaults), - blocks + blocks, + @set.request_class ) end @@ -242,7 +243,11 @@ module ActionDispatch lambda do |env| req = Request.new(env) - uri = URI.parse(path_proc.call(req.symbolized_path_parameters)) + + params = [req.symbolized_path_parameters] + params << req if path_proc.arity > 1 + + uri = URI.parse(path_proc.call(*params)) uri.scheme ||= req.scheme uri.host ||= req.host uri.port ||= req.port unless req.port == 80 diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index c8e4371bb7..f1965f38b9 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -187,18 +187,19 @@ module ActionDispatch attr_accessor :routes, :named_routes attr_accessor :disable_clear_and_finalize, :resources_path_names - attr_accessor :default_url_options + attr_accessor :default_url_options, :request_class def self.default_resources_path_names { :new => 'new', :edit => 'edit' } end - def initialize + def initialize(request_class = ActionDispatch::Request) self.routes = [] self.named_routes = NamedRouteCollection.new self.resources_path_names = self.class.default_resources_path_names.dup self.controller_namespaces = Set.new self.default_url_options = {} + self.request_class = request_class @disable_clear_and_finalize = false clear! @@ -232,7 +233,10 @@ module ActionDispatch @finalized = false routes.clear named_routes.clear - @set = ::Rack::Mount::RouteSet.new(:parameters_key => PARAMETERS_KEY) + @set = ::Rack::Mount::RouteSet.new( + :parameters_key => PARAMETERS_KEY, + :request_class => request_class + ) end def install_helpers(destinations = [ActionController::Base, ActionView::Base], regenerate_code = false) diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 031fa1dfb4..8d107d9aa5 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -266,11 +266,12 @@ module ActionDispatch "HTTP_ACCEPT" => accept } + session = Rack::Test::Session.new(@mock_session) + (rack_environment || {}).each do |key, value| env[key] = value end - session = Rack::Test::Session.new(@mock_session) session.request(path, env) @request_count += 1 diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index 1415966869..5925faf810 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -10,6 +10,12 @@ module ActionView # This allows you to use the same format for links in views # and controllers. module UrlHelper + # This helper may be included in any class that includes the + # URL helpers of a router (router.url_helpers). Some methods + # provided here will only work in the context of a request + # (link_to_unless_current, for instance), which must be provided + # as a method called #request on the context. + extend ActiveSupport::Concern include ActionDispatch::Routing::UrlFor @@ -307,7 +313,6 @@ module ActionView # # </div> # # </form>" # # - def button_to(name, options = {}, html_options = {}) html_options = html_options.stringify_keys convert_boolean_attributes!(html_options, %w( disabled )) diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 3df2bd8eed..3c0cd35359 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -18,7 +18,7 @@ module ActionView attr_reader :source, :identifier, :handler, :virtual_path, :formats - def self.finalizer_for(method_name) + Finalizer = proc do |method_name| proc do ActionView::CompiledTemplates.module_eval do remove_possible_method method_name @@ -77,7 +77,6 @@ module ActionView private def compile(locals, view) method_name = build_method_name(locals) - return method_name if view.respond_to?(method_name) locals_code = locals.keys.map! { |key| "#{key} = local_assigns[:#{key}];" }.join @@ -106,7 +105,8 @@ module ActionView begin ActionView::CompiledTemplates.module_eval(source, identifier, line) - ObjectSpace.define_finalizer(self, self.class.finalizer_for(method_name)) + ObjectSpace.define_finalizer(self, Finalizer[method_name]) + method_name rescue Exception => e # errors from template code if logger = (view && view.logger) diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb index 1e2ee06adc..14c0c3708b 100644 --- a/actionpack/test/controller/integration_test.rb +++ b/actionpack/test/controller/integration_test.rb @@ -236,7 +236,7 @@ class IntegrationProcessTest < ActionController::IntegrationTest end def method - render :text => "method: #{request.method}" + render :text => "method: #{request.method.downcase}" end def cookie_monster diff --git a/actionpack/test/controller/verification_test.rb b/actionpack/test/controller/verification_test.rb index 11d0d10897..0600ec2ec1 100644 --- a/actionpack/test/controller/verification_test.rb +++ b/actionpack/test/controller/verification_test.rb @@ -71,7 +71,7 @@ class VerificationTest < ActionController::TestCase end def guarded_by_method - render :text => "#{request.method}" + render :text => "#{request.method.downcase}" end def guarded_by_xhr diff --git a/actionpack/test/dispatch/rack_test.rb b/actionpack/test/dispatch/rack_test.rb index 504bebbb86..698f980296 100644 --- a/actionpack/test/dispatch/rack_test.rb +++ b/actionpack/test/dispatch/rack_test.rb @@ -142,7 +142,7 @@ class RackRequestTest < BaseRackTest assert_equal "google.com", @request.remote_host assert_equal "kevin", @request.remote_ident assert_equal "kevin", @request.remote_user - assert_equal :get, @request.request_method + assert_equal "GET", @request.request_method assert_equal "/dispatch.fcgi", @request.script_name assert_equal "glu.ttono.us", @request.server_name assert_equal 8007, @request.server_port diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index 9093e1ed65..e5ee412021 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -223,10 +223,17 @@ class RequestTest < ActiveSupport::TestCase assert request.ssl? end - test "symbolized request methods" do + test "String request methods" do [:get, :post, :put, :delete].each do |method| request = stub_request 'REQUEST_METHOD' => method.to_s.upcase - assert_equal method, request.method + assert_equal method.to_s.upcase, request.method + end + end + + test "Symbol forms of request methods via method_symbol" do + [:get, :post, :put, :delete].each do |method| + request = stub_request 'REQUEST_METHOD' => method.to_s.upcase + assert_equal method, request.method_symbol end end @@ -238,9 +245,9 @@ class RequestTest < ActiveSupport::TestCase end test "allow method hacking on post" do - [:get, :options, :put, :post, :delete].each do |method| + %w(GET OPTIONS PUT POST DELETE).each do |method| request = stub_request "REQUEST_METHOD" => method.to_s.upcase - assert_equal(method == :head ? :get : method, request.method) + assert_equal(method == "HEAD" ? "GET" : method, request.method) end end @@ -255,13 +262,14 @@ class RequestTest < ActiveSupport::TestCase [:get, :put, :delete].each do |method| request = stub_request 'REQUEST_METHOD' => method.to_s.upcase, 'action_dispatch.request.request_parameters' => { :_method => 'put' } - assert_equal method, request.method + assert_equal method.to_s.upcase, request.method end end test "head masquerading as get" do request = stub_request 'REQUEST_METHOD' => 'GET', "rack.methodoverride.original_method" => "HEAD" - assert_equal :get, request.method + assert_equal "HEAD", request.method + assert_equal "GET", request.request_method assert request.get? assert request.head? end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 19538cb88b..6ff478aec1 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -39,6 +39,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest match 'account/modulo/:name', :to => redirect("/%{name}s") match 'account/proc/:name', :to => redirect {|params| "/#{params[:name].pluralize}" } + match 'account/proc_req' => redirect {|params, req| "/#{req.method}" } + match 'account/google' => redirect('http://www.google.com/') match 'openid/login', :via => [:get, :post], :to => "openid#login" @@ -187,6 +189,63 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + class TestAltApp < ActionController::IntegrationTest + class AltRequest + def initialize(env) + @env = env + end + + def path_info + "/" + end + + def request_method + "GET" + end + + def x_header + @env["HTTP_X_HEADER"] || "" + end + end + + class XHeader + def call(env) + [200, {"Content-Type" => "text/html"}, ["XHeader"]] + end + end + + class AltApp + def call(env) + [200, {"Content-Type" => "text/html"}, ["Alternative App"]] + end + end + + AltRoutes = ActionDispatch::Routing::RouteSet.new(AltRequest) + AltRoutes.draw do + get "/" => XHeader.new, :constraints => {:x_header => /HEADER/} + get "/" => AltApp.new + end + + def app + AltRoutes + end + + def test_alt_request_without_header + get "/" + assert_equal "Alternative App", @response.body + end + + def test_alt_request_with_matched_header + get "/", {}, "HTTP_X_HEADER" => "HEADER" + assert_equal "XHeader", @response.body + end + + def test_alt_request_with_unmatched_header + get "/", {}, "HTTP_X_HEADER" => "NON_MATCH" + assert_equal "Alternative App", @response.body + end + end + def app Routes end @@ -294,6 +353,15 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_redirect_proc_with_request + with_test_routes do + get '/account/proc_req' + assert_equal 301, @response.status + assert_equal 'http://www.example.com/GET', @response.headers['Location'] + assert_equal 'Moved Permanently', @response.body + end + end + def test_openid with_test_routes do get '/openid/login' |