addressable 2.8.1 → 2.8.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -50,10 +50,11 @@ module Addressable
50
50
  SUB_DELIMS = "\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\="
51
51
  RESERVED = (GEN_DELIMS + SUB_DELIMS).freeze
52
52
  UNRESERVED = (ALPHA + DIGIT + "\\-\\.\\_\\~").freeze
53
+ RESERVED_AND_UNRESERVED = RESERVED + UNRESERVED
53
54
  PCHAR = (UNRESERVED + SUB_DELIMS + "\\:\\@").freeze
54
55
  SCHEME = (ALPHA + DIGIT + "\\-\\+\\.").freeze
55
56
  HOST = (UNRESERVED + SUB_DELIMS + "\\[\\:\\]").freeze
56
- AUTHORITY = (PCHAR + "\\[\\:\\]").freeze
57
+ AUTHORITY = (PCHAR + "\\[\\]").freeze
57
58
  PATH = (PCHAR + "\\/").freeze
58
59
  QUERY = (PCHAR + "\\/\\?").freeze
59
60
  FRAGMENT = (PCHAR + "\\/\\?").freeze
@@ -68,6 +69,18 @@ module Addressable
68
69
  QUERY = %r{[^a-zA-Z0-9\-\.\_\~\!\$\'\(\)\*\+\,\=\:\@\/\?%]|%(?!2B|2b)}
69
70
  end
70
71
 
72
+ module CharacterClassesRegexps
73
+ AUTHORITY = /[^#{CharacterClasses::AUTHORITY}]/
74
+ FRAGMENT = /[^#{CharacterClasses::FRAGMENT}]/
75
+ HOST = /[^#{CharacterClasses::HOST}]/
76
+ PATH = /[^#{CharacterClasses::PATH}]/
77
+ QUERY = /[^#{CharacterClasses::QUERY}]/
78
+ RESERVED = /[^#{CharacterClasses::RESERVED}]/
79
+ RESERVED_AND_UNRESERVED = /[^#{CharacterClasses::RESERVED_AND_UNRESERVED}]/
80
+ SCHEME = /[^#{CharacterClasses::SCHEME}]/
81
+ UNRESERVED = /[^#{CharacterClasses::UNRESERVED}]/
82
+ end
83
+
71
84
  SLASH = '/'
72
85
  EMPTY_STR = ''
73
86
 
@@ -117,7 +130,7 @@ module Addressable
117
130
  uri = uri.to_str
118
131
  rescue TypeError, NoMethodError
119
132
  raise TypeError, "Can't convert #{uri.class} into String."
120
- end if not uri.is_a? String
133
+ end unless uri.is_a?(String)
121
134
 
122
135
  # This Regexp supplied as an example in RFC 3986, and it works great.
123
136
  scan = uri.scan(URIREGEX)
@@ -138,15 +151,15 @@ module Addressable
138
151
  user = userinfo.strip[/^([^:]*):?/, 1]
139
152
  password = userinfo.strip[/:(.*)$/, 1]
140
153
  end
154
+
141
155
  host = authority.sub(
142
156
  /^([^\[\]]*)@/, EMPTY_STR
143
157
  ).sub(
144
158
  /:([^:@\[\]]*?)$/, EMPTY_STR
145
159
  )
160
+
146
161
  port = authority[/:([^:@\[\]]*?)$/, 1]
147
- end
148
- if port == EMPTY_STR
149
- port = nil
162
+ port = nil if port == EMPTY_STR
150
163
  end
151
164
 
152
165
  return new(
@@ -189,7 +202,7 @@ module Addressable
189
202
  uri = uri.to_s
190
203
  end
191
204
 
192
- if !uri.respond_to?(:to_str)
205
+ unless uri.respond_to?(:to_str)
193
206
  raise TypeError, "Can't convert #{uri.class} into String."
194
207
  end
195
208
  # Otherwise, convert to a String
@@ -281,7 +294,7 @@ module Addressable
281
294
  return nil unless path
282
295
  # If a URI object is passed, just return itself.
283
296
  return path if path.kind_of?(self)
284
- if !path.respond_to?(:to_str)
297
+ unless path.respond_to?(:to_str)
285
298
  raise TypeError, "Can't convert #{path.class} into String."
286
299
  end
287
300
  # Otherwise, convert to a String
@@ -329,13 +342,13 @@ module Addressable
329
342
  # #=> #<Addressable::URI:0xcab390 URI:http://example.com/relative/path>
330
343
  def self.join(*uris)
331
344
  uri_objects = uris.collect do |uri|
332
- if !uri.respond_to?(:to_str)
345
+ unless uri.respond_to?(:to_str)
333
346
  raise TypeError, "Can't convert #{uri.class} into String."
334
347
  end
335
348
  uri.kind_of?(self) ? uri : self.parse(uri.to_str)
336
349
  end
337
350
  result = uri_objects.shift.dup
338
- for uri in uri_objects
351
+ uri_objects.each do |uri|
339
352
  result.join!(uri)
340
353
  end
341
354
  return result
@@ -344,17 +357,13 @@ module Addressable
344
357
  ##
345
358
  # Tables used to optimize encoding operations in `self.encode_component`
346
359
  # and `self.normalize_component`
347
- SEQUENCE_ENCODING_TABLE = Hash.new do |hash, sequence|
348
- hash[sequence] = sequence.unpack("C*").map do |c|
349
- format("%02x", c)
350
- end.join
351
- end
360
+ SEQUENCE_ENCODING_TABLE = (0..255).map do |byte|
361
+ format("%02x", byte).freeze
362
+ end.freeze
352
363
 
353
- SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE = Hash.new do |hash, sequence|
354
- hash[sequence] = sequence.unpack("C*").map do |c|
355
- format("%%%02X", c)
356
- end.join
357
- end
364
+ SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE = (0..255).map do |byte|
365
+ format("%%%02X", byte).freeze
366
+ end.freeze
358
367
 
359
368
  ##
360
369
  # Percent encodes a URI component.
@@ -391,9 +400,7 @@ module Addressable
391
400
  # "simple/example", Addressable::URI::CharacterClasses::UNRESERVED
392
401
  # )
393
402
  # => "simple%2Fexample"
394
- def self.encode_component(component, character_class=
395
- CharacterClasses::RESERVED + CharacterClasses::UNRESERVED,
396
- upcase_encoded='')
403
+ def self.encode_component(component, character_class=CharacterClassesRegexps::RESERVED_AND_UNRESERVED, upcase_encoded='')
397
404
  return nil if component.nil?
398
405
 
399
406
  begin
@@ -421,16 +428,17 @@ module Addressable
421
428
  component = component.dup
422
429
  component.force_encoding(Encoding::ASCII_8BIT)
423
430
  # Avoiding gsub! because there are edge cases with frozen strings
424
- component = component.gsub(character_class) do |sequence|
425
- SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE[sequence]
431
+ component = component.gsub(character_class) do |char|
432
+ SEQUENCE_UPCASED_PERCENT_ENCODING_TABLE[char.ord]
426
433
  end
427
434
  if upcase_encoded.length > 0
428
- upcase_encoded_chars = upcase_encoded.chars.map do |char|
429
- SEQUENCE_ENCODING_TABLE[char]
435
+ upcase_encoded_chars = upcase_encoded.bytes.map do |byte|
436
+ SEQUENCE_ENCODING_TABLE[byte]
430
437
  end
431
438
  component = component.gsub(/%(#{upcase_encoded_chars.join('|')})/,
432
439
  &:upcase)
433
440
  end
441
+
434
442
  return component
435
443
  end
436
444
 
@@ -481,7 +489,7 @@ module Addressable
481
489
  leave_encoded.include?(c) ? sequence : c
482
490
  end
483
491
 
484
- result.force_encoding("utf-8")
492
+ result.force_encoding(Encoding::UTF_8)
485
493
  if return_type == String
486
494
  return result
487
495
  elsif return_type == ::Addressable::URI
@@ -542,7 +550,7 @@ module Addressable
542
550
  # )
543
551
  # => "one two%2Fthree&four"
544
552
  def self.normalize_component(component, character_class=
545
- CharacterClasses::RESERVED + CharacterClasses::UNRESERVED,
553
+ CharacterClassesRegexps::RESERVED_AND_UNRESERVED,
546
554
  leave_encoded='')
547
555
  return nil if component.nil?
548
556
 
@@ -560,10 +568,9 @@ module Addressable
560
568
  leave_re = if leave_encoded.length > 0
561
569
  character_class = "#{character_class}%" unless character_class.include?('%')
562
570
 
563
- "|%(?!#{leave_encoded.chars.flat_map do |char|
564
- seq = SEQUENCE_ENCODING_TABLE[char]
565
- [seq.upcase, seq.downcase]
566
- end.join('|')})"
571
+ bytes = leave_encoded.bytes
572
+ leave_encoded_pattern = bytes.map { |b| SEQUENCE_ENCODING_TABLE[b] }.join('|')
573
+ "|%(?!#{leave_encoded_pattern}|#{leave_encoded_pattern.upcase})"
567
574
  end
568
575
 
569
576
  character_class = if leave_re
@@ -579,7 +586,7 @@ module Addressable
579
586
  unencoded = self.unencode_component(component, String, leave_encoded)
580
587
  begin
581
588
  encoded = self.encode_component(
582
- Addressable::IDNA.unicode_normalize_kc(unencoded),
589
+ unencoded.unicode_normalize(:nfc),
583
590
  character_class,
584
591
  leave_encoded
585
592
  )
@@ -623,15 +630,15 @@ module Addressable
623
630
  uri_object = uri.kind_of?(self) ? uri : self.parse(uri)
624
631
  encoded_uri = Addressable::URI.new(
625
632
  :scheme => self.encode_component(uri_object.scheme,
626
- Addressable::URI::CharacterClasses::SCHEME),
633
+ Addressable::URI::CharacterClassesRegexps::SCHEME),
627
634
  :authority => self.encode_component(uri_object.authority,
628
- Addressable::URI::CharacterClasses::AUTHORITY),
635
+ Addressable::URI::CharacterClassesRegexps::AUTHORITY),
629
636
  :path => self.encode_component(uri_object.path,
630
- Addressable::URI::CharacterClasses::PATH),
637
+ Addressable::URI::CharacterClassesRegexps::PATH),
631
638
  :query => self.encode_component(uri_object.query,
632
- Addressable::URI::CharacterClasses::QUERY),
639
+ Addressable::URI::CharacterClassesRegexps::QUERY),
633
640
  :fragment => self.encode_component(uri_object.fragment,
634
- Addressable::URI::CharacterClasses::FRAGMENT)
641
+ Addressable::URI::CharacterClassesRegexps::FRAGMENT)
635
642
  )
636
643
  if return_type == String
637
644
  return encoded_uri.to_s
@@ -687,8 +694,7 @@ module Addressable
687
694
  components.each do |key, value|
688
695
  if value != nil
689
696
  begin
690
- components[key] =
691
- Addressable::IDNA.unicode_normalize_kc(value.to_str)
697
+ components[key] = value.to_str.unicode_normalize(:nfc)
692
698
  rescue ArgumentError
693
699
  # Likely a malformed UTF-8 character, skip unicode normalization
694
700
  components[key] = value.to_str
@@ -697,19 +703,19 @@ module Addressable
697
703
  end
698
704
  encoded_uri = Addressable::URI.new(
699
705
  :scheme => self.encode_component(components[:scheme],
700
- Addressable::URI::CharacterClasses::SCHEME),
706
+ Addressable::URI::CharacterClassesRegexps::SCHEME),
701
707
  :user => self.encode_component(components[:user],
702
- Addressable::URI::CharacterClasses::UNRESERVED),
708
+ Addressable::URI::CharacterClassesRegexps::UNRESERVED),
703
709
  :password => self.encode_component(components[:password],
704
- Addressable::URI::CharacterClasses::UNRESERVED),
710
+ Addressable::URI::CharacterClassesRegexps::UNRESERVED),
705
711
  :host => components[:host],
706
712
  :port => components[:port],
707
713
  :path => self.encode_component(components[:path],
708
- Addressable::URI::CharacterClasses::PATH),
714
+ Addressable::URI::CharacterClassesRegexps::PATH),
709
715
  :query => self.encode_component(components[:query],
710
- Addressable::URI::CharacterClasses::QUERY),
716
+ Addressable::URI::CharacterClassesRegexps::QUERY),
711
717
  :fragment => self.encode_component(components[:fragment],
712
- Addressable::URI::CharacterClasses::FRAGMENT)
718
+ Addressable::URI::CharacterClassesRegexps::FRAGMENT)
713
719
  )
