activesupport 5.2.2.1 → 6.0.2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +416 -365
- data/MIT-LICENSE +1 -1
- data/README.rdoc +3 -2
- data/lib/active_support.rb +2 -1
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/backtrace_cleaner.rb +28 -1
- data/lib/active_support/cache.rb +45 -23
- data/lib/active_support/cache/file_store.rb +22 -22
- data/lib/active_support/cache/mem_cache_store.rb +5 -0
- data/lib/active_support/cache/memory_store.rb +9 -2
- data/lib/active_support/cache/null_store.rb +5 -0
- data/lib/active_support/cache/redis_cache_store.rb +37 -10
- data/lib/active_support/callbacks.rb +16 -5
- data/lib/active_support/concern.rb +31 -4
- data/lib/active_support/configurable.rb +7 -11
- data/lib/active_support/core_ext/array.rb +1 -1
- data/lib/active_support/core_ext/array/access.rb +18 -6
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -6
- data/lib/active_support/core_ext/class/attribute.rb +11 -16
- data/lib/active_support/core_ext/class/subclasses.rb +1 -1
- data/lib/active_support/core_ext/date/calculations.rb +6 -5
- data/lib/active_support/core_ext/date_and_time/calculations.rb +24 -47
- data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
- data/lib/active_support/core_ext/digest.rb +3 -0
- data/lib/active_support/core_ext/enumerable.rb +97 -73
- data/lib/active_support/core_ext/hash.rb +1 -2
- data/lib/active_support/core_ext/hash/compact.rb +2 -26
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +1 -1
- data/lib/active_support/core_ext/hash/keys.rb +0 -29
- data/lib/active_support/core_ext/hash/slice.rb +3 -25
- data/lib/active_support/core_ext/hash/transform_values.rb +2 -29
- data/lib/active_support/core_ext/integer/multiple.rb +1 -1
- data/lib/active_support/core_ext/kernel.rb +0 -1
- data/lib/active_support/core_ext/load_error.rb +1 -1
- data/lib/active_support/core_ext/module.rb +0 -1
- data/lib/active_support/core_ext/module/attribute_accessors.rb +7 -10
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +13 -19
- data/lib/active_support/core_ext/module/delegation.rb +33 -7
- data/lib/active_support/core_ext/module/introspection.rb +37 -13
- data/lib/active_support/core_ext/module/reachable.rb +1 -6
- data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
- data/lib/active_support/core_ext/numeric.rb +0 -1
- data/lib/active_support/core_ext/numeric/conversions.rb +124 -128
- data/lib/active_support/core_ext/numeric/inquiry.rb +2 -25
- data/lib/active_support/core_ext/object/blank.rb +1 -2
- data/lib/active_support/core_ext/object/duplicable.rb +7 -114
- data/lib/active_support/core_ext/object/json.rb +1 -0
- data/lib/active_support/core_ext/object/try.rb +15 -7
- data/lib/active_support/core_ext/object/with_options.rb +1 -1
- data/lib/active_support/core_ext/range.rb +1 -1
- data/lib/active_support/core_ext/range/compare_range.rb +76 -0
- data/lib/active_support/core_ext/range/conversions.rb +31 -29
- data/lib/active_support/core_ext/range/include_range.rb +6 -22
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +2 -2
- data/lib/active_support/core_ext/regexp.rb +0 -4
- data/lib/active_support/core_ext/securerandom.rb +23 -3
- data/lib/active_support/core_ext/string/access.rb +8 -0
- data/lib/active_support/core_ext/string/filters.rb +42 -1
- data/lib/active_support/core_ext/string/inflections.rb +7 -2
- data/lib/active_support/core_ext/string/multibyte.rb +4 -3
- data/lib/active_support/core_ext/string/output_safety.rb +63 -5
- data/lib/active_support/core_ext/string/strip.rb +3 -1
- data/lib/active_support/core_ext/time/calculations.rb +31 -2
- data/lib/active_support/core_ext/uri.rb +1 -0
- data/lib/active_support/current_attributes.rb +8 -0
- data/lib/active_support/dependencies.rb +69 -16
- data/lib/active_support/dependencies/zeitwerk_integration.rb +118 -0
- data/lib/active_support/deprecation.rb +1 -1
- data/lib/active_support/deprecation/behaviors.rb +1 -1
- data/lib/active_support/deprecation/method_wrappers.rb +16 -18
- data/lib/active_support/deprecation/proxy_wrappers.rb +24 -5
- data/lib/active_support/descendants_tracker.rb +56 -9
- data/lib/active_support/duration.rb +6 -5
- data/lib/active_support/duration/iso8601_parser.rb +2 -3
- data/lib/active_support/duration/iso8601_serializer.rb +3 -4
- data/lib/active_support/encrypted_configuration.rb +0 -4
- data/lib/active_support/encrypted_file.rb +2 -1
- data/lib/active_support/evented_file_update_checker.rb +39 -9
- data/lib/active_support/execution_wrapper.rb +1 -0
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/hash_with_indifferent_access.rb +35 -18
- data/lib/active_support/i18n.rb +1 -0
- data/lib/active_support/i18n_railtie.rb +13 -1
- data/lib/active_support/inflector/inflections.rb +1 -4
- data/lib/active_support/inflector/methods.rb +17 -27
- data/lib/active_support/inflector/transliterate.rb +47 -18
- data/lib/active_support/json/decoding.rb +23 -23
- data/lib/active_support/json/encoding.rb +6 -2
- data/lib/active_support/key_generator.rb +0 -32
- data/lib/active_support/lazy_load_hooks.rb +5 -1
- data/lib/active_support/locale/en.rb +31 -0
- data/lib/active_support/log_subscriber.rb +31 -8
- data/lib/active_support/logger.rb +0 -15
- data/lib/active_support/logger_silence.rb +28 -12
- data/lib/active_support/logger_thread_safe_level.rb +28 -5
- data/lib/active_support/message_encryptor.rb +3 -5
- data/lib/active_support/message_verifier.rb +3 -3
- data/lib/active_support/multibyte/chars.rb +29 -48
- data/lib/active_support/multibyte/unicode.rb +44 -281
- data/lib/active_support/notifications.rb +41 -4
- data/lib/active_support/notifications/fanout.rb +100 -15
- data/lib/active_support/notifications/instrumenter.rb +80 -8
- data/lib/active_support/number_helper.rb +7 -0
- data/lib/active_support/number_helper/number_to_currency_converter.rb +2 -2
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_human_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -0
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +5 -3
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/ordered_options.rb +1 -1
- data/lib/active_support/parameter_filter.rb +129 -0
- data/lib/active_support/rails.rb +0 -6
- data/lib/active_support/reloader.rb +4 -5
- data/lib/active_support/security_utils.rb +1 -1
- data/lib/active_support/subscriber.rb +65 -26
- data/lib/active_support/tagged_logging.rb +13 -4
- data/lib/active_support/test_case.rb +91 -0
- data/lib/active_support/testing/assertions.rb +15 -1
- data/lib/active_support/testing/deprecation.rb +0 -1
- data/lib/active_support/testing/file_fixtures.rb +2 -0
- data/lib/active_support/testing/isolation.rb +2 -2
- data/lib/active_support/testing/method_call_assertions.rb +28 -1
- data/lib/active_support/testing/parallelization.rb +128 -0
- data/lib/active_support/testing/stream.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +7 -7
- data/lib/active_support/time_with_zone.rb +15 -5
- data/lib/active_support/values/time_zone.rb +12 -7
- data/lib/active_support/xml_mini.rb +2 -9
- data/lib/active_support/xml_mini/jdom.rb +2 -2
- data/lib/active_support/xml_mini/libxml.rb +2 -2
- data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
- data/lib/active_support/xml_mini/nokogiri.rb +2 -2
- data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
- data/lib/active_support/xml_mini/rexml.rb +2 -2
- metadata +33 -9
- data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
- 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
|
-
|
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
|
-
|
61
|
-
|
62
|
-
|
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
|
-
|
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
|
-
|
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 =
|
156
|
+
dtw = @files.map(&:dirname) + @dirs.keys
|
127
157
|
dtw.compact!
|
128
158
|
dtw.uniq!
|
129
159
|
|
@@ -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
|
@@ -163,6 +164,19 @@ module ActiveSupport
|
|
163
164
|
super(convert_key(key))
|
164
165
|
end
|
165
166
|
|
167
|
+
# Same as <tt>Hash#assoc</tt> where the key passed as argument can be
|
168
|
+
# either a string or a symbol:
|
169
|
+
#
|
170
|
+
# counters = ActiveSupport::HashWithIndifferentAccess.new
|
171
|
+
# counters[:foo] = 1
|
172
|
+
#
|
173
|
+
# counters.assoc('foo') # => ["foo", 1]
|
174
|
+
# counters.assoc(:foo) # => ["foo", 1]
|
175
|
+
# counters.assoc(:zoo) # => nil
|
176
|
+
def assoc(key)
|
177
|
+
super(convert_key(key))
|
178
|
+
end
|
179
|
+
|
166
180
|
# Same as <tt>Hash#fetch</tt> where the key passed as argument can be
|
167
181
|
# either a string or a symbol:
|
168
182
|
#
|
@@ -177,20 +191,18 @@ module ActiveSupport
|
|
177
191
|
super(convert_key(key), *extras)
|
178
192
|
end
|
179
193
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
super(*args)
|
193
|
-
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)
|
194
206
|
end
|
195
207
|
|
196
208
|
# Same as <tt>Hash#default</tt> where the key passed as argument can be
|
@@ -213,8 +225,8 @@ module ActiveSupport
|
|
213
225
|
# hash[:a] = 'x'
|
214
226
|
# hash[:b] = 'y'
|
215
227
|
# hash.values_at('a', 'b') # => ["x", "y"]
|
216
|
-
def values_at(*
|
217
|
-
|
228
|
+
def values_at(*keys)
|
229
|
+
super(*keys.map { |key| convert_key(key) })
|
218
230
|
end
|
219
231
|
|
220
232
|
# Returns an array of the values at the specified indices, but also
|
@@ -227,8 +239,8 @@ module ActiveSupport
|
|
227
239
|
# hash.fetch_values('a', 'c') { |key| 'z' } # => ["x", "z"]
|
228
240
|
# hash.fetch_values('a', 'c') # => KeyError: key not found: "c"
|
229
241
|
def fetch_values(*indices, &block)
|
230
|
-
indices.
|
231
|
-
end
|
242
|
+
super(*indices.map { |key| convert_key(key) }, &block)
|
243
|
+
end
|
232
244
|
|
233
245
|
# Returns a shallow copy of the hash.
|
234
246
|
#
|
@@ -281,6 +293,11 @@ module ActiveSupport
|
|
281
293
|
super(convert_key(key))
|
282
294
|
end
|
283
295
|
|
296
|
+
def except(*keys)
|
297
|
+
slice(*self.keys - keys.map { |key| convert_key(key) })
|
298
|
+
end
|
299
|
+
alias_method :without, :except
|
300
|
+
|
284
301
|
def stringify_keys!; self end
|
285
302
|
def deep_stringify_keys!; self end
|
286
303
|
def stringify_keys; dup end
|
data/lib/active_support/i18n.rb
CHANGED
@@ -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
|
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
|
|
@@ -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!("/"
|
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("::"
|
95
|
-
word.gsub!(inflections.acronyms_underscore_regex) { "#{$1 && '_'
|
96
|
-
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'
|
97
|
-
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'
|
98
|
-
word.tr!("-"
|
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_+/, ""
|
133
|
+
result.sub!(/\A_+/, "")
|
135
134
|
unless keep_id_suffix
|
136
|
-
result.sub!(/_id\z/, ""
|
135
|
+
result.sub!(/_id\z/, "")
|
137
136
|
end
|
138
|
-
result.tr!("_"
|
137
|
+
result.tr!("_", " ")
|
139
138
|
|
140
139
|
result.gsub!(/([a-z\d]*)/i) do |match|
|
141
140
|
"#{inflections.acronyms[match.downcase] || match.downcase}"
|
@@ -200,14 +199,14 @@ module ActiveSupport
|
|
200
199
|
# classify('calculus') # => "Calculus"
|
201
200
|
def classify(table_name)
|
202
201
|
# strip out any leading schema name
|
203
|
-
camelize(singularize(table_name.to_s.sub(/.*\./, ""
|
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("_"
|
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("::"
|
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?
|
@@ -329,6 +328,8 @@ module ActiveSupport
|
|
329
328
|
e.name.to_s == camel_cased_word.to_s)
|
330
329
|
rescue ArgumentError => e
|
331
330
|
raise unless /not missing constant #{const_regexp(camel_cased_word)}!$/.match?(e.message)
|
331
|
+
rescue LoadError => e
|
332
|
+
raise unless /Unable to autoload constant #{const_regexp(camel_cased_word)}/.match?(e.message)
|
332
333
|
end
|
333
334
|
|
334
335
|
# Returns the suffix that should be added to a number to denote the position
|
@@ -341,18 +342,7 @@ module ActiveSupport
|
|
341
342
|
# ordinal(-11) # => "th"
|
342
343
|
# ordinal(-1021) # => "st"
|
343
344
|
def ordinal(number)
|
344
|
-
|
345
|
-
|
346
|
-
if (11..13).include?(abs_number % 100)
|
347
|
-
"th"
|
348
|
-
else
|
349
|
-
case abs_number % 10
|
350
|
-
when 1; "st"
|
351
|
-
when 2; "nd"
|
352
|
-
when 3; "rd"
|
353
|
-
else "th"
|
354
|
-
end
|
355
|
-
end
|
345
|
+
I18n.translate("number.nth.ordinals", number: number)
|
356
346
|
end
|
357
347
|
|
358
348
|
# Turns a number into an ordinal string used to denote the position in an
|
@@ -365,7 +355,7 @@ module ActiveSupport
|
|
365
355
|
# ordinalize(-11) # => "-11th"
|
366
356
|
# ordinalize(-1021) # => "-1021st"
|
367
357
|
def ordinalize(number)
|
368
|
-
"
|
358
|
+
I18n.translate("number.nth.ordinalized", number: number)
|
369
359
|
end
|
370
360
|
|
371
361
|
private
|
@@ -376,7 +366,7 @@ module ActiveSupport
|
|
376
366
|
# const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
|
377
367
|
# const_regexp("::") # => "::"
|
378
368
|
def const_regexp(camel_cased_word)
|
379
|
-
parts = camel_cased_word.split("::"
|
369
|
+
parts = camel_cased_word.split("::")
|
380
370
|
|
381
371
|
return Regexp.escape(camel_cased_word) if parts.blank?
|
382
372
|
|
@@ -51,20 +51,45 @@ module ActiveSupport
|
|
51
51
|
#
|
52
52
|
# Now you can have different transliterations for each locale:
|
53
53
|
#
|
54
|
-
#
|
55
|
-
# transliterate('Jürgen')
|
54
|
+
# transliterate('Jürgen', locale: :en)
|
56
55
|
# # => "Jurgen"
|
57
56
|
#
|
58
|
-
#
|
59
|
-
# transliterate('Jürgen')
|
57
|
+
# transliterate('Jürgen', locale: :de)
|
60
58
|
# # => "Juergen"
|
61
|
-
|
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
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
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
|
-
#
|
79
|
-
#
|
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
|
-
#
|
89
|
-
#
|
90
|
-
#
|
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
|
-
|
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 == "-"
|
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, ""
|
140
|
+
parameterized_string.gsub!(re_leading_trailing_separator, "")
|
112
141
|
end
|
113
142
|
|
114
143
|
parameterized_string.downcase! unless preserve_case
|