activesupport 5.2.5 → 6.0.4.6

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +477 -398
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/backtrace_cleaner.rb +27 -1
  7. data/lib/active_support/cache/file_store.rb +32 -32
  8. data/lib/active_support/cache/mem_cache_store.rb +12 -7
  9. data/lib/active_support/cache/memory_store.rb +15 -9
  10. data/lib/active_support/cache/null_store.rb +8 -3
  11. data/lib/active_support/cache/redis_cache_store.rb +47 -20
  12. data/lib/active_support/cache/strategy/local_cache.rb +22 -22
  13. data/lib/active_support/cache.rb +71 -48
  14. data/lib/active_support/callbacks.rb +16 -8
  15. data/lib/active_support/concern.rb +24 -1
  16. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
  17. data/lib/active_support/concurrency/share_lock.rb +0 -1
  18. data/lib/active_support/configurable.rb +7 -11
  19. data/lib/active_support/core_ext/array/access.rb +18 -6
  20. data/lib/active_support/core_ext/array/conversions.rb +5 -5
  21. data/lib/active_support/core_ext/array/extract.rb +21 -0
  22. data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -6
  23. data/lib/active_support/core_ext/array.rb +1 -1
  24. data/lib/active_support/core_ext/class/attribute.rb +11 -16
  25. data/lib/active_support/core_ext/class/subclasses.rb +1 -1
  26. data/lib/active_support/core_ext/date/calculations.rb +6 -5
  27. data/lib/active_support/core_ext/date_and_time/calculations.rb +24 -47
  28. data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
  29. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  30. data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
  31. data/lib/active_support/core_ext/enumerable.rb +97 -73
  32. data/lib/active_support/core_ext/hash/compact.rb +2 -26
  33. data/lib/active_support/core_ext/hash/conversions.rb +1 -1
  34. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  35. data/lib/active_support/core_ext/hash/except.rb +2 -2
  36. data/lib/active_support/core_ext/hash/keys.rb +0 -29
  37. data/lib/active_support/core_ext/hash/slice.rb +3 -25
  38. data/lib/active_support/core_ext/hash/transform_values.rb +2 -29
  39. data/lib/active_support/core_ext/hash.rb +1 -2
  40. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  41. data/lib/active_support/core_ext/kernel.rb +0 -1
  42. data/lib/active_support/core_ext/load_error.rb +1 -1
  43. data/lib/active_support/core_ext/module/attribute_accessors.rb +7 -10
  44. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +13 -19
  45. data/lib/active_support/core_ext/module/delegation.rb +41 -8
  46. data/lib/active_support/core_ext/module/introspection.rb +38 -13
  47. data/lib/active_support/core_ext/module/reachable.rb +1 -6
  48. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  49. data/lib/active_support/core_ext/module.rb +0 -1
  50. data/lib/active_support/core_ext/numeric/conversions.rb +124 -128
  51. data/lib/active_support/core_ext/numeric/inquiry.rb +2 -25
  52. data/lib/active_support/core_ext/numeric.rb +0 -1
  53. data/lib/active_support/core_ext/object/blank.rb +1 -2
  54. data/lib/active_support/core_ext/object/duplicable.rb +7 -114
  55. data/lib/active_support/core_ext/object/json.rb +2 -1
  56. data/lib/active_support/core_ext/object/try.rb +17 -7
  57. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  58. data/lib/active_support/core_ext/range/compare_range.rb +28 -13
  59. data/lib/active_support/core_ext/range/conversions.rb +31 -29
  60. data/lib/active_support/core_ext/range/each.rb +0 -1
  61. data/lib/active_support/core_ext/range/include_range.rb +6 -0
  62. data/lib/active_support/core_ext/range/include_time_with_zone.rb +2 -2
  63. data/lib/active_support/core_ext/regexp.rb +0 -4
  64. data/lib/active_support/core_ext/securerandom.rb +23 -3
  65. data/lib/active_support/core_ext/string/access.rb +8 -0
  66. data/lib/active_support/core_ext/string/filters.rb +42 -1
  67. data/lib/active_support/core_ext/string/inflections.rb +7 -2
  68. data/lib/active_support/core_ext/string/multibyte.rb +4 -3
  69. data/lib/active_support/core_ext/string/output_safety.rb +68 -10
  70. data/lib/active_support/core_ext/string/strip.rb +3 -1
  71. data/lib/active_support/core_ext/time/calculations.rb +34 -3
  72. data/lib/active_support/core_ext/uri.rb +1 -0
  73. data/lib/active_support/current_attributes.rb +8 -0
  74. data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
  75. data/lib/active_support/dependencies.rb +74 -18
  76. data/lib/active_support/deprecation/behaviors.rb +1 -1
  77. data/lib/active_support/deprecation/method_wrappers.rb +17 -23
  78. data/lib/active_support/deprecation/proxy_wrappers.rb +28 -5
  79. data/lib/active_support/deprecation.rb +1 -1
  80. data/lib/active_support/descendants_tracker.rb +55 -9
  81. data/lib/active_support/duration/iso8601_parser.rb +2 -4
  82. data/lib/active_support/duration/iso8601_serializer.rb +3 -5
  83. data/lib/active_support/duration.rb +7 -8
  84. data/lib/active_support/encrypted_configuration.rb +0 -4
  85. data/lib/active_support/encrypted_file.rb +3 -2
  86. data/lib/active_support/evented_file_update_checker.rb +39 -10
  87. data/lib/active_support/execution_wrapper.rb +17 -13
  88. data/lib/active_support/file_update_checker.rb +0 -1
  89. data/lib/active_support/gem_version.rb +4 -4
  90. data/lib/active_support/hash_with_indifferent_access.rb +22 -18
  91. data/lib/active_support/i18n.rb +1 -0
  92. data/lib/active_support/i18n_railtie.rb +13 -1
  93. data/lib/active_support/inflector/inflections.rb +1 -5
  94. data/lib/active_support/inflector/methods.rb +16 -29
  95. data/lib/active_support/inflector/transliterate.rb +47 -18
  96. data/lib/active_support/json/decoding.rb +23 -24
  97. data/lib/active_support/json/encoding.rb +6 -2
  98. data/lib/active_support/key_generator.rb +0 -32
  99. data/lib/active_support/lazy_load_hooks.rb +5 -2
  100. data/lib/active_support/locale/en.rb +33 -0
  101. data/lib/active_support/log_subscriber.rb +31 -9
  102. data/lib/active_support/logger.rb +1 -16
  103. data/lib/active_support/logger_silence.rb +28 -12
  104. data/lib/active_support/logger_thread_safe_level.rb +26 -4
  105. data/lib/active_support/message_encryptor.rb +4 -6
  106. data/lib/active_support/message_verifier.rb +5 -5
  107. data/lib/active_support/messages/metadata.rb +11 -2
  108. data/lib/active_support/messages/rotator.rb +4 -4
  109. data/lib/active_support/multibyte/chars.rb +29 -49
  110. data/lib/active_support/multibyte/unicode.rb +44 -282
  111. data/lib/active_support/notifications/fanout.rb +98 -13
  112. data/lib/active_support/notifications/instrumenter.rb +80 -9
  113. data/lib/active_support/notifications.rb +41 -4
  114. data/lib/active_support/number_helper/number_converter.rb +4 -5
  115. data/lib/active_support/number_helper/number_to_currency_converter.rb +4 -9
  116. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
  117. data/lib/active_support/number_helper/number_to_human_converter.rb +3 -2
  118. data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -2
  119. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  120. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  121. data/lib/active_support/number_helper/number_to_rounded_converter.rb +5 -4
  122. data/lib/active_support/number_helper/rounding_helper.rb +1 -1
  123. data/lib/active_support/number_helper.rb +11 -0
  124. data/lib/active_support/option_merger.rb +21 -3
  125. data/lib/active_support/ordered_hash.rb +1 -1
  126. data/lib/active_support/ordered_options.rb +5 -1
  127. data/lib/active_support/parameter_filter.rb +128 -0
  128. data/lib/active_support/rails.rb +0 -6
  129. data/lib/active_support/reloader.rb +5 -6
  130. data/lib/active_support/security_utils.rb +1 -1
  131. data/lib/active_support/string_inquirer.rb +0 -1
  132. data/lib/active_support/subscriber.rb +65 -26
  133. data/lib/active_support/tagged_logging.rb +13 -4
  134. data/lib/active_support/test_case.rb +91 -0
  135. data/lib/active_support/testing/assertions.rb +15 -1
  136. data/lib/active_support/testing/deprecation.rb +0 -1
  137. data/lib/active_support/testing/file_fixtures.rb +2 -0
  138. data/lib/active_support/testing/isolation.rb +2 -2
  139. data/lib/active_support/testing/method_call_assertions.rb +28 -1
  140. data/lib/active_support/testing/parallelization.rb +134 -0
  141. data/lib/active_support/testing/stream.rb +1 -2
  142. data/lib/active_support/testing/time_helpers.rb +7 -9
  143. data/lib/active_support/time_with_zone.rb +15 -5
  144. data/lib/active_support/values/time_zone.rb +12 -7
  145. data/lib/active_support/xml_mini/jdom.rb +2 -3
  146. data/lib/active_support/xml_mini/libxml.rb +2 -2
  147. data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
  148. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  149. data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
  150. data/lib/active_support/xml_mini/rexml.rb +2 -2
  151. data/lib/active_support/xml_mini.rb +2 -10
  152. data/lib/active_support.rb +2 -1
  153. metadata +40 -12
  154. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
  155. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -52,16 +52,17 @@ module ActiveSupport
