http-cookie 1.0.0.pre10 → 1.0.0.pre11

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
  SHA1:
3
- metadata.gz: f409469763d06151a3094c2246c64c71f8f0016a
4
- data.tar.gz: 4c6433fa901c541aa2bf16461fbbe817fca00f4e
3
+ metadata.gz: 74a442b5c8222376568f11b2ed2f3ff948cb297a
4
+ data.tar.gz: 27b3d634fdd2e75bff0302f89463b5ed777e936f
5
5
  SHA512:
6
- metadata.gz: ec86eccfc64ce5e1672b0ed786722deda4c0386deb564c7eec89ba965199717b603e517a8acdd0cbd560380585e8ed96e66ff9d8cf227ca716ab9c9b4dfabec1
7
- data.tar.gz: ad8af975e1ec653bd2e978f21a1c8199160598c16a8bad6851a9d6b6a2ee3e069409858b31a7f076e37c59f697eeeec6f43b9309b6eff51f5acbc0767d9ac4e4
6
+ metadata.gz: 48ee26913f44add403e4d620e75b6f64156efaf23b37ab30e7c10543bd765a12adaeb25ff7f6c666410b82d291457f049d718ae4d802906be53db5a28638b89c
7
+ data.tar.gz: 7703dd086e0802411ab9e7ee9c12278a3321a41a0fcecdb203a6de3e2848c5c73e6e5a4141ccb01f65e165e1f80a3c8b3ea1a60be3505bb5ea1bcfa2bf55fc59
data/README.md CHANGED
@@ -25,7 +25,7 @@ Or install it yourself as:
25
25
  ## Usage
26
26
 
27
27
  ########################
28
- # Client side example
28
+ # Client side example 1
29
29
  ########################
30
30
 
31
31
  # Initialize a cookie jar
@@ -34,30 +34,60 @@ Or install it yourself as:
34
34
  # Load from a file
35
35
  jar.load(filename) if File.exist?(filename)
36
36
 
