addressable 2.8.0 → 2.8.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # encoding:utf-8
4
3
  #--
5
4
  # Copyright (C) Bob Aman
6
5
  #
@@ -38,20 +37,26 @@ module Addressable
38
37
  ##
39
38
  # Container for the character classes specified in
40
39
  # <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
40
+ #
41
+ # Note: Concatenated and interpolated `String`s are not affected by the
42
+ # `frozen_string_literal` directive and must be frozen explicitly.
43
+ #
44
+ # Interpolated `String`s *were* frozen this way before Ruby 3.0:
45
+ # https://bugs.ruby-lang.org/issues/17104
41
46
  module CharacterClasses
42
47
  ALPHA = "a-zA-Z"
43
48
  DIGIT = "0-9"
44
49
  GEN_DELIMS = "\\:\\/\\?\\#\\[\\]\\@"
45
50
  SUB_DELIMS = "\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\="
46
- RESERVED = GEN_DELIMS + SUB_DELIMS
47
- UNRESERVED = ALPHA + DIGIT + "\\-\\.\\_\\~"
48
- PCHAR = UNRESERVED + SUB_DELIMS + "\\:\\@"
49
- SCHEME = ALPHA + DIGIT + "\\-\\+\\."
50
- HOST = UNRESERVED + SUB_DELIMS + "\\[\\:\\]"
51
- AUTHORITY = PCHAR + "\\[\\:\\]"
52
- PATH = PCHAR + "\\/"
53
- QUERY = PCHAR + "\\/\\?"
54
- FRAGMENT = PCHAR + "\\/\\?"
51
+ RESERVED = (GEN_DELIMS + SUB_DELIMS).freeze
52
+ UNRESERVED = (ALPHA + DIGIT + "\\-\\.\\_\\~").freeze
53
+ PCHAR = (UNRESERVED + SUB_DELIMS + "\\:\\@").freeze
54
+ SCHEME = (ALPHA + DIGIT + "\\-\\+\\.").freeze
55
+ HOST = (UNRESERVED + SUB_DELIMS + "\\[\\:\\]").freeze
56
+ AUTHORITY = (PCHAR + "\\[\\]").freeze
57
+ PATH = (PCHAR + "\\/").freeze
58
+ QUERY = (PCHAR + "\\/\\?").freeze
59
+ FRAGMENT = (PCHAR + "\\/\\?").freeze
55
60
  end
56
61
 
57
62
  module NormalizeCharacterClasses
@@ -112,7 +117,7 @@ module Addressable
112
117
  uri = uri.to_str
113
118
  rescue TypeError, NoMethodError
114
119
  raise TypeError, "Can't convert #{uri.class} into String."
115
- end if not uri.is_a? String
120
+ end unless uri.is_a?(String)
116
121
 
117
122
  # This Regexp supplied as an example in RFC 3986, and it works great.
118
123
  scan = uri.scan(URIREGEX)
@@ -133,15 +138,15 @@ module Addressable
133
138
  user = userinfo.strip[/^([^:]*):?/, 1]
134
139
  password = userinfo.strip[/:(.*)$/, 1]
135
140
  end
141
+
136
142
  host = authority.sub(
137
143
  /^([^\[\]]*)@/, EMPTY_STR
138
144
  ).sub(
139
145
  /:([^:@\[\]]*?)$/, EMPTY_STR
140
146
  )
147
+
141
148
  port = authority[/:([^:@\[\]]*?)$/, 1]
142
- end
143
- if port == EMPTY_STR
144
- port = nil
149
+ port = nil if port == EMPTY_STR
145
150
  end
146
151
 