52
52
  @pid = Process.pid
53
53
  @boot_mutex = Mutex.new
54
54
 
55
- if (@dtw = directories_to_watch).any?
55
+ dtw = directories_to_watch
56
+ @dtw, @missing = dtw.partition(&:exist?)
57
+
58
+ if @dtw.any?
56
59
  # Loading listen triggers warnings. These are originated by a legit
57
60
  # usage of attr_* macros for private attributes, but adds a lot of noise
58
61
  # to our test suite. Thus, we lazy load it and disable warnings locally.
59
62
  silence_warnings do
60
- begin
61
- require "listen"
62
- rescue LoadError => e
63
- raise LoadError, "Could not load the 'listen' gem. Add `gem 'listen'` to the development group of your Gemfile", e.backtrace
64
- end
63
+ require "listen"
64
+ rescue LoadError => e
65
+ raise LoadError, "Could not load the 'listen' gem. Add `gem 'listen'` to the development group of your Gemfile", e.backtrace
65
66
  end
66
67
  end
67
68
  boot!
@@ -75,6 +76,19 @@ module ActiveSupport
75
76
  @updated.make_true
76
77
  end
77
78
  end
79
+
80
+ if @missing.any?(&:exist?)
81
+ @boot_mutex.synchronize do
82
+ appeared, @missing = @missing.partition(&:exist?)
83
+ shutdown!
84
+
85
+ @dtw += appeared
86
+ boot!
87
+
88
+ @updated.make_true
89
+ end
90
+ end
91
+
78
92
  @updated.true?