37
- # Store received cookies
38
- HTTP::Cookie.parse(set_cookie_header_value, origin: uri) { |cookie|
39
- jar << cookie
37
+ # Store received cookies, where uri is the origin of this header
38
+ header["Set-Cookie"].each { |value|
39
+ jar.parse(value, uri)
40
40
  }
41
41
 
42
- # Get the value for the Cookie field of a request header
43
- cookie_header_value = jar.cookies(uri).join(', ')
42
+ # ...
43
+
44
+ # Set the Cookie header value, where uri is the destination URI
45
+ header["Cookie"] = HTTP::Cookie.cookie_value(jar.cookies(uri))
44
46
 
45
47
  # Save to a file
46
48
  jar.save(filename)
47
49
 
48
50
 
51
+ ########################
52
+ # Client side example 2
53
+ ########################
54
+
55
+ # Initialize a cookie jar using a Mozilla compatible SQLite3 backend
56
+ jar = HTTP::CookieJar.new(store: :mozilla, filename: 'cookies.sqlite')
57
+
58
+ # There is no need for load & save in this backend.
59
+
60
+ # Store received cookies, where uri is the origin of this header
61
+ header["Set-Cookie"].each { |value|
62
+ jar.parse(value, uri)
63
+ }
64
+
65
+ # ...
66
+
67
+ # Set the Cookie header value, where uri is the destination URI
68
+ header["Cookie"] = HTTP::Cookie.cookie_value(jar.cookies(uri))
69
+
70
+
49
71
  ########################
50
72
  # Server side example
51
73
  ########################
52
74
 
53
- # Generate a cookie
54
- cookies = HTTP::Cookie.new("uid", "a12345", domain: 'example.org',
75
+ # Generate a domain cookie
76
+ cookie1 = HTTP::Cookie.new("uid", "u12345", domain: 'example.org',
55
77
  for_domain: true,
56
78
  path: '/',
57
79
  max_age: 7*86400)
58
80
 
59
- # Get the value for the Set-Cookie field of a response header
60
- set_cookie_header_value = cookies.set_cookie_value(my_url)
81
+ # Add it to the Set-Cookie response header
82
+ header['Set-Cookie'] = cookie1.set_cookie_value
83
+
84
+ # Generate a host-only cookie
85
+ cookie2 = HTTP::Cookie.new("aid", "a12345", origin: my_url,
86
+ path: '/',
87
+ max_age: 7*86400)
88
+
89
+ # Add it to the Set-Cookie response header
90
+ header['Set-Cookie'] = cookie2.set_cookie_value
61
91
 
62
92
 
63
93
  ## Incompatibilities with Mechanize::Cookie/CookieJar
@@ -77,14 +107,17 @@ equivalent using HTTP::Cookie:
77
107
 
78
108
  # after
79
109
  cookies1 = HTTP::Cookie.parse(set_cookie1, uri_or_url)
80
- cookies2 = HTTP::Cookie.parse(set_cookie2, uri_or_url, :logger => log)
110
+ cookies2 = HTTP::Cookie.parse(set_cookie2, uri_or_url, logger: log)
111
+ # or you can directly store parsed cookies in your jar
112
+ jar.parse(set_cookie1, uri_or_url)
113
+ jar.parse(set_cookie1, uri_or_url, logger: log)
81
114
 
82
115
  - Mechanize::Cookie#version, #version=
83
116
 
84
- There is no longer a sense of version in HTTP cookie. The only
85
- version number that has ever been defined was zero, and there will
86
- be no other version since the version attribute has been removed
87
- in RFC 6265.
117
+ There is no longer a sense of version in the HTTP cookie
118
+ specification. The only version number ever defined was zero, and
119
+ there will be no other version defined since the version attribute
120
+ has been removed in RFC 6265.
88
121
 
89
122
  - Mechanize::Cookie#comment, #comment=
90
123
 
@@ -3,29 +3,12 @@ require 'http/cookie/version'
3
3
  require 'time'
4
4
  require 'uri'
5
5
  require 'domain_name'
6
+ require 'http/cookie/ruby_compat'
6
7
 
7
8
  module HTTP
8
9
  autoload :CookieJar, 'http/cookie_jar'
9
10
  end
10
11
 
11
- # In Ruby < 1.9.3 URI() does not accept a URI object.
12
- if RUBY_VERSION < "1.9.3"
13
- begin
14
- URI(URI(''))
15
- rescue
16
- def URI(url) # :nodoc:
17
- case url
18
- when URI
19
- url
20
- when String
21
- URI.parse(url)
22
- else
23
- raise ArgumentError, 'bad argument (expected URI object or URI string)'
24
- end
25
- end
26
- end
27
- end
28
-
29
12
  # This class is used to represent an HTTP Cookie.
30
13
  class HTTP::Cookie
31
14
  # Maximum number of bytes per cookie (RFC 6265 6.1 requires 4096 at
@@ -48,31 +31,13 @@ class HTTP::Cookie
48
31
  expires max_age
49
32
  created_at accessed_at
50
33
  ]
51
-
52
- if String.respond_to?(:try_convert)
53
- def check_string_type(object)
54
- String.try_convert(object)
55
- end
56
- private :check_string_type
57
- else
58
- def check_string_type(object)
59
- if object.is_a?(String) ||
60
- (object.respond_to?(:to_str) && (object = object.to_str).is_a?(String))
61
- object
62
- else
63
- nil
64
- end
65
- end
66
- private :check_string_type
67
- end
68
34
  # :startdoc:
69
35
 
70
36
  # The cookie name. It may not be nil or empty.
71
37
  #
72
- # Trying to set a value with the normal setter method will raise
73
- # ArgumentError only when it contains any of these characters:
74
- # control characters (\x00-\x1F and \x7F), space and separators
75
- # `,;\"=`.
38
+ # Assign a string containing any of the following characters will
39
+ # raise ArgumentError: control characters (`\x00-\x1F` and `\x7F`),
40
+ # space and separators `,;\"=`.
76
41
  #
77
42
  # Note that RFC 6265 4.1.1 lists more characters disallowed for use
78
43
  # in a cookie name, which are these: `<>@:/[]?{}`. Using these
@@ -82,9 +47,12 @@ class HTTP::Cookie
82
47
 
83
48
  # The cookie value.
84
49
  #
85
- # Trying to set a value with the normal setter method will raise an
86
- # ArgumentError only when it contains any of these characters:
87
- # control characters (\x00-\x1F and \x7F).
50
+ # Assign a string containing a control character (`\x00-\x1F` and
51
+ # `\x7F`) will raise ArgumentError.
52
+ #
53
+ # Assigning nil sets the value to an empty string and the expiration
54
+ # date to the Unix epoch. This is a handy way to make a cookie for
55
+ # expiration.
88
56
  #
89
57
  # Note that RFC 6265 4.1.1 lists more characters disallowed for use
90
58
  # in a cookie value, which are these: ` ",;\`. Using these
@@ -138,18 +106,21 @@ class HTTP::Cookie
138
106
  # :attr_accessor: max_age
139
107
 
140
108
  # :call-seq:
141
- # new(name, value)
142
- # new(name, value, attr_hash)
143
- # new(attr_hash)
109
+ # new(name, value = nil)
110
+ # new(name, value = nil, **attr_hash)
111
+ # new(**attr_hash)
144
112
  #
145
113
  # Creates a cookie object. For each key of `attr_hash`, the setter
146
114
  # is called if defined. Each key can be either a symbol or a
147
- # string, downcased or not.
115
+ # string of downcased attribute names.
148
116
  #
149
117
  # This methods accepts any attribute name for which a setter method
150
118
  # is defined. Beware, however, any error (typically ArgumentError)
151
119
  # a setter method raises will be passed through.
152
120
  #
121
+ # If `value` is omitted or it is nil, an expiration cookie is
122
+ # created unless `max_age` or `expires` (`expires_at`) is given.
123
+ #
153
124
  # e.g.
154
125
  #
155
126
  # new("uid", "a12345")
@@ -160,51 +131,82 @@ class HTTP::Cookie
160
131
  def initialize(*args)
161
132
  @origin = @domain = @path =
162
133
  @expires = @max_age = nil
163
- @secure = @httponly = false
134
+ @for_domain = @secure = @httponly = false
164
135
  @session = true
165
136
  @created_at = @accessed_at = Time.now
166
-
167
- case args.size
168
- when 2
169
- self.name, self.value = *args
170
- @for_domain = false
171
- return
172
- when 3
173
- self.name, self.value, attr_hash = *args
137
+ case argc = args.size
174
138
  when 1
175
- attr_hash = args.first
139
+ if attr_hash = Hash.try_convert(args.last)
140
+ args.pop
141
+ else
142
+ self.name, self.value = args # value is set to nil
143
+ return
144
+ end
145
+ when 2..3
146
+ if attr_hash = Hash.try_convert(args.last)
147
+ args.pop
148
+ self.name, value = args
149
+ else
150
+ argc == 2 or
151
+ raise ArgumentError, "wrong number of arguments (#{argc} for 1-3)"
152
+ self.name, self.value = args
153
+ return
154
+ end
176
155
  else
177
- raise ArgumentError, "wrong number of arguments (#{args.size} for 1-3)"
156
+ raise ArgumentError, "wrong number of arguments (#{argc} for 1-3)"
178
157
  end
179
158
  for_domain = false
180
159
  domain = max_age = origin = nil
181
160
  attr_hash.each_pair { |key, val|
182
- skey = key.to_s.downcase
183
- if skey.sub!(/\?\z/, '')
184
- val = val ? true : false
185
- end
186
- case skey
187
- when 'for_domain'
188
- for_domain = !!val
189
- when 'domain'
161
+ case key.to_sym
162
+ when :name
163
+ self.name = val
164
+ when :value
165
+ value = val
166
+ when :domain
190
167
  domain = val
191
- when 'origin'
168
+ when :path
169
+ self.path = val
170
+ when :origin
192
171
  origin = val
193
- when 'max_age'
172
+ when :for_domain, :for_domain?
173
+ for_domain = val
174
+ when :max_age
194
175
  # Let max_age take precedence over expires
195
176
  max_age = val
177
+ when :expires, :expires_at
178
+ self.expires = val
179
+ when :httponly, :httponly?
180
+ @httponly = val
181
+ when :secure, :secure?
182
+ @secure = val
183
+ when /[A-Z]/
184
+ warn "keyword should be downcased: #{key}" if $VERBOSE
185
+ key = key.downcase
186
+ redo
187
+ when Symbol
188
+ setter = :"#{key}="
189
+ if respond_to?(setter)
190
+ __send__(setter, val)
191
+ else
192
+ warn "unknown keyword: #{key}" if $VERBOSE
193
+ end
194
+ when String
195
+ key = key.to_sym
196
+ redo
196
197
  else
197
- setter = :"#{skey}="
198
- __send__(setter, val) if respond_to?(setter)
198
+ key = key.to_s
199
+ redo
199
200
  end
200
201
  }
201
- if @name.nil? || @value.nil?
202
- raise ArgumentError, "at least name and value must be specified"
202
+ if @name.nil?
203
+ raise ArgumentError, "name must be specified"
203
204
  end
204
205
  @for_domain = for_domain
205
206
  self.domain = domain if domain
206
207
  self.origin = origin if origin
207
208
  self.max_age = max_age if max_age
209
+ self.value = value.nil? && (@expires || @max_age) ? '' : value
208
210
  end
209
211
 
210
212
  autoload :Scanner, 'http/cookie/scanner'
@@ -238,11 +240,10 @@ class HTTP::Cookie
238
240
  target_path[bsize] == ?/
239
241
  end
240
242
 
241
- # Parses a Set-Cookie header value `set_cookie` into an array of
242
- # Cookie objects taking `origin` as the source URI/URL. Parts
243
- # (separated by commas) that are malformed or invalid are silently
244
- # ignored. For example, cookies that a given origin is not
245
- # allowed to issue are excluded from the resulted array.
243
+ # Parses a Set-Cookie header value `set_cookie` assuming that it
244
+ # is sent from a source URI/URL `origin`, and returns an array of
245
+ # Cookie objects. Parts (separated by commas) that are malformed
246
+ # or considered unacceptable are silently ignored.
246
247
  #
247
248
  # If a block is given, each cookie object is passed to the block.
248
249
  #
@@ -280,9 +281,7 @@ class HTTP::Cookie
280
281
  origin = URI(origin)
281
282
 
282
283
  [].tap { |cookies|
283
- s = Scanner.new(set_cookie, logger)
284
- until s.eos?
285
- name, value, attrs = s.scan_cookie
284
+ Scanner.new(set_cookie, logger).scan_set_cookie { |name, value, attrs|
286
285
  break if name.nil? || name.empty?
287
286
 
288
287
  cookie = new(name, value)
@@ -319,7 +318,24 @@ class HTTP::Cookie
319
318
  yield cookie if block_given?
320
319
 
321
320
  cookies << cookie
322
- end
321
+ }
322
+ }
323
+ end
324
+
325
+ # Takes an array of cookies and returns a string for use in the
326
+ # Cookie header, like "name1=value2; name2=value2".
327
+ def cookie_value(cookies)
328
+ cookies.join('; ')
329
+ end
330
+
331
+ # Parses a Cookie header value into a hash of name-value string
332
+ # pairs. The first appearance takes precedence if multiple pairs
333
+ # with the same name occur.
334
+ def cookie_value_to_hash(cookie_value)
335
+ {}.tap { |hash|
336
+ Scanner.new(cookie_value).scan_cookie { |name, value|
337
+ hash[name] ||= value
338
+ }
323
339
  }
324
340
  end
325
341
  end
@@ -328,7 +344,7 @@ class HTTP::Cookie
328
344
 
329
345
  # See #name.
330
346
  def name=(name)
331
- name = check_string_type(name) or
347
+ name = String.try_convert(name) or
332
348
  raise TypeError, "#{name.class} is not a String"
333
349
  if name.empty?
334
350
  raise ArgumentError, "cookie name cannot be empty"
@@ -345,7 +361,11 @@ class HTTP::Cookie
345
361
 
346
362
  # See #value.
347
363
  def value=(value)
348
- value = check_string_type(value) or
364
+ if value.nil?
365
+ self.expires = UNIX_EPOCH
366
+ return @value = ''
367
+ end
368
+ value = String.try_convert(value) or
349
369
  raise TypeError, "#{value.class} is not a String"
350
370
  if value.match(/[\x00-\x1F\x7F]/)
351
371
  raise ArgumentError, "invalid cookie value"
@@ -373,7 +393,7 @@ class HTTP::Cookie
373
393
  when DomainName
374
394
  @domain_name = domain
375
395
  else
376
- domain = check_string_type(domain) or
396
+ domain = String.try_convert(domain) or
377
397
  raise TypeError, "#{domain.class} is not a String"
378
398
  if domain.start_with?('.')
379
399
  for_domain = true
@@ -418,7 +438,7 @@ class HTTP::Cookie
418
438
 
419
439
  # See #path.
420
440
  def path=(path)
421
- path = check_string_type(path) or
441
+ path = String.try_convert(path) or
422
442
  raise TypeError, "#{path.class} is not a String"
423
443
  @path = path.start_with?('/') ? path : '/'
424
444
  end
@@ -484,7 +504,7 @@ class HTTP::Cookie
484
504
  case sec
485
505
  when Integer, nil
486
506
  else
487
- str = check_string_type(sec) or
507
+ str = String.try_convert(sec) or
488
508
  raise TypeError, "#{sec.class} is not an Integer or String"
489
509
  /\A-?\d+\z/.match(str) or
490
510
  raise ArgumentError, "invalid Max-Age: #{sec.inspect}"
@@ -566,29 +586,32 @@ class HTTP::Cookie
566
586
  acceptable_from_uri?(uri) && HTTP::Cookie.path_match?(@path, uri.path)
567
587
  end
568
588
 
569
- # Returns a string for use in a Cookie header value,
570
- # i.e. "name=value".
589
+ # Returns a string for use in the Cookie header, i.e. `name=value`
590
+ # or `name="value"`.
571
591
  def cookie_value
572
592
  "#{@name}=#{Scanner.quote(@value)}"
573
593
  end
574
594
  alias to_s cookie_value
575
595
 
576
- # Returns a string for use in a Set-Cookie header value. If the
577
- # cookie does not have an origin set, one must be given from the
578
- # argument.
579
- #
580
- # This method does not check if this cookie will be accepted from
581
- # the origin.
582
- def set_cookie_value(origin = nil)
583
- origin = origin ? URI(origin) : @origin or
584
- raise "origin must be specified to produce a value for Set-Cookie"
585
-
596
+ # Returns a string for use in the Set-Cookie header. If necessary
597
+ # information like a path or domain (when `for_domain` is set) is
598
+ # missing, RuntimeError is raised. It is always the best to set an
599
+ # origin before calling this method.
600
+ def set_cookie_value
586
601
  string = cookie_value
587
602
  if @for_domain
588
- string << "; Domain=#{@domain}"
603
+ if @domain
604
+ string << "; Domain=#{@domain}"
605
+ else
606
+ raise "for_domain is specified but domain is known"
607
+ end
589
608
  end
590
- if (origin + './').path != @path
591
- string << "; Path=#{@path}"
609
+ if @path
610
+ if !@origin || (@origin + './').path != @path
611
+ string << "; Path=#{@path}"
612
+ end
613
+ else
614
+ raise "path is known"
592
615
  end
593
616
  if @max_age
594
617
  string << "; Max-Age=#{@max_age}"
@@ -608,7 +631,6 @@ class HTTP::Cookie
608
631
  '#<%s:' % self.class << PERSISTENT_PROPERTIES.map { |key|
609
632
  '%s=%s' % [key, instance_variable_get(:"@#{key}").inspect]
610
633
  }.join(', ') << ' origin=%s>' % [@origin ? @origin.to_s : 'nil']
611
-
612
634
  end
613
635
 
614
636
  # Compares the cookie with another. When there are many cookies with
@@ -643,6 +665,7 @@ class HTTP::Cookie
643
665
  # YAML deserialization helper for Psych.
644
666
  def yaml_initialize(tag, map)
645
667
  expires = nil
668
+ @origin = nil
646
669
  map.each { |key, value|
647
670
  case key
648
671
  when 'expires'
@@ -0,0 +1,59 @@
1
+ class Array
2
+ def select!
3
+ i = 0
4
+ each_with_index { |x, j|
5
+ yield x or next
6
+ self[i] = x if i != j
7
+ i += 1
8
+ }
9
+ return nil if i == size
10
+ self[i..-1] = []
11
+ self
12
+ end unless method_defined?(:select!)
13
+ end
14
+
15
+ class Hash
16
+ class << self
17
+ def try_convert(object)
18
+ if object.is_a?(Hash) ||
19
+ (object.respond_to?(:to_hash) && (object = object.to_hash).is_a?(Hash))
20
+ object
21
+ else
22
+ nil
23
+ end
24
+ end unless method_defined?(:try_convert)
25
+ end
26
+ end
27
+
28
+ class String
29
+ class << self
30
+ def try_convert(object)
31
+ if object.is_a?(String) ||
32
+ (object.respond_to?(:to_str) && (object = object.to_str).is_a?(String))
33
+ object
34
+ else
35
+ nil
36
+ end
37
+ end unless method_defined?(:try_convert)
38
+ end
39
+ end
40
+
41
+ # In Ruby < 1.9.3 URI() does not accept a URI object.
42
+ if RUBY_VERSION < "1.9.3"
43
+ require 'uri'
44
+
45
+ begin
46
+ URI(URI(''))
47
+ rescue
48
+ def URI(url) # :nodoc:
49
+ case url
50
+ when URI
51
+ url
52
+ when String
53
+ URI.parse(url)
54
+ else
55
+ raise ArgumentError, 'bad argument (expected URI object or URI string)'
56
+ end
57
+ end
58
+ end
59
+ end
@@ -143,15 +143,23 @@ class HTTP::Cookie::Scanner < StringScanner
143
143
  year += 2000
144
144
  end
145
145
 
146
- if (time <=> [23,59,59]) > 0
146
+ hh, mm, ss = time
147
+ if hh > 23 || mm > 59 || ss > 59
147
148
  return nil
148
149
  end
149
150
 
150
151
  tuple_to_time(day_of_month, month, year, time)
151
152
  end
152
153
 
153
- def scan_cookie
154
- # cf. RFC 6265 5.2
154
+ def scan_set_cookie
155
+ unless block_given?
156
+ scan_set_cookie { |*values|
157
+ return values
158
+ }
159
+ return
160
+ end
161
+
162
+ # RFC 6265 4.1.1 & 5.2
155
163
  until eos?
156
164
  start = pos
157
165
  len = nil
@@ -159,9 +167,7 @@ class HTTP::Cookie::Scanner < StringScanner
159
167
  skip_wsp
160
168
 
161
169
  name, value = scan_name_value
162
- if name.nil?
163
- break
164
- elsif value.nil?
170
+ if value.nil?
165
171
  @logger.warn("Cookie definition lacks a name-value pair.") if @logger
166
172
  elsif name.empty?
167
173
  @logger.warn("Cookie definition has an empty name.") if @logger
@@ -171,12 +177,13 @@ class HTTP::Cookie::Scanner < StringScanner
171
177
 
172
178
  case
173
179
  when skip(/,/)
180
+ # The comma is used as separator for concatenating multiple
181
+ # values of a header.
174
182
  len = (pos - 1) - start
175
183
  break
176
184
  when skip(/;/)
177
185
  skip_wsp
178
186
  aname, avalue = scan_name_value
179
- break if aname.nil?
180
187
  next if aname.empty? || value.nil?
181
188
  aname.downcase!
182
189
  case aname
@@ -209,7 +216,29 @@ class HTTP::Cookie::Scanner < StringScanner
209
216
  next
210
217
  end
211
218
 
212
- return [name, value, attrs] if value
219
+ yield name, value, attrs if value
220
+ end
221
+ end
222
+
223
+ def scan_cookie
224
+ unless block_given?
225
+ scan_cookie { |*values|
226
+ return values
227
+ }
228
+ return
229
+ end
230
+
231
+ # RFC 6265 4.1.1 & 5.4
232
+ until eos?
233
+ skip_wsp
234
+
235
+ name, value = scan_name_value
236
+
237
+ yield name, value if value
238
+
239
+ # The comma is used as separator for concatenating multiple
240
+ # values of a header.
241
+ skip(/[;,]/)
213
242
  end
214
243
  end
215
244
  end
@@ -1,5 +1,5 @@
1
1
  module HTTP
2
2
  class Cookie
3
- VERSION = "1.0.0.pre10"
3
+ VERSION = "1.0.0.pre11"
4
4
  end
5
5
  end
@@ -49,6 +49,10 @@ class HTTP::CookieJar
49
49
  # any case. A given cookie must have domain and path attributes
50
50
  # set, or ArgumentError is raised.
51
51
  #
52
+ # Whether a cookie with the `for_domain` flag on overwrites another
53
+ # with the flag off or vice versa depends on the store used. See
54
+ # individual store classes for that matter.
55
+ #
52
56
  # ### Compatibility Note for Mechanize::Cookie users
53
57
  #
54
58
  # In HTTP::Cookie, each cookie object can store its origin URI
@@ -70,12 +74,22 @@ class HTTP::CookieJar
70
74
  end
71
75
  alias << add
72
76
 
73
- # Gets an array of cookies that should be sent for the URL/URI.
74
- def cookies(url)
75
- now = Time.now
76
- each(url).select { |cookie|
77
- !cookie.expired? && (cookie.accessed_at = now)
78
- }.sort
77
+ # Deletes a cookie that has the same name, domain and path as a
78
+ # given cookie from the jar and returns self.
79
+ #
80
+ # How the `for_domain` flag value affects the set of deleted cookies
81
+ # depends on the store used. See individual store classes for that
82
+ # matter.
83
+ def delete(cookie)
84
+ @store.delete(cookie)
85
+ self
86
+ end
87
+
88
+ # Gets an array of cookies sorted by the path and creation time. If
89
+ # `url` is given, only ones that should be sent to the URL/URI are
90
+ # selected, with the access time of each of them updated.
91
+ def cookies(url = nil)
92
+ each(url).sort
79
93
  end
80
94
 
81
95
  # Tests if the jar is empty. If `url` is given, tests if there is
@@ -89,7 +103,8 @@ class HTTP::CookieJar
89
103
  end
90
104
  end
91
105
 
92
- # Iterates over all cookies that are not expired.
106
+ # Iterates over all cookies that are not expired in no particular
107
+ # order.
93
108
  #
94
109
  # An optional argument `uri` specifies a URI/URL indicating the
95
110
  # destination of the cookies being selected. Every cookie yielded
@@ -111,10 +126,13 @@ class HTTP::CookieJar
111
126
  end
112
127
  include Enumerable
113
128
 
114
- # Parses a Set-Cookie field value `set_cookie` sent from a URI
115
- # `origin` and adds the cookies parsed as valid to the jar. Returns
116
- # an array of cookies that have been added. If a block is given, it
117
- # is called after each cookie is added.
129
+ # Parses a Set-Cookie field value `set_cookie` assuming that it is
130
+ # sent from a source URL/URI `origin`, and adds the cookies parsed
131
+ # as valid and considered acceptable to the jar. Returns an array
132
+ # of cookies that have been added.
133
+ #
134
+ # If a block is given, it is called for each cookie and the cookie
135
+ # is added only if the block returns a true value.
118
136
  #
119
137
  # `jar.parse(set_cookie, origin)` is a shorthand for this:
120
138
  #
@@ -125,16 +143,15 @@ class HTTP::CookieJar
125
143
  # See HTTP::Cookie.parse for available options.
126
144
  def parse(set_cookie, origin, options = nil) # :yield: cookie
127
145
  if block_given?
128
- HTTP::Cookie.parse(set_cookie, origin, options) { |cookie|
129
- add(cookie)
130
- yield cookie
146
+ HTTP::Cookie.parse(set_cookie, origin, options).tap { |cookies|
147
+ cookies.select! { |cookie|
148
+ yield(cookie) && add(cookie)
149
+ }
131
150
  }
132
151
  else
133
152
  HTTP::Cookie.parse(set_cookie, origin, options) { |cookie|
134
153
  add(cookie)
135
154
  }
136
- # XXX: ruby 1.8 fails to call super from a proc'ized method
137
- # HTTP::Cookie.parse(set_cookie, origin, options, &method(:add)
138
155
  end
139
156
  end
140
157
 
@@ -154,7 +171,7 @@ class HTTP::CookieJar
154
171
  # <dt>:yaml</dt>
155
172
  # <dd>YAML structure (default)</dd>
156
173
  # <dt>:cookiestxt</dt>
157
- # <dd>: Mozilla's cookies.txt format</dd>
174
+ # <dd>Mozilla's cookies.txt format</dd>
158
175
  # </dl>
159
176
  #
160
177
  # * `:session`
@@ -37,7 +37,7 @@ class HTTP::CookieJar::AbstractSaver
37
37
  # Initializes each instance variable of the same name as option
38
38
  # keyword.
39
39
  default_options.each_pair { |key, default|
40
- instance_variable_set("@#{key}", options.key?(key) ? options[key] : default)
40
+ instance_variable_set("@#{key}", options.fetch(key, default))
41
41
  }
42
42
  end
43
43
 
@@ -41,7 +41,7 @@ class HTTP::CookieJar::AbstractStore
41
41
  # Initializes each instance variable of the same name as option
42
42
  # keyword.
43
43
  default_options.each_pair { |key, default|
44
- instance_variable_set("@#{key}", options.key?(key) ? options[key] : default)
44
+ instance_variable_set("@#{key}", options.fetch(key, default))
45
45
  }
46
46
  end
47
47
 
@@ -55,14 +55,14 @@ class HTTP::CookieJar
55
55
  end
56
56
 
57
57
  def add(cookie)
58
- path_cookies = ((@jar[cookie.domain_name.hostname] ||= {})[cookie.path] ||= {})
58
+ path_cookies = ((@jar[cookie.domain] ||= {})[cookie.path] ||= {})
59
59
  path_cookies[cookie.name] = cookie
60
60
  cleanup if (@gc_index += 1) >= @gc_threshold
61
61
  self
62
62
  end
63
63
 
64
64
  def delete(cookie)
65
- path_cookies = ((@jar[cookie.domain_name.hostname] ||= {})[cookie.path] ||= {})
65
+ path_cookies = ((@jar[cookie.domain] ||= {})[cookie.path] ||= {})
66
66
  path_cookies.delete(cookie.name)
67
67
  self
68
68
  end
@@ -255,7 +255,7 @@ class HTTP::CookieJar
255
255
  db_delete(cookie)
256
256
  end
257
257
 
258
- def each(uri = nil)
258
+ def each(uri = nil, &block)
259
259
  now = Time.now
260
260
  if uri
261
261
  @st_cookies_for_domain ||=
@@ -304,7 +304,7 @@ class HTTP::CookieJar
304
304
  yield cookie
305
305
  end
306
306
  }
307
- @sjar.each(uri, &proc)
307
+ @sjar.each(uri, &block)
308
308
  else
309
309
  @st_all_cookies ||=
310
310
  @db.prepare(<<-'SQL')
@@ -333,7 +333,7 @@ class HTTP::CookieJar
333
333
 
334
334
  yield cookie
335
335
  }
336
- @sjar.each(&proc)
336
+ @sjar.each(&block)
337
337
  end
338
338
  self
339
339
  end
@@ -50,10 +50,29 @@ class TestHTTPCookie < Test::Unit::TestCase
50
50
  silently do
51
51
  assert_equal 1, HTTP::Cookie.parse(cookie, url) { |c|
52
52
  assert c.expires, "Tried parsing: #{date}"
53
- assert_equal(true, c.expires < yesterday)
53
+ assert_send [c.expires, :<, yesterday]
54
54
  }.size
55
55
  end
56
56
  end
57
+
58
+ [
59
+ ["PREF=1; expires=Wed, 01 Jan 100 12:34:56 GMT", nil],
60
+ ["PREF=1; expires=Sat, 01 Jan 1600 12:34:56 GMT", nil],
61
+ ["PREF=1; expires=Tue, 01 Jan 69 12:34:56 GMT", 2069],
62
+ ["PREF=1; expires=Thu, 01 Jan 70 12:34:56 GMT", 1970],
63
+ ["PREF=1; expires=Wed, 01 Jan 20 12:34:56 GMT", 2020],
64
+ ["PREF=1; expires=Sat, 01 Jan 2020 12:34:60 GMT", nil],
65
+ ["PREF=1; expires=Sat, 01 Jan 2020 12:60:56 GMT", nil],
66
+ ["PREF=1; expires=Sat, 01 Jan 2020 24:00:00 GMT", nil],
67
+ ["PREF=1; expires=Sat, 32 Jan 2020 12:34:56 GMT", nil],
68
+ ].each { |set_cookie, year|
69
+ cookie, = HTTP::Cookie.parse(set_cookie, url)
70
+ if year
71
+ assert_equal year, cookie.expires.year, "#{set_cookie}: expires in #{year}"
72
+ else
73
+ assert_equal nil, cookie.expires, "#{set_cookie}: invalid expiry date"
74
+ end
75
+ }
57
76
  end
58
77
 
59
78
  def test_parse_empty
@@ -105,6 +124,18 @@ class TestHTTPCookie < Test::Unit::TestCase
105
124
  }.size
