rack-test 0.6.3 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,8 @@
1
- require "uri"
2
- require "time"
1
+ require 'uri'
2
+ require 'time'
3
3
 
4
4
  module Rack
5
5
  module Test
6
-
7
6
  class Cookie # :nodoc:
8
7
  include Rack::Utils
9
8
 
@@ -21,8 +20,8 @@ module Rack
21
20
  @name, @value = parse_query(@name_value_raw, ';').to_a.first
22
21
  @options = parse_query(options, ';')
23
22
 
24
- @options["domain"] ||= (uri.host || default_host)
25
- @options["path"] ||= uri.path.sub(/\/[^\/]*\Z/, "")
23
+ @options['domain'] ||= (uri.host || default_host)
24
+ @options['path'] ||= uri.path.sub(/\/[^\/]*\Z/, '')
26
25
  end
27
26
 
28
27
  def replaces?(other)
@@ -41,21 +40,25 @@ module Rack
41
40
 
42
41
  # :api: private
43
42
  def domain
44
- @options["domain"]
43
+ @options['domain']
45
44
  end
46
45
 
47
46
  def secure?
48
- @options.has_key?("secure")
47
+ @options.key?('secure')
48
+ end
49
+
50
+ def http_only?
51
+ @options.key?('HttpOnly')
49
52
  end
50
53
 
51
54
  # :api: private
52
55
  def path
53
- @options["path"].strip || "/"
56
+ ([*@options['path']].first.split(',').first || '/').strip
54
57
  end
55
58
 
56
59
  # :api: private
57
60
  def expires
58
- Time.parse(@options["expires"]) if @options["expires"]
61
+ Time.parse(@options['expires']) if @options['expires']
59
62
  end
60
63
 
61
64
  # :api: private
@@ -67,19 +70,17 @@ module Rack
67
70
  def valid?(uri)
68
71
  uri ||= default_uri
69
72
 
70
- if uri.host.nil?
71
- uri.host = @default_host
72
- end
73
+ uri.host = @default_host if uri.host.nil?
73
74
 
74
75
  real_domain = domain =~ /^\./ ? domain[1..-1] : domain
75
- (!secure? || (secure? && uri.scheme == "https")) &&
76
- uri.host =~ Regexp.new("#{Regexp.escape(real_domain)}$", Regexp::IGNORECASE) &&
77
- uri.path =~ Regexp.new("^#{Regexp.escape(path)}")
76
+ (!secure? || (secure? && uri.scheme == 'https')) &&
77
+ uri.host =~ Regexp.new("#{Regexp.escape(real_domain)}$", Regexp::IGNORECASE) &&
78
+ uri.path =~ Regexp.new("^#{Regexp.escape(path)}")
78
79
  end
79
80
 
80
81
  # :api: private
81
82
  def matches?(uri)
82
- ! expired? && valid?(uri)
83
+ !expired? && valid?(uri)
83
84
  end
84
85
 
85
86
  # :api: private
@@ -88,15 +89,24 @@ module Rack
88
89
  [name, path, domain.reverse] <=> [other.name, other.path, other.domain.reverse]
89
90
  end
90
91
 
91
- protected
92
+ def to_h
93
+ @options.merge(
94
+ 'value' => @value,
95
+ 'HttpOnly' => http_only?,
96
+ 'secure' => secure?
97
+ )
98
+ end
99
+ alias to_hash to_h
100
+
101
+ protected
92
102
 
93
103
  def default_uri
94
- URI.parse("//" + @default_host + "/")
104
+ URI.parse('//' + @default_host + '/')
95
105
  end
96
-
97
106
  end
98
107
 
99
108
  class CookieJar # :nodoc:
109
+ DELIMITER = '; '.freeze
100
110
 
101
111
  # :api: private
102
112
  def initialize(cookies = [], default_host = DEFAULT_HOST)
@@ -108,13 +118,17 @@ module Rack
108
118
  def [](name)
109
119
  cookies = hash_for(nil)
110
120
  # TODO: Should be case insensitive
111
- cookies[name] && cookies[name].value
121
+ cookies[name.to_s] && cookies[name.to_s].value
112
122
  end
113
123
 
114
124
  def []=(name, value)
115
125
  merge("#{name}=#{Rack::Utils.escape(value)}")
116
126
  end
117
127
 
128
+ def get_cookie(name)
129
+ hash_for(nil).fetch(name, nil)
130
+ end
131
+
118
132
  def delete(name)
119
133
  @cookies.reject! do |cookie|
120
134
  cookie.name == name
@@ -126,7 +140,7 @@ module Rack
126
140
 
127
141
  if raw_cookies.is_a? String