79
93
  end
80
94
 
@@ -93,7 +107,21 @@ module ActiveSupport
93
107
 
94
108
  private
95
109
  def boot!
96
- Listen.to(*@dtw, &method(:changed)).start
110
+ normalize_dirs!
111
+
112
+ unless @dtw.empty?
113
+ Listen.to(*@dtw, &method(:changed)).start
114
+ end
115
+ end
116
+
117
+ def shutdown!
118
+ Listen.stop
119
+ end
120
+
121
+ def normalize_dirs!
122
+ @dirs.transform_keys! do |dir|
123
+ dir.exist? ? dir.realpath : dir
124
+ end
97
125
  end
98
126
 
99
127
  def changed(modified, added, removed)
@@ -113,7 +141,9 @@ module ActiveSupport
113
141
  ext = @ph.normalize_extension(file.extname)
114
142
 
115
143
  file.dirname.ascend do |dir|
116
- if @dirs.fetch(dir, []).include?(ext)
144
+ matching = @dirs[dir]
145
+
146
+ if matching && (matching.empty? || matching.include?(ext))
117
147
  break true
118
148
  elsif dir == @lcsp || dir.root?
119
149
  break false
@@ -123,7 +153,7 @@ module ActiveSupport
123
153
  end
124
154
 
125
155
  def directories_to_watch
126
- dtw = (@files + @dirs.keys).map { |f| @ph.existing_parent(f) }
156
+ dtw = @files.map(&:dirname) + @dirs.keys
127
157
  dtw.compact!
128
158
  dtw.uniq!
129
159
 
@@ -194,7 +224,6 @@ module ActiveSupport
194
224
  end
195
225
 
196
226
  private
197
-
198
227
  def ascendant_of?(base, other)
199
228
  base != other && other.ascend do |ascendant|
200
229
  break true if base == ascendant
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/callbacks"
4
+ require "concurrent/hash"
4
5
 
5
6
  module ActiveSupport
6
7
  class ExecutionWrapper
@@ -62,18 +63,21 @@ module ActiveSupport
62
63
  # after the work has been performed.
63
64
  #
64
65
  # Where possible, prefer +wrap+.
