diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 1fc5018561..f76c47aff1 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -116,6 +116,7 @@ spec = Gem::Specification.new do |s| s.requirements << 'none' s.add_dependency('activesupport', '= 3.0.pre' + PKG_BUILD) + s.add_dependency('rack-test', '~> 0.4.1') s.require_path = 'lib' s.autorequire = 'action_controller' diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 884828a01a..94d4e14e31 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -33,7 +33,9 @@ end require 'rack' -$:.unshift "#{File.dirname(__FILE__)}/action_dispatch/vendor/rack-test" +module Rack + autoload :Test, 'rack/test' +end module ActionDispatch autoload :Request, 'action_dispatch/http/request' diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/mock_session.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/mock_session.rb deleted file mode 100644 index eba6226538..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-test/rack/mock_session.rb +++ /dev/null @@ -1,50 +0,0 @@ -module Rack - - class MockSession - attr_writer :cookie_jar - attr_reader :last_response - - def initialize(app, default_host = Rack::Test::DEFAULT_HOST) - @app = app - @default_host = default_host - end - - def clear_cookies - @cookie_jar = Rack::Test::CookieJar.new([], @default_host) - end - - def set_cookie(cookie, uri = nil) - cookie_jar.merge(cookie, uri) - end - - def request(uri, env) - env["HTTP_COOKIE"] ||= cookie_jar.for(uri) - @last_request = Rack::Request.new(env) - status, headers, body = @app.call(@last_request.env) - @last_response = MockResponse.new(status, headers, body, env["rack.errors"].flush) - cookie_jar.merge(last_response.headers["Set-Cookie"], uri) - - @last_response - end - - # Return the last request issued in the session. Raises an error if no - # requests have been sent yet. - def last_request - raise Rack::Test::Error.new("No request yet. Request a page first.") unless @last_request - @last_request - end - - # Return the last response received in the session. Raises an error if - # no requests have been sent yet. - def last_response - raise Rack::Test::Error.new("No response yet. Request a page first.") unless @last_response - @last_response - end - - def cookie_jar - @cookie_jar ||= Rack::Test::CookieJar.new([], @default_host) - end - - end - -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test.rb deleted file mode 100644 index 70384b1d76..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test.rb +++ /dev/null @@ -1,239 +0,0 @@ -unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__) + "/..")) - $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/..")) -end - -require "uri" -require "rack" -require "rack/mock_session" -require "rack/test/cookie_jar" -require "rack/test/mock_digest_request" -require "rack/test/utils" -require "rack/test/methods" -require "rack/test/uploaded_file" - -module Rack - module Test - - VERSION = "0.3.0" - - DEFAULT_HOST = "example.org" - MULTIPART_BOUNDARY = "----------XnJLe9ZIbbGUYtzPQJ16u1" - - # The common base class for exceptions raised by Rack::Test - class Error < StandardError; end - - class Session - extend Forwardable - include Rack::Test::Utils - - def_delegators :@rack_mock_session, :clear_cookies, :set_cookie, :last_response, :last_request - - # Initialize a new session for the given Rack app - def initialize(app, default_host = DEFAULT_HOST) - @headers = {} - @default_host = default_host - @rack_mock_session = Rack::MockSession.new(app, default_host) - end - - # Issue a GET request for the given URI with the given params and Rack - # environment. Stores the issues request object in #last_request and - # the app's response in #last_response. Yield #last_response to a block - # if given. - # - # Example: - # get "/" - def get(uri, params = {}, env = {}, &block) - env = env_for(uri, env.merge(:method => "GET", :params => params)) - process_request(uri, env, &block) - end - - # Issue a POST request for the given URI. See #get - # - # Example: - # post "/signup", "name" => "Bryan" - def post(uri, params = {}, env = {}, &block) - env = env_for(uri, env.merge(:method => "POST", :params => params)) - process_request(uri, env, &block) - end - - # Issue a PUT request for the given URI. See #get - # - # Example: - # put "/" - def put(uri, params = {}, env = {}, &block) - env = env_for(uri, env.merge(:method => "PUT", :params => params)) - process_request(uri, env, &block) - end - - # Issue a DELETE request for the given URI. See #get - # - # Example: - # delete "/" - def delete(uri, params = {}, env = {}, &block) - env = env_for(uri, env.merge(:method => "DELETE", :params => params)) - process_request(uri, env, &block) - end - - # Issue a HEAD request for the given URI. See #get - # - # Example: - # head "/" - def head(uri, params = {}, env = {}, &block) - env = env_for(uri, env.merge(:method => "HEAD", :params => params)) - process_request(uri, env, &block) - end - - # Issue a request to the Rack app for the given URI and optional Rack - # environment. Stores the issues request object in #last_request and - # the app's response in #last_response. Yield #last_response to a block - # if given. - # - # Example: - # request "/" - def request(uri, env = {}, &block) - env = env_for(uri, env) - process_request(uri, env, &block) - end - - # Set a header to be included on all subsequent requests through the - # session. Use a value of nil to remove a previously configured header. - # - # Example: - # header "User-Agent", "Firefox" - def header(name, value) - if value.nil? - @headers.delete(name) - else - @headers[name] = value - end - end - - # Set the username and password for HTTP Basic authorization, to be - # included in subsequent requests in the HTTP_AUTHORIZATION header. - # - # Example: - # basic_authorize "bryan", "secret" - def basic_authorize(username, password) - encoded_login = ["#{username}:#{password}"].pack("m*") - header('HTTP_AUTHORIZATION', "Basic #{encoded_login}") - end - - alias_method :authorize, :basic_authorize - - def digest_authorize(username, password) - @digest_username = username - @digest_password = password - end - - # Rack::Test will not follow any redirects automatically. This method - # will follow the redirect returned in the last response. If the last - # response was not a redirect, an error will be raised. - def follow_redirect! - unless last_response.redirect? - raise Error.new("Last response was not a redirect. Cannot follow_redirect!") - end - - get(last_response["Location"]) - end - - private - - def env_for(path, env) - uri = URI.parse(path) - uri.host ||= @default_host - - env = default_env.merge(env) - - env.update("HTTPS" => "on") if URI::HTTPS === uri - env["X-Requested-With"] = "XMLHttpRequest" if env[:xhr] - - if (env[:method] == "POST" || env["REQUEST_METHOD"] == "POST") && !env.has_key?(:input) - env["CONTENT_TYPE"] = "application/x-www-form-urlencoded" - - multipart = (Hash === env[:params]) && - env[:params].any? { |_, v| UploadedFile === v } - - if multipart - env[:input] = multipart_body(env.delete(:params)) - env["CONTENT_LENGTH"] ||= env[:input].length.to_s - env["CONTENT_TYPE"] = "multipart/form-data; boundary=#{MULTIPART_BOUNDARY}" - else - env[:input] = params_to_string(env.delete(:params)) - end - end - - params = env[:params] || {} - params.update(parse_query(uri.query)) - - uri.query = requestify(params) - - if env.has_key?(:cookie) - set_cookie(env.delete(:cookie), uri) - end - - Rack::MockRequest.env_for(uri.to_s, env) - end - - def process_request(uri, env) - uri = URI.parse(uri) - uri.host ||= @default_host - - @rack_mock_session.request(uri, env) - - if retry_with_digest_auth?(env) - auth_env = env.merge({ - "HTTP_AUTHORIZATION" => digest_auth_header, - "rack-test.digest_auth_retry" => true - }) - auth_env.delete('rack.request') - process_request(uri.path, auth_env) - else - yield last_response if block_given? - - last_response - end - end - - def digest_auth_header - challenge = last_response["WWW-Authenticate"].split(" ", 2).last - params = Rack::Auth::Digest::Params.parse(challenge) - - params.merge!({ - "username" => @digest_username, - "nc" => "00000001", - "cnonce" => "nonsensenonce", - "uri" => last_request.path_info, - "method" => last_request.env["REQUEST_METHOD"], - }) - - params["response"] = MockDigestRequest.new(params).response(@digest_password) - - "Digest #{params}" - end - - def retry_with_digest_auth?(env) - last_response.status == 401 && - digest_auth_configured? && - !env["rack-test.digest_auth_retry"] - end - - def digest_auth_configured? - @digest_username - end - - def default_env - { "rack.test" => true, "REMOTE_ADDR" => "127.0.0.1" }.merge(@headers) - end - - def params_to_string(params) - case params - when Hash then requestify(params) - when nil then "" - else params - end - end - - end - - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/cookie_jar.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/cookie_jar.rb deleted file mode 100644 index d58c914c9b..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/cookie_jar.rb +++ /dev/null @@ -1,169 +0,0 @@ -require "uri" -module Rack - module Test - - class Cookie - include Rack::Utils - - # :api: private - attr_reader :name, :value - - # :api: private - def initialize(raw, uri = nil, default_host = DEFAULT_HOST) - @default_host = default_host - uri ||= default_uri - - # separate the name / value pair from the cookie options - @name_value_raw, options = raw.split(/[;,] */n, 2) - - @name, @value = parse_query(@name_value_raw, ';').to_a.first - @options = parse_query(options, ';') - - @options["domain"] ||= (uri.host || default_host) - @options["path"] ||= uri.path.sub(/\/[^\/]*\Z/, "") - end - - def replaces?(other) - [name.downcase, domain, path] == [other.name.downcase, other.domain, other.path] - end - - # :api: private - def raw - @name_value_raw - end - - # :api: private - def empty? - @value.nil? || @value.empty? - end - - # :api: private - def domain - @options["domain"] - end - - def secure? - @options.has_key?("secure") - end - - # :api: private - def path - @options["path"].strip || "/" - end - - # :api: private - def expires - Time.parse(@options["expires"]) if @options["expires"] - end - - # :api: private - def expired? - expires && expires < Time.now - end - - # :api: private - def valid?(uri) - uri ||= default_uri - - if uri.host.nil? - uri.host = @default_host - end - - (!secure? || (secure? && uri.scheme == "https")) && - uri.host =~ Regexp.new("#{Regexp.escape(domain)}$", Regexp::IGNORECASE) && - uri.path =~ Regexp.new("^#{Regexp.escape(path)}") - end - - # :api: private - def matches?(uri) - ! expired? && valid?(uri) - end - - # :api: private - def <=>(other) - # Orders the cookies from least specific to most - [name, path, domain.reverse] <=> [other.name, other.path, other.domain.reverse] - end - - protected - - def default_uri - URI.parse("//" + @default_host + "/") - end - - end - - class CookieJar - - # :api: private - def initialize(cookies = [], default_host = DEFAULT_HOST) - @default_host = default_host - @cookies = cookies - @cookies.sort! - end - - def [](name) - cookies = hash_for(nil) - # TODO: Should be case insensitive - cookies[name] && cookies[name].value - end - - def []=(name, value) - # TODO: needs proper escaping - merge("#{name}=#{value}") - end - - def merge(raw_cookies, uri = nil) - return unless raw_cookies - - raw_cookies.each_line do |raw_cookie| - cookie = Cookie.new(raw_cookie, uri, @default_host) - self << cookie if cookie.valid?(uri) - end - end - - def <<(new_cookie) - @cookies.reject! do |existing_cookie| - new_cookie.replaces?(existing_cookie) - end - - @cookies << new_cookie - @cookies.sort! - end - - # :api: private - def for(uri) - hash_for(uri).values.map { |c| c.raw }.join(';') - end - - def to_hash - cookies = {} - - hash_for(nil).each do |name, cookie| - cookies[name] = cookie.value - end - - return cookies - end - - protected - - def hash_for(uri = nil) - cookies = {} - - # The cookies are sorted by most specific first. So, we loop through - # all the cookies in order and add it to a hash by cookie name if - # the cookie can be sent to the current URI. It's added to the hash - # so that when we are done, the cookies will be unique by name and - # we'll have grabbed the most specific to the URI. - @cookies.each do |cookie| - cookies[cookie.name] = cookie if cookie.matches?(uri) - end - - return cookies - end - - end - - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/methods.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/methods.rb deleted file mode 100644 index a191fa23d8..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/methods.rb +++ /dev/null @@ -1,45 +0,0 @@ -require "forwardable" - -module Rack - module Test - module Methods - extend Forwardable - - def rack_test_session - @_rack_test_session ||= Rack::Test::Session.new(app) - end - - def rack_mock_session - @_rack_mock_session ||= Rack::MockSession.new(app) - end - - METHODS = [ - :request, - - # HTTP verbs - :get, - :post, - :put, - :delete, - :head, - - # Redirects - :follow_redirect!, - - # Header-related features - :header, - :set_cookie, - :clear_cookies, - :authorize, - :basic_authorize, - :digest_authorize, - - # Expose the last request and response - :last_response, - :last_request - ] - - def_delegators :rack_test_session, *METHODS - end - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/mock_digest_request.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/mock_digest_request.rb deleted file mode 100644 index 81c398ba51..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/mock_digest_request.rb +++ /dev/null @@ -1,27 +0,0 @@ -module Rack - module Test - - class MockDigestRequest - def initialize(params) - @params = params - end - - def method_missing(sym) - if @params.has_key? k = sym.to_s - return @params[k] - end - - super - end - - def method - @params['method'] - end - - def response(password) - Rack::Auth::Digest::MD5.new(nil).send :digest, self, password - end - end - - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/uploaded_file.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/uploaded_file.rb deleted file mode 100644 index 239302fbe4..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/uploaded_file.rb +++ /dev/null @@ -1,36 +0,0 @@ -require "tempfile" - -module Rack - module Test - - class UploadedFile - # The filename, *not* including the path, of the "uploaded" file - attr_reader :original_filename - - # The content type of the "uploaded" file - attr_accessor :content_type - - def initialize(path, content_type = "text/plain", binary = false) - raise "#{path} file does not exist" unless ::File.exist?(path) - @content_type = content_type - @original_filename = ::File.basename(path) - @tempfile = Tempfile.new(@original_filename) - @tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding) - @tempfile.binmode if binary - FileUtils.copy_file(path, @tempfile.path) - end - - def path - @tempfile.path - end - - alias_method :local_path, :path - - def method_missing(method_name, *args, &block) #:nodoc: - @tempfile.__send__(method_name, *args, &block) - end - - end - - end -end diff --git a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/utils.rb b/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/utils.rb deleted file mode 100644 index d25b849709..0000000000 --- a/actionpack/lib/action_dispatch/vendor/rack-test/rack/test/utils.rb +++ /dev/null @@ -1,75 +0,0 @@ -module Rack - module Test - - module Utils - include Rack::Utils - - def requestify(value, prefix = nil) - case value - when Array - value.map do |v| - requestify(v, "#{prefix}[]") - end.join("&") - when Hash - value.map do |k, v| - requestify(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k)) - end.join("&") - else - "#{prefix}=#{escape(value)}" - end - end - - module_function :requestify - - def multipart_requestify(params, first=true) - p = Hash.new - - params.each do |key, value| - k = first ? key.to_s : "[#{key}]" - - if Hash === value - multipart_requestify(value, false).each do |subkey, subvalue| - p[k + subkey] = subvalue - end - else - p[k] = value - end - end - - return p - end - - module_function :multipart_requestify - - def multipart_body(params) - multipart_requestify(params).map do |key, value| - if value.respond_to?(:original_filename) - ::File.open(value.path, "rb") do |f| - f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding) - - <<-EOF ---#{MULTIPART_BOUNDARY}\r -Content-Disposition: form-data; name="#{key}"; filename="#{escape(value.original_filename)}"\r -Content-Type: #{value.content_type}\r -Content-Length: #{::File.stat(value.path).size}\r -\r -#{f.read}\r -EOF - end - else -<<-EOF ---#{MULTIPART_BOUNDARY}\r -Content-Disposition: form-data; name="#{key}"\r -\r -#{value}\r -EOF - end - end.join("")+"--#{MULTIPART_BOUNDARY}--\r" - end - - module_function :multipart_body - - end - - end -end