128
142
  raw_cookies = raw_cookies.split("\n")
129
- raw_cookies.reject!{|c| c.empty? }
143
+ raw_cookies.reject!(&:empty?)
130
144
  end
131
145
 
132
146
  raw_cookies.each do |raw_cookie|
@@ -146,7 +160,7 @@ module Rack
146
160
 
147
161
  # :api: private
148
162
  def for(uri)
149
- hash_for(uri).values.map { |c| c.raw }.join(';')
163
+ hash_for(uri).values.map(&:raw).join(DELIMITER)
150
164
  end
151
165
 
152
166
  def to_hash
@@ -156,10 +170,10 @@ module Rack
156
170
  cookies[name] = cookie.value
157
171
  end
158
172
 
159
- return cookies
173
+ cookies
160
174
  end
161
175
 
162
- protected
176
+ protected
163
177
 
164
178
  def hash_for(uri = nil)
165
179
  cookies = {}
@@ -173,10 +187,8 @@ module Rack
173
187
  cookies[cookie.name] = cookie if !uri || cookie.matches?(uri)
174
188
  end
175
189
 
176
- return cookies
190
+ cookies
177
191
  end
178
-
179
192
  end
180
-
181
193
  end
182
194
  end
@@ -1,8 +1,7 @@
1
- require "forwardable"
1
+ require 'forwardable'
2
2
 
3
3
  module Rack
4
4
  module Test
5
-
6
5
  # This module serves as the primary integration point for using Rack::Test
7
6
  # in a testing environment. It depends on an app method being defined in the
8
7
  # same context, and provides the Rack::Test API methods (see Rack::Test::Session
@@ -56,26 +55,27 @@ module Rack
56
55
  @_current_session_names ||= [:default]
57
56
  end
58
57
 
59
- METHODS = [
60
- :request,
61
- :get,
62
- :post,
63
- :put,
64
- :patch,
65
- :delete,
66
- :options,
67
- :head,
68
- :follow_redirect!,
69
- :header,
70
- :env,
71
- :set_cookie,
72
- :clear_cookies,
73
- :authorize,
74
- :basic_authorize,
75
- :digest_authorize,
76
- :last_response,
77
- :last_request
78
- ]
58
+ METHODS = %i[
59
+ request
60
+ get
61
+ post
62
+ put
63
+ patch
64
+ delete
65
+ options
66
+ head
67
+ custom_request
68
+ follow_redirect!
69
+ header
70
+ env
71
+ set_cookie
72
+ clear_cookies
73
+ authorize
74
+ basic_authorize
75
+ digest_authorize
76
+ last_response
77
+ last_request
78
+ ].freeze
79
79
 
80
80
  def_delegators :current_session, *METHODS
81
81
  end
@@ -1,14 +1,12 @@
1
1
  module Rack
2
2
  module Test
3
-
4
3
  class MockDigestRequest # :nodoc:
5
-
6
4
  def initialize(params)
7
5
  @params = params
8
6
  end
9
7
 
10
8
  def method_missing(sym)
11
- if @params.has_key? k = sym.to_s
9
+ if @params.key? k = sym.to_s
12
10
  return @params[k]
13
11
  end
14
12
 
@@ -22,8 +20,6 @@ module Rack
22
20
  def response(password)
23
21
  Rack::Auth::Digest::MD5.new(nil).send :digest, self, password
24
22
  end
25
-
26
23
  end
27
-
28
24
  end
29
25
  end
@@ -1,16 +1,15 @@
1
- require "tempfile"
2
- require "fileutils"
1
+ require 'fileutils'
2
+ require 'pathname'
3
+ require 'tempfile'
3
4
 
4
5
  module Rack
5
6
  module Test
6
-
7
7
  # Wraps a Tempfile with a content type. Including one or more UploadedFile's
8
8
  # in the params causes Rack::Test to build and issue a multipart request.
9
9
  #
10
10
  # Example:
11
11
  # post "/photos", "file" => Rack::Test::UploadedFile.new("me.jpg", "image/jpeg")
12
12
  class UploadedFile
13
-
14
13
  # The filename, *not* including the path, of the "uploaded" file
15
14
  attr_reader :original_filename
16
15
 
@@ -20,34 +19,67 @@ module Rack
20
19
  # The content type of the "uploaded" file
21
20
  attr_accessor :content_type
22
21
 