65
- def self.run!
66
- if active?
67
- Null
66
+ def self.run!(reset: false)
67
+ if reset
68
+ lost_instance = active.delete(Thread.current)
69
+ lost_instance&.complete!
68
70
  else
69
- new.tap do |instance|
70
- success = nil
71
- begin
72
- instance.run!
73
- success = true
74
- ensure
75
- instance.complete! unless success
76
- end
71
+ return Null if active?
72
+ end
73
+
74
+ new.tap do |instance|
75
+ success = nil
76
+ begin
77
+ instance.run!
78
+ success = true
79
+ ensure
80
+ instance.complete! unless success
77
81
  end
78
82
  end
79
83
  end
@@ -102,11 +106,11 @@ module ActiveSupport
102
106
  self.active = Concurrent::Hash.new
103
107
 
104
108
  def self.active? # :nodoc:
105
- @active[Thread.current]
109
+ @active.key?(Thread.current)
106
110
  end
107
111
 
108
112
  def run! # :nodoc:
109
- self.class.active[Thread.current] = true
113
+ self.class.active[Thread.current] = self
110
114
  run_callbacks(:run)
111
115
  end
112
116
 
@@ -98,7 +98,6 @@ module ActiveSupport
98
98
  end
99
99
 
100
100
  private
101
-
102
101
  def watched
103
102
  @watched || begin
104
103
  all = @files.select { |f| File.exist?(f) }
@@ -7,10 +7,10 @@ module ActiveSupport
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 5
11
- MINOR = 2
12
- TINY = 5
13
- PRE = nil
10
+ MAJOR = 6
11
+ MINOR = 0
12
+ TINY = 4
13
+ PRE = "6"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "active_support/core_ext/hash/keys"
4
4
  require "active_support/core_ext/hash/reverse_merge"
5
+ require "active_support/core_ext/hash/except"
5
6
 
6
7
  module ActiveSupport
7
8
  # Implements a hash where keys <tt>:foo</tt> and <tt>"foo"</tt> are considered
@@ -190,20 +191,18 @@ module ActiveSupport
190
191
  super(convert_key(key), *extras)
191
192
  end
192
193
 
193
- if Hash.new.respond_to?(:dig)
194
- # Same as <tt>Hash#dig</tt> where the key passed as argument can be
195
- # either a string or a symbol:
196
- #
197
- # counters = ActiveSupport::HashWithIndifferentAccess.new
198
- # counters[:foo] = { bar: 1 }
199
- #
200
- # counters.dig('foo', 'bar') # => 1
201
- # counters.dig(:foo, :bar) # => 1
202
- # counters.dig(:zoo) # => nil
203
- def dig(*args)
204
- args[0] = convert_key(args[0]) if args.size > 0
205
- super(*args)
206
- end
194
+ # Same as <tt>Hash#dig</tt> where the key passed as argument can be
195
+ # either a string or a symbol:
196
+ #
197
+ # counters = ActiveSupport::HashWithIndifferentAccess.new
198
+ # counters[:foo] = { bar: 1 }
199
+ #
200
+ # counters.dig('foo', 'bar') # => 1
201
+ # counters.dig(:foo, :bar) # => 1
202
+ # counters.dig(:zoo) # => nil
203
+ def dig(*args)
204
+ args[0] = convert_key(args[0]) if args.size > 0
205
+ super(*args)
207
206
  end
208
207
 
209
208
  # Same as <tt>Hash#default</tt> where the key passed as argument can be
@@ -226,8 +225,8 @@ module ActiveSupport
226
225
  # hash[:a] = 'x'
227
226
  # hash[:b] = 'y'
228
227
  # hash.values_at('a', 'b') # => ["x", "y"]
229
- def values_at(*indices)
230
- indices.collect { |key| self[convert_key(key)] }
228
+ def values_at(*keys)
229
+ super(*keys.map { |key| convert_key(key) })
231
230
  end
232
231
 
233
232
  # Returns an array of the values at the specified indices, but also
@@ -240,8 +239,8 @@ module ActiveSupport
240
239
  # hash.fetch_values('a', 'c') { |key| 'z' } # => ["x", "z"]
241
240
  # hash.fetch_values('a', 'c') # => KeyError: key not found: "c"
242
241
  def fetch_values(*indices, &block)
243
- indices.collect { |key| fetch(key, &block) }
244
- end if Hash.method_defined?(:fetch_values)
242
+ super(*indices.map { |key| convert_key(key) }, &block)
243
+ end
245
244
 
246
245
  # Returns a shallow copy of the hash.