147
152
  return new(
@@ -184,7 +189,7 @@ module Addressable
184
189
  uri = uri.to_s
185
190
  end
186
191
 
187
- if !uri.respond_to?(:to_str)
192
+ unless uri.respond_to?(:to_str)
188
193
  raise TypeError, "Can't convert #{uri.class} into String."
189
194
  end
190
195
  # Otherwise, convert to a String
@@ -276,7 +281,7 @@ module Addressable
276
281
  return nil unless path
277
282
  # If a URI object is passed, just return itself.
278
283
  return path if path.kind_of?(self)
279
- if !path.respond_to?(:to_str)
284
+ unless path.respond_to?(:to_str)
280
285
  raise TypeError, "Can't convert #{path.class} into String."
281
286
  end
282
287
  # Otherwise, convert to a String
@@ -324,13 +329,13 @@ module Addressable
324
329
  # #=> #<Addressable::URI:0xcab390 URI:http://example.com/relative/path>
325
330
  def self.join(*uris)
326
331
  uri_objects = uris.collect do |uri|
327
- if !uri.respond_to?(:to_str)
332
+ unless uri.respond_to?(:to_str)
328
333
  raise TypeError, "Can't convert #{uri.class} into String."
329
334
  end
330
335
  uri.kind_of?(self) ? uri : self.parse(uri.to_str)
331
336
  end
332
337
  result = uri_objects.shift.dup
333
- for uri in uri_objects
338
+ uri_objects.each do |uri|
334
339
  result.join!(uri)
335
340
  end
336
341
  return result
@@ -469,20 +474,14 @@ module Addressable
469
474
  "Expected Class (String or Addressable::URI), " +
470
475
  "got #{return_type.inspect}"
471
476
  end
472
- uri = uri.dup
473
- # Seriously, only use UTF-8. I'm really not kidding!
474
- uri.force_encoding("utf-8")
475
477
 
476
- unless leave_encoded.empty?
477
- leave_encoded = leave_encoded.dup.force_encoding("utf-8")
478
- end
479
-
480
- result = uri.gsub(/%[0-9a-f]{2}/iu) do |sequence|
478
+ result = uri.gsub(/%[0-9a-f]{2}/i) do |sequence|
481
479
  c = sequence[1..3].to_i(16).chr
482
- c.force_encoding("utf-8")
480
+ c.force_encoding(sequence.encoding)
483
481
  leave_encoded.include?(c) ? sequence : c
484
482
  end
485
- result.force_encoding("utf-8")
483
+
484
+ result.force_encoding(Encoding::UTF_8)
486
485
  if return_type == String
487
486
  return result
488
487
  elsif return_type == ::Addressable::URI
@@ -561,10 +560,10 @@ module Addressable
561
560
  leave_re = if leave_encoded.length > 0
562
561
  character_class = "#{character_class}%" unless character_class.include?('%')
563
562
 
564
- "|%(?!#{leave_encoded.chars.map do |char|
563
+ "|%(?!#{leave_encoded.chars.flat_map do |char|
565
564
  seq = SEQUENCE_ENCODING_TABLE[char]
566
565
  [seq.upcase, seq.downcase]
567
- end.flatten.join('|')})"
566
+ end.join('|')})"
568
567
  end
569
568
 
570
569
  character_class = if leave_re
@@ -580,7 +579,7 @@ module Addressable
580
579
  unencoded = self.unencode_component(component, String, leave_encoded)
581
580
  begin
582
581
  encoded = self.encode_component(
583
- Addressable::IDNA.unicode_normalize_kc(unencoded),
582
+ unencoded.unicode_normalize(:nfc),
584
583
  character_class,
585
584
  leave_encoded
586
585
  )
@@ -688,8 +687,7 @@ module Addressable
688
687
  components.each do |key, value|
689
688
  if value != nil
690
689
  begin
691
- components[key] =
692
- Addressable::IDNA.unicode_normalize_kc(value.to_str)
690
+ components[key] = value.to_str.unicode_normalize(:nfc)
693
691
  rescue ArgumentError
694
692
  # Likely a malformed UTF-8 character, skip unicode normalization
695
693
  components[key] = value.to_str
@@ -837,7 +835,9 @@ module Addressable
837
835
  end
838
836
  end
839
837
 
840
- self.defer_validation do
838
+ reset_ivs
839
+
840
+ defer_validation do
841
841
  # Bunch of crazy logic required because of the composite components