106
125
  end
107
126
 
127
+ def test_parse_no_nothing
128
+ cookie = '; "", ;'
129
+ url = URI.parse('http://www.example.com/')
130
+ assert_equal 0, HTTP::Cookie.parse(cookie, url).size
131
+ end
132
+
133
+ def test_parse_no_name
134
+ cookie = '=no-name; path=/'
135
+ url = URI.parse('http://www.example.com/')
136
+ assert_equal 0, HTTP::Cookie.parse(cookie, url).size
137
+ end
138
+
108
139
  def test_parse_weird_cookie
109
140
  cookie = 'n/a, ASPSESSIONIDCSRRQDQR=FBLDGHPBNDJCPCGNCPAENELB; path=/'
110
141
  url = URI.parse('http://www.searchinnovation.com/')
@@ -400,13 +431,51 @@ class TestHTTPCookie < Test::Unit::TestCase
400
431
  cookie = HTTP::Cookie.new('foo', value)
401
432
  assert_equal(cookie_value, cookie.cookie_value)
402
433
  }
434
+
435
+ pairs = [
436
+ ['Foo', 'value1'],
437
+ ['Bar', 'value 2'],
438
+ ['Baz', 'value3'],
439
+ ['Bar', 'value"4'],
440
+ ]
441
+
442
+ cookie_value = HTTP::Cookie.cookie_value(pairs.map { |name, value|
443
+ HTTP::Cookie.new(:name => name, :value => value)
444
+ })
445
+
446
+ assert_equal 'Foo=value1; Bar="value 2"; Baz=value3; Bar="value\\"4"', cookie_value
447
+
448
+ hash = HTTP::Cookie.cookie_value_to_hash(cookie_value)
449
+
450
+ assert_equal 3, hash.size
451
+
452
+ hash.each_pair { |name, value|
453
+ _, pvalue = pairs.assoc(name)
454
+ assert_equal pvalue, value
455
+ }
403
456
  end
