activesupport 5.2.0 → 6.1.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 +4 -4
- data/CHANGELOG.md +362 -333
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -3
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/array_inquirer.rb +4 -2
- data/lib/active_support/backtrace_cleaner.rb +29 -3
- data/lib/active_support/benchmarkable.rb +1 -1
- data/lib/active_support/cache/file_store.rb +33 -33
- data/lib/active_support/cache/mem_cache_store.rb +31 -29
- data/lib/active_support/cache/memory_store.rb +59 -33
- data/lib/active_support/cache/null_store.rb +8 -3
- data/lib/active_support/cache/redis_cache_store.rb +84 -45
- data/lib/active_support/cache/strategy/local_cache.rb +41 -26
- data/lib/active_support/cache.rb +174 -113
- data/lib/active_support/callbacks.rb +81 -64
- data/lib/active_support/concern.rb +76 -5
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
- data/lib/active_support/concurrency/share_lock.rb +0 -1
- data/lib/active_support/configurable.rb +10 -14
- data/lib/active_support/configuration_file.rb +46 -0
- data/lib/active_support/core_ext/array/access.rb +18 -6
- data/lib/active_support/core_ext/array/conversions.rb +5 -5
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- data/lib/active_support/core_ext/array.rb +1 -1
- data/lib/active_support/core_ext/benchmark.rb +2 -2
- data/lib/active_support/core_ext/class/attribute.rb +32 -47
- data/lib/active_support/core_ext/class/subclasses.rb +17 -38
- data/lib/active_support/core_ext/date/calculations.rb +6 -5
- data/lib/active_support/core_ext/date/conversions.rb +2 -1
- data/lib/active_support/core_ext/date_and_time/calculations.rb +37 -47
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
- data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
- data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
- data/lib/active_support/core_ext/digest.rb +3 -0
- data/lib/active_support/core_ext/enumerable.rb +171 -70
- data/lib/active_support/core_ext/file/atomic.rb +1 -1
- data/lib/active_support/core_ext/hash/conversions.rb +3 -3
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +2 -2
- data/lib/active_support/core_ext/hash/keys.rb +1 -30
- data/lib/active_support/core_ext/hash/slice.rb +6 -27
- data/lib/active_support/core_ext/hash.rb +1 -2
- 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/marshal.rb +2 -0
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +30 -39
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +17 -19
- data/lib/active_support/core_ext/module/concerning.rb +8 -2
- data/lib/active_support/core_ext/module/delegation.rb +76 -33
- data/lib/active_support/core_ext/module/introspection.rb +16 -15
- data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
- data/lib/active_support/core_ext/module.rb +0 -1
- data/lib/active_support/core_ext/name_error.rb +29 -2
- data/lib/active_support/core_ext/numeric/conversions.rb +129 -129
- data/lib/active_support/core_ext/numeric.rb +0 -1
- data/lib/active_support/core_ext/object/blank.rb +1 -2
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/duplicable.rb +7 -114
- data/lib/active_support/core_ext/object/json.rb +7 -2
- data/lib/active_support/core_ext/object/to_query.rb +5 -2
- data/lib/active_support/core_ext/object/try.rb +17 -7
- data/lib/active_support/core_ext/object/with_options.rb +1 -1
- data/lib/active_support/core_ext/range/compare_range.rb +82 -0
- data/lib/active_support/core_ext/range/conversions.rb +31 -29
- data/lib/active_support/core_ext/range/each.rb +0 -1
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
- data/lib/active_support/core_ext/range.rb +1 -1
- data/lib/active_support/core_ext/regexp.rb +8 -5
- data/lib/active_support/core_ext/securerandom.rb +23 -3
- data/lib/active_support/core_ext/string/access.rb +5 -16
- data/lib/active_support/core_ext/string/conversions.rb +1 -0
- data/lib/active_support/core_ext/string/filters.rb +42 -1
- data/lib/active_support/core_ext/string/inflections.rb +45 -6
- data/lib/active_support/core_ext/string/inquiry.rb +1 -0
- data/lib/active_support/core_ext/string/multibyte.rb +6 -5
- data/lib/active_support/core_ext/string/output_safety.rb +69 -12
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
- data/lib/active_support/core_ext/string/strip.rb +3 -1
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/time/calculations.rb +50 -3
- data/lib/active_support/core_ext/time/conversions.rb +1 -0
- data/lib/active_support/core_ext/uri.rb +7 -5
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +15 -2
- data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
- data/lib/active_support/dependencies.rb +118 -35
- data/lib/active_support/deprecation/behaviors.rb +20 -3
- data/lib/active_support/deprecation/disallowed.rb +56 -0
- data/lib/active_support/deprecation/instance_delegator.rb +0 -1
- data/lib/active_support/deprecation/method_wrappers.rb +21 -13
- data/lib/active_support/deprecation/proxy_wrappers.rb +29 -6
- data/lib/active_support/deprecation/reporting.rb +51 -8
- data/lib/active_support/deprecation.rb +6 -1
- data/lib/active_support/descendants_tracker.rb +59 -9
- data/lib/active_support/duration/iso8601_parser.rb +2 -4
- data/lib/active_support/duration/iso8601_serializer.rb +18 -14
- data/lib/active_support/duration.rb +90 -38
- data/lib/active_support/encrypted_configuration.rb +1 -5
- data/lib/active_support/encrypted_file.rb +23 -5
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/evented_file_update_checker.rb +82 -117
- data/lib/active_support/execution_wrapper.rb +1 -0
- data/lib/active_support/file_update_checker.rb +0 -1
- data/lib/active_support/fork_tracker.rb +62 -0
- data/lib/active_support/gem_version.rb +2 -2
- data/lib/active_support/hash_with_indifferent_access.rb +78 -41
- data/lib/active_support/i18n.rb +1 -0
- data/lib/active_support/i18n_railtie.rb +16 -5
- data/lib/active_support/inflector/inflections.rb +2 -7
- data/lib/active_support/inflector/methods.rb +50 -57
- data/lib/active_support/inflector/transliterate.rb +47 -18
- data/lib/active_support/json/decoding.rb +25 -26
- data/lib/active_support/json/encoding.rb +11 -3
- data/lib/active_support/key_generator.rb +1 -33
- data/lib/active_support/lazy_load_hooks.rb +5 -2
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/locale/en.yml +7 -3
- data/lib/active_support/log_subscriber.rb +39 -9
- data/lib/active_support/logger.rb +2 -17
- data/lib/active_support/logger_silence.rb +11 -19
- data/lib/active_support/logger_thread_safe_level.rb +52 -7
- data/lib/active_support/message_encryptor.rb +8 -13
- data/lib/active_support/message_verifier.rb +10 -10
- data/lib/active_support/messages/metadata.rb +11 -2
- data/lib/active_support/messages/rotation_configuration.rb +2 -1
- data/lib/active_support/messages/rotator.rb +10 -9
- data/lib/active_support/multibyte/chars.rb +10 -68
- data/lib/active_support/multibyte/unicode.rb +15 -327
- data/lib/active_support/notifications/fanout.rb +116 -16
- data/lib/active_support/notifications/instrumenter.rb +71 -9
- data/lib/active_support/notifications.rb +72 -8
- data/lib/active_support/number_helper/number_converter.rb +5 -6
- data/lib/active_support/number_helper/number_to_currency_converter.rb +4 -9
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
- data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -3
- 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 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +8 -7
- data/lib/active_support/number_helper/rounding_helper.rb +12 -28
- data/lib/active_support/number_helper.rb +38 -12
- data/lib/active_support/option_merger.rb +22 -3
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/ordered_options.rb +13 -3
- data/lib/active_support/parameter_filter.rb +133 -0
- data/lib/active_support/per_thread_registry.rb +1 -1
- data/lib/active_support/rails.rb +1 -10
- data/lib/active_support/railtie.rb +23 -1
- data/lib/active_support/reloader.rb +4 -5
- data/lib/active_support/secure_compare_rotator.rb +51 -0
- data/lib/active_support/security_utils.rb +19 -12
- data/lib/active_support/string_inquirer.rb +4 -3
- data/lib/active_support/subscriber.rb +72 -24
- data/lib/active_support/tagged_logging.rb +42 -8
- data/lib/active_support/test_case.rb +92 -1
- data/lib/active_support/testing/assertions.rb +30 -9
- 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/server.rb +78 -0
- data/lib/active_support/testing/parallelization/worker.rb +100 -0
- data/lib/active_support/testing/parallelization.rb +51 -0
- data/lib/active_support/testing/setup_and_teardown.rb +5 -9
- data/lib/active_support/testing/stream.rb +1 -2
- data/lib/active_support/testing/time_helpers.rb +47 -12
- data/lib/active_support/time_with_zone.rb +81 -47
- data/lib/active_support/values/time_zone.rb +34 -18
- data/lib/active_support/xml_mini/jdom.rb +2 -3
- 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 +10 -3
- data/lib/active_support/xml_mini.rb +2 -10
- data/lib/active_support.rb +14 -1
- metadata +57 -30
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
- data/lib/active_support/core_ext/hash/compact.rb +0 -29
- data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
- data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
- data/lib/active_support/core_ext/module/reachable.rb +0 -11
- data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
- data/lib/active_support/core_ext/range/include_range.rb +0 -25
- data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,39 +1,41 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module ActiveSupport
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
module ActiveSupport
|
4
|
+
module RangeWithFormat
|
5
|
+
RANGE_FORMATS = {
|
6
|
+
db: -> (start, stop) do
|
7
|
+
case start
|
8
|
+
when String then "BETWEEN '#{start}' AND '#{stop}'"
|
9
|
+
else
|
10
|
+
"BETWEEN '#{start.to_s(:db)}' AND '#{stop.to_s(:db)}'"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
}
|
14
|
+
|
15
|
+
# Convert range to a formatted string. See RANGE_FORMATS for predefined formats.
|
16
|
+
#
|
17
|
+
# range = (1..100) # => 1..100
|
18
|
+
#
|
19
|
+
# range.to_s # => "1..100"
|
20
|
+
# range.to_s(:db) # => "BETWEEN '1' AND '100'"
|
21
|
+
#
|
22
|
+
# == Adding your own range formats to to_s
|
23
|
+
# You can add your own formats to the Range::RANGE_FORMATS hash.
|
24
|
+
# Use the format name as the hash key and a Proc instance.
|
25
|
+
#
|
26
|
+
# # config/initializers/range_formats.rb
|
27
|
+
# Range::RANGE_FORMATS[:short] = ->(start, stop) { "Between #{start.to_s(:db)} and #{stop.to_s(:db)}" }
|
28
|
+
def to_s(format = :default)
|
29
|
+
if formatter = RANGE_FORMATS[format]
|
30
|
+
formatter.call(first, last)
|
8
31
|
else
|
9
|
-
|
32
|
+
super()
|
10
33
|
end
|
11
34
|
end
|
12
|
-
}
|
13
35
|
|
14
|
-
|
15
|
-
|
16
|
-
# range = (1..100) # => 1..100
|
17
|
-
#
|
18
|
-
# range.to_s # => "1..100"
|
19
|
-
# range.to_s(:db) # => "BETWEEN '1' AND '100'"
|
20
|
-
#
|
21
|
-
# == Adding your own range formats to to_s
|
22
|
-
# You can add your own formats to the Range::RANGE_FORMATS hash.
|
23
|
-
# Use the format name as the hash key and a Proc instance.
|
24
|
-
#
|
25
|
-
# # config/initializers/range_formats.rb
|
26
|
-
# Range::RANGE_FORMATS[:short] = ->(start, stop) { "Between #{start.to_s(:db)} and #{stop.to_s(:db)}" }
|
27
|
-
def to_s(format = :default)
|
28
|
-
if formatter = RANGE_FORMATS[format]
|
29
|
-
formatter.call(first, last)
|
30
|
-
else
|
31
|
-
super()
|
32
|
-
end
|
36
|
+
alias_method :to_default_s, :to_s
|
37
|
+
alias_method :to_formatted_s, :to_s
|
33
38
|
end
|
34
|
-
|
35
|
-
alias_method :to_default_s, :to_s
|
36
|
-
alias_method :to_formatted_s, :to_s
|
37
39
|
end
|
38
40
|
|
39
41
|
Range.prepend(ActiveSupport::RangeWithFormat)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/time_with_zone"
|
4
|
+
require "active_support/deprecation"
|
4
5
|
|
5
6
|
module ActiveSupport
|
6
7
|
module IncludeTimeWithZone #:nodoc:
|
@@ -9,9 +10,13 @@ module ActiveSupport
|
|
9
10
|
# (1.hour.ago..1.hour.from_now).include?(Time.current) # => true
|
10
11
|
#
|
11
12
|
def include?(value)
|
12
|
-
if
|
13
|
-
|
14
|
-
|
13
|
+
if self.begin.is_a?(TimeWithZone) || self.end.is_a?(TimeWithZone)
|
14
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
15
|
+
Using `Range#include?` to check the inclusion of a value in
|
16
|
+
a date time range is deprecated.
|
17
|
+
It is recommended to use `Range#cover?` instead of `Range#include?` to
|
18
|
+
check the inclusion of a value in a date time range.
|
19
|
+
MSG
|
15
20
|
cover?(value)
|
16
21
|
else
|
17
22
|
super
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/core_ext/range/conversions"
|
4
|
-
require "active_support/core_ext/range/
|
4
|
+
require "active_support/core_ext/range/compare_range"
|
5
5
|
require "active_support/core_ext/range/include_time_with_zone"
|
6
6
|
require "active_support/core_ext/range/overlaps"
|
7
7
|
require "active_support/core_ext/range/each"
|
@@ -1,11 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class Regexp
|
3
|
+
class Regexp
|
4
|
+
# Returns +true+ if the regexp has the multiline flag set.
|
5
|
+
#
|
6
|
+
# (/./).multiline? # => false
|
7
|
+
# (/./m).multiline? # => true
|
8
|
+
#
|
9
|
+
# Regexp.new(".").multiline? # => false
|
10
|
+
# Regexp.new(".", Regexp::MULTILINE).multiline? # => true
|
4
11
|
def multiline?
|
5
12
|
options & MULTILINE == MULTILINE
|
6
13
|
end
|
7
|
-
|
8
|
-
def match?(string, pos = 0)
|
9
|
-
!!match(string, pos)
|
10
|
-
end unless //.respond_to?(:match?)
|
11
14
|
end
|
@@ -4,17 +4,18 @@ require "securerandom"
|
|
4
4
|
|
5
5
|
module SecureRandom
|
6
6
|
BASE58_ALPHABET = ("0".."9").to_a + ("A".."Z").to_a + ("a".."z").to_a - ["0", "O", "I", "l"]
|
7
|
+
BASE36_ALPHABET = ("0".."9").to_a + ("a".."z").to_a
|
8
|
+
|
7
9
|
# SecureRandom.base58 generates a random base58 string.
|
8
10
|
#
|
9
|
-
# The argument _n_ specifies the length
|
11
|
+
# The argument _n_ specifies the length of the random string to be generated.
|
10
12
|
#
|
11
13
|
# If _n_ is not specified or is +nil+, 16 is assumed. It may be larger in the future.
|
12
14
|
#
|
13
|
-
# The result may contain alphanumeric characters except 0, O, I and l
|
15
|
+
# The result may contain alphanumeric characters except 0, O, I and l.
|
14
16
|
#
|
15
17
|
# p SecureRandom.base58 # => "4kUgL2pdQMSCQtjE"
|
16
18
|
# p SecureRandom.base58(24) # => "77TMHrHJFvFDwodq8w7Ev2m7"
|
17
|
-
#
|
18
19
|
def self.base58(n = 16)
|
19
20
|
SecureRandom.random_bytes(n).unpack("C*").map do |byte|
|
20
21
|
idx = byte % 64
|
@@ -22,4 +23,23 @@ module SecureRandom
|
|
22
23
|
BASE58_ALPHABET[idx]
|
23
24
|
end.join
|
24
25
|
end
|
26
|
+
|
27
|
+
# SecureRandom.base36 generates a random base36 string in lowercase.
|
28
|
+
#
|
29
|
+
# The argument _n_ specifies the length of the random string to be generated.
|
30
|
+
#
|
31
|
+
# If _n_ is not specified or is +nil+, 16 is assumed. It may be larger in the future.
|
32
|
+
# This method can be used over +base58+ if a deterministic case key is necessary.
|
33
|
+
#
|
34
|
+
# The result will contain alphanumeric characters in lowercase.
|
35
|
+
#
|
36
|
+
# p SecureRandom.base36 # => "4kugl2pdqmscqtje"
|
37
|
+
# p SecureRandom.base36(24) # => "77tmhrhjfvfdwodq8w7ev2m7"
|
38
|
+
def self.base36(n = 16)
|
39
|
+
SecureRandom.random_bytes(n).unpack("C*").map do |byte|
|
40
|
+
idx = byte % 64
|
41
|
+
idx = SecureRandom.random_number(36) if idx >= 36
|
42
|
+
BASE36_ALPHABET[idx]
|
43
|
+
end.join
|
44
|
+
end
|
25
45
|
end
|
@@ -44,7 +44,7 @@ class String
|
|
44
44
|
# str.from(0).to(-1) # => "hello"
|
45
45
|
# str.from(1).to(-2) # => "ell"
|
46
46
|
def from(position)
|
47
|
-
self[position
|
47
|
+
self[position, length]
|
48
48
|
end
|
49
49
|
|
50
50
|
# Returns a substring from the beginning of the string to the given position.
|
@@ -61,7 +61,8 @@ class String
|
|
61
61
|
# str.from(0).to(-1) # => "hello"
|
62
62
|
# str.from(1).to(-2) # => "ell"
|
63
63
|
def to(position)
|
64
|
-
|
64
|
+
position += size if position < 0
|
65
|
+
self[0, position + 1] || +""
|
65
66
|
end
|
66
67
|
|
67
68
|
# Returns the first character. If a limit is supplied, returns a substring
|
@@ -75,13 +76,7 @@ class String
|
|
75
76
|
# str.first(0) # => ""
|
76
77
|
# str.first(6) # => "hello"
|
77
78
|
def first(limit = 1)
|
78
|
-
|
79
|
-
""
|
80
|
-
elsif limit >= size
|
81
|
-
dup
|
82
|
-
else
|
83
|
-
to(limit - 1)
|
84
|
-
end
|
79
|
+
self[0, limit] || raise(ArgumentError, "negative limit")
|
85
80
|
end
|
86
81
|
|
87
82
|
# Returns the last character of the string. If a limit is supplied, returns a substring
|
@@ -95,12 +90,6 @@ class String
|
|
95
90
|
# str.last(0) # => ""
|
96
91
|
# str.last(6) # => "hello"
|
97
92
|
def last(limit = 1)
|
98
|
-
|
99
|
-
""
|
100
|
-
elsif limit >= size
|
101
|
-
dup
|
102
|
-
else
|
103
|
-
from(-limit)
|
104
|
-
end
|
93
|
+
self[[length - limit, 0].max, limit] || raise(ArgumentError, "negative limit")
|
105
94
|
end
|
106
95
|
end
|
@@ -18,6 +18,7 @@ class String
|
|
18
18
|
# "2012-12-13T06:12".to_time # => 2012-12-13 06:12:00 +0100
|
19
19
|
# "2012-12-13T06:12".to_time(:utc) # => 2012-12-13 06:12:00 UTC
|
20
20
|
# "12/13/2012".to_time # => ArgumentError: argument out of range
|
21
|
+
# "1604326192".to_time # => ArgumentError: argument out of range
|
21
22
|
def to_time(form = :local)
|
22
23
|
parts = Date._parse(self, false)
|
23
24
|
used_keys = %i(year mon mday hour min sec sec_fraction offset)
|
@@ -75,7 +75,48 @@ class String
|
|
75
75
|
length_with_room_for_omission
|
76
76
|
end
|
77
77
|
|
78
|
-
"#{self[0, stop]}#{omission}"
|
78
|
+
+"#{self[0, stop]}#{omission}"
|
79
|
+
end
|
80
|
+
|
81
|
+
# Truncates +text+ to at most <tt>bytesize</tt> bytes in length without
|
82
|
+
# breaking string encoding by splitting multibyte characters or breaking
|
83
|
+
# grapheme clusters ("perceptual characters") by truncating at combining
|
84
|
+
# characters.
|
85
|
+
#
|
86
|
+
# >> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".size
|
87
|
+
# => 20
|
88
|
+
# >> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".bytesize
|
89
|
+
# => 80
|
90
|
+
# >> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".truncate_bytes(20)
|
91
|
+
# => "🔪🔪🔪🔪…"
|
92
|
+
#
|
93
|
+
# The truncated text ends with the <tt>:omission</tt> string, defaulting
|
94
|
+
# to "…", for a total length not exceeding <tt>bytesize</tt>.
|
95
|
+
def truncate_bytes(truncate_at, omission: "…")
|
96
|
+
omission ||= ""
|
97
|
+
|
98
|
+
case
|
99
|
+
when bytesize <= truncate_at
|
100
|
+
dup
|
101
|
+
when omission.bytesize > truncate_at
|
102
|
+
raise ArgumentError, "Omission #{omission.inspect} is #{omission.bytesize}, larger than the truncation length of #{truncate_at} bytes"
|
103
|
+
when omission.bytesize == truncate_at
|
104
|
+
omission.dup
|
105
|
+
else
|
106
|
+
self.class.new.tap do |cut|
|
107
|
+
cut_at = truncate_at - omission.bytesize
|
108
|
+
|
109
|
+
scan(/\X/) do |grapheme|
|
110
|
+
if cut.bytesize + grapheme.bytesize <= cut_at
|
111
|
+
cut << grapheme
|
112
|
+
else
|
113
|
+
break
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
cut << omission
|
118
|
+
end
|
119
|
+
end
|
79
120
|
end
|
80
121
|
|
81
122
|
# Truncates a given +text+ after a given number of words (<tt>words_count</tt>):
|
@@ -30,6 +30,8 @@ class String
|
|
30
30
|
# 'apple'.pluralize(2) # => "apples"
|
31
31
|
# 'ley'.pluralize(:es) # => "leyes"
|
32
32
|
# 'ley'.pluralize(1, :es) # => "ley"
|
33
|
+
#
|
34
|
+
# See ActiveSupport::Inflector.pluralize.
|
33
35
|
def pluralize(count = nil, locale = :en)
|
34
36
|
locale = count if count.is_a?(Symbol)
|
35
37
|
if count == 1
|
@@ -53,28 +55,34 @@ class String
|
|
53
55
|
# 'the blue mailmen'.singularize # => "the blue mailman"
|
54
56
|
# 'CamelOctopi'.singularize # => "CamelOctopus"
|
55
57
|
# 'leyes'.singularize(:es) # => "ley"
|
58
|
+
#
|
59
|
+
# See ActiveSupport::Inflector.singularize.
|
56
60
|
def singularize(locale = :en)
|
57
61
|
ActiveSupport::Inflector.singularize(self, locale)
|
58
62
|
end
|
59
63
|
|
60
64
|
# +constantize+ tries to find a declared constant with the name specified
|
61
65
|
# in the string. It raises a NameError when the name is not in CamelCase
|
62
|
-
# or is not initialized.
|
66
|
+
# or is not initialized.
|
63
67
|
#
|
64
68
|
# 'Module'.constantize # => Module
|
65
69
|
# 'Class'.constantize # => Class
|
66
70
|
# 'blargle'.constantize # => NameError: wrong constant name blargle
|
71
|
+
#
|
72
|
+
# See ActiveSupport::Inflector.constantize.
|
67
73
|
def constantize
|
68
74
|
ActiveSupport::Inflector.constantize(self)
|
69
75
|
end
|
70
76
|
|
71
77
|
# +safe_constantize+ tries to find a declared constant with the name specified
|
72
78
|
# in the string. It returns +nil+ when the name is not in CamelCase
|
73
|
-
# or is not initialized.
|
79
|
+
# or is not initialized.
|
74
80
|
#
|
75
81
|
# 'Module'.safe_constantize # => Module
|
76
82
|
# 'Class'.safe_constantize # => Class
|
77
83
|
# 'blargle'.safe_constantize # => nil
|
84
|
+
#
|
85
|
+
# See ActiveSupport::Inflector.safe_constantize.
|
78
86
|
def safe_constantize
|
79
87
|
ActiveSupport::Inflector.safe_constantize(self)
|
80
88
|
end
|
@@ -88,6 +96,10 @@ class String
|
|
88
96
|
# 'active_record'.camelize(:lower) # => "activeRecord"
|
89
97
|
# 'active_record/errors'.camelize # => "ActiveRecord::Errors"
|
90
98
|
# 'active_record/errors'.camelize(:lower) # => "activeRecord::Errors"
|
99
|
+
#
|
100
|
+
# +camelize+ is also aliased as +camelcase+.
|
101
|
+
#
|
102
|
+
# See ActiveSupport::Inflector.camelize.
|
91
103
|
def camelize(first_letter = :upper)
|
92
104
|
case first_letter
|
93
105
|
when :upper
|
@@ -108,11 +120,13 @@ class String
|
|
108
120
|
# optional parameter +keep_id_suffix+ to true.
|
109
121
|
# By default, this parameter is false.
|
110
122
|
#
|
111
|
-
# +titleize+ is also aliased as +titlecase+.
|
112
|
-
#
|
113
123
|
# 'man from the boondocks'.titleize # => "Man From The Boondocks"
|
114
124
|
# 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
|
115
125
|
# 'string_ending_with_id'.titleize(keep_id_suffix: true) # => "String Ending With Id"
|
126
|
+
#
|
127
|
+
# +titleize+ is also aliased as +titlecase+.
|
128
|
+
#
|
129
|
+
# See ActiveSupport::Inflector.titleize.
|
116
130
|
def titleize(keep_id_suffix: false)
|
117
131
|
ActiveSupport::Inflector.titleize(self, keep_id_suffix: keep_id_suffix)
|
118
132
|
end
|
@@ -124,6 +138,8 @@ class String
|
|
124
138
|
#
|
125
139
|
# 'ActiveModel'.underscore # => "active_model"
|
126
140
|
# 'ActiveModel::Errors'.underscore # => "active_model/errors"
|
141
|
+
#
|
142
|
+
# See ActiveSupport::Inflector.underscore.
|
127
143
|
def underscore
|
128
144
|
ActiveSupport::Inflector.underscore(self)
|
129
145
|
end
|
@@ -131,6 +147,8 @@ class String
|
|
131
147
|
# Replaces underscores with dashes in the string.
|
132
148
|
#
|
133
149
|
# 'puni_puni'.dasherize # => "puni-puni"
|
150
|
+
#
|
151
|
+
# See ActiveSupport::Inflector.dasherize.
|
134
152
|
def dasherize
|
135
153
|
ActiveSupport::Inflector.dasherize(self)
|
136
154
|
end
|
@@ -142,6 +160,8 @@ class String
|
|
142
160
|
# '::Inflections'.demodulize # => "Inflections"
|
143
161
|
# ''.demodulize # => ''
|
144
162
|
#
|
163
|
+
# See ActiveSupport::Inflector.demodulize.
|
164
|
+
#
|
145
165
|
# See also +deconstantize+.
|
146
166
|
def demodulize
|
147
167
|
ActiveSupport::Inflector.demodulize(self)
|
@@ -155,6 +175,8 @@ class String
|
|
155
175
|
# '::String'.deconstantize # => ""
|
156
176
|
# ''.deconstantize # => ""
|
157
177
|
#
|
178
|
+
# See ActiveSupport::Inflector.deconstantize.
|
179
|
+
#
|
158
180
|
# See also +demodulize+.
|
159
181
|
def deconstantize
|
160
182
|
ActiveSupport::Inflector.deconstantize(self)
|
@@ -162,6 +184,11 @@ class String
|
|
162
184
|
|
163
185
|
# Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
|
164
186
|
#
|
187
|
+
# If the optional parameter +locale+ is specified,
|
188
|
+
# the word will be parameterized as a word of that language.
|
189
|
+
# By default, this parameter is set to <tt>nil</tt> and it will use
|
190
|
+
# the configured <tt>I18n.locale</tt>.
|
191
|
+
#
|
165
192
|
# class Person
|
166
193
|
# def to_param
|
167
194
|
# "#{id}-#{name.parameterize}"
|
@@ -187,8 +214,10 @@ class String
|
|
187
214
|
#
|
188
215
|
# <%= link_to(@person.name, person_path) %>
|
189
216
|
# # => <a href="/person/1-Donald-E-Knuth">Donald E. Knuth</a>
|
190
|
-
|
191
|
-
|
217
|
+
#
|
218
|
+
# See ActiveSupport::Inflector.parameterize.
|
219
|
+
def parameterize(separator: "-", preserve_case: false, locale: nil)
|
220
|
+
ActiveSupport::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case, locale: locale)
|
192
221
|
end
|
193
222
|
|
194
223
|
# Creates the name of a table like Rails does for models to table names. This method
|
@@ -197,6 +226,8 @@ class String
|
|
197
226
|
# 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
|
198
227
|
# 'ham_and_egg'.tableize # => "ham_and_eggs"
|
199
228
|
# 'fancyCategory'.tableize # => "fancy_categories"
|
229
|
+
#
|
230
|
+
# See ActiveSupport::Inflector.tableize.
|
200
231
|
def tableize
|
201
232
|
ActiveSupport::Inflector.tableize(self)
|
202
233
|
end
|
@@ -207,6 +238,8 @@ class String
|
|
207
238
|
#
|
208
239
|
# 'ham_and_eggs'.classify # => "HamAndEgg"
|
209
240
|
# 'posts'.classify # => "Post"
|
241
|
+
#
|
242
|
+
# See ActiveSupport::Inflector.classify.
|
210
243
|
def classify
|
211
244
|
ActiveSupport::Inflector.classify(self)
|
212
245
|
end
|
@@ -228,6 +261,8 @@ class String
|
|
228
261
|
# 'author_id'.humanize(capitalize: false) # => "author"
|
229
262
|
# '_id'.humanize # => "Id"
|
230
263
|
# 'author_id'.humanize(keep_id_suffix: true) # => "Author Id"
|
264
|
+
#
|
265
|
+
# See ActiveSupport::Inflector.humanize.
|
231
266
|
def humanize(capitalize: true, keep_id_suffix: false)
|
232
267
|
ActiveSupport::Inflector.humanize(self, capitalize: capitalize, keep_id_suffix: keep_id_suffix)
|
233
268
|
end
|
@@ -237,6 +272,8 @@ class String
|
|
237
272
|
# 'what a Lovely Day'.upcase_first # => "What a Lovely Day"
|
238
273
|
# 'w'.upcase_first # => "W"
|
239
274
|
# ''.upcase_first # => ""
|
275
|
+
#
|
276
|
+
# See ActiveSupport::Inflector.upcase_first.
|
240
277
|
def upcase_first
|
241
278
|
ActiveSupport::Inflector.upcase_first(self)
|
242
279
|
end
|
@@ -248,6 +285,8 @@ class String
|
|
248
285
|
# 'Message'.foreign_key # => "message_id"
|
249
286
|
# 'Message'.foreign_key(false) # => "messageid"
|
250
287
|
# 'Admin::Post'.foreign_key # => "post_id"
|
288
|
+
#
|
289
|
+
# See ActiveSupport::Inflector.foreign_key.
|
251
290
|
def foreign_key(separate_class_name_and_id_with_underscore = true)
|
252
291
|
ActiveSupport::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
|
253
292
|
end
|
@@ -11,12 +11,13 @@ class String
|
|
11
11
|
# encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
|
12
12
|
# class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string.
|
13
13
|
#
|
14
|
-
# >> "lj".upcase
|
15
|
-
# => "lj"
|
16
14
|
# >> "lj".mb_chars.upcase.to_s
|
17
15
|
# => "LJ"
|
18
16
|
#
|
19
|
-
# NOTE:
|
17
|
+
# NOTE: Ruby 2.4 and later support native Unicode case mappings:
|
18
|
+
#
|
19
|
+
# >> "lj".upcase
|
20
|
+
# => "LJ"
|
20
21
|
#
|
21
22
|
# == Method chaining
|
22
23
|
#
|
@@ -46,9 +47,9 @@ class String
|
|
46
47
|
# iso_str.is_utf8? # => false
|
47
48
|
def is_utf8?
|
48
49
|
case encoding
|
49
|
-
when Encoding::UTF_8
|
50
|
+
when Encoding::UTF_8, Encoding::US_ASCII
|
50
51
|
valid_encoding?
|
51
|
-
when Encoding::ASCII_8BIT
|
52
|
+
when Encoding::ASCII_8BIT
|
52
53
|
dup.force_encoding(Encoding::UTF_8).valid_encoding?
|
53
54
|
else
|
54
55
|
false
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "erb"
|
4
|
-
require "active_support/core_ext/kernel/singleton_class"
|
5
4
|
require "active_support/core_ext/module/redefine_method"
|
6
5
|
require "active_support/multibyte/unicode"
|
7
6
|
|
@@ -134,10 +133,13 @@ end
|
|
134
133
|
module ActiveSupport #:nodoc:
|
135
134
|
class SafeBuffer < String
|
136
135
|
UNSAFE_STRING_METHODS = %w(
|
137
|
-
capitalize chomp chop delete
|
138
|
-
|
136
|
+
capitalize chomp chop delete delete_prefix delete_suffix
|
137
|
+
downcase lstrip next reverse rstrip scrub slice squeeze strip
|
138
|
+
succ swapcase tr tr_s unicode_normalize upcase
|
139
139
|
)
|
140
140
|
|
141
|
+
UNSAFE_STRING_METHODS_WITH_BACKREF = %w(gsub sub)
|
142
|
+
|
141
143
|
alias_method :original_concat, :concat
|
142
144
|
private :original_concat
|
143
145
|
|
@@ -149,15 +151,13 @@ module ActiveSupport #:nodoc:
|
|
149
151
|
end
|
150
152
|
|
151
153
|
def [](*args)
|
152
|
-
if
|
153
|
-
super
|
154
|
-
elsif html_safe?
|
155
|
-
new_safe_buffer = super
|
154
|
+
if html_safe?
|
155
|
+
new_string = super
|
156
156
|
|
157
|
-
|
158
|
-
new_safe_buffer.instance_variable_set :@html_safe, true
|
159
|
-
end
|
157
|
+
return unless new_string
|
160
158
|
|
159
|
+
new_safe_buffer = new_string.is_a?(SafeBuffer) ? new_string : SafeBuffer.new(new_string)
|
160
|
+
new_safe_buffer.instance_variable_set :@html_safe, true
|
161
161
|
new_safe_buffer
|
162
162
|
else
|
163
163
|
to_str[*args]
|
@@ -188,18 +188,41 @@ module ActiveSupport #:nodoc:
|
|
188
188
|
end
|
189
189
|
alias << concat
|
190
190
|
|
191
|
+
def insert(index, value)
|
192
|
+
super(index, html_escape_interpolated_argument(value))
|
193
|
+
end
|
194
|
+
|
191
195
|
def prepend(value)
|
192
196
|
super(html_escape_interpolated_argument(value))
|
193
197
|
end
|
194
198
|
|
199
|
+
def replace(value)
|
200
|
+
super(html_escape_interpolated_argument(value))
|
201
|
+
end
|
202
|
+
|
203
|
+
def []=(*args)
|
204
|
+
if args.length == 3
|
205
|
+
super(args[0], args[1], html_escape_interpolated_argument(args[2]))
|
206
|
+
else
|
207
|
+
super(args[0], html_escape_interpolated_argument(args[1]))
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
195
211
|
def +(other)
|
196
212
|
dup.concat(other)
|
197
213
|
end
|
198
214
|
|
215
|
+
def *(*)
|
216
|
+
new_string = super
|
217
|
+
new_safe_buffer = new_string.is_a?(SafeBuffer) ? new_string : SafeBuffer.new(new_string)
|
218
|
+
new_safe_buffer.instance_variable_set(:@html_safe, @html_safe)
|
219
|
+
new_safe_buffer
|
220
|
+
end
|
221
|
+
|
199
222
|
def %(args)
|
200
223
|
case args
|
201
224
|
when Hash
|
202
|
-
escaped_args =
|
225
|
+
escaped_args = args.transform_values { |arg| html_escape_interpolated_argument(arg) }
|
203
226
|
else
|
204
227
|
escaped_args = Array(args).map { |arg| html_escape_interpolated_argument(arg) }
|
205
228
|
end
|
@@ -238,11 +261,45 @@ module ActiveSupport #:nodoc:
|
|
238
261
|
end
|
239
262
|
end
|
240
263
|
|
241
|
-
|
264
|
+
UNSAFE_STRING_METHODS_WITH_BACKREF.each do |unsafe_method|
|
265
|
+
if unsafe_method.respond_to?(unsafe_method)
|
266
|
+
class_eval <<-EOT, __FILE__, __LINE__ + 1
|
267
|
+
def #{unsafe_method}(*args, &block) # def gsub(*args, &block)
|
268
|
+
if block # if block
|
269
|
+
to_str.#{unsafe_method}(*args) { |*params| # to_str.gsub(*args) { |*params|
|
270
|
+
set_block_back_references(block, $~) # set_block_back_references(block, $~)
|
271
|
+
block.call(*params) # block.call(*params)
|
272
|
+
} # }
|
273
|
+
else # else
|
274
|
+
to_str.#{unsafe_method}(*args) # to_str.gsub(*args)
|
275
|
+
end # end
|
276
|
+
end # end
|
277
|
+
|
278
|
+
def #{unsafe_method}!(*args, &block) # def gsub!(*args, &block)
|
279
|
+
@html_safe = false # @html_safe = false
|
280
|
+
if block # if block
|
281
|
+
super(*args) { |*params| # super(*args) { |*params|
|
282
|
+
set_block_back_references(block, $~) # set_block_back_references(block, $~)
|
283
|
+
block.call(*params) # block.call(*params)
|
284
|
+
} # }
|
285
|
+
else # else
|
286
|
+
super # super
|
287
|
+
end # end
|
288
|
+
end # end
|
289
|
+
EOT
|
290
|
+
end
|
291
|
+
end
|
242
292
|
|
293
|
+
private
|
243
294
|
def html_escape_interpolated_argument(arg)
|
244
295
|
(!html_safe? || arg.html_safe?) ? arg : CGI.escapeHTML(arg.to_s)
|
245
296
|
end
|
297
|
+
|
298
|
+
def set_block_back_references(block, match_data)
|
299
|
+
block.binding.eval("proc { |m| $~ = m }").call(match_data)
|
300
|
+
rescue ArgumentError
|
301
|
+
# Can't create binding from C level Proc
|
302
|
+
end
|
246
303
|
end
|
247
304
|
end
|
248
305
|
|
@@ -20,6 +20,8 @@ class String
|
|
20
20
|
# Technically, it looks for the least indented non-empty line
|
21
21
|
# in the whole string, and removes that amount of leading whitespace.
|
22
22
|
def strip_heredoc
|
23
|
-
gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, "".
|
23
|
+
gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, "").tap do |stripped|
|
24
|
+
stripped.freeze if frozen?
|
25
|
+
end
|
24
26
|
end
|
25
27
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Symbol
|
4
|
+
def start_with?(*prefixes)
|
5
|
+
to_s.start_with?(*prefixes)
|
6
|
+
end unless method_defined?(:start_with?)
|
7
|
+
|
8
|
+
def end_with?(*suffixes)
|
9
|
+
to_s.end_with?(*suffixes)
|
10
|
+
end unless method_defined?(:end_with?)
|
11
|
+
|
12
|
+
alias :starts_with? :start_with?
|
13
|
+
alias :ends_with? :end_with?
|
14
|
+
end
|