247
246
  #
@@ -294,6 +293,11 @@ module ActiveSupport
294
293
  super(convert_key(key))
295
294
  end
296
295
 
296
+ def except(*keys)
297
+ slice(*self.keys - keys.map { |key| convert_key(key) })
298
+ end
299
+ alias_method :without, :except
300
+
297
301
  def stringify_keys!; self end
298
302
  def deep_stringify_keys!; self end
299
303
  def stringify_keys; dup end
@@ -13,3 +13,4 @@ require "active_support/lazy_load_hooks"
13
13
 
14
14
  ActiveSupport.run_load_hooks(:i18n)
15
15
  I18n.load_path << File.expand_path("locale/en.yml", __dir__)
16
+ I18n.load_path << File.expand_path("locale/en.rb", __dir__)
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support"
4
- require "active_support/file_update_checker"
5
4
  require "active_support/core_ext/array/wrap"
6
5
 
7
6
  # :enddoc:
@@ -13,6 +12,10 @@ module I18n
13
12
  config.i18n.load_path = []
14
13
  config.i18n.fallbacks = ActiveSupport::OrderedOptions.new
15
14
 
15
+ if I18n.respond_to?(:eager_load!)
16
+ config.eager_load_namespaces << I18n
17
+ end
18
+
16
19
  # Set the i18n configuration after initialization since a lot of
17
20
  # configuration is still usually done in application initializers.
18
21
  config.after_initialize do |app|
@@ -92,6 +95,15 @@ module I18n
92
95
  end
93
96
 
94
97
  if args.empty? || args.first.is_a?(Hash)
98
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
99
+ Using I18n fallbacks with an empty `defaults` sets the defaults to
100
+ include the `default_locale`. This behavior will change in Rails 6.1.
101
+ If you desire the default locale to be included in the defaults, please
102
+ explicitly configure it with `config.i18n.fallbacks.defaults =
103
+ [I18n.default_locale]` or `config.i18n.fallbacks = [I18n.default_locale,
104
+ {...}]`. If you want to opt-in to the new behavior, use
105
+ `config.i18n.fallbacks.defaults = [nil, {...}]`.
106
+ MSG
95
107
  args.unshift I18n.default_locale
96
108
  end
97
109
 
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "concurrent/map"
4
- require "active_support/core_ext/array/prepend_and_append"
5
- require "active_support/core_ext/regexp"
6
4
  require "active_support/i18n"
7
5
  require "active_support/deprecation"
8
6
 
@@ -67,8 +65,7 @@ module ActiveSupport
67
65
  @__instance__[locale] ||= new
68
66
  end
69
67
 
70
- attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex
71
- deprecate :acronym_regex
68
+ attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms
72
69
 
73
70
  attr_reader :acronyms_camelize_regex, :acronyms_underscore_regex # :nodoc:
74
71
 
@@ -233,7 +230,6 @@ module ActiveSupport
233
230
  end
234
231
 
235
232
  private
236
-
237
233
  def define_acronym_regex_patterns
238
234
  @acronym_regex = @acronyms.empty? ? /(?=a)b/ : /#{@acronyms.values.join("|")}/
239
235
  @acronyms_camelize_regex = /^(?:#{@acronym_regex}(?=\b|[A-Z_])|\w)/
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/inflections"
4
- require "active_support/core_ext/regexp"
5
4
 
6
5
  module ActiveSupport
7
6
  # The Inflector transforms words from singular to plural, class names to table
@@ -74,7 +73,7 @@ module ActiveSupport
74
73
  string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase }
75
74
  end
76
75
  string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
77
- string.gsub!("/".freeze, "::".freeze)
76
+ string.gsub!("/", "::")
78
77
  string
79
78
  end
80
79
 
@@ -91,11 +90,11 @@ module ActiveSupport
91
90
  # camelize(underscore('SSLError')) # => "SslError"
92
91
  def underscore(camel_cased_word)
93
92
  return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
94
- word = camel_cased_word.to_s.gsub("::".freeze, "/".freeze)
95
- word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_'.freeze }#{$2.downcase}" }
96
- word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze)
97
- word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'.freeze)
98
- word.tr!("-".freeze, "_".freeze)
93
+ word = camel_cased_word.to_s.gsub("::", "/")
94
+ word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_' }#{$2.downcase}" }
95
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
96
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
97
+ word.tr!("-", "_")
99
98
  word.downcase!
100
99
  word
101
100
  end