404
457
 
405
458
  def test_set_cookie_value
406
- url = URI.parse('http://rubyforge.org/')
459
+ url = URI.parse('http://rubyforge.org/path/')
460
+
461
+ [
462
+ HTTP::Cookie.new('a', 'b', :domain => 'rubyforge.org', :path => '/path/'),
463
+ HTTP::Cookie.new('a', 'b', :origin => url),
464
+ ].each { |cookie|
465
+ cookie.set_cookie_value
466
+ }
467
+
468
+ [
469
+ HTTP::Cookie.new('a', 'b', :domain => 'rubyforge.org'),
470
+ HTTP::Cookie.new('a', 'b', :for_domain => true, :path => '/path/'),
471
+ ].each { |cookie|
472
+ assert_raises(RuntimeError) {
473
+ cookie.set_cookie_value
474
+ }
475
+ }
407
476
 
408
477
  ['foo=bar', 'foo="bar"', 'foo="ba\"r baz"'].each { |cookie_value|
409
- cookie_params = @cookie_params.merge('secure' => 'secure', 'max-age' => 'Max-Age=1000')
478
+ cookie_params = @cookie_params.merge('path' => '/path/', 'secure' => 'secure', 'max-age' => 'Max-Age=1000')
410
479
  date = Time.at(Time.now.to_i)
411
480
  cookie_params.keys.combine.each do |keys|
412
481
  cookie_text = [cookie_value, *keys.map { |key| cookie_params[key] }].join('; ')
@@ -510,11 +579,34 @@ class TestHTTPCookie < Test::Unit::TestCase
510
579
  cookie.acceptable?
511
580
  }
