activesupport 4.2.0 → 5.2.0
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 +5 -5
- data/CHANGELOG.md +366 -232
- data/MIT-LICENSE +2 -2
- data/README.rdoc +4 -5
- data/lib/active_support.rb +17 -7
- data/lib/active_support/all.rb +5 -3
- data/lib/active_support/array_inquirer.rb +48 -0
- data/lib/active_support/backtrace_cleaner.rb +7 -5
- data/lib/active_support/benchmarkable.rb +6 -4
- data/lib/active_support/builder.rb +3 -1
- data/lib/active_support/cache.rb +271 -177
- data/lib/active_support/cache/file_store.rb +41 -35
- data/lib/active_support/cache/mem_cache_store.rb +97 -88
- data/lib/active_support/cache/memory_store.rb +27 -30
- data/lib/active_support/cache/null_store.rb +7 -8
- data/lib/active_support/cache/redis_cache_store.rb +454 -0
- data/lib/active_support/cache/strategy/local_cache.rb +67 -34
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
- data/lib/active_support/callbacks.rb +654 -560
- data/lib/active_support/concern.rb +5 -3
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +17 -0
- data/lib/active_support/concurrency/share_lock.rb +227 -0
- data/lib/active_support/configurable.rb +8 -5
- data/lib/active_support/core_ext.rb +3 -1
- data/lib/active_support/core_ext/array.rb +9 -6
- data/lib/active_support/core_ext/array/access.rb +29 -1
- data/lib/active_support/core_ext/array/conversions.rb +22 -18
- data/lib/active_support/core_ext/array/extract_options.rb +2 -0
- data/lib/active_support/core_ext/array/grouping.rb +11 -18
- data/lib/active_support/core_ext/array/inquiry.rb +19 -0
- data/lib/active_support/core_ext/array/prepend_and_append.rb +5 -3
- data/lib/active_support/core_ext/array/wrap.rb +7 -4
- data/lib/active_support/core_ext/benchmark.rb +3 -1
- data/lib/active_support/core_ext/big_decimal.rb +3 -1
- data/lib/active_support/core_ext/big_decimal/conversions.rb +10 -12
- data/lib/active_support/core_ext/class.rb +4 -3
- data/lib/active_support/core_ext/class/attribute.rb +41 -22
- data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
- data/lib/active_support/core_ext/class/subclasses.rb +20 -8
- data/lib/active_support/core_ext/date.rb +6 -4
- data/lib/active_support/core_ext/date/acts_like.rb +3 -1
- data/lib/active_support/core_ext/date/blank.rb +14 -0
- data/lib/active_support/core_ext/date/calculations.rb +11 -9
- data/lib/active_support/core_ext/date/conversions.rb +31 -23
- data/lib/active_support/core_ext/date/zones.rb +4 -2
- data/lib/active_support/core_ext/date_and_time/calculations.rb +179 -56
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +12 -12
- data/lib/active_support/core_ext/date_time.rb +7 -4
- data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
- data/lib/active_support/core_ext/date_time/blank.rb +14 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +58 -20
- data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +16 -12
- data/lib/active_support/core_ext/digest/uuid.rb +7 -5
- data/lib/active_support/core_ext/enumerable.rb +107 -28
- data/lib/active_support/core_ext/file.rb +3 -1
- data/lib/active_support/core_ext/file/atomic.rb +38 -31
- data/lib/active_support/core_ext/hash.rb +11 -9
- data/lib/active_support/core_ext/hash/compact.rb +24 -15
- data/lib/active_support/core_ext/hash/conversions.rb +63 -43
- data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
- data/lib/active_support/core_ext/hash/except.rb +11 -8
- data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -3
- data/lib/active_support/core_ext/hash/keys.rb +33 -27
- data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
- data/lib/active_support/core_ext/hash/slice.rb +8 -8
- data/lib/active_support/core_ext/hash/transform_values.rb +16 -7
- data/lib/active_support/core_ext/integer.rb +5 -3
- data/lib/active_support/core_ext/integer/inflections.rb +3 -1
- data/lib/active_support/core_ext/integer/multiple.rb +2 -0
- data/lib/active_support/core_ext/integer/time.rb +11 -33
- data/lib/active_support/core_ext/kernel.rb +6 -5
- data/lib/active_support/core_ext/kernel/agnostics.rb +2 -0
- data/lib/active_support/core_ext/kernel/concern.rb +5 -1
- data/lib/active_support/core_ext/kernel/reporting.rb +4 -83
- data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
- data/lib/active_support/core_ext/load_error.rb +3 -22
- data/lib/active_support/core_ext/marshal.rb +13 -10
- data/lib/active_support/core_ext/module.rb +14 -11
- data/lib/active_support/core_ext/module/aliasing.rb +6 -44
- data/lib/active_support/core_ext/module/anonymous.rb +12 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +8 -9
- data/lib/active_support/core_ext/module/attribute_accessors.rb +43 -40
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +150 -0
- data/lib/active_support/core_ext/module/concerning.rb +11 -12
- data/lib/active_support/core_ext/module/delegation.rb +121 -39
- data/lib/active_support/core_ext/module/deprecation.rb +4 -2
- data/lib/active_support/core_ext/module/introspection.rb +9 -9
- data/lib/active_support/core_ext/module/reachable.rb +5 -2
- data/lib/active_support/core_ext/module/redefine_method.rb +49 -0
- data/lib/active_support/core_ext/module/remove_method.rb +8 -3
- data/lib/active_support/core_ext/name_error.rb +22 -2
- data/lib/active_support/core_ext/numeric.rb +6 -3
- data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +79 -74
- data/lib/active_support/core_ext/numeric/inquiry.rb +28 -0
- data/lib/active_support/core_ext/numeric/time.rb +35 -38
- data/lib/active_support/core_ext/object.rb +14 -13
- data/lib/active_support/core_ext/object/acts_like.rb +12 -1
- data/lib/active_support/core_ext/object/blank.rb +29 -4
- data/lib/active_support/core_ext/object/conversions.rb +6 -4
- data/lib/active_support/core_ext/object/deep_dup.rb +13 -4
- data/lib/active_support/core_ext/object/duplicable.rb +98 -45
- data/lib/active_support/core_ext/object/inclusion.rb +5 -3
- data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
- data/lib/active_support/core_ext/object/json.rb +49 -19
- data/lib/active_support/core_ext/object/to_param.rb +3 -1
- data/lib/active_support/core_ext/object/to_query.rb +6 -4
- data/lib/active_support/core_ext/object/try.rb +70 -22
- data/lib/active_support/core_ext/object/with_options.rb +16 -3
- data/lib/active_support/core_ext/range.rb +7 -4
- data/lib/active_support/core_ext/range/conversions.rb +27 -7
- data/lib/active_support/core_ext/range/each.rb +19 -17
- data/lib/active_support/core_ext/range/include_range.rb +21 -19
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
- data/lib/active_support/core_ext/range/overlaps.rb +2 -0
- data/lib/active_support/core_ext/regexp.rb +6 -0
- data/lib/active_support/core_ext/securerandom.rb +25 -0
- data/lib/active_support/core_ext/string.rb +15 -13
- data/lib/active_support/core_ext/string/access.rb +9 -7
- data/lib/active_support/core_ext/string/behavior.rb +3 -1
- data/lib/active_support/core_ext/string/conversions.rb +8 -5
- data/lib/active_support/core_ext/string/exclude.rb +2 -0
- data/lib/active_support/core_ext/string/filters.rb +10 -8
- data/lib/active_support/core_ext/string/indent.rb +6 -4
- data/lib/active_support/core_ext/string/inflections.rb +61 -24
- data/lib/active_support/core_ext/string/inquiry.rb +3 -1
- data/lib/active_support/core_ext/string/multibyte.rb +15 -7
- data/lib/active_support/core_ext/string/output_safety.rb +35 -35
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -0
- data/lib/active_support/core_ext/string/strip.rb +4 -5
- data/lib/active_support/core_ext/string/zones.rb +4 -2
- data/lib/active_support/core_ext/time.rb +7 -5
- data/lib/active_support/core_ext/time/acts_like.rb +3 -1
- data/lib/active_support/core_ext/time/calculations.rb +101 -51
- data/lib/active_support/core_ext/time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/time/conversions.rb +20 -13
- data/lib/active_support/core_ext/time/zones.rb +41 -7
- data/lib/active_support/core_ext/uri.rb +5 -4
- data/lib/active_support/current_attributes.rb +195 -0
- data/lib/active_support/dependencies.rb +143 -160
- data/lib/active_support/dependencies/autoload.rb +2 -0
- data/lib/active_support/dependencies/interlock.rb +57 -0
- data/lib/active_support/deprecation.rb +12 -9
- data/lib/active_support/deprecation/behaviors.rb +41 -12
- data/lib/active_support/deprecation/constant_accessor.rb +52 -0
- data/lib/active_support/deprecation/instance_delegator.rb +17 -2
- data/lib/active_support/deprecation/method_wrappers.rb +54 -21
- data/lib/active_support/deprecation/proxy_wrappers.rb +56 -28
- data/lib/active_support/deprecation/reporting.rb +32 -12
- data/lib/active_support/descendants_tracker.rb +2 -0
- data/lib/active_support/digest.rb +20 -0
- data/lib/active_support/duration.rb +326 -30
- data/lib/active_support/duration/iso8601_parser.rb +125 -0
- data/lib/active_support/duration/iso8601_serializer.rb +55 -0
- data/lib/active_support/encrypted_configuration.rb +49 -0
- data/lib/active_support/encrypted_file.rb +99 -0
- data/lib/active_support/evented_file_update_checker.rb +205 -0
- data/lib/active_support/execution_wrapper.rb +128 -0
- data/lib/active_support/executor.rb +8 -0
- data/lib/active_support/file_update_checker.rb +63 -37
- data/lib/active_support/gem_version.rb +4 -2
- data/lib/active_support/gzip.rb +7 -5
- data/lib/active_support/hash_with_indifferent_access.rb +130 -30
- data/lib/active_support/i18n.rb +8 -6
- data/lib/active_support/i18n_railtie.rb +34 -14
- data/lib/active_support/inflections.rb +13 -11
- data/lib/active_support/inflector.rb +7 -5
- data/lib/active_support/inflector/inflections.rb +61 -12
- data/lib/active_support/inflector/methods.rb +161 -136
- data/lib/active_support/inflector/transliterate.rb +48 -27
- data/lib/active_support/json.rb +4 -2
- data/lib/active_support/json/decoding.rb +16 -13
- data/lib/active_support/json/encoding.rb +15 -57
- data/lib/active_support/key_generator.rb +25 -25
- data/lib/active_support/lazy_load_hooks.rb +50 -20
- data/lib/active_support/locale/en.yml +2 -0
- data/lib/active_support/log_subscriber.rb +13 -10
- data/lib/active_support/log_subscriber/test_helper.rb +14 -12
- data/lib/active_support/logger.rb +54 -3
- data/lib/active_support/logger_silence.rb +12 -7
- data/lib/active_support/logger_thread_safe_level.rb +33 -0
- data/lib/active_support/message_encryptor.rb +173 -51
- data/lib/active_support/message_verifier.rb +150 -17
- data/lib/active_support/messages/metadata.rb +71 -0
- data/lib/active_support/messages/rotation_configuration.rb +22 -0
- data/lib/active_support/messages/rotator.rb +56 -0
- data/lib/active_support/multibyte.rb +4 -2
- data/lib/active_support/multibyte/chars.rb +37 -24
- data/lib/active_support/multibyte/unicode.rb +100 -96
- data/lib/active_support/notifications.rb +11 -7
- data/lib/active_support/notifications/fanout.rb +10 -8
- data/lib/active_support/notifications/instrumenter.rb +27 -7
- data/lib/active_support/number_helper.rb +94 -68
- data/lib/active_support/number_helper/number_converter.rb +13 -11
- data/lib/active_support/number_helper/number_to_currency_converter.rb +9 -9
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +9 -3
- data/lib/active_support/number_helper/number_to_human_converter.rb +11 -9
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +9 -8
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +13 -4
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +23 -56
- data/lib/active_support/number_helper/rounding_helper.rb +66 -0
- data/lib/active_support/option_merger.rb +3 -1
- data/lib/active_support/ordered_hash.rb +6 -4
- data/lib/active_support/ordered_options.rb +22 -4
- data/lib/active_support/per_thread_registry.rb +13 -6
- data/lib/active_support/proxy_object.rb +2 -0
- data/lib/active_support/rails.rb +16 -8
- data/lib/active_support/railtie.rb +43 -9
- data/lib/active_support/reloader.rb +131 -0
- data/lib/active_support/rescuable.rb +108 -53
- data/lib/active_support/security_utils.rb +17 -6
- data/lib/active_support/string_inquirer.rb +11 -3
- data/lib/active_support/subscriber.rb +15 -14
- data/lib/active_support/tagged_logging.rb +14 -11
- data/lib/active_support/test_case.rb +18 -46
- data/lib/active_support/testing/assertions.rb +137 -20
- data/lib/active_support/testing/autorun.rb +4 -2
- data/lib/active_support/testing/constant_lookup.rb +2 -1
- data/lib/active_support/testing/declarative.rb +3 -1
- data/lib/active_support/testing/deprecation.rb +14 -10
- data/lib/active_support/testing/file_fixtures.rb +36 -0
- data/lib/active_support/testing/isolation.rb +34 -25
- data/lib/active_support/testing/method_call_assertions.rb +43 -0
- data/lib/active_support/testing/setup_and_teardown.rb +12 -3
- data/lib/active_support/testing/stream.rb +44 -0
- data/lib/active_support/testing/tagged_logging.rb +3 -1
- data/lib/active_support/testing/time_helpers.rb +96 -27
- data/lib/active_support/time.rb +14 -12
- data/lib/active_support/time_with_zone.rb +195 -53
- data/lib/active_support/values/time_zone.rb +200 -61
- data/lib/active_support/values/unicode_tables.dat +0 -0
- data/lib/active_support/version.rb +3 -1
- data/lib/active_support/xml_mini.rb +69 -51
- data/lib/active_support/xml_mini/jdom.rb +116 -113
- data/lib/active_support/xml_mini/libxml.rb +17 -16
- data/lib/active_support/xml_mini/libxmlsax.rb +16 -18
- data/lib/active_support/xml_mini/nokogiri.rb +15 -15
- data/lib/active_support/xml_mini/nokogirisax.rb +15 -16
- data/lib/active_support/xml_mini/rexml.rb +17 -16
- metadata +55 -43
- data/lib/active_support/concurrency/latch.rb +0 -27
- data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -14
- data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
- data/lib/active_support/core_ext/date_time/zones.rb +0 -6
- data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
- data/lib/active_support/core_ext/module/method_transplanting.rb +0 -11
- data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
- data/lib/active_support/core_ext/object/itself.rb +0 -15
- data/lib/active_support/core_ext/struct.rb +0 -6
- data/lib/active_support/core_ext/thread.rb +0 -86
- data/lib/active_support/core_ext/time/marshal.rb +0 -30
data/lib/active_support/i18n.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/hash/deep_merge"
|
4
|
+
require "active_support/core_ext/hash/except"
|
5
|
+
require "active_support/core_ext/hash/slice"
|
4
6
|
begin
|
5
|
-
require
|
7
|
+
require "i18n"
|
6
8
|
rescue LoadError => e
|
7
9
|
$stderr.puts "The i18n gem is not available. Please add it to your Gemfile and run bundle install"
|
8
10
|
raise e
|
9
11
|
end
|
10
|
-
require
|
12
|
+
require "active_support/lazy_load_hooks"
|
11
13
|
|
12
14
|
ActiveSupport.run_load_hooks(:i18n)
|
13
|
-
I18n.load_path <<
|
15
|
+
I18n.load_path << File.expand_path("locale/en.yml", __dir__)
|
@@ -1,7 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support"
|
2
4
|
require "active_support/file_update_checker"
|
3
5
|
require "active_support/core_ext/array/wrap"
|
4
6
|
|
7
|
+
# :enddoc:
|
8
|
+
|
5
9
|
module I18n
|
6
10
|
class Railtie < Rails::Railtie
|
7
11
|
config.i18n = ActiveSupport::OrderedOptions.new
|
@@ -21,8 +25,6 @@ module I18n
|
|
21
25
|
I18n::Railtie.initialize_i18n(app)
|
22
26
|
end
|
23
27
|
|
24
|
-
protected
|
25
|
-
|
26
28
|
@i18n_inited = false
|
27
29
|
|
28
30
|
# Setup i18n configuration.
|
@@ -37,10 +39,12 @@ module I18n
|
|
37
39
|
enforce_available_locales = I18n.enforce_available_locales if enforce_available_locales.nil?
|
38
40
|
I18n.enforce_available_locales = false
|
39
41
|
|
42
|
+
reloadable_paths = []
|
40
43
|
app.config.i18n.each do |setting, value|
|
41
44
|
case setting
|
42
45
|
when :railties_load_path
|
43
|
-
|
46
|
+
reloadable_paths = value
|
47
|
+
app.config.i18n.load_path.unshift(*value.flat_map(&:existent))
|
44
48
|
when :load_path
|
45
49
|
I18n.load_path += value
|
46
50
|
else
|
@@ -53,29 +57,39 @@ module I18n
|
|
53
57
|
# Restore available locales check so it will take place from now on.
|
54
58
|
I18n.enforce_available_locales = enforce_available_locales
|
55
59
|
|
56
|
-
|
60
|
+
directories = watched_dirs_with_extensions(reloadable_paths)
|
61
|
+
reloader = app.config.file_watcher.new(I18n.load_path.dup, directories) do
|
62
|
+
I18n.load_path.keep_if { |p| File.exist?(p) }
|
63
|
+
I18n.load_path |= reloadable_paths.flat_map(&:existent)
|
64
|
+
|
65
|
+
I18n.reload!
|
66
|
+
end
|
67
|
+
|
57
68
|
app.reloaders << reloader
|
58
|
-
|
69
|
+
app.reloader.to_run do
|
70
|
+
reloader.execute_if_updated { require_unload_lock! }
|
71
|
+
end
|
59
72
|
reloader.execute
|
60
73
|
|
61
74
|
@i18n_inited = true
|
62
75
|
end
|
63
76
|
|
64
77
|
def self.include_fallbacks_module
|
65
|
-
I18n.backend.class.
|
78
|
+
I18n.backend.class.include(I18n::Backend::Fallbacks)
|
66
79
|
end
|
67
80
|
|
68
81
|
def self.init_fallbacks(fallbacks)
|
69
82
|
include_fallbacks_module
|
70
83
|
|
71
|
-
args =
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
Array
|
76
|
-
|
77
|
-
|
78
|
-
|
84
|
+
args = \
|
85
|
+
case fallbacks
|
86
|
+
when ActiveSupport::OrderedOptions
|
87
|
+
[*(fallbacks[:defaults] || []) << fallbacks[:map]].compact
|
88
|
+
when Hash, Array
|
89
|
+
Array.wrap(fallbacks)
|
90
|
+
else # TrueClass
|
91
|
+
[]
|
92
|
+
end
|
79
93
|
|
80
94
|
I18n.fallbacks = I18n::Locale::Fallbacks.new(*args)
|
81
95
|
end
|
@@ -90,5 +104,11 @@ module I18n
|
|
90
104
|
raise "Unexpected fallback type #{fallbacks.inspect}"
|
91
105
|
end
|
92
106
|
end
|
107
|
+
|
108
|
+
def self.watched_dirs_with_extensions(paths)
|
109
|
+
paths.each_with_object({}) do |path, result|
|
110
|
+
result[path.absolute_current] = path.extensions
|
111
|
+
end
|
112
|
+
end
|
93
113
|
end
|
94
114
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/inflector/inflections"
|
2
4
|
|
3
5
|
#--
|
4
6
|
# Defines the standard inflection rules. These are the starting point for
|
@@ -8,8 +10,8 @@ require 'active_support/inflector/inflections'
|
|
8
10
|
#++
|
9
11
|
module ActiveSupport
|
10
12
|
Inflector.inflections(:en) do |inflect|
|
11
|
-
inflect.plural(/$/,
|
12
|
-
inflect.plural(/s$/i,
|
13
|
+
inflect.plural(/$/, "s")
|
14
|
+
inflect.plural(/s$/i, "s")
|
13
15
|
inflect.plural(/^(ax|test)is$/i, '\1es')
|
14
16
|
inflect.plural(/(octop|vir)us$/i, '\1i')
|
15
17
|
inflect.plural(/(octop|vir)i$/i, '\1i')
|
@@ -18,7 +20,7 @@ module ActiveSupport
|
|
18
20
|
inflect.plural(/(buffal|tomat)o$/i, '\1oes')
|
19
21
|
inflect.plural(/([ti])um$/i, '\1a')
|
20
22
|
inflect.plural(/([ti])a$/i, '\1a')
|
21
|
-
inflect.plural(/sis$/i,
|
23
|
+
inflect.plural(/sis$/i, "ses")
|
22
24
|
inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
|
23
25
|
inflect.plural(/(hive)$/i, '\1s')
|
24
26
|
inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
|
@@ -30,7 +32,7 @@ module ActiveSupport
|
|
30
32
|
inflect.plural(/^(oxen)$/i, '\1')
|
31
33
|
inflect.plural(/(quiz)$/i, '\1zes')
|
32
34
|
|
33
|
-
inflect.singular(/s$/i,
|
35
|
+
inflect.singular(/s$/i, "")
|
34
36
|
inflect.singular(/(ss)$/i, '\1')
|
35
37
|
inflect.singular(/(n)ews$/i, '\1ews')
|
36
38
|
inflect.singular(/([ti])a$/i, '\1um')
|
@@ -58,12 +60,12 @@ module ActiveSupport
|
|
58
60
|
inflect.singular(/(quiz)zes$/i, '\1')
|
59
61
|
inflect.singular(/(database)s$/i, '\1')
|
60
62
|
|
61
|
-
inflect.irregular(
|
62
|
-
inflect.irregular(
|
63
|
-
inflect.irregular(
|
64
|
-
inflect.irregular(
|
65
|
-
inflect.irregular(
|
66
|
-
inflect.irregular(
|
63
|
+
inflect.irregular("person", "people")
|
64
|
+
inflect.irregular("man", "men")
|
65
|
+
inflect.irregular("child", "children")
|
66
|
+
inflect.irregular("sex", "sexes")
|
67
|
+
inflect.irregular("move", "moves")
|
68
|
+
inflect.irregular("zombie", "zombies")
|
67
69
|
|
68
70
|
inflect.uncountable(%w(equipment information rice money species series fish sheep jeans police))
|
69
71
|
end
|
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# in case active_support/inflector is required without the rest of active_support
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
4
|
+
require "active_support/inflector/inflections"
|
5
|
+
require "active_support/inflector/transliterate"
|
6
|
+
require "active_support/inflector/methods"
|
5
7
|
|
6
|
-
require
|
7
|
-
require
|
8
|
+
require "active_support/inflections"
|
9
|
+
require "active_support/core_ext/string/inflections"
|
@@ -1,6 +1,10 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "concurrent/map"
|
4
|
+
require "active_support/core_ext/array/prepend_and_append"
|
5
|
+
require "active_support/core_ext/regexp"
|
6
|
+
require "active_support/i18n"
|
7
|
+
require "active_support/deprecation"
|
4
8
|
|
5
9
|
module ActiveSupport
|
6
10
|
module Inflector
|
@@ -25,23 +29,60 @@ module ActiveSupport
|
|
25
29
|
# singularization rules that is runs. This guarantees that your rules run
|
26
30
|
# before any of the rules that may already have been loaded.
|
27
31
|
class Inflections
|
28
|
-
@__instance__ =
|
32
|
+
@__instance__ = Concurrent::Map.new
|
33
|
+
|
34
|
+
class Uncountables < Array
|
35
|
+
def initialize
|
36
|
+
@regex_array = []
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
def delete(entry)
|
41
|
+
super entry
|
42
|
+
@regex_array.delete(to_regex(entry))
|
43
|
+
end
|
44
|
+
|
45
|
+
def <<(*word)
|
46
|
+
add(word)
|
47
|
+
end
|
48
|
+
|
49
|
+
def add(words)
|
50
|
+
words = words.flatten.map(&:downcase)
|
51
|
+
concat(words)
|
52
|
+
@regex_array += words.map { |word| to_regex(word) }
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
def uncountable?(str)
|
57
|
+
@regex_array.any? { |regex| regex.match? str }
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
def to_regex(string)
|
62
|
+
/\b#{::Regexp.escape(string)}\Z/i
|
63
|
+
end
|
64
|
+
end
|
29
65
|
|
30
66
|
def self.instance(locale = :en)
|
31
67
|
@__instance__[locale] ||= new
|
32
68
|
end
|
33
69
|
|
34
70
|
attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex
|
71
|
+
deprecate :acronym_regex
|
72
|
+
|
73
|
+
attr_reader :acronyms_camelize_regex, :acronyms_underscore_regex # :nodoc:
|
35
74
|
|
36
75
|
def initialize
|
37
|
-
@plurals, @singulars, @uncountables, @humans, @acronyms
|
76
|
+
@plurals, @singulars, @uncountables, @humans, @acronyms = [], [], Uncountables.new, [], {}
|
77
|
+
define_acronym_regex_patterns
|
38
78
|
end
|
39
79
|
|
40
80
|
# Private, for the test suite.
|
41
81
|
def initialize_dup(orig) # :nodoc:
|
42
|
-
%w(plurals singulars uncountables humans acronyms
|
82
|
+
%w(plurals singulars uncountables humans acronyms).each do |scope|
|
43
83
|
instance_variable_set("@#{scope}", orig.send(scope).dup)
|
44
84
|
end
|
85
|
+
define_acronym_regex_patterns
|
45
86
|
end
|
46
87
|
|
47
88
|
# Specifies a new acronym. An acronym must be specified as it will appear
|
@@ -95,7 +136,7 @@ module ActiveSupport
|
|
95
136
|
# camelize 'mcdonald' # => 'McDonald'
|
96
137
|
def acronym(word)
|
97
138
|
@acronyms[word.downcase] = word
|
98
|
-
|
139
|
+
define_acronym_regex_patterns
|
99
140
|
end
|
100
141
|
|
101
142
|
# Specifies a new pluralization rule and its replacement. The rule can
|
@@ -160,7 +201,7 @@ module ActiveSupport
|
|
160
201
|
# uncountable 'money', 'information'
|
161
202
|
# uncountable %w( money information rice )
|
162
203
|
def uncountable(*words)
|
163
|
-
@uncountables
|
204
|
+
@uncountables.add(words)
|
164
205
|
end
|
165
206
|
|
166
207
|
# Specifies a humanized form of a string by a regular expression rule or
|
@@ -184,12 +225,20 @@ module ActiveSupport
|
|
184
225
|
# clear :plurals
|
185
226
|
def clear(scope = :all)
|
186
227
|
case scope
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
228
|
+
when :all
|
229
|
+
@plurals, @singulars, @uncountables, @humans = [], [], Uncountables.new, []
|
230
|
+
else
|
231
|
+
instance_variable_set "@#{scope}", []
|
191
232
|
end
|
192
233
|
end
|
234
|
+
|
235
|
+
private
|
236
|
+
|
237
|
+
def define_acronym_regex_patterns
|
238
|
+
@acronym_regex = @acronyms.empty? ? /(?=a)b/ : /#{@acronyms.values.join("|")}/
|
239
|
+
@acronyms_camelize_regex = /^(?:#{@acronym_regex}(?=\b|[A-Z_])|\w)/
|
240
|
+
@acronyms_underscore_regex = /(?:(?<=([A-Za-z\d]))|\b)(#{@acronym_regex})(?=\b|[^a-z])/
|
241
|
+
end
|
193
242
|
end
|
194
243
|
|
195
244
|
# Yields a singleton instance of Inflector::Inflections so you can specify
|
@@ -1,6 +1,7 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "active_support/inflections"
|
4
|
+
require "active_support/core_ext/regexp"
|
4
5
|
|
5
6
|
module ActiveSupport
|
6
7
|
# The Inflector transforms words from singular to plural, class names to table
|
@@ -22,58 +23,58 @@ module ActiveSupport
|
|
22
23
|
# pluralized using rules defined for that language. By default,
|
23
24
|
# this parameter is set to <tt>:en</tt>.
|
24
25
|
#
|
25
|
-
# 'post'
|
26
|
-
# 'octopus'
|
27
|
-
# 'sheep'
|
28
|
-
# 'words'
|
29
|
-
# 'CamelOctopus'
|
30
|
-
# 'ley'
|
26
|
+
# pluralize('post') # => "posts"
|
27
|
+
# pluralize('octopus') # => "octopi"
|
28
|
+
# pluralize('sheep') # => "sheep"
|
29
|
+
# pluralize('words') # => "words"
|
30
|
+
# pluralize('CamelOctopus') # => "CamelOctopi"
|
31
|
+
# pluralize('ley', :es) # => "leyes"
|
31
32
|
def pluralize(word, locale = :en)
|
32
|
-
apply_inflections(word, inflections(locale).plurals)
|
33
|
+
apply_inflections(word, inflections(locale).plurals, locale)
|
33
34
|
end
|
34
35
|
|
35
|
-
# The reverse of
|
36
|
+
# The reverse of #pluralize, returns the singular form of a word in a
|
36
37
|
# string.
|
37
38
|
#
|
38
39
|
# If passed an optional +locale+ parameter, the word will be
|
39
40
|
# singularized using rules defined for that language. By default,
|
40
41
|
# this parameter is set to <tt>:en</tt>.
|
41
42
|
#
|
42
|
-
# 'posts'
|
43
|
-
# 'octopi'
|
44
|
-
# 'sheep'
|
45
|
-
# 'word'
|
46
|
-
# 'CamelOctopi'
|
47
|
-
# 'leyes'
|
43
|
+
# singularize('posts') # => "post"
|
44
|
+
# singularize('octopi') # => "octopus"
|
45
|
+
# singularize('sheep') # => "sheep"
|
46
|
+
# singularize('word') # => "word"
|
47
|
+
# singularize('CamelOctopi') # => "CamelOctopus"
|
48
|
+
# singularize('leyes', :es) # => "ley"
|
48
49
|
def singularize(word, locale = :en)
|
49
|
-
apply_inflections(word, inflections(locale).singulars)
|
50
|
+
apply_inflections(word, inflections(locale).singulars, locale)
|
50
51
|
end
|
51
52
|
|
52
|
-
#
|
53
|
-
#
|
53
|
+
# Converts strings to UpperCamelCase.
|
54
|
+
# If the +uppercase_first_letter+ parameter is set to false, then produces
|
54
55
|
# lowerCamelCase.
|
55
56
|
#
|
56
|
-
#
|
57
|
+
# Also converts '/' to '::' which is useful for converting
|
57
58
|
# paths to namespaces.
|
58
59
|
#
|
59
|
-
# 'active_model'
|
60
|
-
# 'active_model'
|
61
|
-
# 'active_model/errors'
|
62
|
-
# 'active_model/errors'
|
60
|
+
# camelize('active_model') # => "ActiveModel"
|
61
|
+
# camelize('active_model', false) # => "activeModel"
|
62
|
+
# camelize('active_model/errors') # => "ActiveModel::Errors"
|
63
|
+
# camelize('active_model/errors', false) # => "activeModel::Errors"
|
63
64
|
#
|
64
65
|
# As a rule of thumb you can think of +camelize+ as the inverse of
|
65
|
-
#
|
66
|
+
# #underscore, though there are cases where that does not hold:
|
66
67
|
#
|
67
|
-
# 'SSLError'
|
68
|
+
# camelize(underscore('SSLError')) # => "SslError"
|
68
69
|
def camelize(term, uppercase_first_letter = true)
|
69
70
|
string = term.to_s
|
70
71
|
if uppercase_first_letter
|
71
|
-
string = string.sub(/^[a-z\d]*/) { inflections.acronyms[
|
72
|
+
string = string.sub(/^[a-z\d]*/) { |match| inflections.acronyms[match] || match.capitalize }
|
72
73
|
else
|
73
|
-
string = string.sub(
|
74
|
+
string = string.sub(inflections.acronyms_camelize_regex) { |match| match.downcase }
|
74
75
|
end
|
75
76
|
string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
|
76
|
-
string.gsub!(
|
77
|
+
string.gsub!("/".freeze, "::".freeze)
|
77
78
|
string
|
78
79
|
end
|
79
80
|
|
@@ -81,125 +82,146 @@ module ActiveSupport
|
|
81
82
|
#
|
82
83
|
# Changes '::' to '/' to convert namespaces to paths.
|
83
84
|
#
|
84
|
-
# 'ActiveModel'
|
85
|
-
# 'ActiveModel::Errors'
|
85
|
+
# underscore('ActiveModel') # => "active_model"
|
86
|
+
# underscore('ActiveModel::Errors') # => "active_model/errors"
|
86
87
|
#
|
87
88
|
# As a rule of thumb you can think of +underscore+ as the inverse of
|
88
|
-
#
|
89
|
+
# #camelize, though there are cases where that does not hold:
|
89
90
|
#
|
90
|
-
# 'SSLError'
|
91
|
+
# camelize(underscore('SSLError')) # => "SslError"
|
91
92
|
def underscore(camel_cased_word)
|
92
|
-
return camel_cased_word unless
|
93
|
-
word = camel_cased_word.to_s.gsub(
|
94
|
-
word.gsub!(
|
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!("-", "_")
|
93
|
+
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)
|
98
99
|
word.downcase!
|
99
100
|
word
|
100
101
|
end
|
101
102
|
|
102
103
|
# Tweaks an attribute name for display to end users.
|
103
104
|
#
|
104
|
-
# Specifically,
|
105
|
-
#
|
106
|
-
# * Applies human inflection rules to the argument.
|
107
|
-
# * Deletes leading underscores, if any.
|
108
|
-
# * Removes a "_id" suffix if present.
|
109
|
-
# * Replaces underscores with spaces, if any.
|
110
|
-
# * Downcases all words except acronyms.
|
111
|
-
# * Capitalizes the first word.
|
105
|
+
# Specifically, performs these transformations:
|
112
106
|
#
|
107
|
+
# * Applies human inflection rules to the argument.
|
108
|
+
# * Deletes leading underscores, if any.
|
109
|
+
# * Removes a "_id" suffix if present.
|
110
|
+
# * Replaces underscores with spaces, if any.
|
111
|
+
# * Downcases all words except acronyms.
|
112
|
+
# * Capitalizes the first word.
|
113
113
|
# The capitalization of the first word can be turned off by setting the
|
114
114
|
# +:capitalize+ option to false (default is true).
|
115
115
|
#
|
116
|
-
#
|
117
|
-
#
|
118
|
-
#
|
119
|
-
# humanize('
|
116
|
+
# The trailing '_id' can be kept and capitalized by setting the
|
117
|
+
# optional parameter +keep_id_suffix+ to true (default is false).
|
118
|
+
#
|
119
|
+
# humanize('employee_salary') # => "Employee salary"
|
120
|
+
# humanize('author_id') # => "Author"
|
121
|
+
# humanize('author_id', capitalize: false) # => "author"
|
122
|
+
# humanize('_id') # => "Id"
|
123
|
+
# humanize('author_id', keep_id_suffix: true) # => "Author Id"
|
120
124
|
#
|
121
125
|
# If "SSL" was defined to be an acronym:
|
122
126
|
#
|
123
127
|
# humanize('ssl_error') # => "SSL error"
|
124
128
|
#
|
125
|
-
def humanize(lower_case_and_underscored_word,
|
129
|
+
def humanize(lower_case_and_underscored_word, capitalize: true, keep_id_suffix: false)
|
126
130
|
result = lower_case_and_underscored_word.to_s.dup
|
127
131
|
|
128
132
|
inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
|
129
133
|
|
130
|
-
result.sub!(/\A_+/,
|
131
|
-
|
132
|
-
|
134
|
+
result.sub!(/\A_+/, "".freeze)
|
135
|
+
unless keep_id_suffix
|
136
|
+
result.sub!(/_id\z/, "".freeze)
|
137
|
+
end
|
138
|
+
result.tr!("_".freeze, " ".freeze)
|
133
139
|
|
134
140
|
result.gsub!(/([a-z\d]*)/i) do |match|
|
135
|
-
"#{inflections.acronyms[match] || match.downcase}"
|
141
|
+
"#{inflections.acronyms[match.downcase] || match.downcase}"
|
136
142
|
end
|
137
143
|
|
138
|
-
if
|
144
|
+
if capitalize
|
139
145
|
result.sub!(/\A\w/) { |match| match.upcase }
|
140
146
|
end
|
141
147
|
|
142
148
|
result
|
143
149
|
end
|
144
150
|
|
151
|
+
# Converts just the first character to uppercase.
|
152
|
+
#
|
153
|
+
# upcase_first('what a Lovely Day') # => "What a Lovely Day"
|
154
|
+
# upcase_first('w') # => "W"
|
155
|
+
# upcase_first('') # => ""
|
156
|
+
def upcase_first(string)
|
157
|
+
string.length > 0 ? string[0].upcase.concat(string[1..-1]) : ""
|
158
|
+
end
|
159
|
+
|
145
160
|
# Capitalizes all the words and replaces some characters in the string to
|
146
161
|
# create a nicer looking title. +titleize+ is meant for creating pretty
|
147
162
|
# output. It is not used in the Rails internals.
|
148
163
|
#
|
164
|
+
# The trailing '_id','Id'.. can be kept and capitalized by setting the
|
165
|
+
# optional parameter +keep_id_suffix+ to true.
|
166
|
+
# By default, this parameter is false.
|
167
|
+
#
|
149
168
|
# +titleize+ is also aliased as +titlecase+.
|
150
169
|
#
|
151
|
-
# 'man from the boondocks'
|
152
|
-
# 'x-men: the last stand'
|
153
|
-
# 'TheManWithoutAPast'
|
154
|
-
# 'raiders_of_the_lost_ark'
|
155
|
-
|
156
|
-
|
170
|
+
# titleize('man from the boondocks') # => "Man From The Boondocks"
|
171
|
+
# titleize('x-men: the last stand') # => "X Men: The Last Stand"
|
172
|
+
# titleize('TheManWithoutAPast') # => "The Man Without A Past"
|
173
|
+
# titleize('raiders_of_the_lost_ark') # => "Raiders Of The Lost Ark"
|
174
|
+
# titleize('string_ending_with_id', keep_id_suffix: true) # => "String Ending With Id"
|
175
|
+
def titleize(word, keep_id_suffix: false)
|
176
|
+
humanize(underscore(word), keep_id_suffix: keep_id_suffix).gsub(/\b(?<!\w['’`])[a-z]/) do |match|
|
177
|
+
match.capitalize
|
178
|
+
end
|
157
179
|
end
|
158
180
|
|
159
|
-
#
|
160
|
-
# method uses the
|
181
|
+
# Creates the name of a table like Rails does for models to table names.
|
182
|
+
# This method uses the #pluralize method on the last word in the string.
|
161
183
|
#
|
162
|
-
# 'RawScaledScorer'
|
163
|
-
# '
|
164
|
-
# 'fancyCategory'
|
184
|
+
# tableize('RawScaledScorer') # => "raw_scaled_scorers"
|
185
|
+
# tableize('ham_and_egg') # => "ham_and_eggs"
|
186
|
+
# tableize('fancyCategory') # => "fancy_categories"
|
165
187
|
def tableize(class_name)
|
166
188
|
pluralize(underscore(class_name))
|
167
189
|
end
|
168
190
|
|
169
|
-
#
|
191
|
+
# Creates a class name from a plural table name like Rails does for table
|
170
192
|
# names to models. Note that this returns a string and not a Class (To
|
171
|
-
# convert to an actual class follow +classify+ with
|
193
|
+
# convert to an actual class follow +classify+ with #constantize).
|
172
194
|
#
|
173
|
-
# '
|
174
|
-
# 'posts'
|
195
|
+
# classify('ham_and_eggs') # => "HamAndEgg"
|
196
|
+
# classify('posts') # => "Post"
|
175
197
|
#
|
176
198
|
# Singular names are not handled correctly:
|
177
199
|
#
|
178
|
-
# 'calculus'
|
200
|
+
# classify('calculus') # => "Calculus"
|
179
201
|
def classify(table_name)
|
180
202
|
# strip out any leading schema name
|
181
|
-
camelize(singularize(table_name.to_s.sub(/.*\./,
|
203
|
+
camelize(singularize(table_name.to_s.sub(/.*\./, "".freeze)))
|
182
204
|
end
|
183
205
|
|
184
206
|
# Replaces underscores with dashes in the string.
|
185
207
|
#
|
186
|
-
# 'puni_puni'
|
208
|
+
# dasherize('puni_puni') # => "puni-puni"
|
187
209
|
def dasherize(underscored_word)
|
188
|
-
underscored_word.tr(
|
210
|
+
underscored_word.tr("_".freeze, "-".freeze)
|
189
211
|
end
|
190
212
|
|
191
213
|
# Removes the module part from the expression in the string.
|
192
214
|
#
|
193
|
-
# '
|
194
|
-
# 'Inflections'
|
195
|
-
# '::Inflections'
|
196
|
-
# ''
|
215
|
+
# demodulize('ActiveSupport::Inflector::Inflections') # => "Inflections"
|
216
|
+
# demodulize('Inflections') # => "Inflections"
|
217
|
+
# demodulize('::Inflections') # => "Inflections"
|
218
|
+
# demodulize('') # => ""
|
197
219
|
#
|
198
|
-
# See also
|
220
|
+
# See also #deconstantize.
|
199
221
|
def demodulize(path)
|
200
222
|
path = path.to_s
|
201
|
-
if i = path.rindex(
|
202
|
-
path[(i+2)..-1]
|
223
|
+
if i = path.rindex("::")
|
224
|
+
path[(i + 2)..-1]
|
203
225
|
else
|
204
226
|
path
|
205
227
|
end
|
@@ -207,32 +229,32 @@ module ActiveSupport
|
|
207
229
|
|
208
230
|
# Removes the rightmost segment from the constant expression in the string.
|
209
231
|
#
|
210
|
-
# 'Net::HTTP'
|
211
|
-
# '::Net::HTTP'
|
212
|
-
# 'String'
|
213
|
-
# '::String'
|
214
|
-
# ''
|
232
|
+
# deconstantize('Net::HTTP') # => "Net"
|
233
|
+
# deconstantize('::Net::HTTP') # => "::Net"
|
234
|
+
# deconstantize('String') # => ""
|
235
|
+
# deconstantize('::String') # => ""
|
236
|
+
# deconstantize('') # => ""
|
215
237
|
#
|
216
|
-
# See also
|
238
|
+
# See also #demodulize.
|
217
239
|
def deconstantize(path)
|
218
|
-
path.to_s[0, path.rindex(
|
240
|
+
path.to_s[0, path.rindex("::") || 0] # implementation based on the one in facets' Module#spacename
|
219
241
|
end
|
220
242
|
|
221
243
|
# Creates a foreign key name from a class name.
|
222
244
|
# +separate_class_name_and_id_with_underscore+ sets whether
|
223
245
|
# the method should put '_' between the name and 'id'.
|
224
246
|
#
|
225
|
-
# 'Message'
|
226
|
-
# 'Message'
|
227
|
-
# 'Admin::Post'
|
247
|
+
# foreign_key('Message') # => "message_id"
|
248
|
+
# foreign_key('Message', false) # => "messageid"
|
249
|
+
# foreign_key('Admin::Post') # => "post_id"
|
228
250
|
def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
|
229
251
|
underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
|
230
252
|
end
|
231
253
|
|
232
254
|
# Tries to find a constant with the name specified in the argument string.
|
233
255
|
#
|
234
|
-
# 'Module'
|
235
|
-
# '
|
256
|
+
# constantize('Module') # => Module
|
257
|
+
# constantize('Foo::Bar') # => Foo::Bar
|
236
258
|
#
|
237
259
|
# The name is assumed to be the one of a top-level constant, no matter
|
238
260
|
# whether it starts with "::" or not. No lexical context is taken into
|
@@ -241,14 +263,14 @@ module ActiveSupport
|
|
241
263
|
# C = 'outside'
|
242
264
|
# module M
|
243
265
|
# C = 'inside'
|
244
|
-
# C
|
245
|
-
# 'C'
|
266
|
+
# C # => 'inside'
|
267
|
+
# constantize('C') # => 'outside', same as ::C
|
246
268
|
# end
|
247
269
|
#
|
248
270
|
# NameError is raised when the name is not in CamelCase or the constant is
|
249
271
|
# unknown.
|
250
272
|
def constantize(camel_cased_word)
|
251
|
-
names = camel_cased_word.split(
|
273
|
+
names = camel_cased_word.split("::".freeze)
|
252
274
|
|
253
275
|
# Trigger a built-in NameError exception including the ill-formed constant in the message.
|
254
276
|
Object.const_get(camel_cased_word) if names.empty?
|
@@ -266,7 +288,7 @@ module ActiveSupport
|
|
266
288
|
|
267
289
|
# Go down the ancestors to check if it is owned directly. The check
|
268
290
|
# stops when we reach Object or the end of ancestors tree.
|
269
|
-
constant = constant.ancestors.inject do |const, ancestor|
|
291
|
+
constant = constant.ancestors.inject(constant) do |const, ancestor|
|
270
292
|
break const if ancestor == Object
|
271
293
|
break ancestor if ancestor.const_defined?(name, false)
|
272
294
|
const
|
@@ -280,8 +302,8 @@ module ActiveSupport
|
|
280
302
|
|
281
303
|
# Tries to find a constant with the name specified in the argument string.
|
282
304
|
#
|
283
|
-
# 'Module'
|
284
|
-
# '
|
305
|
+
# safe_constantize('Module') # => Module
|
306
|
+
# safe_constantize('Foo::Bar') # => Foo::Bar
|
285
307
|
#
|
286
308
|
# The name is assumed to be the one of a top-level constant, no matter
|
287
309
|
# whether it starts with "::" or not. No lexical context is taken into
|
@@ -290,23 +312,23 @@ module ActiveSupport
|
|
290
312
|
# C = 'outside'
|
291
313
|
# module M
|
292
314
|
# C = 'inside'
|
293
|
-
# C
|
294
|
-
# 'C'
|
315
|
+
# C # => 'inside'
|
316
|
+
# safe_constantize('C') # => 'outside', same as ::C
|
295
317
|
# end
|
296
318
|
#
|
297
319
|
# +nil+ is returned when the name is not in CamelCase or the constant (or
|
298
320
|
# part of it) is unknown.
|
299
321
|
#
|
300
|
-
# 'blargle'
|
301
|
-
# 'UnknownModule'
|
302
|
-
# 'UnknownModule::Foo::Bar'
|
322
|
+
# safe_constantize('blargle') # => nil
|
323
|
+
# safe_constantize('UnknownModule') # => nil
|
324
|
+
# safe_constantize('UnknownModule::Foo::Bar') # => nil
|
303
325
|
def safe_constantize(camel_cased_word)
|
304
326
|
constantize(camel_cased_word)
|
305
327
|
rescue NameError => e
|
306
328
|
raise if e.name && !(camel_cased_word.to_s.split("::").include?(e.name.to_s) ||
|
307
329
|
e.name.to_s == camel_cased_word.to_s)
|
308
330
|
rescue ArgumentError => e
|
309
|
-
raise unless
|
331
|
+
raise unless /not missing constant #{const_regexp(camel_cased_word)}!$/.match?(e.message)
|
310
332
|
end
|
311
333
|
|
312
334
|
# Returns the suffix that should be added to a number to denote the position
|
@@ -325,10 +347,10 @@ module ActiveSupport
|
|
325
347
|
"th"
|
326
348
|
else
|
327
349
|
case abs_number % 10
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
350
|
+
when 1; "st"
|
351
|
+
when 2; "nd"
|
352
|
+
when 3; "rd"
|
353
|
+
else "th"
|
332
354
|
end
|
333
355
|
end
|
334
356
|
end
|
@@ -348,36 +370,39 @@ module ActiveSupport
|
|
348
370
|
|
349
371
|
private
|
350
372
|
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
373
|
+
# Mounts a regular expression, returned as a string to ease interpolation,
|
374
|
+
# that will match part by part the given constant.
|
375
|
+
#
|
376
|
+
# const_regexp("Foo::Bar::Baz") # => "Foo(::Bar(::Baz)?)?"
|
377
|
+
# const_regexp("::") # => "::"
|
378
|
+
def const_regexp(camel_cased_word)
|
379
|
+
parts = camel_cased_word.split("::".freeze)
|
358
380
|
|
359
|
-
|
381
|
+
return Regexp.escape(camel_cased_word) if parts.blank?
|
360
382
|
|
361
|
-
|
383
|
+
last = parts.pop
|
362
384
|
|
363
|
-
|
364
|
-
|
385
|
+
parts.reverse.inject(last) do |acc, part|
|
386
|
+
part.empty? ? acc : "#{part}(::#{acc})?"
|
387
|
+
end
|
365
388
|
end
|
366
|
-
end
|
367
|
-
|
368
|
-
# Applies inflection rules for +singularize+ and +pluralize+.
|
369
|
-
#
|
370
|
-
# apply_inflections('post', inflections.plurals) # => "posts"
|
371
|
-
# apply_inflections('posts', inflections.singulars) # => "post"
|
372
|
-
def apply_inflections(word, rules)
|
373
|
-
result = word.to_s.dup
|
374
389
|
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
390
|
+
# Applies inflection rules for +singularize+ and +pluralize+.
|
391
|
+
#
|
392
|
+
# If passed an optional +locale+ parameter, the uncountables will be
|
393
|
+
# found for that locale.
|
394
|
+
#
|
395
|
+
# apply_inflections('post', inflections.plurals, :en) # => "posts"
|
396
|
+
# apply_inflections('posts', inflections.singulars, :en) # => "post"
|
397
|
+
def apply_inflections(word, rules, locale = :en)
|
398
|
+
result = word.to_s.dup
|
399
|
+
|
400
|
+
if word.empty? || inflections(locale).uncountables.uncountable?(result)
|
401
|
+
result
|
402
|
+
else
|
403
|
+
rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
|
404
|
+
result
|
405
|
+
end
|
380
406
|
end
|
381
|
-
end
|
382
407
|
end
|
383
408
|
end
|