@@ -131,11 +130,11 @@ module ActiveSupport
131
130
 
132
131
  inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
133
132
 
134
- result.sub!(/\A_+/, "".freeze)
133
+ result.sub!(/\A_+/, "")
135
134
  unless keep_id_suffix
136
- result.sub!(/_id\z/, "".freeze)
135
+ result.sub!(/_id\z/, "")
137
136
  end
138
- result.tr!("_".freeze, " ".freeze)
137
+ result.tr!("_", " ")
139
138
 
140
139
  result.gsub!(/([a-z\d]*)/i) do |match|
141
140
  "#{inflections.acronyms[match.downcase] || match.downcase}"
@@ -197,17 +196,17 @@ module ActiveSupport
197
196
  #
198
197
  # Singular names are not handled correctly:
199
198
  #
200
- # classify('calculus') # => "Calculus"
199
+ # classify('calculus') # => "Calculu"
201
200
  def classify(table_name)
202
201
  # strip out any leading schema name
203
- camelize(singularize(table_name.to_s.sub(/.*\./, "".freeze)))
202
+ camelize(singularize(table_name.to_s.sub(/.*\./, "")))
204
203
  end
205
204
 
206
205
  # Replaces underscores with dashes in the string.
207
206
  #
208
207
  # dasherize('puni_puni') # => "puni-puni"
209
208
  def dasherize(underscored_word)
210
- underscored_word.tr("_".freeze, "-".freeze)
209
+ underscored_word.tr("_", "-")
211
210
  end
212
211
 
213
212
  # Removes the module part from the expression in the string.
@@ -270,7 +269,7 @@ module ActiveSupport
270
269
  # NameError is raised when the name is not in CamelCase or the constant is
271
270
  # unknown.
272
271
  def constantize(camel_cased_word)
273
- names = camel_cased_word.split("::".freeze)
272
+ names = camel_cased_word.split("::")
274
273
 
275
274
  # Trigger a built-in NameError exception including the ill-formed constant in the message.
276
275
  Object.const_get(camel_cased_word) if names.empty?
@@ -343,18 +342,7 @@ module ActiveSupport
343
342
  # ordinal(-11) # => "th"
344
343
  # ordinal(-1021) # => "st"
345
344
  def ordinal(number)
346
- abs_number = number.to_i.abs
347
-
348
- if (11..13).include?(abs_number % 100)
349
- "th"
350
- else
351
- case abs_number % 10
352
- when 1; "st"
353
- when 2; "nd"
354
- when 3; "rd"
355
- else "th"
356
- end
357
- end
345
+ I18n.translate("number.nth.ordinals", number: number)
358
346
  end
359
347
 
360
348
  # Turns a number into an ordinal string used to denote the position in an
@@ -367,18 +355,17 @@ module ActiveSupport
367
355
  # ordinalize(-11) # => "-11th"
368
356
  # ordinalize(-1021) # => "-1021st"
369
357
  def ordinalize(number)
370
- "#{number}#{ordinal(number)}"
358
+ I18n.translate("number.nth.ordinalized", number: number)
371
359
  end
372
360
 
373
361
  private
374
-
375
362
  # Mounts a regular expression, returned as a string to ease interpolation,
376
363
  # that will match part by part the given constant.
377
364
  #
378
365
  # const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
379
366
  # const_regexp("::") # => "::"
380
367
  def const_regexp(camel_cased_word)
381
- parts = camel_cased_word.split("::".freeze)
368
+ parts = camel_cased_word.split("::")
382
369
 
383
370
  return Regexp.escape(camel_cased_word) if parts.blank?
384
371
 
@@ -51,20 +51,45 @@ module ActiveSupport
51
51
  #
52
52
  # Now you can have different transliterations for each locale:
53
53
  #
54
- # I18n.locale = :en
55
- # transliterate('Jürgen')
54
+ # transliterate('Jürgen', locale: :en)
56
55
  # # => "Jurgen"
57
56
  #
58
- # I18n.locale = :de
59
- # transliterate('Jürgen')
57
+ # transliterate('Jürgen', locale: :de)
60
58
  # # => "Juergen"
61
- def transliterate(string, replacement = "?".freeze)
59
+ #
60
+ # Transliteration is restricted to UTF-8, US-ASCII and GB18030 strings
61
+ # Other encodings will raise an ArgumentError.
62
+ def transliterate(string, replacement = "?", locale: nil)
63
+ string = string.dup if string.frozen?
62
64
  raise ArgumentError, "Can only transliterate strings. Received #{string.class.name}" unless string.is_a?(String)