714
720
  if return_type == String
715
721
  return encoded_uri.to_s
@@ -760,11 +766,11 @@ module Addressable
760
766
  [
761
767
  self.encode_component(
762
768
  key.gsub(/(\r\n|\n|\r)/, "\r\n"),
763
- CharacterClasses::UNRESERVED
769
+ CharacterClassesRegexps::UNRESERVED
764
770
  ).gsub("%20", "+"),
765
771
  self.encode_component(
766
772
  value.gsub(/(\r\n|\n|\r)/, "\r\n"),
767
- CharacterClasses::UNRESERVED
773
+ CharacterClassesRegexps::UNRESERVED
768
774
  ).gsub("%20", "+")
769
775
  ]
770
776
  end
@@ -836,7 +842,9 @@ module Addressable
836
842
  end
837
843
  end
838
844
 
839
- self.defer_validation do
845
+ reset_ivs
846
+
847
+ defer_validation do
840
848
  # Bunch of crazy logic required because of the composite components
841
849
  # like userinfo and authority.
842
850
  self.scheme = options[:scheme] if options[:scheme]
@@ -851,7 +859,8 @@ module Addressable
851
859
  self.query_values = options[:query_values] if options[:query_values]
852
860
  self.fragment = options[:fragment] if options[:fragment]
