rack-test 1.1.0 → 2.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 14e25e843eada244c949970f14897865edcf1687b17889560923e32306632423
4
- data.tar.gz: ac325910df89080a58b47b7ec9f7e84c552a597d09a0ed3e1c5600a7cc4a6d5a
3
+ metadata.gz: 2c648ce9c6c053f9168116fa0ff70ac3ce4d6944326cca519fc2f7f1bb3c4f1d
4
+ data.tar.gz: 8c8d8c7b5c41facc400a1f95533af2e94c7c6eb42ed55ba92b5e38689134e3d9
5
5
  SHA512:
6
- metadata.gz: a86f02b6cc8a0c7bbebbe64612e174f250abce84458ab732457e3b132f70f93b31f0ca791b75c167796690d3293fb4de02d895be85baeada4b619977b1b1ac39
7
- data.tar.gz: 002d971754b2ac28d329c8b7231d97068939e6b0e17d24d7d7d87088ac15cff4597d560843168a0289098e58639bc39f9c3b22129550a39e0d5f07a03413d70b
6
+ metadata.gz: 5414edd21a3f105513f6068d799fd25fd61672dcd50d0eedbb04b8e05e5224f62f9fcd5014456c99b17ae02c86e38e16077cce27b2c04bcc285214bb49213d45
7
+ data.tar.gz: 5f21ac1f5f8ee2f82d6c3ce65e0cb88ed66e055178c120b935be24a838ee1fc126d1a6070ea4833626e314d631c052748eccdce66db801bfafda01d7bc9c2b42
data/History.md CHANGED
@@ -1,3 +1,136 @@
1
+ ## 2.2.0 / 2024-12-23
2
+
3
+ * Bug fixes:
4
+ * `Rack::Test::Cookie` now parses cookie parameters using a
5
+ case-insensitive approach (Guillaume Malette #349)
6
+
7
+ * Minor enhancements:
8
+ * Arrays of cookies containing a blank cookie are now handled
9
+ correctly when processing responses. (Martin Emde #343)
10
+ * `Rack::Test::UploadedFile` no longer uses a finalizer for named
11
+ paths to close and unlink the created Tempfile. Tempfile itself
12
+ uses a finalizer to close and unlink itself, so there is no
13
+ reason for `Rack::Test::UploadedFile` to do so (Jeremy Evans #338)
14
+
15
+ ## 2.1.0 / 2023-03-14
16
+
17
+ * Breaking changes:
18
+ * Digest authentication support, deprecated in 2.0.0, has been
19
+ removed (Jeremy Evans #307)
20
+ * requiring rack/mock_session, deprecated in 2.0.0, has been removed
21
+ (Jeremy Evans #307)
22
+
23
+ * Minor enhancements:
24
+ * The `original_filename` for `Rack::Test::UploadedFile` can now be
25
+ set even if the content of the file comes from a file path
26
+ (Stuart Chinery #314)
27
+ * Add `Rack::Test::Session#restore_state`, for executing a block
28
+ and restoring current state (last request, last response, and
29
+ cookies) after the block (Jeremy Evans #316)
30
+ * Make `Rack::Test::Methods` support `default_host` method similar to
31
+ `app`, which will set the default host used for requests to the app
32
+ (Jeremy Evans #317 #318)
33
+ * Allow responses to set cookie paths not matching the current
34
+ request URI. Such cookies will only be sent for paths matching
35
+ the cookie path (Chris Waters #322)
36
+ * Ignore leading dot for cookie domains, per RFC 6265 (Stephen Crosby
37
+ #329)
38
+ * Avoid creating empty multipart body if params is empty in
39
+ `Rack::Test::Session#env_for` (Ryunosuke Sato #331)
40
+
41
+ ## 2.0.2 / 2022-06-28
42
+
43
+ * Bug fixes:
44
+ * Fix additional incompatible character encodings error when building
45
+ uploaded bodies (Jeremy Evans #311)
46
+
47
+ ## 2.0.1 / 2022-06-27
48
+
49
+ * Bug fixes:
50
+ * Fix incompatible character encodings error when building uploaded
51
+ file bodies (Jeremy Evans #308 #309)
52
+
53
+ ## 2.0.0 / 2022-06-24
54
+
55
+ * Breaking changes:
56
+ * Digest authentication support is now deprecated, as it relies on
57
+ digest authentication support in rack, which has been deprecated
58
+ (Jeremy Evans #294)
59
+ * `Rack::Test::Utils.build_primitive_part` no longer handles array
60
+ values (Jeremy Evans #292)
61
+ * `Rack::Test::Utils` module methods other than `build_nested_query`
62
+ and `build_multipart` are now private methods (Jeremy Evans #297)
63
+ * `Rack::MockSession` has been combined into `Rack::Test::Session`,
64
+ and remains as an alias to `Rack::Test::Session`, but to keep some
65
+ backwards compatibility, `Rack::Test::Session.new` will accept a
66
+ `Rack::Test::Session` instance and return it (Jeremy Evans #297)
67
+ * Previously protected methods in `Rack::Test::Cookie{,Jar}` are now
68
+ private methods (Jeremy Evans #297)
69
+ * `Rack::Test::Methods` no longer defines `build_rack_mock_session`,
70
+ but for backwards compatibility, `build_rack_test_session` will call
71
+ `build_rack_mock_session` if it is defined (Jeremy Evans #297)
72
+ * `Rack::Test::Methods::METHODS` is no longer defined
73
+ (Jeremy Evans #297)
74
+ * `Rack::Test::Methods#_current_session_names` has been removed
75
+ (Jeremy Evans #297)
76
+ * Headers used/accessed by rack-test are now lower case, for rack 3
77
+ compliance (Jeremy Evans #295)
78
+ * Frozen literal strings are now used internally, which may break
79
+ code that mutates static strings returned by rack-test, if any
80
+ (Jeremy Evans #304)
81
+
82
+ * Minor enhancements:
83
+ * rack-test now works with the rack main branch (what will be rack 3)
84
+ (Jeremy Evans #280 #292)
85
+ * rack-test only loads the parts of rack it uses when running on the
86
+ rack main branch (what will be rack 3) (Jeremy Evans #292)
87
+ * Development dependencies have been significantly reduced, and are
88
+ now a subset of the development dependencies of rack itself
89
+ (Jeremy Evans #292)
90
+ * Avoid creating multiple large copies of uploaded file data in
91
+ memory (Jeremy Evans #286)
92
+ * Specify HTTP/1.0 when submitting requests, to avoid responses with
93
+ Transfer-Encoding: chunked (Jeremy Evans #288)
94
+ * Support `:query_params` in rack environment for parameters that
95
+ are appended to the query string instead of used in the request
96
+ body (Jeremy Evans #150 #287)
97
+ * Reduce required ruby version to 2.0, since tests run fine on
98
+ Ruby 2.0 (Jeremy Evans #292)
99
+ * Support :multipart env key for request methods to force multipart
100
+ input (Jeremy Evans #303)
101
+ * Force multipart input for request methods if content type starts
102
+ with multipart (Jeremy Evans #303)
103
+ * Improve performance of Utils.build_multipart by using an
104
+ append-only design (Jeremy Evans #304)
105
+ * Improve performance of Utils.build_nested_query for array values
106
+ (Jeremy Evans #304)
107
+
108
+ * Bug fixes:
109
+ * The `CONTENT_TYPE` of multipart requests is now respected, if it
110
+ starts with `multipart/` (Tom Knig #238)
111
+ * Work correctly with responses that respond to `to_a` but not
112
+ `to_ary` (Sergio Faria #276)
113
+ * Raise an ArgumentError instead of a TypeError when providing a
114
+ StringIO without an original filename when creating an
115
+ UploadedFile (Nuno Correia #279)
116
+ * Allow combining both an UploadedFile and a plain string when
117
+ building a multipart upload (Mitsuhiro Shibuya #278)
118
+ * Fix the generation of filenames with spaces to use path
119
+ escaping instead of regular escaping, since path unescaping is
120
+ used to decode it (Muir Manders, Jeremy Evans #275 #284)
121
+ * Rewind tempfile used for multipart uploads before it is
122
+ submitted to the application
123
+ (Jeremy Evans, Alexander Dervish #261 #268 #286)
124
+ * Fix Rack::Test.encoding_aware_strings to be true only on rack
125
+ 1.6+ (Jeremy Evans #292)
126
+ * Make Rack::Test::CookieJar#valid? return true/false
127
+ (Jeremy Evans #292)
128
+ * Cookies without a domain attribute no longer are submitted to
129
+ requests for subdomains of that domain, for RFC 6265
130
+ compliance (Jeremy Evans #292)
131
+ * Increase required rack version to 1.3, since tests fail on
132
+ rack 1.2 and below (Jeremy Evans #293)
133
+
1
134
  ## 1.1.0 / 2018-07-21
2
135
 
3
136
  * Breaking changes:
@@ -8,7 +141,7 @@
8
141
  * follow_direct: Include rack.session.options (Mark Edmondson #233)
9
142
  * [CI] Add simplecov (fatkodima #227)
10
143
 
11
- Bug fixes:
144
+ * Bug fixes:
12
145
  * Follow relative locations correctly. (Samuel Williams #230)
13
146
 
14
147
  ## 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,154 +1,199 @@
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)
19
-
20
- @name, @value = parse_query(@name_value_raw, ';').to_a.first
21
- @options = parse_query(options, ';')
28
+ @raw, options = raw.split(/[;,] */n, 2)
29
+
30
+ @name, @value = parse_query(@raw, ';').to_a.first
31
+ @options = Hash[parse_query(options, ';').map { |k, v| [k.downcase, v] }]
32
+
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
22
42
 
23
- @options['domain'] ||= (uri.host || default_host)
24
- @options['path'] ||= uri.path.sub(/\/[^\/]*\Z/, '')
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')
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
- 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(domain)}$", Regexp::IGNORECASE))
79
98
  end
80
99
 
81
- # :api: private
100
+ # Cookies that do not match the URI will not be sent in requests to the URI.
82
101
  def matches?(uri)
83
- !expired? && valid?(uri)
102
+ !expired? && valid?(uri) && uri.path.start_with?(path)
84
103
  end
85
104
 
86
- # :api: private
105
+ # Order cookies by name, path, and domain.
87
106
  def <=>(other)
88
- # Orders the cookies from least specific to most
89
107
  [name, path, domain.reverse] <=> [other.name, other.path, other.domain.reverse]
90
108
  end
91
109
 
110
+ # A hash of cookie options, including the cookie value, but excluding the cookie name.
92
111
  def to_h
93
- @options.merge(
112
+ hash = @options.merge(
94
113
  'value' => @value,
95
114
  'HttpOnly' => http_only?,
96
115
  'secure' => secure?
97
116
  )
117
+ hash.delete('httponly')
118
+ hash
98
119
  end
99
120
  alias to_hash to_h
100
121
 
101
- protected
122
+ private
102
123
 
124
+ # The default URI to use for the cookie, including just the host.
103
125
  def default_uri
104
126
  URI.parse('//' + @default_host + '/')
105
127
  end
106
128
  end
107
129
 
130
+ # Represents all cookies for a session, handling adding and
131
+ # removing cookies, and finding which cookies apply to a given
132
+ # request. This is considered private API and behavior of this
133
+ # class can change at any time.
108
134
  class CookieJar # :nodoc:
109
135
  DELIMITER = '; '.freeze
110
136
 
111
- # :api: private
112
137
  def initialize(cookies = [], default_host = DEFAULT_HOST)
113
138
  @default_host = default_host
114
- @cookies = cookies
115
- @cookies.sort!
139
+ @cookies = cookies.sort!
116
140
  end
117
141
 
142
+ # Ensure the copy uses a distinct cookies array.
143
+ def initialize_copy(other)
144
+ super
145
+ @cookies = @cookies.dup
146
+ end
147
+
148
+ # Return the value for first cookie with the given name, or nil
149
+ # if no such cookie exists.
118
150
  def [](name)
119
- cookies = hash_for(nil)
120
- # TODO: Should be case insensitive
121
- cookies[name.to_s] && cookies[name.to_s].value
151
+ name = name.to_s
152
+ @cookies.each do |cookie|
153
+ return cookie.value if cookie.name == name
154
+ end
155
+ nil
122
156
  end
123
157
 
158
+ # Set a cookie with the given name and value in the
159
+ # cookie jar.
124
160
  def []=(name, value)
125
161
  merge("#{name}=#{Rack::Utils.escape(value)}")
126
162
  end
127
163
 
164
+ # Return the first cookie with the given name, or nil if
165
+ # no such cookie exists.
128
166
  def get_cookie(name)
129
- hash_for(nil).fetch(name, nil)
167
+ @cookies.each do |cookie|
168
+ return cookie if cookie.name == name
169
+ end
170
+ nil
130
171
  end
131
172
 
173
+ # Delete all cookies with the given name from the cookie jar.
132
174
  def delete(name)
133
175
  @cookies.reject! do |cookie|
134
176
  cookie.name == name
135
177
  end
178
+ nil
136
179
  end
137
180
 
181
+ # Add a string of raw cookie information to the cookie jar,
182
+ # if the cookie is valid for the given URI.
183
+ # Cookies should be separated with a newline.
138
184
  def merge(raw_cookies, uri = nil)
139
185
  return unless raw_cookies
140
186
 
141
- if raw_cookies.is_a? String
142
- raw_cookies = raw_cookies.split("\n")
143
- raw_cookies.reject!(&:empty?)
144
- end
187
+ raw_cookies = raw_cookies.split("\n") if raw_cookies.is_a? String
145
188
 
146
189
  raw_cookies.each do |raw_cookie|
190
+ next if raw_cookie.empty?
147
191
  cookie = Cookie.new(raw_cookie, uri, @default_host)
148
192
  self << cookie if cookie.valid?(uri)
149
193
  end
150
194
  end
151
195
 
196
+ # Add a Cookie to the cookie jar.
152
197
  def <<(new_cookie)
153
198
  @cookies.reject! do |existing_cookie|
154
199
  new_cookie.replaces?(existing_cookie)
@@ -158,36 +203,48 @@ module Rack
158
203
  @cookies.sort!
159
204
  end
160
205
 
161
- # :api: private
206
+ # Return a raw cookie string for the cookie header to
207
+ # use for the given URI.
162
208
  def for(uri)
163
- hash_for(uri).values.map(&:raw).join(DELIMITER)
209
+ buf = String.new
210
+ delimiter = nil
211
+
212
+ each_cookie_for(uri) do |cookie|
213
+ if delimiter
214
+ buf << delimiter
215
+ else
216
+ delimiter = DELIMITER
217
+ end
218
+ buf << cookie.raw
219
+ end
220
+
221
+ buf
164
222
  end
165
223
 
224
+ # Return a hash cookie names and cookie values for cookies in the jar.
166
225
  def to_hash
167
226
  cookies = {}
168
227
 
169
- hash_for(nil).each do |name, cookie|
170
- cookies[name] = cookie.value
228
+ @cookies.each do |cookie|
229
+ cookies[cookie.name] = cookie.value
171
230
  end
172
231
 
173
232
  cookies
174
233
  end
175
234
 
176
- protected
177
-
178
- def hash_for(uri = nil)
179
- cookies = {}
235
+ private
180
236
 
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.
237
+ # Yield each cookie that matches for the URI.
238
+ #
239
+ # The cookies are sorted by most specific first. So, we loop through
240
+ # all the cookies in order and add it to a hash by cookie name if
241
+ # the cookie can be sent to the current URI. It's added to the hash
242
+ # so that when we are done, the cookies will be unique by name and
243
+ # we'll have grabbed the most specific to the URI.
244
+ def each_cookie_for(uri)
186
245
  @cookies.each do |cookie|
187
- cookies[cookie.name] = cookie if !uri || cookie.matches?(uri)
246
+ yield cookie if !uri || cookie.matches?(uri)
188
247
  end
189
-
190
- cookies
191
248
  end
192
249
  end
193
250
  end