512
581
 
513
- assert_raises(ArgumentError) { HTTP::Cookie.new(:name => 'name') }
582
+ assert_raises(ArgumentError) { HTTP::Cookie.new() }
514
583
  assert_raises(ArgumentError) { HTTP::Cookie.new(:value => 'value') }
515
584
  assert_raises(ArgumentError) { HTTP::Cookie.new('', 'value') }
516
585
  assert_raises(ArgumentError) { HTTP::Cookie.new('key=key', 'value') }
517
586
  assert_raises(ArgumentError) { HTTP::Cookie.new("key\tkey", 'value') }
587
+ assert_raises(ArgumentError) { HTTP::Cookie.new('key', 'value', 'something') }
588
+ assert_raises(ArgumentError) { HTTP::Cookie.new('key', 'value', {}, 'something') }
589
+
590
+ [
591
+ HTTP::Cookie.new(:name => 'name'),
592
+ HTTP::Cookie.new("key", nil, :for_domain => true),
593
+ HTTP::Cookie.new("key", nil),
594
+ HTTP::Cookie.new("key", :secure => true),
595
+ HTTP::Cookie.new("key"),
596
+ ].each { |cookie|
597
+ assert_equal '', cookie.value
598
+ assert_equal true, cookie.expired?
599
+ }
600
+
601
+ [
602
+ HTTP::Cookie.new(:name => 'name', :max_age => 3600),
603
+ HTTP::Cookie.new("key", nil, :expires => Time.now + 3600),
604
+ HTTP::Cookie.new("key", :expires => Time.now + 3600),
605
+ HTTP::Cookie.new("key", :expires => Time.now + 3600, :value => nil),
606
+ ].each { |cookie|
607
+ assert_equal '', cookie.value
608
+ assert_equal false, cookie.expired?
609
+ }
518
610
  end