23
- def initialize(path, content_type = "text/plain", binary = false)
24
- raise "#{path} file does not exist" unless ::File.exist?(path)
25
-
22
+ # Creates a new UploadedFile instance.
23
+ #
24
+ # @param content [IO, Pathname, String, StringIO] a path to a file, or an {IO} or {StringIO} object representing the
25
+ # file.
26
+ # @param content_type [String]
27
+ # @param binary [Boolean] an optional flag that indicates whether the file should be open in binary mode or not.
28
+ # @param original_filename [String] an optional parameter that provides the original filename if `content` is a StringIO
29
+ # object. Not used for other kind of `content` objects.
30
+ def initialize(content, content_type = 'text/plain', binary = false, original_filename: nil)
31
+ if original_filename
32
+ initialize_from_stringio(content, original_filename)
33
+ else
34
+ initialize_from_file_path(content)
35
+ end
26
36
  @content_type = content_type
27
- @original_filename = ::File.basename(path)
28
-
29
- @tempfile = Tempfile.new(@original_filename)
30
- @tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
31
37
  @tempfile.binmode if binary
32
-
33
- FileUtils.copy_file(path, @tempfile.path)
34
38
  end
35
39
 
36
40
  def path
37
- @tempfile.path
41
+ tempfile.path
38
42
  end
39
43
 
40
- alias_method :local_path, :path
44
+ alias local_path path
41
45
 
42
46
  def method_missing(method_name, *args, &block) #:nodoc:
43
- @tempfile.__send__(method_name, *args, &block)
47
+ tempfile.public_send(method_name, *args, &block)
44
48
  end
45
49
 
46
- def respond_to?(method_name, include_private = false) #:nodoc:
47
- @tempfile.respond_to?(method_name, include_private) || super
50
+ def respond_to_missing?(method_name, include_private = false) #:nodoc:
51
+ tempfile.respond_to?(method_name, include_private) || super
48
52
  end
49
53
 
50
- end
54
+ def self.finalize(file)
55
+ proc { actually_finalize file }
56
+ end
57
+
58
+ def self.actually_finalize(file)
59
+ file.close
60
+ file.unlink
61
+ end
51
62
 
63
+ private
64
+
65
+ def initialize_from_stringio(stringio, original_filename)
66
+ @tempfile = stringio
67
+ @original_filename = original_filename || raise(ArgumentError, 'Missing `original_filename` for StringIO object')
68
+ end
69
+
70
+ def initialize_from_file_path(path)
71
+ raise "#{path} file does not exist" unless ::File.exist?(path)
72
+
73
+ @original_filename = ::File.basename(path)
74
+ extension = ::File.extname(@original_filename)
75
+
76
+ @tempfile = Tempfile.new([::File.basename(@original_filename, extension), extension])
77
+ @tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
78
+
79
+ ObjectSpace.define_finalizer(self, self.class.finalize(@tempfile))
80
+
81
+ FileUtils.copy_file(path, @tempfile.path)
82
+ end
83
+ end
52
84
  end
53
85
  end
@@ -1,38 +1,36 @@
1
1
  module Rack
2
2
  module Test
3
-
4
3
  module Utils # :nodoc:
5
4
  include Rack::Utils
5
+ extend Rack::Utils
6
6
 
7
7
  def build_nested_query(value, prefix = nil)
8
8
  case value
9
9
  when Array
10
- value.map do |v|
11
- unless unescape(prefix) =~ /\[\]$/
12
- prefix = "#{prefix}[]"
13
- end
14
- build_nested_query(v, "#{prefix}")
15
- end.join("&")
10
+ if value.empty?
11
+ "#{prefix}[]="
12
+ else
13
+ value.map do |v|
14
+ prefix = "#{prefix}[]" unless unescape(prefix) =~ /\[\]$/
15
+ build_nested_query(v, prefix.to_s)
16
+ end.join('&')
17
+ end
16
18
  when Hash
17
19
  value.map do |k, v|
18
20
  build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
19
- end.join("&")
21
+ end.join('&')
20
22
  when NilClass
21
23
  prefix.to_s
22
24
  else
23
25
  "#{prefix}=#{escape(value)}"
24
26
  end
25
27
  end
26
-
27
28
  module_function :build_nested_query
28
29
 
29
- def build_multipart(params, first = true)
30
+ def build_multipart(params, first = true, multipart = false)
30
31
  if first
31
- unless params.is_a?(Hash)
32
- raise ArgumentError, "value must be a Hash"
33
- end
32
+ raise ArgumentError, 'value must be a Hash' unless params.is_a?(Hash)
34
33
 
