rack-test 1.1.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 14e25e843eada244c949970f14897865edcf1687b17889560923e32306632423
4
- data.tar.gz: ac325910df89080a58b47b7ec9f7e84c552a597d09a0ed3e1c5600a7cc4a6d5a
3
+ metadata.gz: 98bb9656717c512790c5f65b20a8f0cfc7c2f14e4d9d98d5069e6e5910b256e5
4
+ data.tar.gz: 6305cac1636eef820a082a1431cef2bc24b445b1f902b65c550f976ba84f08e6
5
5
  SHA512:
6
- metadata.gz: a86f02b6cc8a0c7bbebbe64612e174f250abce84458ab732457e3b132f70f93b31f0ca791b75c167796690d3293fb4de02d895be85baeada4b619977b1b1ac39
7
- data.tar.gz: 002d971754b2ac28d329c8b7231d97068939e6b0e17d24d7d7d87088ac15cff4597d560843168a0289098e58639bc39f9c3b22129550a39e0d5f07a03413d70b
6
+ metadata.gz: 420e60a08ec2fa5fd92a358d28c0b17e0a943eb30962756360185091753bdd4eb55b7b58005aa84bb1907cfb7e8129166d1aed6282596f45b8b0f42dd1003306
7
+ data.tar.gz: b7446d71bf41a0f69b9812e867f7b472488ee0534d6206435b77e8266617486687044a814dd6c085003b823e0e349c83b1ca47ea296f1f23346957d66cbed0bc
data/History.md CHANGED
@@ -1,3 +1,122 @@
1
+ ## 2.1.0 / 2023-03-14
2
+
3
+ * Breaking changes:
4
+ * Digest authentication support, deprecated in 2.0.0, has been
5
+ removed (Jeremy Evans #307)
6
+ * requiring rack/mock_session, deprecated in 2.0.0, has been removed
7
+ (Jeremy Evans #307)
8
+
9
+ * Minor enhancements:
10
+ * The `original_filename` for `Rack::Test::UploadedFile` can now be
11
+ set even if the content of the file comes from a file path
12
+ (Stuart Chinery #314)
13
+ * Add `Rack::Test::Session#restore_state`, for executing a block
14
+ and restoring current state (last request, last response, and
15
+ cookies) after the block (Jeremy Evans #316)
16
+ * Make `Rack::Test::Methods` support `default_host` method similar to
17
+ `app`, which will set the default host used for requests to the app
18
+ (Jeremy Evans #317 #318)
19
+ * Allow responses to set cookie paths not matching the current
20
+ request URI. Such cookies will only be sent for paths matching
21
+ the cookie path (Chris Waters #322)
22
+ * Ignore leading dot for cookie domains, per RFC 6265 (Stephen Crosby
23
+ #329)
24
+ * Avoid creating empty multipart body if params is empty in
25
+ `Rack::Test::Session#env_for` (Ryunosuke Sato #331)
26
+
27
+ ## 2.0.2 / 2022-06-28
28
+
29
+ * Bug fixes:
30
+ * Fix additional incompatible character encodings error when building
31
+ uploaded bodies (Jeremy Evans #311)
32
+
33
+ ## 2.0.1 / 2022-06-27
34
+
35
+ * Bug fixes:
36
+ * Fix incompatible character encodings error when building uploaded
37
+ file bodies (Jeremy Evans #308 #309)
38
+
39
+ ## 2.0.0 / 2022-06-24
40
+
41
+ * Breaking changes:
42
+ * Digest authentication support is now deprecated, as it relies on
43
+ digest authentication support in rack, which has been deprecated
44
+ (Jeremy Evans #294)
45
+ * `Rack::Test::Utils.build_primitive_part` no longer handles array
46
+ values (Jeremy Evans #292)
47
+ * `Rack::Test::Utils` module methods other than `build_nested_query`
48
+ and `build_multipart` are now private methods (Jeremy Evans #297)
49
+ * `Rack::MockSession` has been combined into `Rack::Test::Session`,
50
+ and remains as an alias to `Rack::Test::Session`, but to keep some
51
+ backwards compatibility, `Rack::Test::Session.new` will accept a
52
+ `Rack::Test::Session` instance and return it (Jeremy Evans #297)
53
+ * Previously protected methods in `Rack::Test::Cookie{,Jar}` are now
54
+ private methods (Jeremy Evans #297)
55
+ * `Rack::Test::Methods` no longer defines `build_rack_mock_session`,
56
+ but for backwards compatibility, `build_rack_test_session` will call
57
+ `build_rack_mock_session` if it is defined (Jeremy Evans #297)
58
+ * `Rack::Test::Methods::METHODS` is no longer defined
59
+ (Jeremy Evans #297)
60
+ * `Rack::Test::Methods#_current_session_names` has been removed
61
+ (Jeremy Evans #297)
62
+ * Headers used/accessed by rack-test are now lower case, for rack 3
63
+ compliance (Jeremy Evans #295)
64
+ * Frozen literal strings are now used internally, which may break
65
+ code that mutates static strings returned by rack-test, if any
66
+ (Jeremy Evans #304)
67
+
68
+ * Minor enhancements:
69
+ * rack-test now works with the rack main branch (what will be rack 3)
70
+ (Jeremy Evans #280 #292)
71
+ * rack-test only loads the parts of rack it uses when running on the
72
+ rack main branch (what will be rack 3) (Jeremy Evans #292)
73
+ * Development dependencies have been significantly reduced, and are
74
+ now a subset of the development dependencies of rack itself
75
+ (Jeremy Evans #292)
76
+ * Avoid creating multiple large copies of uploaded file data in
77
+ memory (Jeremy Evans #286)
78
+ * Specify HTTP/1.0 when submitting requests, to avoid responses with
79
+ Transfer-Encoding: chunked (Jeremy Evans #288)
80
+ * Support `:query_params` in rack environment for parameters that
81
+ are appended to the query string instead of used in the request
82
+ body (Jeremy Evans #150 #287)
83
+ * Reduce required ruby version to 2.0, since tests run fine on
84
+ Ruby 2.0 (Jeremy Evans #292)
85
+ * Support :multipart env key for request methods to force multipart
86
+ input (Jeremy Evans #303)
87
+ * Force multipart input for request methods if content type starts
88
+ with multipart (Jeremy Evans #303)
89
+ * Improve performance of Utils.build_multipart by using an
90
+ append-only design (Jeremy Evans #304)
91
+ * Improve performance of Utils.build_nested_query for array values
92
+ (Jeremy Evans #304)
93
+
94
+ * Bug fixes:
95
+ * The `CONTENT_TYPE` of multipart requests is now respected, if it
96
+ starts with `multipart/` (Tom Knig #238)
97
+ * Work correctly with responses that respond to `to_a` but not
98
+ `to_ary` (Sergio Faria #276)
99
+ * Raise an ArgumentError instead of a TypeError when providing a
100
+ StringIO without an original filename when creating an
101
+ UploadedFile (Nuno Correia #279)
102
+ * Allow combining both an UploadedFile and a plain string when
103
+ building a multipart upload (Mitsuhiro Shibuya #278)
104
+ * Fix the generation of filenames with spaces to use path
105
+ escaping instead of regular escaping, since path unescaping is
106
+ used to decode it (Muir Manders, Jeremy Evans #275 #284)
107
+ * Rewind tempfile used for multipart uploads before it is
108
+ submitted to the application
109
+ (Jeremy Evans, Alexander Dervish #261 #268 #286)
110
+ * Fix Rack::Test.encoding_aware_strings to be true only on rack
111
+ 1.6+ (Jeremy Evans #292)
112
+ * Make Rack::Test::CookieJar#valid? return true/false
113
+ (Jeremy Evans #292)
114
+ * Cookies without a domain attribute no longer are submitted to
115
+ requests for subdomains of that domain, for RFC 6265
116
+ compliance (Jeremy Evans #292)
117
+ * Increase required rack version to 1.3, since tests fail on
118
+ rack 1.2 and below (Jeremy Evans #293)
119
+
1
120
  ## 1.1.0 / 2018-07-21
2
121
 
3
122
  * Breaking changes:
@@ -8,7 +127,7 @@
8
127
  * follow_direct: Include rack.session.options (Mark Edmondson #233)
9
128
  * [CI] Add simplecov (fatkodima #227)
10
129
 
11
- Bug fixes:
130
+ * Bug fixes:
12
131
  * Follow relative locations correctly. (Samuel Williams #230)
13
132
 
14
133
  ## 1.0.0 / 2018-03-27
data/MIT-LICENSE.txt CHANGED
@@ -1,4 +1,5 @@
1
1
  Copyright (c) 2008-2009 Bryan Helmkamp, Engine Yard Inc.
2
+ Copyright (c) 2022 Jeremy Evans
2
3
 
3
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
5
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,10 +1,7 @@
1
1
  # Rack::Test
2
2
  [![Gem Version](https://badge.fury.io/rb/rack-test.svg)](https://badge.fury.io/rb/rack-test)
3
- [<img src="https://travis-ci.org/rack-test/rack-test.svg?branch=master" />](https://travis-ci.org/rack-test/rack-test)
4
- [![Code Climate](https://codeclimate.com/github/rack-test/rack-test/badges/gpa.svg)](https://codeclimate.com/github/rack-test/rack-test)
5
- [![Test Coverage](https://codeclimate.com/github/rack-test/rack-test/badges/coverage.svg)](https://codeclimate.com/github/rack-test/rack-test/coverage)
6
3
 
7
- Code: https://github.com/rack-test/rack-test
4
+ Code: https://github.com/rack/rack-test
8
5
 
9
6
  ## Description
10
7
 
@@ -14,76 +11,66 @@ to build on.
14
11
 
15
12
  ## Features
16
13
 
14
+ * Allows for submitting requests and testing responses
17
15
  * Maintains a cookie jar across requests
18
- * Easily follow redirects when desired
19
- * Set request headers to be used by all subsequent requests
20
- * Small footprint. Approximately 200 LOC
21
-
22
- ## Supported platforms
23
-
24
- * 2.2.2+
25
- * 2.3
26
- * 2.4
27
- * JRuby 9.1.+
28
-
29
- If you are using Ruby 1.8, 1.9 or JRuby 1.7, use rack-test 0.6.3.
30
-
31
- ## Known incompatibilites
32
-
33
- * `rack-test >= 0.71` _does not_ work with older Capybara versions (`< 2.17`). See [#214](https://github.com/rack-test/rack-test/issues/214) for more details.
16
+ * Supports request headers used for subsequent requests
17
+ * Follow redirects when requested
34
18
 
35
19
  ## Examples
36
- (The examples use `Test::Unit` but it's equally possible to use `rack-test` with other testing frameworks like `rspec`.)
20
+
21
+ These examples use `test/unit` but it's equally possible to use `rack-test` with
22
+ other testing frameworks such as `minitest` or `rspec`.
37
23
 
38
24
  ```ruby
39
25
  require "test/unit"
40
26
  require "rack/test"
27
+ require "json"
41
28
 
42
29
  class HomepageTest < Test::Unit::TestCase
43
30
  include Rack::Test::Methods
44
31
 
45
32
  def app
46
- app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['All responses are OK']] }
47
- builder = Rack::Builder.new
48
- builder.run app
33
+ lambda { |env| [200, {'content-type' => 'text/plain'}, ['All responses are OK']] }
49
34
  end
50
35
 
51
36
  def test_response_is_ok
52
- get '/'
53
-
54
- assert last_response.ok?
55
- assert_equal last_response.body, 'All responses are OK'
56
- end
37
+ # Optionally set headers used for all requests in this spec:
38
+ #header 'accept-charset', 'utf-8'
57
39
 
58
- def set_request_headers
59
- header 'Accept-Charset', 'utf-8'
40
+ # First argument is treated as the path
60
41
  get '/'
61
42
 
62
43
  assert last_response.ok?
63
- assert_equal last_response.body, 'All responses are OK'
44
+ assert_equal 'All responses are OK', last_response.body
64
45
  end
65
46
 
66
- def test_response_is_ok_for_other_paths
67
- get '/other_paths'
68
-
69
- assert last_response.ok?
70
- assert_equal last_response.body, 'All responses are OK'
47
+ def delete_with_url_params_and_body
48
+ # First argument can have a query string
49
+ #
50
+ # Second argument is used as the parameters for the request, which will be
51
+ # included in the request body for non-GET requests.
52
+ delete '/?foo=bar', JSON.generate('baz' => 'zot')
71
53
  end
72
54
 
73
55
  def post_with_json
74
- # No assertion in this, we just demonstrate how you can post a JSON-encoded string.
75
- # By default, Rack::Test will use HTTP form encoding if you pass in a Hash as the
76
- # parameters, so make sure that `json` below is already a JSON-serialized string.
77
- post(uri, json, { 'CONTENT_TYPE' => 'application/json' })
78
- end
79
-
80
- def delete_with_url_params_and_body
81
- delete '/?foo=bar', JSON.generate('baz' => 'zot')
56
+ # Third argument is the rack environment to use for the request. The following
57
+ # entries in the submitted rack environment are treated specially (in addition
58
+ # to options supported by `Rack::MockRequest#env_for`:
59
+ #
60
+ # :cookie : Set a cookie for the current session before submitting the request.
61
+ #
62
+ # :query_params : Set parameters for the query string (as opposed to the body).
63
+ # Value should be a hash of parameters.
64
+ #
65
+ # :xhr : Set HTTP_X_REQUESTED_WITH env key to XMLHttpRequest.
66
+ post(uri, JSON.generate('baz' => 'zot'), 'CONTENT_TYPE' => 'application/json')
82
67
  end
83
68
  end
84
69
  ```
85
70
 
86
- If you want to test one app in isolation, you just return that app as shown above. But if you want to test the entire app stack, including middlewares, cascades etc. you need to parse the app defined in config.ru.
71
+ `rack-test` will test the app returned by the `app` method. If you are loading middleware
72
+ in a `config.ru` file, and want to test that, you should load the Rack app created from
73
+ the `config.ru` file:
87
74
 
88
75
  ```ruby
89
76
  OUTER_APP = Rack::Builder.parse_file("config.ru").first
@@ -102,7 +89,6 @@ class TestApp < Test::Unit::TestCase
102
89
  end
103
90
  ```
104
91
 
105
-
106
92
  ## Install
107
93
 
108
94
  To install the latest release as a gem:
@@ -111,40 +97,43 @@ To install the latest release as a gem:
111
97
  gem install rack-test
112
98
  ```
113
99
 
114
- Or via Bundler:
100
+ Or add to your `Gemfile`:
115
101
 
116
102
  ```
117
103
  gem 'rack-test'
118
104
  ```
119
105
 
120
- Or to install unreleased version via Bundler:
106
+ ## Contribution
121
107
 
122
- ```
123
- gem 'rack-test', github: 'rack-test', branch: 'master'
124
- ```
108
+ Contributions are welcome. Please make sure to:
109
+
110
+ * Use a regular forking workflow
111
+ * Write tests for the new or changed behaviour
112
+ * Provide an explanation/motivation in your commit message / PR message
113
+ * Ensure `History.md` is updated
125
114
 
126
115
  ## Authors
127
116
 
128
- - Contributions from Bryan Helmkamp, Simon Rozet, Pat Nakajima and others
117
+ - Contributions from Bryan Helmkamp, Jeremy Evans, Simon Rozet, and others
129
118
  - Much of the original code was extracted from Merb 1.0's request helper
130
119
 
131
120
  ## License
132
- `rack-test` is released under the [MIT License](MIT-LICENSE.txt).
133
121
 
134
- ## Contribution
122
+ `rack-test` is released under the [MIT License](MIT-LICENSE.txt).
135
123
 
136
- Contributions are welcome. Please make sure to:
124
+ ## Supported platforms
137
125
 
138
- * Use a regular forking workflow
139
- * Write tests for the new or changed behaviour
140
- * Provide an explanation/motivation in your commit message / PR message
141
- * Ensure History.txt is updated
126
+ * Ruby 2.0+
127
+ * JRuby 9.1+
142
128
 
143
129
  ## Releasing
144
130
 
145
- * Ensure `History.md` is up-to-date
146
131
  * Bump VERSION in lib/rack/test/version.rb
147
- * `git commit . -m 'Release 1.1.0'`
132
+ * Ensure `History.md` is up-to-date, including correct version and date
133
+ * `git commit . -m 'Release $VERSION'`
148
134
  * `git push`
149
- * bundle exec thor :release
150
- * Updated the [GitHub releases page](https://github.com/rack-test/rack-test/releases)
135
+ * `git tag -a -m 'Tag the $VERSION release' $VERSION`
136
+ * `git push --tags`
137
+ * `gem build rack-test.gemspec`
138
+ * `gem push rack-test-$VERSION.gem`
139
+ * Add a discussion post for the release
@@ -1,94 +1,114 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'uri'
2
4
  require 'time'
3
5
 
4
6
  module Rack
5
7
  module Test
8
+ # Represents individual cookies in the cookie jar. This is considered private
9
+ # API and behavior of this class can change at any time.
6
10
  class Cookie # :nodoc:
7
11
  include Rack::Utils
8
12
 
9
- # :api: private
10
- attr_reader :name, :value
13
+ # The name of the cookie, will be a string
14
+ attr_reader :name
15
+
16
+ # The value of the cookie, will be a string or nil if there is no value.
17
+ attr_reader :value
18
+
19
+ # The raw string for the cookie, without options. Will generally be in
20
+ # name=value format is name and value are provided.
21
+ attr_reader :raw
11
22
 
12
- # :api: private
13
23
  def initialize(raw, uri = nil, default_host = DEFAULT_HOST)
14
24
  @default_host = default_host
15
25
  uri ||= default_uri
16
26
 
17
27
  # separate the name / value pair from the cookie options
18
- @name_value_raw, options = raw.split(/[;,] */n, 2)
28
+ @raw, options = raw.split(/[;,] */n, 2)
19
29
 
20
- @name, @value = parse_query(@name_value_raw, ';').to_a.first
30
+ @name, @value = parse_query(@raw, ';').to_a.first
21
31
  @options = parse_query(options, ';')
22
32
 
23
- @options['domain'] ||= (uri.host || default_host)
24
- @options['path'] ||= uri.path.sub(/\/[^\/]*\Z/, '')
33
+ if domain = @options['domain']
34
+ @exact_domain_match = false
35
+ domain[0] = '' if domain[0] == '.'
36
+ else
37
+ # If the domain attribute is not present in the cookie,
38
+ # the domain must match exactly.
39
+ @exact_domain_match = true
40
+ @options['domain'] = (uri.host || default_host)
41
+ end
42
+
43
+ # Set the path for the cookie to the directory containing
44
+ # the request if it isn't set.
45
+ @options['path'] ||= uri.path.sub(/\/[^\/]*\Z/, '')
25
46
  end
26
47
 
48
+ # Wether the given cookie can replace the current cookie in the cookie jar.
27
49
  def replaces?(other)
28
50
  [name.downcase, domain, path] == [other.name.downcase, other.domain, other.path]
29
51
  end
30
52
 
31
- # :api: private
32
- def raw
33
- @name_value_raw
34
- end
35
-
36
- # :api: private
53
+ # Whether the cookie has a value.
37
54
  def empty?
38
55
  @value.nil? || @value.empty?
39
56
  end
40
57
 
41
- # :api: private
58
+ # The explicit or implicit domain for the cookie.
42
59
  def domain
43
60
  @options['domain']
44
61
  end
45
62
 
63
+ # Whether the cookie has the secure flag, indicating it can only be sent over
64
+ # an encrypted connection.
46
65
  def secure?
47
66
  @options.key?('secure')
48
67
  end
49
68
 
69
+ # Whether the cookie has the httponly flag, indicating it is not available via
70
+ # a javascript API.
50
71
  def http_only?
51
- @options.key?('HttpOnly')
72
+ @options.key?('HttpOnly') || @options.key?('httponly')
52
73
  end
53
74
 
54
- # :api: private
75
+ # The explicit or implicit path for the cookie.
55
76
  def path
56
77
  ([*@options['path']].first.split(',').first || '/').strip
57
78
  end
58
79
 
59
- # :api: private
80
+ # A Time value for when the cookie expires, if the expires option is set.
60
81
  def expires
61
82
  Time.parse(@options['expires']) if @options['expires']
62
83
  end
63
84
 
64
- # :api: private
85
+ # Whether the cookie is currently expired.
65
86
  def expired?
66
87
  expires && expires < Time.now
67
88
  end
68
89
 
69
- # :api: private
90
+ # Whether the cookie is valid for the given URI.
70
91
  def valid?(uri)
71
92
  uri ||= default_uri
72
93
 
73
94
  uri.host = @default_host if uri.host.nil?
74
95
 
75
96
  real_domain = domain =~ /^\./ ? domain[1..-1] : domain
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)}")
97
+ !!((!secure? || (secure? && uri.scheme == 'https')) &&
98
+ uri.host =~ Regexp.new("#{'^' if @exact_domain_match}#{Regexp.escape(real_domain)}$", Regexp::IGNORECASE))
79
99
  end
80
100
 
81
- # :api: private
101
+ # Cookies that do not match the URI will not be sent in requests to the URI.
82
102
  def matches?(uri)
83
- !expired? && valid?(uri)
103
+ !expired? && valid?(uri) && uri.path.start_with?(path)
84
104
  end
85
105
 
86
- # :api: private
106
+ # Order cookies by name, path, and domain.
87
107
  def <=>(other)
88
- # Orders the cookies from least specific to most
89
108
  [name, path, domain.reverse] <=> [other.name, other.path, other.domain.reverse]
90
109
  end
91
110
 
111
+ # A hash of cookie options, including the cookie value, but excluding the cookie name.
92
112
  def to_h
93
113
  @options.merge(
94
114
  'value' => @value,
@@ -98,43 +118,68 @@ module Rack
98
118
  end
99
119
  alias to_hash to_h
100
120
 
101
- protected
121
+ private
102
122
 
123
+ # The default URI to use for the cookie, including just the host.
103
124
  def default_uri
104
125
  URI.parse('//' + @default_host + '/')
105
126
  end
106
127
  end
107
128
 
129
+ # Represents all cookies for a session, handling adding and
130
+ # removing cookies, and finding which cookies apply to a given
131
+ # request. This is considered private API and behavior of this
132
+ # class can change at any time.
108
133
  class CookieJar # :nodoc:
109
134
  DELIMITER = '; '.freeze
110
135
 
111
- # :api: private
112
136
  def initialize(cookies = [], default_host = DEFAULT_HOST)
113
137
  @default_host = default_host
114
- @cookies = cookies
115
- @cookies.sort!
138
+ @cookies = cookies.sort!
116
139
  end
117
140
 
141
+ # Ensure the copy uses a distinct cookies array.
142
+ def initialize_copy(other)
143
+ super
144
+ @cookies = @cookies.dup
145
+ end
146
+
147
+ # Return the value for first cookie with the given name, or nil
148
+ # if no such cookie exists.
118
149
  def [](name)
119
- cookies = hash_for(nil)
120
- # TODO: Should be case insensitive
121
- cookies[name.to_s] && cookies[name.to_s].value
150
+ name = name.to_s
151
+ @cookies.each do |cookie|
152
+ return cookie.value if cookie.name == name
153
+ end
154
+ nil
122
155
  end
123
156
 
157
+ # Set a cookie with the given name and value in the
158
+ # cookie jar.
124
159
  def []=(name, value)
125
160
  merge("#{name}=#{Rack::Utils.escape(value)}")
126
161
  end
127
162
 
163
+ # Return the first cookie with the given name, or nil if
164
+ # no such cookie exists.
128
165
  def get_cookie(name)
129
- hash_for(nil).fetch(name, nil)
166
+ @cookies.each do |cookie|
167
+ return cookie if cookie.name == name
168
+ end
169
+ nil
130
170
  end
131
171
 
172
+ # Delete all cookies with the given name from the cookie jar.
132
173
  def delete(name)
133
174
  @cookies.reject! do |cookie|
134
175
  cookie.name == name
135
176
  end
177
+ nil
136
178
  end
137
179
 
180
+ # Add a string of raw cookie information to the cookie jar,
181
+ # if the cookie is valid for the given URI.
182
+ # Cookies should be separated with a newline.
138
183
  def merge(raw_cookies, uri = nil)
139
184
  return unless raw_cookies
140
185
 
@@ -149,6 +194,7 @@ module Rack
149
194
  end
150
195
  end
151
196
 
197
+ # Add a Cookie to the cookie jar.
152
198
  def <<(new_cookie)
153
199
  @cookies.reject! do |existing_cookie|
154
200
  new_cookie.replaces?(existing_cookie)
@@ -158,36 +204,48 @@ module Rack
158
204
  @cookies.sort!
159
205
  end
160
206
 
161
- # :api: private
207
+ # Return a raw cookie string for the cookie header to
208
+ # use for the given URI.
162
209
  def for(uri)
163
- hash_for(uri).values.map(&:raw).join(DELIMITER)
210
+ buf = String.new
211
+ delimiter = nil
212
+
213
+ each_cookie_for(uri) do |cookie|
214
+ if delimiter
215
+ buf << delimiter
216
+ else
217
+ delimiter = DELIMITER
218
+ end
219
+ buf << cookie.raw
220
+ end
221
+
222
+ buf
164
223
  end
165
224
 
225
+ # Return a hash cookie names and cookie values for cookies in the jar.
166
226
  def to_hash
167
227
  cookies = {}
168
228
 
169
- hash_for(nil).each do |name, cookie|
170
- cookies[name] = cookie.value
229
+ @cookies.each do |cookie|
230
+ cookies[cookie.name] = cookie.value
171
231
  end
172
232
 
173
233
  cookies
174
234
  end
175
235
 
176
- protected
177
-
178
- def hash_for(uri = nil)
179
- cookies = {}
236
+ private
180
237
 
181
- # The cookies are sorted by most specific first. So, we loop through
182
- # all the cookies in order and add it to a hash by cookie name if
183
- # the cookie can be sent to the current URI. It's added to the hash
184
- # so that when we are done, the cookies will be unique by name and
185
- # we'll have grabbed the most specific to the URI.
238
+ # Yield each cookie that matches for the URI.
239
+ #
240
+ # The cookies are sorted by most specific first. So, we loop through
241
+ # all the cookies in order and add it to a hash by cookie name if
242
+ # the cookie can be sent to the current URI. It's added to the hash
243
+ # so that when we are done, the cookies will be unique by name and
244
+ # we'll have grabbed the most specific to the URI.
245
+ def each_cookie_for(uri)
186
246
  @cookies.each do |cookie|
187
- cookies[cookie.name] = cookie if !uri || cookie.matches?(uri)
247
+ yield cookie if !uri || cookie.matches?(uri)
188
248
  end
189
-
190
- cookies
191
249
  end
192
250
  end
193
251
  end