519
611
 
520
612
  def cookie_values(options = {})
@@ -681,6 +773,22 @@ class TestHTTPCookie < Test::Unit::TestCase
681
773
  assert_equal false, cookie.acceptable?
682
774
  end
683
775
 
776
+ def test_value
777
+ cookie = HTTP::Cookie.new('name', 'value')
778
+ assert_equal 'value', cookie.value
779
+
780
+ cookie.value = 'new value'
781
+ assert_equal 'new value', cookie.value
782
+
783
+ assert_raises(ArgumentError) { cookie.value = "a\tb" }
784
+ assert_raises(ArgumentError) { cookie.value = "a\nb" }
785
+
786
+ assert_equal false, cookie.expired?
787
+ cookie.value = nil
788
+ assert_equal '', cookie.value
789
+ assert_equal true, cookie.expired?
790
+ end
791
+
684
792
  def test_path
685
793
  uri = URI.parse('http://example.com/foo/bar')
686
794
 
@@ -2,6 +2,24 @@ require File.expand_path('helper', File.dirname(__FILE__))
2
2
  require 'tmpdir'
3
3
 
4
4
  module TestHTTPCookieJar
5
+ class TestBasic < Test::Unit::TestCase
6
+ def test_store
7
+ jar = HTTP::CookieJar.new(:store => :hash)
8
+ assert_instance_of HTTP::CookieJar::HashStore, jar.store
9
+
10
+ assert_raises(IndexError) {
11
+ jar = HTTP::CookieJar.new(:store => :nonexistent)
12
+ }
13
+
14
+ jar = HTTP::CookieJar.new(:store => HTTP::CookieJar::HashStore.new)
15
+ assert_instance_of HTTP::CookieJar::HashStore, jar.store
16
+
17
+ assert_raises(TypeError) {
18
+ jar = HTTP::CookieJar.new(:store => HTTP::CookieJar::HashStore)
19
+ }
20
+ end
21
+ end
22
+
5
23
  module Tests