35
- multipart = false
36
34
  query = lambda { |value|
37
35
  case value
38
36
  when Array
@@ -47,7 +45,7 @@ module Rack
47
45
  return nil unless multipart
48
46
  end
49
47
 
50
- flattened_params = Hash.new
48
+ flattened_params = {}
51
49
 
52
50
  params.each do |key, value|
53
51
  k = first ? key.to_s : "[#{key}]"
@@ -55,23 +53,21 @@ module Rack
55
53
  case value
56
54
  when Array
57
55
  value.map do |v|
58
-
59
- if (v.is_a?(Hash))
56
+ if v.is_a?(Hash)
60
57
  nested_params = {}
61
- build_multipart(v, false).each { |subkey, subvalue|
58
+ build_multipart(v, false).each do |subkey, subvalue|
62
59
  nested_params[subkey] = subvalue
63
- }
60
+ end
64
61
  flattened_params["#{k}[]"] ||= []
65
62
  flattened_params["#{k}[]"] << nested_params
66
63
  else
67
64
  flattened_params["#{k}[]"] = value
68
65
  end
69
-
70
66
  end
71
67
  when Hash
72
- build_multipart(value, false).each { |subkey, subvalue|
68
+ build_multipart(value, false).each do |subkey, subvalue|
73
69
  flattened_params[k + subkey] = subvalue
74
- }
70
+ end
75
71
  else
76
72
  flattened_params[k] = value
77
73
  end
@@ -83,27 +79,28 @@ module Rack
83
79
  flattened_params
84
80
  end
85
81
  end
86
-
87
82
  module_function :build_multipart
88
83
 
89
- private
84
+ private
85
+
90
86
  def build_parts(parameters)
91
87
  get_parts(parameters).join + "--#{MULTIPART_BOUNDARY}--\r"
92
88
  end
89
+ module_function :build_parts
93
90
 
94
91
  def get_parts(parameters)
95
- parameters.map { |name, value|
96
- if name =~ /\[\]\Z/ && value.is_a?(Array) && value.all? {|v| v.is_a?(Hash)}
97
- value.map { |hash|
92
+ parameters.map do |name, value|
93
+ if name =~ /\[\]\Z/ && value.is_a?(Array) && value.all? { |v| v.is_a?(Hash) }
94
+ value.map do |hash|
98
95
  new_value = {}
99
- hash.each { |k, v| new_value[name+k] = v }
96
+ hash.each { |k, v| new_value[name + k] = v }
100
97
  get_parts(new_value).join
101
- }.join
98
+ end.join
102
99
  else
103
100
  if value.respond_to?(:original_filename)
104
101
  build_file_part(name, value)
105
102
 
106
- elsif value.is_a?(Array) and value.all? { |v| v.respond_to?(:original_filename) }
103
+ elsif value.is_a?(Array) && value.all? { |v| v.respond_to?(:original_filename) }
107
104
  value.map do |v|
108
105
  build_file_part(name, v)
109
106
  end.join
@@ -113,15 +110,14 @@ module Rack
113
110
  Rack::Test.encoding_aware_strings? ? primitive_part.force_encoding('BINARY') : primitive_part
114
111
  end
115
112
  end
116
- }
113
+ end
117
114
  end
115
+ module_function :get_parts
118
116
 
119
117
  def build_primitive_part(parameter_name, value)
120
- unless value.is_a? Array
121
- value = [value]
122
- end
118
+ value = [value] unless value.is_a? Array
123
119
  value.map do |v|
124
- <<-EOF
120
+ <<-EOF
125
121
  --#{MULTIPART_BOUNDARY}\r
126
122
  Content-Disposition: form-data; name="#{parameter_name}"\r
127
123
  \r
@@ -129,22 +125,20 @@ Content-Disposition: form-data; name="#{parameter_name}"\r
129
125
  EOF
130
126
  end.join
131
127
  end
128
+ module_function :build_primitive_part
132
129
 
133
130
  def build_file_part(parameter_name, uploaded_file)
134
- ::File.open(uploaded_file.path, "rb") do |physical_file|
135
- physical_file.set_encoding(Encoding::BINARY) if physical_file.respond_to?(:set_encoding)
136
- <<-EOF
131
+ uploaded_file.set_encoding(Encoding::BINARY) if uploaded_file.respond_to?(:set_encoding)
132
+ <<-EOF
137
133
  --#{MULTIPART_BOUNDARY}\r
138
134
  Content-Disposition: form-data; name="#{parameter_name}"; filename="#{escape(uploaded_file.original_filename)}"\r
139
135
  Content-Type: #{uploaded_file.content_type}\r
140
- Content-Length: #{::File.stat(uploaded_file.path).size}\r
136
+ Content-Length: #{uploaded_file.size}\r
141
137
  \r
142
- #{physical_file.read}\r
138
+ #{uploaded_file.read}\r
143
139
  EOF
144
- end
145
140
  end
146
-
141
+ module_function :build_file_part
147
142
  end
148
-
149
143
  end
150
144
  end