rack-test 0.6.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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