6
24
  def setup(options = nil, options2 = nil)
7
25
  default_options = {
@@ -13,7 +31,7 @@ module TestHTTPCookieJar
13
31
  @store_type = new_options[:store]
14
32
  @gc_threshold = new_options[:gc_threshold]
15
33
  @jar = HTTP::CookieJar.new(new_options)
16
- @jar2 = HTTP::CookieJar.new(new_options)
34
+ @jar2 = HTTP::CookieJar.new(new_options2)
17
35
  end
18
36
 
19
37
  def hash_store?
@@ -303,6 +321,14 @@ module TestHTTPCookieJar
303
321
  assert_equal(0, @jar.cookies(url).length)
304
322
  end
305
323
 
324
+ def test_save_nonexistent_saver
325
+ Dir.mktmpdir { |dir|
326
+ assert_raises(ArgumentError) {
327
+ @jar.save(File.join(dir, "file"), :nonexistent)
328
+ }
329
+ }
330
+ end
331
+
306
332
  def test_save_cookies_yaml
307
333
  url = URI 'http://rubyforge.org/'
308
334
 
@@ -310,7 +336,6 @@ module TestHTTPCookieJar
310
336
  cookie = HTTP::Cookie.new(cookie_values(:origin => url))
311
337
  s_cookie = HTTP::Cookie.new(cookie_values(:name => 'Bar',
312
338
  :expires => nil,
313
- :session => true,
314
339
  :origin => url))
315
340
 
316
341
  @jar.add(cookie)
@@ -335,14 +360,53 @@ module TestHTTPCookieJar
335
360
  assert_equal(3, @jar.cookies(url).length)
336
361
  end
337
362
 
363
+ def test_save_load_signature
364
+ Dir.mktmpdir { |dir|
365
+ filename = File.join(dir, "cookies.yml")
366
+
367
+ @jar.save(filename, :format => :cookiestxt, :session => true)
368
+ @jar.save(filename, :format => :cookiestxt, :session => true)
369
+ @jar.save(filename, :format => :cookiestxt)
370
+ @jar.save(filename, :cookiestxt, :session => true)
371
+ @jar.save(filename, :cookiestxt)
372
+ @jar.save(filename, :session => true)
373
+ @jar.save(filename)
374
+ assert_raises(ArgumentError) {
375
+ @jar.save()
376
+ }
377
+ assert_raises(ArgumentError) {
378
+ @jar.save(filename, { :format => :cookiestxt }, { :session => true })
379
+ }
380
+ assert_raises(ArgumentError) {
381
+ @jar.save(filename, :cookiestxt, { :session => true }, { :format => :cookiestxt })
382
+ }
383
+
384
+ @jar.load(filename, :format => :cookiestxt, :linefeed => "\n")
385
+ @jar.load(filename, :format => :cookiestxt, :linefeed => "\n")
386
+ @jar.load(filename, :format => :cookiestxt)
387
+ @jar.load(filename, :cookiestxt, :linefeed => "\n")
388
+ @jar.load(filename, :cookiestxt)
389
+ @jar.load(filename, :linefeed => "\n")
390
+ @jar.load(filename)
391
+ assert_raises(ArgumentError) {
392
+ @jar.load()
393
+ }
394
+ assert_raises(ArgumentError) {
395
+ @jar.load(filename, { :format => :cookiestxt }, { :linefeed => "\n" })
396
+ }
397
+ assert_raises(ArgumentError) {
398
+ @jar.load(filename, :cookiestxt, { :linefeed => "\n" }, { :format => :cookiestxt })
399
+ }
400
+ }
401
+ end
402
+
338
403
  def test_save_session_cookies_yaml
339
404
  url = URI 'http://rubyforge.org/'
340
405
 
341
406
  # Add one cookie with an expiration date in the future
342
407
  cookie = HTTP::Cookie.new(cookie_values)
343
408
  s_cookie = HTTP::Cookie.new(cookie_values(:name => 'Bar',
344
- :expires => nil,
345
- :session => true))
409
+ :expires => nil))
346
410
 
347
411
  @jar.add(cookie)
348
412
  @jar.add(s_cookie)
@@ -360,7 +424,6 @@ module TestHTTPCookieJar
360
424
  assert_equal(3, @jar.cookies(url).length)
361
425
  end
362
426
 
363
-
364
427
  def test_save_and_read_cookiestxt
365
428
  url = URI 'http://rubyforge.org/foo/'
366
429
 
@@ -368,8 +431,7 @@ module TestHTTPCookieJar
368
431
  cookie = HTTP::Cookie.new(cookie_values)