842
842
  # like userinfo and authority.
843
843
  self.scheme = options[:scheme] if options[:scheme]
@@ -852,7 +852,8 @@ module Addressable
852
852
  self.query_values = options[:query_values] if options[:query_values]
853
853
  self.fragment = options[:fragment] if options[:fragment]
854
854
  end
855
- self.to_s
855
+
856
+ to_s # force path validation
856
857
  end
857
858
 
858
859
  ##
@@ -879,9 +880,7 @@ module Addressable
879
880
  # The scheme component for this URI.
880
881
  #
881
882
  # @return [String] The scheme component.
882
- def scheme
883
- return defined?(@scheme) ? @scheme : nil
884
- end
883
+ attr_reader :scheme
885
884
 
886
885
  ##
887
886
  # The scheme component for this URI, normalized.
@@ -889,8 +888,8 @@ module Addressable
889
888
  # @return [String] The scheme component, normalized.
890
889
  def normalized_scheme
891
890
  return nil unless self.scheme
892
- @normalized_scheme ||= begin
893
- if self.scheme =~ /^\s*ssh\+svn\s*$/i
891
+ if @normalized_scheme == NONE
892
+ @normalized_scheme = if self.scheme =~ /^\s*ssh\+svn\s*$/i
894
893
  "svn+ssh".dup
895
894
  else
