module Rack::TestApp

Constants

VERSION

Public Class Methods

new_env(meth=:GET, path="/", query: nil, form: nil, multipart: nil, json: nil, input: nil, headers: nil, cookies: nil, env: nil) click to toggle source

Builds environ hash object.

ex:

json = {"x"=>1, "y"=>2}
env = Rack::TestApp.new_env(:POST, '/api/entry?x=1', json: json)
p env['REQUEST_METHOD']    #=> 'POST'
p env['PATH_INFO']         #=> '/api/entry'
p env['QUERY_STRING']      #=> 'x=1'
p env['CONTENT_TYPE']      #=> 'application/json'
p JSON.parse(env['rack.input'].read())  #=> {"x"=>1, "y"=>2}
# File lib/rack/test_app.rb, line 183
def self.new_env(meth=:GET, path="/", query: nil, form: nil, multipart: nil, json: nil, input: nil, headers: nil, cookies: nil, env: nil)
  #uri = "http://localhost:80#{path}"
  #opts["REQUEST_METHOD"] = meth
  #env = Rack::MockRequest.env_for(uri, opts)
  #
  #; [!j879z] sets 'HTTPS' with 'on' when 'rack.url_scheme' is 'https'.
  #; [!vpwvu] sets 'HTTPS' with 'on' when 'HTTPS' is 'on'.
  https = env && (env['rack.url_scheme'] == 'https' || env['HTTPS'] == 'on')
  #
  err = proc {|a, b|
    ArgumentError.new("new_env(): not allowed both '#{a}' and '#{b}' at a time.")
  }
  ctype = nil
  #; [!2uvyb] raises ArgumentError when both query string and 'query' kwarg specified.
  if query
    arr = path.split('?', 2)
    arr.length != 2  or
      raise ArgumentError.new("new_env(): not allowed both query string and 'query' kwarg at a time.")
  #; [!8tq3m] accepts query string in path string.
  else
    path, query = path.split('?', 2)
  end
  #; [!d1c83] when 'form' kwarg specified...
  if form
    #; [!c779l] raises ArgumentError when both 'form' and 'json' are specified.
    ! json  or raise err.call('form', 'json')
    input = Util.build_query_string(form)
    #; [!5iv35] sets content type with 'application/x-www-form-urlencoded'.
    ctype = "application/x-www-form-urlencoded"
  end
  #; [!prv5z] when 'json' kwarg specified...
  if json
    #; [!2o0ph] raises ArgumentError when both 'json' and 'multipart' are specified.
    ! multipart  or raise err.call('json', 'multipart')
    input = json.is_a?(String) ? json : JSON.dump(json)
    #; [!ta24a] sets content type with 'application/json'.
    ctype = "application/json"
  end
  #; [!dnvgj] when 'multipart' kwarg specified...
  if multipart
    #; [!b1d1t] raises ArgumentError when both 'multipart' and 'form' are specified.
    ! form  or raise err.call('multipart', 'form')
    #; [!gko8g] 'multipart:' kwarg accepts Hash object (which is converted into multipart data).
    if multipart.is_a?(Hash)
      dict = multipart
      multipart = dict.each_with_object(MultipartBuilder.new) do |(k, v), mp|
        v.is_a?(::File) ? mp.add_file(k, v) : mp.add(k, v.to_s)
      end
    end
    input = multipart.to_s
    #; [!dq33d] sets content type with 'multipart/form-data'.
    input =~ /\A--(\S+)\r\n/  or
      raise ArgumentError.new("invalid multipart format.")
    boundary = $1
    ctype = "multipart/form-data; boundary=#{boundary}"
  end
  #; [!iamrk] uses 'application/x-www-form-urlencoded' as default content type of input.
  if input && ! ctype
    ctype ||= headers['Content-Type'] || headers['content-type'] if headers
    ctype ||= env['CONTENT_TYPE'] if env
    ctype ||= "application/x-www-form-urlencoded"
  end
  #; [!7hfri] converts input string into binary.
  input ||= ""
  input = input.encode('ascii-8bit') if input.encoding != Encoding::ASCII_8BIT
  #; [!r3soc] converts query string into binary.
  query_str = Util.build_query_string(query || "")
  query_str = query_str.encode('ascii-8bit')
  #; [!na9w6] builds environ hash object.
  environ = {
    "rack.version"      => Rack::VERSION,
    "rack.input"        => StringIO.new(input),
    "rack.errors"       => StringIO.new,
    "rack.multithread"  => true,
    "rack.multiprocess" => true,
    "rack.run_once"     => false,
    "rack.url_scheme"   => https ? "https" : "http",
    "REQUEST_METHOD"    => meth.to_s,
    "SERVER_NAME"       => "localhost",
    "SERVER_PORT"       => https ? "443" : "80",
    "QUERY_STRING"      => query_str,
    "PATH_INFO"         => path,
    "HTTPS"             => https ? "on" : "off",
    "SCRIPT_NAME"       => "",
    "CONTENT_LENGTH"    => (input ? input.bytesize.to_s : "0"),
    "CONTENT_TYPE"      => ctype,
  }
  #; [!ezvdn] unsets CONTENT_TYPE when not input.
  environ.delete("CONTENT_TYPE") if input.empty?
  #; [!r4jz8] copies 'headers' kwarg content into environ with 'HTTP_' prefix.
  #; [!ai9t3] don't add 'HTTP_' to Content-Length and Content-Type headers.
  excepts = ['CONTENT_LENGTH', 'CONTENT_TYPE']
  headers.each do |name, value|
    name =~ /\A[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*\z/  or
      raise ArgumentError.new("invalid http header name: #{name.inspect}")
    value.is_a?(String)  or
      raise ArgumentError.new("http header value should be a string but got: #{value.inspect}")
    ## ex: 'X-Requested-With' -> 'HTTP_X_REQUESTED_WITH'
    k = name.upcase.gsub(/-/, '_')
    k = "HTTP_#{k}" unless excepts.include?(k)
    environ[k] = value
  end if headers
  #; [!a47n9] copies 'env' kwarg content into environ.
  env.each do |name, value|
    case name
    when /\Arack\./
      # ok
    when /\A[A-Z]+(_[A-Z0-9]+)*\z/
      value.is_a?(String)  or
        raise ArgumentError.new("rack env value should be a string but got: #{value.inspect}")
    else
      raise ArgumentError.new("invalid rack env key: #{name}")
    end
    environ[name] = value
  end if env
  #; [!pmefk] sets 'HTTP_COOKIE' when 'cookie' kwarg specified.
  if cookies
    s = cookies.is_a?(Hash) ? cookies.map {|k, v|
      #; [!qj7b8] cookie value can be {:name=>'name', :value=>'value'}.
      v = v[:value] if v.is_a?(Hash) && v[:value]
      "#{Util.percent_encode(k)}=#{Util.percent_encode(v)}"
    }.join('; ') : cookies.to_s
    s = "#{environ['HTTP_COOKIE']}; #{s}" if environ['HTTP_COOKIE']
    environ['HTTP_COOKIE'] = s
  end
  #; [!b3ts8] returns environ hash object.
  return environ
end
wrap(app, env=nil) click to toggle source

Use Rack::TestApp.wrap(app) instead of Rack::TestApp::Wrapper.new(app).

# File lib/rack/test_app.rb, line 477
def self.wrap(app, env=nil)
  #; [!grqlf] creates new Wrapper object.
  return Wrapper.new(app, env)
end