63
65
 
64
- I18n.transliterate(
65
- ActiveSupport::Multibyte::Unicode.normalize(
66
- ActiveSupport::Multibyte::Unicode.tidy_bytes(string), :c),
67
- replacement: replacement)
66
+ allowed_encodings = [Encoding::UTF_8, Encoding::US_ASCII, Encoding::GB18030]
67
+ raise ArgumentError, "Can not transliterate strings with #{string.encoding} encoding" unless allowed_encodings.include?(string.encoding)
68
+
69
+ input_encoding = string.encoding
70
+
71
+ # US-ASCII is a subset of UTF-8 so we'll force encoding as UTF-8 if
72
+ # US-ASCII is given. This way we can let tidy_bytes handle the string
73
+ # in the same way as we do for UTF-8
74
+ string.force_encoding(Encoding::UTF_8) if string.encoding == Encoding::US_ASCII
75
+
76
+ # GB18030 is Unicode compatible but is not a direct mapping so needs to be
77
+ # transcoded. Using invalid/undef :replace will result in loss of data in
78
+ # the event of invalid characters, but since tidy_bytes will replace
79
+ # invalid/undef with a "?" we're safe to do the same beforehand
80
+ string.encode!(Encoding::UTF_8, invalid: :replace, undef: :replace) if string.encoding == Encoding::GB18030
81
+
82
+ transliterated = I18n.transliterate(
83
+ ActiveSupport::Multibyte::Unicode.tidy_bytes(string).unicode_normalize(:nfc),
84
+ replacement: replacement,
85
+ locale: locale
86
+ )
87
+
88
+ # Restore the string encoding of the input if it was not UTF-8.
89
+ # Apply invalid/undef :replace as tidy_bytes does
90
+ transliterated.encode!(input_encoding, invalid: :replace, undef: :replace) if input_encoding != transliterated.encoding
91
+
92
+ transliterated
68
93
  end
69
94
 
70
95
  # Replaces special characters in a string so that it may be used as part of
@@ -75,8 +100,8 @@ module ActiveSupport
75
100
  #
76
101
  # To use a custom separator, override the +separator+ argument.
77
102
  #
78
- # parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
79
- # parameterize("^très|Jolie__ ", separator: '_') # => "tres_jolie"
103
+ # parameterize("Donald E. Knuth", separator: '_') # => "donald_e_knuth"
104
+ # parameterize("^très|Jolie__ ", separator: '_') # => "tres_jolie"
80
105
  #
81
106
  # To preserve the case of the characters in a string, use the +preserve_case+ argument.
82
107
  #
@@ -85,19 +110,23 @@ module ActiveSupport
85
110
  #
86
111
  # It preserves dashes and underscores unless they are used as separators:
87
112
  #
88
- # parameterize("^très|Jolie__ ") # => "tres-jolie__"
89
- # parameterize("^très|Jolie-- ", separator: "_") # => "tres_jolie--"
90
- # parameterize("^très_Jolie-- ", separator: ".") # => "tres_jolie--"
113
+ # parameterize("^très|Jolie__ ") # => "tres-jolie__"
114
+ # parameterize("^très|Jolie-- ", separator: "_") # => "tres_jolie--"
115
+ # parameterize("^très_Jolie-- ", separator: ".") # => "tres_jolie--"
91
116
  #
92
- def parameterize(string, separator: "-", preserve_case: false)
117
+ # If the optional parameter +locale+ is specified,
118
+ # the word will be parameterized as a word of that language.
119
+ # By default, this parameter is set to <tt>nil</tt> and it will use
120
+ # the configured <tt>I18n.locale<tt>.
121
+ def parameterize(string, separator: "-", preserve_case: false, locale: nil)
93
122
  # Replace accented chars with their ASCII equivalents.
94
- parameterized_string = transliterate(string)
123
+ parameterized_string = transliterate(string, locale: locale)
95
124
 
96
125
  # Turn unwanted chars into the separator.
97
126
  parameterized_string.gsub!(/[^a-z0-9\-_]+/i, separator)
98
127
 
99
128
  unless separator.nil? || separator.empty?
100
- if separator == "-".freeze
129
+ if separator == "-"
101
130
  re_duplicate_separator = /-{2,}/
102
131
  re_leading_trailing_separator = /^-|-$/i
103
132
  else
@@ -108,7 +137,7 @@ module ActiveSupport
108
137
  # No more than one of the separator in a row.