369
432
  expires = cookie.expires
370
433
  s_cookie = HTTP::Cookie.new(cookie_values(:name => 'Bar',
371
- :expires => nil,
372
- :session => true))
434
+ :expires => nil))
373
435
  cookie2 = HTTP::Cookie.new(cookie_values(:name => 'Baz',
374
436
  :value => 'Foo#Baz',
375
437
  :path => '/foo/',
@@ -394,6 +456,13 @@ module TestHTTPCookieJar
394
456
 
395
457
  content = File.read(filename)
396
458
 
459
+ filename2 = File.join(dir, "cookies2.txt")
460
+ open(filename2, 'w') { |w|
461
+ w.puts '# HTTP Cookie File'
462
+ @jar.save(w, :cookiestxt, :header => nil)
463
+ }
464
+ assert_equal content, File.read(filename2)
465
+
397
466
  assert_match(/^\.rubyforge\.org\t.*\tFoo\t/, content)
398
467
  assert_match(/^rubyforge\.org\t.*\tBaz\t/, content)
399
468
  assert_match(/^#HttpOnly_\.rubyforge\.org\t/, content)
@@ -554,17 +623,43 @@ module TestHTTPCookieJar
554
623
  assert_equal('Foo1 Foo2', @jar.cookies(surl).map { |c| c.name }.sort.join(' ') )
555
624
  end
556
625
 
557
- def _test_delete
558
- nurl = URI 'http://rubyforge.org/login'
559
- surl = URI 'https://rubyforge.org/login'
626
+ def test_delete
627
+ cookie1 = HTTP::Cookie.new(cookie_values)
628
+ cookie2 = HTTP::Cookie.new(:name => 'Foo', :value => '',
629
+ :domain => 'rubyforge.org',
630
+ :for_domain => false,
631
+ :path => '/')
632
+ cookie3 = HTTP::Cookie.new(:name => 'Foo', :value => '',
633
+ :domain => 'rubyforge.org',
634
+ :for_domain => true,
635
+ :path => '/')
560
636
 
561
- cookie1 = HTTP::Cookie.new(cookie_values(:name => 'Foo1', :origin => nurl))
562
- cookie2 = HTTP::Cookie.new(cookie_values(:name => 'Foo1', :origin => surl))
637
+ @jar.add(cookie1)
638
+ @jar.delete(cookie2)
563
639
 
564
- @jar.add(nncookie)
565
- @jar.add(sncookie)
566
- @jar.add(nscookie)
567
- @jar.add(sscookie)
640
+ if mozilla_store?
641
+ assert_equal(1, @jar.to_a.length)
642
+ @jar.delete(cookie3)
643
+ end
644
+
645
+ assert_equal(0, @jar.to_a.length)
646
+ end
647
+
648
+ def test_accessed_at
649
+ orig = HTTP::Cookie.new(cookie_values(:expires => nil))
650
+ @jar.add(orig)
651
+
652
+ time = orig.accessed_at
653
+
654
+ assert_in_delta 1.0, time, Time.now, "accessed_at is initialized to the current time"
655
+
656
+ cookie, = @jar.to_a
657
+
658
+ assert_equal time, cookie.accessed_at, "accessed_at is not updated by each()"
659
+
660
+ cookie, = @jar.cookies("http://rubyforge.org/")
661
+
662
+ assert_send [cookie.accessed_at, :>, time], "accessed_at is not updated by each(url)"
568
663
  end
569
664
 
570
665
  def test_max_cookies
@@ -614,14 +709,38 @@ module TestHTTPCookieJar
614
709
  }
615
710
  }
616
711
 
617
- assert_equal true, count > slimit
618
- assert_equal true, @jar.to_a.size <= slimit
712
+ assert_send [count, :>, slimit]
713
+ assert_send [@jar.to_a.size, :<=, slimit]
619
714
  @jar.cleanup
620
715
  assert_equal hlimit, @jar.to_a.size
621
716
  assert_equal false, @jar.any? { |cookie|
622
717
  cookie.domain == cookie.value
623
718
  }
624
719
  end
720
+
721
+ def test_parse
722
+ set_cookie = [
723
+ "name=Akinori; Domain=rubyforge.org; Expires=Sun, 08 Aug 2076 19:00:00 GMT; Path=/",
724
+ "country=Japan; Domain=rubyforge.org; Expires=Sun, 08 Aug 2076 19:00:00 GMT; Path=/",
725
+ "city=Tokyo; Domain=rubyforge.org; Expires=Sun, 08 Aug 2076 19:00:00 GMT; Path=/",
726
+ ].join(', ')
727
+
728
+ cookies = @jar.parse(set_cookie, 'http://rubyforge.org/')
729
+ assert_equal %w[Akinori Japan Tokyo], cookies.map { |c| c.value }
730
+ assert_equal %w[Tokyo Japan Akinori], @jar.to_a.sort_by { |c| c.name }.map { |c| c.value }
731
+ end
732
+
733
+ def test_parse_with_block
734
+ set_cookie = [
735
+ "name=Akinori; Domain=rubyforge.org; Expires=Sun, 08 Aug 2076 19:00:00 GMT; Path=/",
736
+ "country=Japan; Domain=rubyforge.org; Expires=Sun, 08 Aug 2076 19:00:00 GMT; Path=/",
737
+ "city=Tokyo; Domain=rubyforge.org; Expires=Sun, 08 Aug 2076 19:00:00 GMT; Path=/",
738
+ ].join(', ')
739
+
740
+ cookies = @jar.parse(set_cookie, 'http://rubyforge.org/') { |c| c.name != 'city' }
741
+ assert_equal %w[Akinori Japan], cookies.map { |c| c.value }
742
+ assert_equal %w[Japan Akinori], @jar.to_a.sort_by { |c| c.name }.map { |c| c.value }
743
+ end
625
744
  end
626
745
 
627
746
  class WithHashStore < Test::Unit::TestCase
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: http-cookie
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre10
4
+ version: 1.0.0.pre11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Akinori MUSHA
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2013-04-07 00:00:00.000000000 Z
14
+ date: 2013-04-15 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: domain_name
@@ -132,6 +132,7 @@ files:
132
132
  - http-cookie.gemspec
133
133
  - lib/http-cookie.rb
134
134
  - lib/http/cookie.rb
135
+ - lib/http/cookie/ruby_compat.rb
135
136
  - lib/http/cookie/scanner.rb
136
137
  - lib/http/cookie/version.rb
137
138
  - lib/http/cookie_jar.rb