896
895
  Addressable::URI.normalize_component(
@@ -900,7 +899,7 @@ module Addressable
900
899
  end
901
900
  end
902
901
  # All normalized values should be UTF-8
903
- @normalized_scheme.force_encoding(Encoding::UTF_8) if @normalized_scheme
902
+ force_utf8_encoding_if_needed(@normalized_scheme)
904
903
  @normalized_scheme
905
904
  end
906
905
 
@@ -921,7 +920,7 @@ module Addressable
921
920
  @scheme = nil if @scheme.to_s.strip.empty?
922
921
 
923
922
  # Reset dependent values
924
- remove_instance_variable(:@normalized_scheme) if defined?(@normalized_scheme)
923
+ @normalized_scheme = NONE
925
924
  remove_composite_values
926
925
 
927
926
  # Ensure we haven't created an invalid URI
@@ -932,9 +931,7 @@ module Addressable
932
931
  # The user component for this URI.
933
932
  #
934
933
  # @return [String] The user component.
935
- def user
936
- return defined?(@user) ? @user : nil
937
- end
934
+ attr_reader :user
938
935
 
939
936
  ##
940
937
  # The user component for this URI, normalized.
@@ -942,8 +939,8 @@ module Addressable
942
939
  # @return [String] The user component, normalized.
943
940
  def normalized_user
944
941
  return nil unless self.user
945
- return @normalized_user if defined?(@normalized_user)
946
- @normalized_user ||= begin
942
+ return @normalized_user unless @normalized_user == NONE
943
+ @normalized_user = begin
947
944
  if normalized_scheme =~ /https?/ && self.user.strip.empty? &&
948
945
  (!self.password || self.password.strip.empty?)
949
946
  nil
@@ -955,7 +952,7 @@ module Addressable
955
952
  end
956
953
  end
957
954
  # All normalized values should be UTF-8
958
- @normalized_user.force_encoding(Encoding::UTF_8) if @normalized_user
955
+ force_utf8_encoding_if_needed(@normalized_user)
959
956
  @normalized_user
960
957
  end
961
958
 
@@ -971,14 +968,14 @@ module Addressable
971
968
 
972
969
  # You can't have a nil user with a non-nil password
973
970
  if password != nil
974
- @user = EMPTY_STR if @user.nil?
971
+ @user = EMPTY_STR unless user
975
972
  end
976
973
 
977
974
  # Reset dependent values
978
- remove_instance_variable(:@userinfo) if defined?(@userinfo)
979
- remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
980
- remove_instance_variable(:@authority) if defined?(@authority)
981
- remove_instance_variable(:@normalized_user) if defined?(@normalized_user)
975
+ @userinfo = nil
976
+ @normalized_userinfo = NONE
977
+ @authority = nil
978
+ @normalized_user = NONE
982
979
  remove_composite_values
983
980
 
984
981
  # Ensure we haven't created an invalid URI
@@ -989,9 +986,7 @@ module Addressable
989
986
  # The password component for this URI.
990
987
  #
991
988
  # @return [String] The password component.
992
- def password
993
- return defined?(@password) ? @password : nil
994
- end
989
+ attr_reader :password
995
990
 
996
991
  ##
997
992
  # The password component for this URI, normalized.
@@ -999,8 +994,8 @@ module Addressable
999
994
  # @return [String] The password component, normalized.
1000
995
  def normalized_password
1001
996
  return nil unless self.password
1002
- return @normalized_password if defined?(@normalized_password)
1003
- @normalized_password ||= begin
997
+ return @normalized_password unless @normalized_password == NONE
998
+ @normalized_password = begin
1004
999
  if self.normalized_scheme =~ /https?/ && self.password.strip.empty? &&
1005
1000
  (!self.user || self.user.strip.empty?)
1006
1001
  nil
@@ -1012,9 +1007,7 @@ module Addressable
1012
1007
  end
1013
1008
  end
1014
1009
  # All normalized values should be UTF-8
1015
- if @normalized_password
1016
- @normalized_password.force_encoding(Encoding::UTF_8)
1017
- end
1010
+ force_utf8_encoding_if_needed(@normalized_password)
1018
1011
  @normalized_password
1019
1012
  end
1020
1013
 
@@ -1029,17 +1022,15 @@ module Addressable
1029
1022
  @password = new_password ? new_password.to_str : nil
1030
1023
 
1031
1024
  # You can't have a nil user with a non-nil password
1032
- @password ||= nil
1033
- @user ||= nil
1034
1025
  if @password != nil
1035
- @user = EMPTY_STR if @user.nil?
1026
+ self.user = EMPTY_STR if user.nil?
1036
1027
  end
1037
1028
 
1038
1029
  # Reset dependent values
1039
- remove_instance_variable(:@userinfo) if defined?(@userinfo)
1040
- remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
1041
- remove_instance_variable(:@authority) if defined?(@authority)
1042
- remove_instance_variable(:@normalized_password) if defined?(@normalized_password)
1030
+ @userinfo = nil
1031
+ @normalized_userinfo = NONE
1032
+ @authority = nil
1033
+ @normalized_password = NONE
1043
1034
  remove_composite_values
1044
1035
 
1045
1036
  # Ensure we haven't created an invalid URI
@@ -1069,8 +1060,8 @@ module Addressable
1069
1060
  # @return [String] The userinfo component, normalized.
1070
1061
  def normalized_userinfo
1071
1062
  return nil unless self.userinfo
1072
- return @normalized_userinfo if defined?(@normalized_userinfo)
1073
- @normalized_userinfo ||= begin
1063
+ return @normalized_userinfo unless @normalized_userinfo == NONE
1064
+ @normalized_userinfo = begin
1074
1065
  current_user = self.normalized_user
1075
1066
  current_password = self.normalized_password
1076
1067
  if !current_user && !current_password
@@ -1082,9 +1073,7 @@ module Addressable
1082
1073
  end
1083
1074
  end
1084
1075
  # All normalized values should be UTF-8
1085
- if @normalized_userinfo
1086
- @normalized_userinfo.force_encoding(Encoding::UTF_8)
1087
- end
1076
+ force_utf8_encoding_if_needed(@normalized_userinfo)
1088
1077
  @normalized_userinfo
1089
1078
  end
1090
1079
 
@@ -1110,7 +1099,7 @@ module Addressable
1110
1099
  self.user = new_user
1111
1100
 
1112
1101
  # Reset dependent values
1113
- remove_instance_variable(:@authority) if defined?(@authority)
1102
+ @authority = nil
1114
1103
  remove_composite_values
1115
1104
 
1116
1105
  # Ensure we haven't created an invalid URI
@@ -1121,9 +1110,7 @@ module Addressable
1121
1110
  # The host component for this URI.
1122
1111
  #
1123
1112
  # @return [String] The host component.
1124
- def host
1125
- return defined?(@host) ? @host : nil
1126
- end
1113
+ attr_reader :host
1127
1114
 
1128
1115
  ##
1129
1116
  # The host component for this URI, normalized.
@@ -1151,9 +1138,7 @@ module Addressable
1151
1138
  end
1152
1139
  end
1153
1140
  # All normalized values should be UTF-8
1154
- if @normalized_host && !@normalized_host.empty?
1155
- @normalized_host.force_encoding(Encoding::UTF_8)
1156
- end
1141
+ force_utf8_encoding_if_needed(@normalized_host)
1157
1142
  @normalized_host
1158
1143
  end
1159
1144
 
@@ -1168,8 +1153,8 @@ module Addressable
1168
1153
  @host = new_host ? new_host.to_str : nil
1169
1154
 
1170
1155
  # Reset dependent values
1171
- remove_instance_variable(:@authority) if defined?(@authority)
1172
- remove_instance_variable(:@normalized_host) if defined?(@normalized_host)
1156
+ @authority = nil
1157
+ @normalized_host = nil
1173
1158
  remove_composite_values
1174
1159
 
1175
1160
  # Ensure we haven't created an invalid URI
@@ -1271,9 +1256,7 @@ module Addressable
1271
1256
  authority
1272
1257
  end
1273
1258
  # All normalized values should be UTF-8
1274
- if @normalized_authority
1275
- @normalized_authority.force_encoding(Encoding::UTF_8)
1276
- end
1259
+ force_utf8_encoding_if_needed(@normalized_authority)
1277
1260
  @normalized_authority
1278
1261
  end
1279
1262
 
@@ -1302,14 +1285,14 @@ module Addressable
1302
1285
  end
1303
1286
 
1304
1287
  # Password assigned first to ensure validity in case of nil
1305
- self.password = defined?(new_password) ? new_password : nil
1306
- self.user = defined?(new_user) ? new_user : nil
1307
- self.host = defined?(new_host) ? new_host : nil
1308
- self.port = defined?(new_port) ? new_port : nil
1288
+ self.password = new_password
1289
+ self.user = new_user
1290
+ self.host = new_host
1291
+ self.port = new_port
1309
1292
 
1310
1293
  # Reset dependent values
1311
- remove_instance_variable(:@userinfo) if defined?(@userinfo)
1312
- remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
1294
+ @userinfo = nil
1295
+ @normalized_userinfo = NONE
1313
1296
  remove_composite_values
1314
1297
 
1315
1298
  # Ensure we haven't created an invalid URI
@@ -1357,16 +1340,16 @@ module Addressable
1357
1340
  new_port = new_origin[/:([^:@\[\]\/]*?)$/, 1]
1358
1341
  end
1359
1342
 
1360
- self.scheme = defined?(new_scheme) ? new_scheme : nil
1361
- self.host = defined?(new_host) ? new_host : nil
1362
- self.port = defined?(new_port) ? new_port : nil
1343
+ self.scheme = new_scheme
1344
+ self.host = new_host
1345
+ self.port = new_port
1363
1346
  self.userinfo = nil
1364
1347
 
1365
1348
  # Reset dependent values
1366
- remove_instance_variable(:@userinfo) if defined?(@userinfo)
1367
- remove_instance_variable(:@normalized_userinfo) if defined?(@normalized_userinfo)
1368
- remove_instance_variable(:@authority) if defined?(@authority)
1369
- remove_instance_variable(:@normalized_authority) if defined?(@normalized_authority)
1349
+ @userinfo = nil
1350
+ @normalized_userinfo = NONE
1351
+ @authority = nil
1352
+ @normalized_authority = nil
1370
1353
  remove_composite_values
1371
1354
 
1372
1355
  # Ensure we haven't created an invalid URI
@@ -1393,9 +1376,7 @@ module Addressable
1393
1376
  # infer port numbers from default values.
1394
1377
  #
1395
1378
  # @return [Integer] The port component.
1396
- def port
1397
- return defined?(@port) ? @port : nil
1398
- end
1379
+ attr_reader :port
1399
1380
 
1400
1381
  ##
1401
1382
  # The port component for this URI, normalized.
@@ -1403,8 +1384,8 @@ module Addressable
1403
1384
  # @return [Integer] The port component, normalized.
1404
1385
  def normalized_port
1405
1386
  return nil unless self.port
1406
- return @normalized_port if defined?(@normalized_port)
1407
- @normalized_port ||= begin
1387
+ return @normalized_port unless @normalized_port == NONE
1388
+ @normalized_port = begin
1408
1389
  if URI.port_mapping[self.normalized_scheme] == self.port
1409
1390
  nil
1410
1391
  else
@@ -1435,8 +1416,8 @@ module Addressable
1435
1416
  @port = nil if @port == 0
1436
1417
 
1437
1418
  # Reset dependent values
1438
- remove_instance_variable(:@authority) if defined?(@authority)
1439
- remove_instance_variable(:@normalized_port) if defined?(@normalized_port)
1419
+ @authority = nil
1420
+ @normalized_port = NONE
1440
1421
  remove_composite_values
1441
1422
 
1442
1423
  # Ensure we haven't created an invalid URI
@@ -1507,7 +1488,7 @@ module Addressable
1507
1488
  site_string
1508
1489
  end
1509
1490
  # All normalized values should be UTF-8
1510
- @normalized_site.force_encoding(Encoding::UTF_8) if @normalized_site
1491
+ force_utf8_encoding_if_needed(@normalized_site)
1511
1492
  @normalized_site
1512
1493
  end
1513
1494
 
@@ -1537,9 +1518,7 @@ module Addressable
1537
1518
  # The path component for this URI.
1538
1519
  #
1539
1520
  # @return [String] The path component.
1540
- def path
1541
- return defined?(@path) ? @path : EMPTY_STR
1542
- end
1521
+ attr_reader :path
1543
1522
 
1544
1523
  NORMPATH = /^(?!\/)[^\/:]*:.*$/
1545
1524
  ##
@@ -1570,7 +1549,7 @@ module Addressable
1570
1549
  result
1571
1550
  end
1572
1551
  # All normalized values should be UTF-8
1573
- @normalized_path.force_encoding(Encoding::UTF_8) if @normalized_path
1552
+ force_utf8_encoding_if_needed(@normalized_path)
1574
1553
  @normalized_path
1575
1554
  end
1576
1555
 
@@ -1588,7 +1567,7 @@ module Addressable
1588
1567
  end
1589
1568
 
1590
1569
  # Reset dependent values
1591
- remove_instance_variable(:@normalized_path) if defined?(@normalized_path)
1570
+ @normalized_path = nil
1592
1571
  remove_composite_values
1593
1572
 
1594
1573
  # Ensure we haven't created an invalid URI
@@ -1618,9 +1597,7 @@ module Addressable
1618
1597
  # The query component for this URI.
1619
1598
  #
1620
1599
  # @return [String] The query component.
1621
- def query
1622
- return defined?(@query) ? @query : nil
1623
- end
1600
+ attr_reader :query
1624
1601
 
1625
1602
  ##
1626
1603
  # The query component for this URI, normalized.
@@ -1628,8 +1605,8 @@ module Addressable
1628
1605
  # @return [String] The query component, normalized.
1629
1606
  def normalized_query(*flags)
1630
1607
  return nil unless self.query
1631
- return @normalized_query if defined?(@normalized_query)
1632
- @normalized_query ||= begin
1608
+ return @normalized_query unless @normalized_query == NONE
1609
+ @normalized_query = begin
1633
1610
  modified_query_class = Addressable::URI::CharacterClasses::QUERY.dup
1634
1611
  # Make sure possible key-value pair delimiters are escaped.
1635
1612
  modified_query_class.sub!("\\&", "").sub!("\\;", "")
@@ -1646,7 +1623,7 @@ module Addressable
1646
1623
  component == "" ? nil : component
1647
1624
  end
1648
1625
  # All normalized values should be UTF-8
1649
- @normalized_query.force_encoding(Encoding::UTF_8) if @normalized_query
1626
+ force_utf8_encoding_if_needed(@normalized_query)
1650
1627
  @normalized_query
1651
1628
  end
1652
1629
 
@@ -1661,7 +1638,7 @@ module Addressable
1661
1638
  @query = new_query ? new_query.to_str : nil
1662
1639
 
1663
1640
  # Reset dependent values
1664
- remove_instance_variable(:@normalized_query) if defined?(@normalized_query)
1641
+ @normalized_query = NONE
1665
1642
  remove_composite_values
1666
1643
  end
1667
1644
 
@@ -1823,9 +1800,7 @@ module Addressable
1823
1800
  # The fragment component for this URI.
1824
1801
  #
1825
1802
  # @return [String] The fragment component.
1826
- def fragment
1827
- return defined?(@fragment) ? @fragment : nil
1828
- end
1803
+ attr_reader :fragment
1829
1804
 
1830
1805
  ##
1831
1806
  # The fragment component for this URI, normalized.
@@ -1833,8 +1808,8 @@ module Addressable
1833
1808
  # @return [String] The fragment component, normalized.
1834
1809
  def normalized_fragment
1835
1810
  return nil unless self.fragment
1836
- return @normalized_fragment if defined?(@normalized_fragment)
1837
- @normalized_fragment ||= begin
1811
+ return @normalized_fragment unless @normalized_fragment == NONE
1812
+ @normalized_fragment = begin
1838
1813
  component = Addressable::URI.normalize_component(
1839
1814
  self.fragment,
1840
1815
  Addressable::URI::NormalizeCharacterClasses::FRAGMENT
@@ -1842,9 +1817,7 @@ module Addressable
1842
1817
  component == "" ? nil : component
1843
1818
  end
1844
1819
  # All normalized values should be UTF-8
1845
- if @normalized_fragment
1846
- @normalized_fragment.force_encoding(Encoding::UTF_8)
1847
- end
1820
+ force_utf8_encoding_if_needed(@normalized_fragment)
1848
1821
  @normalized_fragment
1849
1822
  end
1850
1823
 
@@ -1859,7 +1832,7 @@ module Addressable
1859
1832
  @fragment = new_fragment ? new_fragment.to_str : nil
1860
1833
 
1861
1834
  # Reset dependent values
1862
- remove_instance_variable(:@normalized_fragment) if defined?(@normalized_fragment)
1835
+ @normalized_fragment = NONE
1863
1836
  remove_composite_values
1864
1837
 
1865
1838
  # Ensure we haven't created an invalid URI
@@ -2025,7 +1998,7 @@ module Addressable
2025
1998
  #
2026
1999
  # @see Hash#merge
2027
2000
  def merge(hash)
2028
- if !hash.respond_to?(:to_hash)
2001
+ unless hash.respond_to?(:to_hash)
2029
2002
  raise TypeError, "Can't convert #{hash.class} into Hash."
2030
2003
  end
2031
2004
  hash = hash.to_hash
@@ -2419,7 +2392,8 @@ module Addressable
2419
2392
  yield
2420
2393
  @validation_deferred = false
2421
2394
  validate
2422
- return nil
2395
+ ensure
2396
+ @validation_deferred = false
2423
2397
  end
2424
2398
 
2425
2399
  protected
@@ -2440,30 +2414,35 @@ module Addressable
2440
2414
  def self.normalize_path(path)
2441
2415
  # Section 5.2.4 of RFC 3986
2442
2416
 
2443
- return nil if path.nil?
2417
+ return if path.nil?
2444
2418
  normalized_path = path.dup
2445
- begin
2446
- mod = nil
2419
+ loop do
2447
2420
  mod ||= normalized_path.gsub!(RULE_2A, SLASH)
2448
2421
 
2449
2422
  pair = normalized_path.match(RULE_2B_2C)
2450
- parent, current = pair[1], pair[2] if pair
2423
+ if pair
2424
+ parent = pair[1]
2425
+ current = pair[2]
2426
+ else
2427
+ parent = nil
2428
+ current = nil
2429
+ end
2430
+
2431
+ regexp = "/#{Regexp.escape(parent.to_s)}/\\.\\./|"
2432
+ regexp += "(/#{Regexp.escape(current.to_s)}/\\.\\.$)"
2433
+
2451
2434
  if pair && ((parent != SELF_REF && parent != PARENT) ||
2452
2435
  (current != SELF_REF && current != PARENT))
2453
- mod ||= normalized_path.gsub!(
2454
- Regexp.new(
2455
- "/#{Regexp.escape(parent.to_s)}/\\.\\./|" +
2456
- "(/#{Regexp.escape(current.to_s)}/\\.\\.$)"
2457
- ), SLASH
2458
- )
2436
+ mod ||= normalized_path.gsub!(Regexp.new(regexp), SLASH)
2459
2437
  end
2460
2438
 
2461
2439
  mod ||= normalized_path.gsub!(RULE_2D, EMPTY_STR)
2462
2440
  # Non-standard, removes prefixed dotted segments from path.
2463
2441
  mod ||= normalized_path.gsub!(RULE_PREFIXED_PARENT, SLASH)
2464
- end until mod.nil?
2442
+ break if mod.nil?
2443
+ end
2465
2444
 
2466
- return normalized_path
2445
+ normalized_path
2467
2446
  end
2468
2447
 
2469
2448
  ##
@@ -2513,11 +2492,7 @@ module Addressable
2513
2492
  # @return [Addressable::URI] <code>self</code>.
2514
2493
  def replace_self(uri)
2515
2494
  # Reset dependent values
2516
- instance_variables.each do |var|
2517
- if instance_variable_defined?(var) && var != :@validation_deferred
2518
- remove_instance_variable(var)
2519
- end
2520
- end
2495
+ reset_ivs
2521
2496
 
2522
2497
  @scheme = uri.scheme
2523
2498
  @user = uri.user
@@ -2549,8 +2524,53 @@ module Addressable
2549
2524
  #
2550
2525
  # @api private
2551
2526
  def remove_composite_values
2552
- remove_instance_variable(:@uri_string) if defined?(@uri_string)
2553
- remove_instance_variable(:@hash) if defined?(@hash)
2527
+ @uri_string = nil
2528
+ @hash = nil
2529
+ end
2530
+
2531
+ ##
2532
+ # Converts the string to be UTF-8 if it is not already UTF-8
2533
+ #
2534
+ # @api private
2535
+ def force_utf8_encoding_if_needed(str)
2536
+ if str && str.encoding != Encoding::UTF_8
2537
+ str.force_encoding(Encoding::UTF_8)
2538
+ end
2554
2539
  end
2540
+
2541
+ private
2542
+
2543
+ ##
2544
+ # Resets instance variables
2545
+ #
2546
+ # @api private
2547
+ def reset_ivs
2548
+ @scheme = nil
2549
+ @user = nil
2550
+ @normalized_scheme = NONE
2551
+ @normalized_user = NONE
2552
+ @uri_string = nil
2553
+ @hash = nil
2554
+ @userinfo = nil
2555
+ @normalized_userinfo = NONE
2556
+ @authority = nil
2557
+ @password = nil
2558
+ @normalized_authority = nil
2559
+ @port = nil
2560
+ @normalized_password = NONE
2561
+ @host = nil
2562
+ @normalized_host = nil
2563
+ @normalized_port = NONE
2564
+ @path = EMPTY_STR
2565
+ @normalized_path = nil
2566
+ @normalized_query = NONE
2567
+ @fragment = nil
2568
+ @normalized_fragment = NONE
2569
+ @query = nil
2570
+ end
2571
+
2572
+ NONE = Object.new.freeze
2573
+
2574
+ private_constant :NONE
2555
2575
  end
2556
2576
  end