109
138
  parameterized_string.gsub!(re_duplicate_separator, separator)
110
139
  # Remove leading/trailing separator.
111
- parameterized_string.gsub!(re_leading_trailing_separator, "".freeze)
140
+ parameterized_string.gsub!(re_leading_trailing_separator, "")
112
141
  end
113
142
 
114
143
  parameterized_string.downcase! unless preserve_case
@@ -44,33 +44,32 @@ module ActiveSupport
44
44
  end
45
45
 
46
46
  private
47
-
48
- def convert_dates_from(data)
49
- case data
50
- when nil
51
- nil
52
- when DATE_REGEX
53
- begin
54
- Date.parse(data)
55
- rescue ArgumentError
56
- data
57
- end
58
- when DATETIME_REGEX
59
- begin
60
- Time.zone.parse(data)
61
- rescue ArgumentError
47
+ def convert_dates_from(data)
48
+ case data
49
+ when nil
50
+ nil
51
+ when DATE_REGEX
52
+ begin
53
+ Date.parse(data)
54
+ rescue ArgumentError
55
+ data
56
+ end
57
+ when DATETIME_REGEX
58
+ begin
59
+ Time.zone.parse(data)
60
+ rescue ArgumentError
61
+ data
62
+ end
63
+ when Array
64
+ data.map! { |d| convert_dates_from(d) }
65
+ when Hash
66
+ data.each do |key, value|
67
+ data[key] = convert_dates_from(value)
68
+ end
69
+ else
62
70
  data
63
71
  end
64
- when Array
65
- data.map! { |d| convert_dates_from(d) }
66
- when Hash
67
- data.each do |key, value|
68
- data[key] = convert_dates_from(value)
69
- end
70
- else
71
- data
72
72
  end
73
- end
74
73
  end
75
74
  end
76
75
  end
@@ -54,9 +54,13 @@ module ActiveSupport
54
54
  class EscapedString < String #:nodoc:
55
55
  def to_json(*)
56
56
  if Encoding.escape_html_entities_in_json
57
- super.gsub ESCAPE_REGEX_WITH_HTML_ENTITIES, ESCAPED_CHARS
57
+ s = super
58
+ s.gsub! ESCAPE_REGEX_WITH_HTML_ENTITIES, ESCAPED_CHARS
59
+ s
58
60
  else
59
- super.gsub ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS
61
+ s = super
62
+ s.gsub! ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS
63
+ s
60
64
  end
61
65
  end
62
66
 
@@ -38,36 +38,4 @@ module ActiveSupport
38
38
  @cache_keys[args.join] ||= @key_generator.generate_key(*args)
39
39
  end
40
40
  end
41
-
42
- class LegacyKeyGenerator # :nodoc:
43
- SECRET_MIN_LENGTH = 30 # Characters
44
-
45
- def initialize(secret)
46
- ensure_secret_secure(secret)
47
- @secret = secret
48
- end
49
-
50
- def generate_key(salt)
51
- @secret
52
- end
53
-
54
- private
55
-
56
- # To prevent users from using something insecure like "Password" we make sure that the
57
- # secret they've provided is at least 30 characters in length.
58
- def ensure_secret_secure(secret)
59
- if secret.blank?
60
- raise ArgumentError, "A secret is required to generate an integrity hash " \
61
- "for cookie session data. Set a secret_key_base of at least " \
62
- "#{SECRET_MIN_LENGTH} characters in via `bin/rails credentials:edit`."
63
- end
64
-
65
- if secret.length < SECRET_MIN_LENGTH
66
- raise ArgumentError, "Secret should be something secure, " \
67
- "like \"#{SecureRandom.hex(16)}\". The value you " \
68
- "provided, \"#{secret}\", is shorter than the minimum length " \
69
- "of #{SECRET_MIN_LENGTH} characters."
70
- end
71
- end
72
- end
73
41
  end
@@ -54,7 +54,6 @@ module ActiveSupport
54
54
  end
55
55
 
56
56
  private
57
-
58
57
  def with_execution_control(name, block, once)
59
58
  unless @run_once[name].include?(block)
60
59
  @run_once[name] << block if once
@@ -68,7 +67,11 @@ module ActiveSupport
68
67
  if options[:yield]
69
68
  block.call(base)
70
69
  else
71
- base.instance_eval(&block)
70
+ if base.is_a?(Module)
71
+ base.class_eval(&block)
72
+ else
73
+ base.instance_eval(&block)
74
+ end
72
75
  end
73
76
  end
74
77
  end