853
861
  end
854
- self.to_s
862
+
863
+ to_s # force path validation
855
864
  end
856
865
 
857
866
  ##
@@ -878,9 +887,7 @@ module Addressable
878
887
  # The scheme component for this URI.
879
888
  #
880
889
  # @return [String] The scheme component.
881
- def scheme
882
- return defined?(@scheme) ? @scheme : nil
883
- end
890
+ attr_reader :scheme
884
891
 
885
892
  ##
886
893
  # The scheme component for this URI, normalized.
@@ -888,8 +895,8 @@ module Addressable
888
895
  # @return [String] The scheme component, normalized.
889
896
  def normalized_scheme
890
897
  return nil unless self.scheme
891
- @normalized_scheme ||= begin
892
- if self.scheme =~ /^\s*ssh\+svn\s*$/i
898
+ if @normalized_scheme == NONE
899
+ @normalized_scheme = if self.scheme =~ /^\s*ssh\+svn\s*$/i
893
900
  "svn+ssh".dup
894
901
  else
895
902
  Addressable::URI.normalize_component(
@@ -920,7 +927,7 @@ module Addressable
920
927
  @scheme = nil if @scheme.to_s.strip.empty?
921
928
 
922
929
  # Reset dependent values
923
- remove_instance_variable(:@normalized_scheme) if defined?(@normalized_scheme)
930
+ @normalized_scheme = NONE
924
931
  remove_composite_values
925
932
 
926
933
  # Ensure we haven't created an invalid URI
@@ -931,9 +938,7 @@ module Addressable
931
938
  # The user component for this URI.
932
939
  #
933
940
  # @return [String] The user component.
934
- def user
935
- return defined?(@user) ? @user : nil
936
- end
941
+ attr_reader :user
937
942
 
938
943
  ##
939
944
  # The user component for this URI, normalized.
@@ -941,8 +946,8 @@ module Addressable
941
946
  # @return [String] The user component, normalized.
942
947
  def normalized_user
943
948
  return nil unless self.user
944
- return @normalized_user if defined?(@normalized_user)
945
- @normalized_user ||= begin
949
+ return @normalized_user unless @normalized_user == NONE
950
+ @normalized_user = begin
946
951
  if normalized_scheme =~ /https?/ && self.user.strip.empty? &&
947
952
  (!self.password || self.password.strip.empty?)
948
953
  nil
@@ -970,14 +975,14 @@ module Addressable
970
975
 
971
976
  # You can't have a nil user with a non-nil password
972
977
  if password != nil
973
- @user = EMPTY_STR if @user.nil?
978
+ @user = EMPTY_STR unless user
974
979
  end
975
980
 
976
981
  # Reset dependent values
977
- remove_instance_variable(:@userinfo) if defined?(@userinfo)
978
- remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
979
- remove_instance_variable(:@authority) if defined?(@authority)
980
- remove_instance_variable(:@normalized_user) if defined?(@normalized_user)
982
+ @userinfo = nil
983
+ @normalized_userinfo = NONE
984
+ @authority = nil
985
+ @normalized_user = NONE
981
986
  remove_composite_values
982
987
 
983
988
  # Ensure we haven't created an invalid URI
@@ -988,9 +993,7 @@ module Addressable
988
993
  # The password component for this URI.
989
994
  #
990
995
  # @return [String] The password component.
991
- def password
992
- return defined?(@password) ? @password : nil
993
- end
996
+ attr_reader :password
994
997
 
995
998
  ##
996
999
  # The password component for this URI, normalized.
@@ -998,8 +1001,8 @@ module Addressable
998
1001
  # @return [String] The password component, normalized.
999
1002
  def normalized_password
1000
1003
  return nil unless self.password
1001
- return @normalized_password if defined?(@normalized_password)
1002
- @normalized_password ||= begin
1004
+ return @normalized_password unless @normalized_password == NONE
1005
+ @normalized_password = begin
1003
1006
  if self.normalized_scheme =~ /https?/ && self.password.strip.empty? &&
1004
1007
  (!self.user || self.user.strip.empty?)
1005
1008
  nil
@@ -1026,17 +1029,15 @@ module Addressable
1026
1029
  @password = new_password ? new_password.to_str : nil
1027
1030
 
1028
1031
  # You can't have a nil user with a non-nil password
1029
- @password ||= nil
1030
- @user ||= nil
1031
1032
  if @password != nil
1032
- @user = EMPTY_STR if @user.nil?
1033
+ self.user = EMPTY_STR if user.nil?
1033
1034
  end
1034
1035
 
1035
1036
  # Reset dependent values
1036
- remove_instance_variable(:@userinfo) if defined?(@userinfo)
1037
- remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
1038
- remove_instance_variable(:@authority) if defined?(@authority)
1039
- remove_instance_variable(:@normalized_password) if defined?(@normalized_password)
1037
+ @userinfo = nil
1038
+ @normalized_userinfo = NONE
1039
+ @authority = nil
1040
+ @normalized_password = NONE
1040
1041
  remove_composite_values
1041
1042
 
1042
1043
  # Ensure we haven't created an invalid URI
@@ -1066,8 +1067,8 @@ module Addressable
1066
1067
  # @return [String] The userinfo component, normalized.
1067
1068
  def normalized_userinfo
1068
1069
  return nil unless self.userinfo
1069
- return @normalized_userinfo if defined?(@normalized_userinfo)
1070
- @normalized_userinfo ||= begin
1070
+ return @normalized_userinfo unless @normalized_userinfo == NONE
1071
+ @normalized_userinfo = begin
1071
1072
  current_user = self.normalized_user
1072
1073
  current_password = self.normalized_password
1073
1074
  if !current_user && !current_password
@@ -1105,7 +1106,7 @@ module Addressable
1105
1106
  self.user = new_user
1106
1107
 
1107
1108
  # Reset dependent values
1108
- remove_instance_variable(:@authority) if defined?(@authority)
1109
+ @authority = nil
1109
1110
  remove_composite_values
1110
1111
 
1111
1112
  # Ensure we haven't created an invalid URI
@@ -1116,9 +1117,7 @@ module Addressable
1116
1117
  # The host component for this URI.
1117
1118
  #
1118
1119
  # @return [String] The host component.
1119
- def host
1120
- return defined?(@host) ? @host : nil
1121
- end
1120
+ attr_reader :host
1122
1121
 
1123
1122
  ##
1124
1123
  # The host component for this URI, normalized.
@@ -1161,8 +1160,8 @@ module Addressable
1161
1160
  @host = new_host ? new_host.to_str : nil
1162
1161
 
1163
1162
  # Reset dependent values
1164
- remove_instance_variable(:@authority) if defined?(@authority)
1165
- remove_instance_variable(:@normalized_host) if defined?(@normalized_host)
1163
+ @authority = nil
1164
+ @normalized_host = nil
1166
1165
  remove_composite_values
1167
1166
 
1168
1167
  # Ensure we haven't created an invalid URI
@@ -1293,14 +1292,14 @@ module Addressable
1293
1292
  end
1294
1293
 
1295
1294
  # Password assigned first to ensure validity in case of nil
1296
- self.password = defined?(new_password) ? new_password : nil
1297
- self.user = defined?(new_user) ? new_user : nil
1298
- self.host = defined?(new_host) ? new_host : nil
1299
- self.port = defined?(new_port) ? new_port : nil
1295
+ self.password = new_password
1296
+ self.user = new_user
1297
+ self.host = new_host
1298
+ self.port = new_port
1300
1299
 
1301
1300
  # Reset dependent values
1302
- remove_instance_variable(:@userinfo) if defined?(@userinfo)
1303
- remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
1301
+ @userinfo = nil
1302
+ @normalized_userinfo = NONE
1304
1303
  remove_composite_values
1305
1304
 
1306
1305
  # Ensure we haven't created an invalid URI
@@ -1348,16 +1347,16 @@ module Addressable
1348
1347
  new_port = new_origin[/:([^:@\[\]\/]*?)$/, 1]
1349
1348
  end
1350
1349
 
1351
- self.scheme = defined?(new_scheme) ? new_scheme : nil
1352
- self.host = defined?(new_host) ? new_host : nil
1353
- self.port = defined?(new_port) ? new_port : nil
1350
+ self.scheme = new_scheme
1351
+ self.host = new_host
1352
+ self.port = new_port
1354
1353
  self.userinfo = nil
1355
1354
 
1356
1355
  # Reset dependent values
1357
- remove_instance_variable(:@userinfo) if defined?(@userinfo)
1358
- remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
1359
- remove_instance_variable(:@authority) if defined?(@authority)
1360
- remove_instance_variable(:@normalized_authority) if defined?(@normalized_authority)
1356
+ @userinfo = nil
1357
+ @normalized_userinfo = NONE
1358
+ @authority = nil
1359
+ @normalized_authority = nil
1361
1360
  remove_composite_values
1362
1361
 
1363
1362
  # Ensure we haven't created an invalid URI
@@ -1384,9 +1383,7 @@ module Addressable
1384
1383
  # infer port numbers from default values.
1385
1384
  #
1386
1385
  # @return [Integer] The port component.
1387
- def port
1388
- return defined?(@port) ? @port : nil
1389
- end
1386
+ attr_reader :port
1390
1387
 
1391
1388
  ##
1392
1389
  # The port component for this URI, normalized.
@@ -1394,8 +1391,8 @@ module Addressable
1394
1391
  # @return [Integer] The port component, normalized.
1395
1392
  def normalized_port
1396
1393
  return nil unless self.port
1397
- return @normalized_port if defined?(@normalized_port)
1398
- @normalized_port ||= begin
1394
+ return @normalized_port unless @normalized_port == NONE
1395
+ @normalized_port = begin
1399
1396
  if URI.port_mapping[self.normalized_scheme] == self.port
1400
1397
  nil
1401
1398
  else
@@ -1426,8 +1423,8 @@ module Addressable
1426
1423
  @port = nil if @port == 0
1427
1424
 
1428
1425
  # Reset dependent values
1429
- remove_instance_variable(:@authority) if defined?(@authority)
1430
- remove_instance_variable(:@normalized_port) if defined?(@normalized_port)
1426
+ @authority = nil
1427
+ @normalized_port = NONE
1431
1428
  remove_composite_values
1432
1429
 
1433
1430
  # Ensure we haven't created an invalid URI
@@ -1528,9 +1525,7 @@ module Addressable
1528
1525
  # The path component for this URI.
1529
1526
  #
1530
1527
  # @return [String] The path component.
1531
- def path
1532
- return defined?(@path) ? @path : EMPTY_STR
1533
- end
1528
+ attr_reader :path
1534
1529
 
1535
1530
  NORMPATH = /^(?!\/)[^\/:]*:.*$/
1536
1531
  ##
@@ -1579,7 +1574,7 @@ module Addressable
1579
1574
  end
1580
1575
 
1581
1576
  # Reset dependent values
1582
- remove_instance_variable(:@normalized_path) if defined?(@normalized_path)
1577
+ @normalized_path = nil
1583
1578
  remove_composite_values
1584
1579
 
1585
1580
  # Ensure we haven't created an invalid URI
@@ -1609,9 +1604,7 @@ module Addressable
1609
1604
  # The query component for this URI.
1610
1605
  #
1611
1606
  # @return [String] The query component.
1612
- def query
1613
- return defined?(@query) ? @query : nil
1614
- end
1607
+ attr_reader :query
1615
1608
 
1616
1609
  ##
1617
1610
  # The query component for this URI, normalized.
@@ -1619,8 +1612,8 @@ module Addressable
1619
1612
  # @return [String] The query component, normalized.
1620
1613
  def normalized_query(*flags)
1621
1614
  return nil unless self.query
1622
- return @normalized_query if defined?(@normalized_query)
1623
- @normalized_query ||= begin
1615
+ return @normalized_query unless @normalized_query == NONE
1616
+ @normalized_query = begin
1624
1617
  modified_query_class = Addressable::URI::CharacterClasses::QUERY.dup
1625
1618
  # Make sure possible key-value pair delimiters are escaped.
1626
1619
  modified_query_class.sub!("\\&", "").sub!("\\;", "")
@@ -1652,7 +1645,7 @@ module Addressable
1652
1645
  @query = new_query ? new_query.to_str : nil
1653
1646
 
1654
1647
  # Reset dependent values
1655
- remove_instance_variable(:@normalized_query) if defined?(@normalized_query)
1648
+ @normalized_query = NONE
1656
1649
  remove_composite_values
1657
1650
  end
1658
1651
 
@@ -1752,20 +1745,20 @@ module Addressable
1752
1745
  buffer = "".dup
1753
1746
  new_query_values.each do |key, value|
1754
1747
  encoded_key = URI.encode_component(
1755
- key, CharacterClasses::UNRESERVED
1748
+ key, CharacterClassesRegexps::UNRESERVED
1756
1749
  )
1757
1750
  if value == nil
1758
1751
  buffer << "#{encoded_key}&"
1759
1752
  elsif value.kind_of?(Array)
1760
1753
  value.each do |sub_value|
1761
1754
  encoded_value = URI.encode_component(
1762
- sub_value, CharacterClasses::UNRESERVED
1755
+ sub_value, CharacterClassesRegexps::UNRESERVED
1763
1756
  )
1764
1757
  buffer << "#{encoded_key}=#{encoded_value}&"
1765
1758
  end
1766
1759
  else
1767
1760
  encoded_value = URI.encode_component(
1768
- value, CharacterClasses::UNRESERVED
1761
+ value, CharacterClassesRegexps::UNRESERVED
1769
1762
  )
1770
1763
  buffer << "#{encoded_key}=#{encoded_value}&"
1771
1764
  end
@@ -1814,9 +1807,7 @@ module Addressable
1814
1807
  # The fragment component for this URI.
1815
1808
  #
1816
1809
  # @return [String] The fragment component.
1817
- def fragment
1818
- return defined?(@fragment) ? @fragment : nil
1819
- end
1810
+ attr_reader :fragment
1820
1811
 
1821
1812
  ##
1822
1813
  # The fragment component for this URI, normalized.
@@ -1824,8 +1815,8 @@ module Addressable
1824
1815
  # @return [String] The fragment component, normalized.
1825
1816
  def normalized_fragment
1826
1817
  return nil unless self.fragment
1827
- return @normalized_fragment if defined?(@normalized_fragment)
1828
- @normalized_fragment ||= begin
1818
+ return @normalized_fragment unless @normalized_fragment == NONE
1819
+ @normalized_fragment = begin
1829
1820
  component = Addressable::URI.normalize_component(
1830
1821
  self.fragment,
1831
1822
  Addressable::URI::NormalizeCharacterClasses::FRAGMENT
@@ -1848,7 +1839,7 @@ module Addressable
1848
1839
  @fragment = new_fragment ? new_fragment.to_str : nil
1849
1840
 
1850
1841
  # Reset dependent values
1851
- remove_instance_variable(:@normalized_fragment) if defined?(@normalized_fragment)
1842
+ @normalized_fragment = NONE
1852
1843
  remove_composite_values
1853
1844
 
1854
1845
  # Ensure we haven't created an invalid URI
@@ -2014,7 +2005,7 @@ module Addressable
2014
2005
  #
2015
2006
  # @see Hash#merge
2016
2007
  def merge(hash)
2017
- if !hash.respond_to?(:to_hash)
2008
+ unless hash.respond_to?(:to_hash)
2018
2009
  raise TypeError, "Can't convert #{hash.class} into Hash."
2019
2010
  end
2020
2011
  hash = hash.to_hash
@@ -2408,7 +2399,27 @@ module Addressable
2408
2399
  yield
2409
2400
  @validation_deferred = false
2410
2401
  validate
2411
- return nil
2402
+ ensure
2403
+ @validation_deferred = false
2404
+ end
2405
+
2406
+ def encode_with(coder)
2407
+ instance_variables.each do |ivar|
2408
+ value = instance_variable_get(ivar)
2409
+ if value != NONE
2410
+ key = ivar.to_s.slice(1..-1)
2411
+ coder[key] = value
2412
+ end
2413
+ end
2414
+ nil
2415
+ end
2416
+
2417
+ def init_with(coder)
2418
+ reset_ivs
2419
+ coder.map.each do |key, value|
2420
+ instance_variable_set("@#{key}", value)
2421
+ end
2422
+ nil
2412
2423
  end
2413
2424
 
2414
2425
  protected
@@ -2507,11 +2518,7 @@ module Addressable
2507
2518
  # @return [Addressable::URI] <code>self</code>.
2508
2519
  def replace_self(uri)
2509
2520
  # Reset dependent values
2510
- instance_variables.each do |var|
2511
- if instance_variable_defined?(var) && var != :@validation_deferred
2512
- remove_instance_variable(var)
2513
- end
2514
- end
2521
+ reset_ivs
2515
2522
 
2516
2523
  @scheme = uri.scheme
2517
2524
  @user = uri.user
@@ -2543,8 +2550,8 @@ module Addressable
2543
2550
  #
2544
2551
  # @api private
2545
2552
  def remove_composite_values
2546
- remove_instance_variable(:@uri_string) if defined?(@uri_string)
2547
- remove_instance_variable(:@hash) if defined?(@hash)
2553
+ @uri_string = nil
2554
+ @hash = nil
2548
2555
  end
2549
2556
 
2550
2557
  ##
@@ -2556,5 +2563,40 @@ module Addressable
2556
2563
  str.force_encoding(Encoding::UTF_8)
2557
2564
  end
2558
2565
  end
2566
+
2567
+ private
2568
+
2569
+ ##
2570
+ # Resets instance variables
2571
+ #
2572
+ # @api private
2573
+ def reset_ivs
2574
+ @scheme = nil
2575
+ @user = nil
2576
+ @normalized_scheme = NONE
2577
+ @normalized_user = NONE
2578
+ @uri_string = nil
2579
+ @hash = nil
2580
+ @userinfo = nil
2581
+ @normalized_userinfo = NONE
2582
+ @authority = nil
2583
+ @password = nil
2584
+ @normalized_authority = nil
2585
+ @port = nil
2586
+ @normalized_password = NONE
2587
+ @host = nil
2588
+ @normalized_host = nil
2589
+ @normalized_port = NONE
2590
+ @path = EMPTY_STR
2591
+ @normalized_path = nil
2592
+ @normalized_query = NONE
2593
+ @fragment = nil
2594
+ @normalized_fragment = NONE
2595
+ @query = nil
2596
+ end
2597
+
2598
+ NONE = Module.new.freeze
2599
+
2600
+ private_constant :NONE
2559
2601
  end
2560
2602
  end