rack-test 1.1.0 → 2.0.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: f6f9d525a0e1136056dcc7ebfb288dd4a0f2d82b02bc7f6b4de8a8b1e1b2f653
4
+ data.tar.gz: 2503679c0681795e9db75ec5963eaea12f2f934b4e5e3d23a30396a318e2ad3c
5
5
  SHA512:
6
- metadata.gz: a86f02b6cc8a0c7bbebbe64612e174f250abce84458ab732457e3b132f70f93b31f0ca791b75c167796690d3293fb4de02d895be85baeada4b619977b1b1ac39
7
- data.tar.gz: 002d971754b2ac28d329c8b7231d97068939e6b0e17d24d7d7d87088ac15cff4597d560843168a0289098e58639bc39f9c3b22129550a39e0d5f07a03413d70b
6
+ metadata.gz: 2b593ae59ec047f4048c96af421865855129563be719e9a0da088d275f20b3143300d83b4783aa073230bc8cc9118289562f83797682dd1a09073e0cbedc8fcb
7
+ data.tar.gz: 9f98f0e2d36fe2cf33193a279acca2c62bcf3e5c91bb8adc3a0a71d929ff92b24ab27e7bb826c70cecc52fda5c67c7af57a7d471ff7456c13421d6c719ace434
data/History.md CHANGED
@@ -1,3 +1,84 @@
1
+ ## 2.0.0 / 2022-06-24
2
+
3
+ * Breaking changes:
4
+ * Digest authentication support is now deprecated, as it relies on
5
+ digest authentication support in rack, which has been deprecated
6
+ (Jeremy Evans #294)
7
+ * `Rack::Test::Utils.build_primitive_part` no longer handles array
8
+ values (Jeremy Evans #292)
9
+ * `Rack::Test::Utils` module methods other than `build_nested_query`
10
+ and `build_multipart` are now private methods (Jeremy Evans #297)
11
+ * `Rack::MockSession` has been combined into `Rack::Test::Session`,
12
+ and remains as an alias to `Rack::Test::Session`, but to keep some
13
+ backwards compatibility, `Rack::Test::Session.new` will accept a
14
+ `Rack::Test::Session` instance and return it (Jeremy Evans #297)
15
+ * Previously protected methods in `Rack::Test::Cookie{,Jar}` are now
16
+ private methods (Jeremy Evans #297)
17
+ * `Rack::Test::Methods` no longer defines `build_rack_mock_session`,
18
+ but for backwards compatibility, `build_rack_test_session` will call
19
+ `build_rack_mock_session` if it is defined (Jeremy Evans #297)
20
+ * `Rack::Test::Methods::METHODS` is no longer defined
21
+ (Jeremy Evans #297)
22
+ * `Rack::Test::Methods#_current_session_names` has been removed
23
+ (Jeremy Evans #297)
24
+ * Headers used/accessed by rack-test are now lower case, for rack 3
25
+ compliance (Jeremy Evans #295)
26
+ * Frozen literal strings are now used internally, which may break
27
+ code that mutates static strings returned by rack-test, if any
28
+ (Jeremy Evans #304)
29
+
30
+ * Minor enhancements:
31
+ * rack-test now works with the rack main branch (what will be rack 3)
32
+ (Jeremy Evans #280 #292)
33
+ * rack-test only loads the parts of rack it uses when running on the
34
+ rack main branch (what will be rack 3) (Jeremy Evans #292)
35
+ * Development dependencies have been significantly reduced, and are
36
+ now a subset of the development dependencies of rack itself
37
+ (Jeremy Evans #292)
38
+ * Avoid creating multiple large copies of uploaded file data in
39
+ memory (Jeremy Evans #286)
40
+ * Specify HTTP/1.0 when submitting requests, to avoid responses with
41
+ Transfer-Encoding: chunked (Jeremy Evans #288)
42
+ * Support `:query_params` in rack environment for parameters that
43
+ are appended to the query string instead of used in the request
44
+ body (Jeremy Evans #150 #287)
45
+ * Reduce required ruby version to 2.0, since tests run fine on
46
+ Ruby 2.0 (Jeremy Evans #292)
47
+ * Support :multipart env key for request methods to force multipart
48
+ input (Jeremy Evans #303)
49
+ * Force multipart input for request methods if content type starts
50
+ with multipart (Jeremy Evans #303)
51
+ * Improve performance of Utils.build_multipart by using an
52
+ append-only design (Jeremy Evans #304)
53
+ * Improve performance of Utils.build_nested_query for array values
54
+ (Jeremy Evans #304)
55
+
56
+ * Bug fixes:
57
+ * The `CONTENT_TYPE` of multipart requests is now respected, if it
58
+ starts with `multipart/` (Tom Knig #238)
59
+ * Work correctly with responses that respond to `to_a` but not
60
+ `to_ary` (Sergio Faria #276)
61
+ * Raise an ArgumentError instead of a TypeError when providing a
62
+ StringIO without an original filename when creating an
63
+ UploadedFile (Nuno Correia #279)
64
+ * Allow combining both an UploadedFile and a plain string when
65
+ building a multipart upload (Mitsuhiro Shibuya #278)
66
+ * Fix the generation of filenames with spaces to use path
67
+ escaping instead of regular escaping, since path unescaping is
68
+ used to decode it (Muir Manders, Jeremy Evans #275 #284)
69
+ * Rewind tempfile used for multipart uploads before it is
70
+ submitted to the application
71
+ (Jeremy Evans, Alexander Dervish #261 #268 #286)
72
+ * Fix Rack::Test.encoding_aware_strings to be true only on rack
73
+ 1.6+ (Jeremy Evans #292)
74
+ * Make Rack::Test::CookieJar#valid? return true/false
75
+ (Jeremy Evans #292)
76
+ * Cookies without a domain attribute no longer are submitted to
77
+ requests for subdomains of that domain, for RFC 6265
78
+ compliance (Jeremy Evans #292)
79
+ * Increase required rack version to 1.3, since tests fail on
80
+ rack 1.2 and below (Jeremy Evans #293)
81
+
1
82
  ## 1.1.0 / 2018-07-21
2
83
 
3
84
  * Breaking changes:
@@ -8,7 +89,7 @@
8
89
  * follow_direct: Include rack.session.options (Mark Edmondson #233)
9
90
  * [CI] Add simplecov (fatkodima #227)
10
91
 
11
- Bug fixes:
92
+ * Bug fixes:
12
93
  * Follow relative locations correctly. (Samuel Williams #230)
13
94
 
14
95
  ## 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 '/'
37
+ # Optionally set headers used for all requests in this spec:
38
+ #header 'accept-charset', 'utf-8'
53
39
 
54
- assert last_response.ok?
55
- assert_equal last_response.body, 'All responses are OK'
56
- end
57
-
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
44
  assert_equal last_response.body, 'All responses are OK'
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,63 +1,2 @@
1
- module Rack
2
- class MockSession # :nodoc:
3
- attr_writer :cookie_jar
4
- attr_reader :default_host
5
-
6
- def initialize(app, default_host = Rack::Test::DEFAULT_HOST)
7
- @app = app
8
- @after_request = []
9
- @default_host = default_host
10
- @last_request = nil
11
- @last_response = nil
12
- end
13
-
14
- def after_request(&block)
15
- @after_request << block
16
- end
17
-
18
- def clear_cookies
19
- @cookie_jar = Rack::Test::CookieJar.new([], @default_host)
20
- end
21
-
22
- def set_cookie(cookie, uri = nil)
23
- cookie_jar.merge(cookie, uri)
24
- end
25
-
26
- def request(uri, env)
27
- env['HTTP_COOKIE'] ||= cookie_jar.for(uri)
28
- @last_request = Rack::Request.new(env)
29
- status, headers, body = @app.call(@last_request.env)
30
-
31
- @last_response = MockResponse.new(status, headers, body, env['rack.errors'].flush)
32
- body.close if body.respond_to?(:close)
33
-
34
- cookie_jar.merge(last_response.headers['Set-Cookie'], uri)
35
-
36
- @after_request.each(&:call)
37
-
38
- if @last_response.respond_to?(:finish)
39
- @last_response.finish
40
- else
41
- @last_response
42
- end
43
- end
44
-
45
- # Return the last request issued in the session. Raises an error if no
46
- # requests have been sent yet.
47
- def last_request
48
- raise Rack::Test::Error, 'No request yet. Request a page first.' unless @last_request
49
- @last_request
50
- end
51
-
52
- # Return the last response received in the session. Raises an error if
53
- # no requests have been sent yet.
54
- def last_response
55
- raise Rack::Test::Error, 'No response yet. Request a page first.' unless @last_response
56
- @last_response
57
- end
58
-
59
- def cookie_jar
60
- @cookie_jar ||= Rack::Test::CookieJar.new([], @default_host)
61
- end
62
- end
63
- end
1
+ warn("requiring rack/mock_session is deprecated, require rack/test and use Rack::Test::Session", uplevel: 1)
2
+ require_relative 'test'
@@ -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 @options['domain']
34
+ @exact_domain_match = false
35
+ else
36
+ # If the domain attribute is not present in the cookie,
37
+ # the domain must match exactly.
38
+ @exact_domain_match = true
39
+ @options['domain'] = (uri.host || default_host)
40
+ end
41
+
42
+ # Set the path for the cookie to the directory containing
43
+ # the request if it isn't set.
44
+ @options['path'] ||= uri.path.sub(/\/[^\/]*\Z/, '')
25
45
  end
26
46
 
47
+ # Wether the given cookie can replace the current cookie in the cookie jar.
27
48
  def replaces?(other)
28
49
  [name.downcase, domain, path] == [other.name.downcase, other.domain, other.path]
29
50
  end
30
51
 
31
- # :api: private
32
- def raw
33
- @name_value_raw
34
- end
35
-
36
- # :api: private
52
+ # Whether the cookie has a value.
37
53
  def empty?
38
54
  @value.nil? || @value.empty?
39
55
  end
40
56
 
41
- # :api: private
57
+ # The explicit or implicit domain for the cookie.
42
58
  def domain
43
59
  @options['domain']
44
60
  end
45
61
 
62
+ # Whether the cookie has the secure flag, indicating it can only be sent over
63
+ # an encrypted connection.
46
64
  def secure?
47
65
  @options.key?('secure')
48
66
  end
49
67
 
68
+ # Whether the cookie has the httponly flag, indicating it is not available via
69
+ # a javascript API.
50
70
  def http_only?
51
- @options.key?('HttpOnly')
71
+ @options.key?('HttpOnly') || @options.key?('httponly')
52
72
  end
53
73
 
54
- # :api: private
74
+ # The explicit or implicit path for the cookie.
55
75
  def path
56
76
  ([*@options['path']].first.split(',').first || '/').strip
57
77
  end
58
78
 
59
- # :api: private
79
+ # A Time value for when the cookie expires, if the expires option is set.
60
80
  def expires
61
81
  Time.parse(@options['expires']) if @options['expires']
62
82
  end
63
83
 
64
- # :api: private
84
+ # Whether the cookie is currently expired.
65
85
  def expired?
66
86
  expires && expires < Time.now
67
87
  end
68
88
 
69
- # :api: private
89
+ # Whether the cookie is valid for the given URI.
70
90
  def valid?(uri)
71
91
  uri ||= default_uri
72
92
 
73
93
  uri.host = @default_host if uri.host.nil?
74
94
 
75
95
  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)}")
96
+ !!((!secure? || (secure? && uri.scheme == 'https')) &&
97
+ uri.host =~ Regexp.new("#{'^' if @exact_domain_match}#{Regexp.escape(real_domain)}$", Regexp::IGNORECASE) &&
98
+ uri.path =~ Regexp.new("^#{Regexp.escape(path)}"))
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
103
  !expired? && valid?(uri)
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,62 @@ 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
+ # Return the value for first cookie with the given name, or nil
142
+ # if no such cookie exists.
118
143
  def [](name)
119
- cookies = hash_for(nil)
120
- # TODO: Should be case insensitive
121
- cookies[name.to_s] && cookies[name.to_s].value
144
+ name = name.to_s
145
+ @cookies.each do |cookie|
146
+ return cookie.value if cookie.name == name
147
+ end
148
+ nil
122
149
  end
123
150
 
151
+ # Set a cookie with the given name and value in the
152
+ # cookie jar.
124
153
  def []=(name, value)
125
154
  merge("#{name}=#{Rack::Utils.escape(value)}")
126
155
  end
127
156
 
157
+ # Return the first cookie with the given name, or nil if
158
+ # no such cookie exists.
128
159
  def get_cookie(name)
129
- hash_for(nil).fetch(name, nil)
160
+ @cookies.each do |cookie|
161
+ return cookie if cookie.name == name
162
+ end
163
+ nil
130
164
  end
131
165
 
166
+ # Delete all cookies with the given name from the cookie jar.
132
167
  def delete(name)
133
168
  @cookies.reject! do |cookie|
134
169
  cookie.name == name
135
170
  end
171
+ nil
136
172
  end
137
173
 
174
+ # Add a string of raw cookie information to the cookie jar,
175
+ # if the cookie is valid for the given URI.
176
+ # Cookies should be separated with a newline.
138
177
  def merge(raw_cookies, uri = nil)
139
178
  return unless raw_cookies
140
179
 
@@ -149,6 +188,7 @@ module Rack
149
188
  end
150
189
  end
151
190
 
191
+ # Add a Cookie to the cookie jar.
152
192
  def <<(new_cookie)
153
193
  @cookies.reject! do |existing_cookie|
154
194
  new_cookie.replaces?(existing_cookie)
@@ -158,36 +198,48 @@ module Rack
158
198
  @cookies.sort!
159
199
  end
160
200
 
161
- # :api: private
201
+ # Return a raw cookie string for the cookie header to
202
+ # use for the given URI.
162
203
  def for(uri)
163
- hash_for(uri).values.map(&:raw).join(DELIMITER)
204
+ buf = String.new
205
+ delimiter = nil
206
+
207
+ each_cookie_for(uri) do |cookie|
208
+ if delimiter
209
+ buf << delimiter
210
+ else
211
+ delimiter = DELIMITER
212
+ end
213
+ buf << cookie.raw
214
+ end
215
+
216
+ buf
164
217
  end
165
218
 
219
+ # Return a hash cookie names and cookie values for cookies in the jar.
166
220
  def to_hash
167
221
  cookies = {}
168
222
 
169
- hash_for(nil).each do |name, cookie|
170
- cookies[name] = cookie.value
223
+ @cookies.each do |cookie|
224
+ cookies[cookie.name] = cookie.value
171
225
  end
172
226
 
173
227
  cookies
174
228
  end
175
229
 
176
- protected
230
+ private
177
231
 
178
- def hash_for(uri = nil)
179
- cookies = {}
180
-
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.
232
+ # Yield each cookie that matches for the URI.
233
+ #
234
+ # The cookies are sorted by most specific first. So, we loop through
235
+ # all the cookies in order and add it to a hash by cookie name if
236
+ # the cookie can be sent to the current URI. It's added to the hash
237
+ # so that when we are done, the cookies will be unique by name and
238
+ # we'll have grabbed the most specific to the URI.
239
+ def each_cookie_for(uri)
186
240
  @cookies.each do |cookie|
187
- cookies[cookie.name] = cookie if !uri || cookie.matches?(uri)
241
+ yield cookie if !uri || cookie.matches?(uri)
188
242
  end
189
-
190
- cookies
191
243
  end
192
244
  end
193
245
  end