activesupport 6.1.0 → 7.1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1075 -325
- data/MIT-LICENSE +1 -1
- data/README.rdoc +7 -7
- data/lib/active_support/actionable_error.rb +4 -2
- data/lib/active_support/array_inquirer.rb +2 -2
- data/lib/active_support/backtrace_cleaner.rb +32 -7
- data/lib/active_support/benchmarkable.rb +3 -2
- data/lib/active_support/broadcast_logger.rb +251 -0
- data/lib/active_support/builder.rb +1 -1
- data/lib/active_support/cache/coder.rb +153 -0
- data/lib/active_support/cache/entry.rb +134 -0
- data/lib/active_support/cache/file_store.rb +53 -20
- data/lib/active_support/cache/mem_cache_store.rb +201 -62
- data/lib/active_support/cache/memory_store.rb +86 -24
- data/lib/active_support/cache/null_store.rb +16 -2
- data/lib/active_support/cache/redis_cache_store.rb +186 -193
- data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
- data/lib/active_support/cache/strategy/local_cache.rb +63 -71
- data/lib/active_support/cache.rb +487 -249
- data/lib/active_support/callbacks.rb +227 -105
- data/lib/active_support/code_generator.rb +70 -0
- data/lib/active_support/concern.rb +9 -7
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +44 -7
- data/lib/active_support/concurrency/null_lock.rb +13 -0
- data/lib/active_support/concurrency/share_lock.rb +2 -2
- data/lib/active_support/configurable.rb +18 -5
- data/lib/active_support/configuration_file.rb +7 -2
- data/lib/active_support/core_ext/array/access.rb +1 -5
- data/lib/active_support/core_ext/array/conversions.rb +15 -13
- data/lib/active_support/core_ext/array/grouping.rb +6 -6
- data/lib/active_support/core_ext/array/inquiry.rb +2 -2
- data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
- data/lib/active_support/core_ext/class/subclasses.rb +37 -26
- data/lib/active_support/core_ext/date/blank.rb +1 -1
- data/lib/active_support/core_ext/date/calculations.rb +24 -9
- data/lib/active_support/core_ext/date/conversions.rb +16 -15
- data/lib/active_support/core_ext/date_and_time/calculations.rb +14 -4
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
- data/lib/active_support/core_ext/date_time/blank.rb +1 -1
- data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +19 -15
- data/lib/active_support/core_ext/digest/uuid.rb +30 -13
- data/lib/active_support/core_ext/enumerable.rb +85 -83
- data/lib/active_support/core_ext/erb/util.rb +196 -0
- data/lib/active_support/core_ext/file/atomic.rb +3 -1
- data/lib/active_support/core_ext/hash/conversions.rb +1 -2
- data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +3 -3
- data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
- data/lib/active_support/core_ext/hash/keys.rb +4 -4
- data/lib/active_support/core_ext/integer/inflections.rb +12 -12
- data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
- data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
- data/lib/active_support/core_ext/module/attribute_accessors.rb +8 -0
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +49 -22
- data/lib/active_support/core_ext/module/concerning.rb +6 -6
- data/lib/active_support/core_ext/module/delegation.rb +81 -43
- data/lib/active_support/core_ext/module/deprecation.rb +15 -12
- data/lib/active_support/core_ext/module/introspection.rb +0 -1
- data/lib/active_support/core_ext/name_error.rb +2 -8
- data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +82 -77
- data/lib/active_support/core_ext/object/acts_like.rb +29 -5
- data/lib/active_support/core_ext/object/blank.rb +2 -2
- data/lib/active_support/core_ext/object/deep_dup.rb +17 -1
- data/lib/active_support/core_ext/object/duplicable.rb +31 -11
- data/lib/active_support/core_ext/object/inclusion.rb +13 -5
- data/lib/active_support/core_ext/object/instance_variables.rb +22 -12
- data/lib/active_support/core_ext/object/json.rb +49 -27
- data/lib/active_support/core_ext/object/to_query.rb +2 -4
- data/lib/active_support/core_ext/object/try.rb +20 -20
- data/lib/active_support/core_ext/object/with.rb +44 -0
- data/lib/active_support/core_ext/object/with_options.rb +25 -6
- data/lib/active_support/core_ext/object.rb +1 -0
- data/lib/active_support/core_ext/pathname/blank.rb +16 -0
- data/lib/active_support/core_ext/pathname/existence.rb +23 -0
- data/lib/active_support/core_ext/pathname.rb +4 -0
- data/lib/active_support/core_ext/range/compare_range.rb +0 -25
- data/lib/active_support/core_ext/range/conversions.rb +34 -13
- data/lib/active_support/core_ext/range/each.rb +1 -1
- data/lib/active_support/core_ext/range/overlap.rb +40 -0
- data/lib/active_support/core_ext/range.rb +1 -2
- data/lib/active_support/core_ext/securerandom.rb +25 -13
- data/lib/active_support/core_ext/string/conversions.rb +2 -2
- data/lib/active_support/core_ext/string/filters.rb +21 -15
- data/lib/active_support/core_ext/string/indent.rb +1 -1
- data/lib/active_support/core_ext/string/inflections.rb +17 -10
- data/lib/active_support/core_ext/string/inquiry.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +85 -165
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
- data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
- data/lib/active_support/core_ext/time/calculations.rb +30 -8
- data/lib/active_support/core_ext/time/conversions.rb +15 -13
- data/lib/active_support/core_ext/time/zones.rb +12 -28
- data/lib/active_support/core_ext.rb +2 -1
- data/lib/active_support/current_attributes.rb +47 -20
- data/lib/active_support/deep_mergeable.rb +53 -0
- data/lib/active_support/dependencies/autoload.rb +17 -12
- data/lib/active_support/dependencies/interlock.rb +10 -18
- data/lib/active_support/dependencies/require_dependency.rb +28 -0
- data/lib/active_support/dependencies.rb +58 -788
- data/lib/active_support/deprecation/behaviors.rb +66 -40
- data/lib/active_support/deprecation/constant_accessor.rb +5 -4
- data/lib/active_support/deprecation/deprecators.rb +104 -0
- data/lib/active_support/deprecation/disallowed.rb +6 -8
- data/lib/active_support/deprecation/instance_delegator.rb +31 -4
- data/lib/active_support/deprecation/method_wrappers.rb +9 -26
- data/lib/active_support/deprecation/proxy_wrappers.rb +38 -23
- data/lib/active_support/deprecation/reporting.rb +43 -26
- data/lib/active_support/deprecation.rb +32 -5
- data/lib/active_support/deprecator.rb +7 -0
- data/lib/active_support/descendants_tracker.rb +150 -72
- data/lib/active_support/digest.rb +5 -3
- data/lib/active_support/duration/iso8601_parser.rb +3 -3
- data/lib/active_support/duration/iso8601_serializer.rb +9 -3
- data/lib/active_support/duration.rb +83 -52
- data/lib/active_support/encrypted_configuration.rb +72 -9
- data/lib/active_support/encrypted_file.rb +29 -13
- data/lib/active_support/environment_inquirer.rb +23 -3
- data/lib/active_support/error_reporter/test_helper.rb +15 -0
- data/lib/active_support/error_reporter.rb +203 -0
- data/lib/active_support/evented_file_update_checker.rb +20 -7
- data/lib/active_support/execution_context/test_helper.rb +13 -0
- data/lib/active_support/execution_context.rb +53 -0
- data/lib/active_support/execution_wrapper.rb +44 -22
- data/lib/active_support/executor/test_helper.rb +7 -0
- data/lib/active_support/file_update_checker.rb +4 -2
- data/lib/active_support/fork_tracker.rb +28 -11
- data/lib/active_support/gem_version.rb +4 -4
- data/lib/active_support/gzip.rb +2 -0
- data/lib/active_support/hash_with_indifferent_access.rb +44 -19
- data/lib/active_support/html_safe_translation.rb +53 -0
- data/lib/active_support/i18n.rb +2 -1
- data/lib/active_support/i18n_railtie.rb +21 -14
- data/lib/active_support/inflector/inflections.rb +25 -7
- data/lib/active_support/inflector/methods.rb +50 -64
- data/lib/active_support/inflector/transliterate.rb +4 -2
- data/lib/active_support/isolated_execution_state.rb +76 -0
- data/lib/active_support/json/decoding.rb +2 -1
- data/lib/active_support/json/encoding.rb +27 -45
- data/lib/active_support/key_generator.rb +31 -6
- data/lib/active_support/lazy_load_hooks.rb +33 -7
- data/lib/active_support/locale/en.yml +4 -2
- data/lib/active_support/log_subscriber/test_helper.rb +2 -2
- data/lib/active_support/log_subscriber.rb +97 -35
- data/lib/active_support/logger.rb +9 -60
- data/lib/active_support/logger_thread_safe_level.rb +11 -34
- data/lib/active_support/message_encryptor.rb +206 -56
- data/lib/active_support/message_encryptors.rb +141 -0
- data/lib/active_support/message_pack/cache_serializer.rb +23 -0
- data/lib/active_support/message_pack/extensions.rb +292 -0
- data/lib/active_support/message_pack/serializer.rb +63 -0
- data/lib/active_support/message_pack.rb +50 -0
- data/lib/active_support/message_verifier.rb +235 -84
- data/lib/active_support/message_verifiers.rb +135 -0
- data/lib/active_support/messages/codec.rb +65 -0
- data/lib/active_support/messages/metadata.rb +112 -46
- data/lib/active_support/messages/rotation_coordinator.rb +93 -0
- data/lib/active_support/messages/rotator.rb +34 -32
- data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
- data/lib/active_support/multibyte/chars.rb +12 -11
- data/lib/active_support/multibyte/unicode.rb +9 -49
- data/lib/active_support/multibyte.rb +1 -1
- data/lib/active_support/notifications/fanout.rb +304 -114
- data/lib/active_support/notifications/instrumenter.rb +117 -35
- data/lib/active_support/notifications.rb +25 -25
- data/lib/active_support/number_helper/number_converter.rb +14 -7
- data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -4
- data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +10 -6
- data/lib/active_support/number_helper/rounding_helper.rb +2 -6
- data/lib/active_support/number_helper.rb +379 -319
- data/lib/active_support/option_merger.rb +10 -18
- data/lib/active_support/ordered_hash.rb +4 -4
- data/lib/active_support/ordered_options.rb +15 -1
- data/lib/active_support/parameter_filter.rb +105 -81
- data/lib/active_support/proxy_object.rb +2 -0
- data/lib/active_support/railtie.rb +83 -21
- data/lib/active_support/reloader.rb +13 -5
- data/lib/active_support/rescuable.rb +18 -16
- data/lib/active_support/ruby_features.rb +7 -0
- data/lib/active_support/secure_compare_rotator.rb +18 -11
- data/lib/active_support/security_utils.rb +1 -1
- data/lib/active_support/string_inquirer.rb +3 -3
- data/lib/active_support/subscriber.rb +11 -40
- data/lib/active_support/syntax_error_proxy.rb +60 -0
- data/lib/active_support/tagged_logging.rb +65 -25
- data/lib/active_support/test_case.rb +166 -27
- data/lib/active_support/testing/assertions.rb +61 -15
- data/lib/active_support/testing/autorun.rb +0 -2
- data/lib/active_support/testing/constant_stubbing.rb +32 -0
- data/lib/active_support/testing/deprecation.rb +53 -2
- data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
- data/lib/active_support/testing/isolation.rb +30 -29
- data/lib/active_support/testing/method_call_assertions.rb +24 -11
- data/lib/active_support/testing/parallelization/server.rb +4 -0
- data/lib/active_support/testing/parallelization/worker.rb +3 -0
- data/lib/active_support/testing/parallelization.rb +4 -0
- data/lib/active_support/testing/parallelize_executor.rb +81 -0
- data/lib/active_support/testing/setup_and_teardown.rb +2 -0
- data/lib/active_support/testing/stream.rb +4 -6
- data/lib/active_support/testing/strict_warnings.rb +39 -0
- data/lib/active_support/testing/tagged_logging.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +49 -16
- data/lib/active_support/time_with_zone.rb +39 -28
- data/lib/active_support/values/time_zone.rb +50 -18
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/xml_mini/jdom.rb +4 -11
- data/lib/active_support/xml_mini/libxml.rb +5 -5
- data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
- data/lib/active_support/xml_mini/nokogiri.rb +5 -5
- data/lib/active_support/xml_mini/nokogirisax.rb +2 -2
- data/lib/active_support/xml_mini/rexml.rb +2 -2
- data/lib/active_support/xml_mini.rb +7 -6
- data/lib/active_support.rb +28 -1
- metadata +150 -18
- data/lib/active_support/core_ext/marshal.rb +0 -26
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -28
- data/lib/active_support/core_ext/range/overlaps.rb +0 -10
- data/lib/active_support/core_ext/uri.rb +0 -29
- data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
- data/lib/active_support/per_thread_registry.rb +0 -60
@@ -3,32 +3,32 @@
|
|
3
3
|
require "delegate"
|
4
4
|
|
5
5
|
module ActiveSupport
|
6
|
-
module Tryable
|
7
|
-
def try(
|
8
|
-
if
|
9
|
-
if
|
10
|
-
instance_eval(&
|
6
|
+
module Tryable # :nodoc:
|
7
|
+
def try(*args, &block)
|
8
|
+
if args.empty? && block_given?
|
9
|
+
if block.arity == 0
|
10
|
+
instance_eval(&block)
|
11
11
|
else
|
12
12
|
yield self
|
13
13
|
end
|
14
|
-
elsif respond_to?(
|
15
|
-
public_send(
|
14
|
+
elsif respond_to?(args.first)
|
15
|
+
public_send(*args, &block)
|
16
16
|
end
|
17
17
|
end
|
18
|
-
ruby2_keywords(:try)
|
18
|
+
ruby2_keywords(:try)
|
19
19
|
|
20
|
-
def try!(
|
21
|
-
if
|
22
|
-
if
|
23
|
-
instance_eval(&
|
20
|
+
def try!(*args, &block)
|
21
|
+
if args.empty? && block_given?
|
22
|
+
if block.arity == 0
|
23
|
+
instance_eval(&block)
|
24
24
|
else
|
25
25
|
yield self
|
26
26
|
end
|
27
27
|
else
|
28
|
-
public_send(
|
28
|
+
public_send(*args, &block)
|
29
29
|
end
|
30
30
|
end
|
31
|
-
ruby2_keywords(:try!)
|
31
|
+
ruby2_keywords(:try!)
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
@@ -39,7 +39,7 @@ class Object
|
|
39
39
|
# :method: try
|
40
40
|
#
|
41
41
|
# :call-seq:
|
42
|
-
# try(*
|
42
|
+
# try(*args, &block)
|
43
43
|
#
|
44
44
|
# Invokes the public method whose name goes as first argument just like
|
45
45
|
# +public_send+ does, except that if the receiver does not respond to it the
|
@@ -104,7 +104,7 @@ class Object
|
|
104
104
|
# :method: try!
|
105
105
|
#
|
106
106
|
# :call-seq:
|
107
|
-
# try!(*
|
107
|
+
# try!(*args, &block)
|
108
108
|
#
|
109
109
|
# Same as #try, but raises a +NoMethodError+ exception if the receiver is
|
110
110
|
# not +nil+ and does not implement the tried method.
|
@@ -121,7 +121,7 @@ class Delegator
|
|
121
121
|
# :method: try
|
122
122
|
#
|
123
123
|
# :call-seq:
|
124
|
-
# try(
|
124
|
+
# try(*args, &block)
|
125
125
|
#
|
126
126
|
# See Object#try
|
127
127
|
|
@@ -129,7 +129,7 @@ class Delegator
|
|
129
129
|
# :method: try!
|
130
130
|
#
|
131
131
|
# :call-seq:
|
132
|
-
# try!(
|
132
|
+
# try!(*args, &block)
|
133
133
|
#
|
134
134
|
# See Object#try!
|
135
135
|
end
|
@@ -145,14 +145,14 @@ class NilClass
|
|
145
145
|
#
|
146
146
|
# With +try+
|
147
147
|
# @person.try(:children).try(:first).try(:name)
|
148
|
-
def try(
|
148
|
+
def try(*)
|
149
149
|
nil
|
150
150
|
end
|
151
151
|
|
152
152
|
# Calling +try!+ on +nil+ always returns +nil+.
|
153
153
|
#
|
154
154
|
# nil.try!(:name) # => nil
|
155
|
-
def try!(
|
155
|
+
def try!(*)
|
156
156
|
nil
|
157
157
|
end
|
158
158
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Object
|
4
|
+
# Set and restore public attributes around a block.
|
5
|
+
#
|
6
|
+
# client.timeout # => 5
|
7
|
+
# client.with(timeout: 1) do
|
8
|
+
# client.timeout # => 1
|
9
|
+
# end
|
10
|
+
# client.timeout # => 5
|
11
|
+
#
|
12
|
+
# This method is a shorthand for the common begin/ensure pattern:
|
13
|
+
#
|
14
|
+
# old_value = object.attribute
|
15
|
+
# begin
|
16
|
+
# object.attribute = new_value
|
17
|
+
# # do things
|
18
|
+
# ensure
|
19
|
+
# object.attribute = old_value
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# It can be used on any object as long as both the reader and writer methods
|
23
|
+
# are public.
|
24
|
+
def with(**attributes)
|
25
|
+
old_values = {}
|
26
|
+
begin
|
27
|
+
attributes.each do |key, value|
|
28
|
+
old_values[key] = public_send(key)
|
29
|
+
public_send("#{key}=", value)
|
30
|
+
end
|
31
|
+
yield
|
32
|
+
ensure
|
33
|
+
old_values.each do |key, old_value|
|
34
|
+
public_send("#{key}=", old_value)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# #with isn't usable on immediates, so we might as well undefine the
|
41
|
+
# method in common immediate classes to avoid potential confusion.
|
42
|
+
[NilClass, TrueClass, FalseClass, Integer, Float, Symbol].each do |klass|
|
43
|
+
klass.undef_method(:with)
|
44
|
+
end
|
@@ -5,9 +5,9 @@ require "active_support/option_merger"
|
|
5
5
|
class Object
|
6
6
|
# An elegant way to factor duplication out of options passed to a series of
|
7
7
|
# method calls. Each method called in the block, with the block variable as
|
8
|
-
# the receiver, will have its options merged with the default +options+
|
9
|
-
# provided. Each method called on
|
10
|
-
# hash as its final argument.
|
8
|
+
# the receiver, will have its options merged with the default +options+
|
9
|
+
# <tt>Hash</tt> or <tt>Hash</tt>-like object provided. Each method called on
|
10
|
+
# the block variable must take an options hash as its final argument.
|
11
11
|
#
|
12
12
|
# Without <tt>with_options</tt>, this code contains duplication:
|
13
13
|
#
|
@@ -64,19 +64,38 @@ class Object
|
|
64
64
|
#
|
65
65
|
# Hence the inherited default for +if+ key is ignored.
|
66
66
|
#
|
67
|
-
# NOTE: You cannot call class methods implicitly inside of with_options
|
67
|
+
# NOTE: You cannot call class methods implicitly inside of +with_options+.
|
68
68
|
# You can access these methods using the class name instead:
|
69
69
|
#
|
70
70
|
# class Phone < ActiveRecord::Base
|
71
|
-
# enum phone_number_type
|
71
|
+
# enum :phone_number_type, { home: 0, office: 1, mobile: 2 }
|
72
72
|
#
|
73
73
|
# with_options presence: true do
|
74
74
|
# validates :phone_number_type, inclusion: { in: Phone.phone_number_types.keys }
|
75
75
|
# end
|
76
76
|
# end
|
77
77
|
#
|
78
|
+
# When the block argument is omitted, the decorated Object instance is returned:
|
79
|
+
#
|
80
|
+
# module MyStyledHelpers
|
81
|
+
# def styled
|
82
|
+
# with_options style: "color: red;"
|
83
|
+
# end
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# styled.link_to "I'm red", "/"
|
87
|
+
# # => <a href="/" style="color: red;">I'm red</a>
|
88
|
+
#
|
89
|
+
# styled.button_tag "I'm red too!"
|
90
|
+
# # => <button style="color: red;">I'm red too!</button>
|
91
|
+
#
|
78
92
|
def with_options(options, &block)
|
79
93
|
option_merger = ActiveSupport::OptionMerger.new(self, options)
|
80
|
-
|
94
|
+
|
95
|
+
if block
|
96
|
+
block.arity.zero? ? option_merger.instance_eval(&block) : block.call(option_merger)
|
97
|
+
else
|
98
|
+
option_merger
|
99
|
+
end
|
81
100
|
end
|
82
101
|
end
|
@@ -13,4 +13,5 @@ require "active_support/core_ext/object/instance_variables"
|
|
13
13
|
require "active_support/core_ext/object/json"
|
14
14
|
require "active_support/core_ext/object/to_param"
|
15
15
|
require "active_support/core_ext/object/to_query"
|
16
|
+
require "active_support/core_ext/object/with"
|
16
17
|
require "active_support/core_ext/object/with_options"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pathname"
|
4
|
+
|
5
|
+
class Pathname
|
6
|
+
# An Pathname is blank if it's empty:
|
7
|
+
#
|
8
|
+
# Pathname.new("").blank? # => true
|
9
|
+
# Pathname.new(" ").blank? # => false
|
10
|
+
# Pathname.new("test").blank? # => false
|
11
|
+
#
|
12
|
+
# @return [true, false]
|
13
|
+
def blank?
|
14
|
+
to_s.empty?
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pathname"
|
4
|
+
|
5
|
+
class Pathname
|
6
|
+
# Returns the receiver if the named file exists otherwise returns +nil+.
|
7
|
+
# <tt>pathname.existence</tt> is equivalent to
|
8
|
+
#
|
9
|
+
# pathname.exist? ? pathname : nil
|
10
|
+
#
|
11
|
+
# For example, something like
|
12
|
+
#
|
13
|
+
# content = pathname.read if pathname.exist?
|
14
|
+
#
|
15
|
+
# becomes
|
16
|
+
#
|
17
|
+
# content = pathname.existence&.read
|
18
|
+
#
|
19
|
+
# @return [Pathname]
|
20
|
+
def existence
|
21
|
+
self if exist?
|
22
|
+
end
|
23
|
+
end
|
@@ -51,31 +51,6 @@ module ActiveSupport
|
|
51
51
|
super
|
52
52
|
end
|
53
53
|
end
|
54
|
-
|
55
|
-
# Extends the default Range#cover? to support range comparisons.
|
56
|
-
# (1..5).cover?(1..5) # => true
|
57
|
-
# (1..5).cover?(2..3) # => true
|
58
|
-
# (1..5).cover?(1...6) # => true
|
59
|
-
# (1..5).cover?(2..6) # => false
|
60
|
-
#
|
61
|
-
# The native Range#cover? behavior is untouched.
|
62
|
-
# ('a'..'f').cover?('c') # => true
|
63
|
-
# (5..9).cover?(11) # => false
|
64
|
-
#
|
65
|
-
# The given range must be fully bounded, with both start and end.
|
66
|
-
def cover?(value)
|
67
|
-
if value.is_a?(::Range)
|
68
|
-
is_backwards_op = value.exclude_end? ? :>= : :>
|
69
|
-
return false if value.begin && value.end && value.begin.public_send(is_backwards_op, value.end)
|
70
|
-
# 1...10 covers 1..9 but it does not cover 1..10.
|
71
|
-
# 1..10 covers 1...11 but it does not cover 1...12.
|
72
|
-
operator = exclude_end? && !value.exclude_end? ? :< : :<=
|
73
|
-
value_max = !exclude_end? && value.exclude_end? ? value.max : value.last
|
74
|
-
super(value.first) && (self.end.nil? || value_max.public_send(operator, last))
|
75
|
-
else
|
76
|
-
super
|
77
|
-
end
|
78
|
-
end
|
79
54
|
end
|
80
55
|
end
|
81
56
|
|
@@ -1,40 +1,61 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveSupport
|
4
|
+
# = \Range With Format
|
4
5
|
module RangeWithFormat
|
5
6
|
RANGE_FORMATS = {
|
6
7
|
db: -> (start, stop) do
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
if start && stop
|
9
|
+
case start
|
10
|
+
when String then "BETWEEN '#{start}' AND '#{stop}'"
|
11
|
+
else
|
12
|
+
"BETWEEN '#{start.to_fs(:db)}' AND '#{stop.to_fs(:db)}'"
|
13
|
+
end
|
14
|
+
elsif start
|
15
|
+
case start
|
16
|
+
when String then ">= '#{start}'"
|
17
|
+
else
|
18
|
+
">= '#{start.to_fs(:db)}'"
|
19
|
+
end
|
20
|
+
elsif stop
|
21
|
+
case stop
|
22
|
+
when String then "<= '#{stop}'"
|
23
|
+
else
|
24
|
+
"<= '#{stop.to_fs(:db)}'"
|
25
|
+
end
|
11
26
|
end
|
12
27
|
end
|
13
28
|
}
|
14
29
|
|
15
30
|
# Convert range to a formatted string. See RANGE_FORMATS for predefined formats.
|
16
31
|
#
|
32
|
+
# This method is aliased to <tt>to_formatted_s</tt>.
|
33
|
+
#
|
17
34
|
# range = (1..100) # => 1..100
|
18
35
|
#
|
19
36
|
# range.to_s # => "1..100"
|
20
|
-
# range.
|
37
|
+
# range.to_fs(:db) # => "BETWEEN '1' AND '100'"
|
38
|
+
#
|
39
|
+
# range = (1..) # => 1..
|
40
|
+
# range.to_fs(:db) # => ">= '1'"
|
21
41
|
#
|
22
|
-
#
|
42
|
+
# range = (..100) # => ..100
|
43
|
+
# range.to_fs(:db) # => "<= '100'"
|
44
|
+
#
|
45
|
+
# == Adding your own range formats to to_fs
|
23
46
|
# You can add your own formats to the Range::RANGE_FORMATS hash.
|
24
47
|
# Use the format name as the hash key and a Proc instance.
|
25
48
|
#
|
26
49
|
# # config/initializers/range_formats.rb
|
27
|
-
# Range::RANGE_FORMATS[:short] = ->(start, stop) { "Between #{start.
|
28
|
-
def
|
50
|
+
# Range::RANGE_FORMATS[:short] = ->(start, stop) { "Between #{start.to_fs(:db)} and #{stop.to_fs(:db)}" }
|
51
|
+
def to_fs(format = :default)
|
29
52
|
if formatter = RANGE_FORMATS[format]
|
30
|
-
formatter.call(
|
53
|
+
formatter.call(self.begin, self.end)
|
31
54
|
else
|
32
|
-
|
55
|
+
to_s
|
33
56
|
end
|
34
57
|
end
|
35
|
-
|
36
|
-
alias_method :to_default_s, :to_s
|
37
|
-
alias_method :to_formatted_s, :to_s
|
58
|
+
alias_method :to_formatted_s, :to_fs
|
38
59
|
end
|
39
60
|
end
|
40
61
|
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Range
|
4
|
+
# Compare two ranges and see if they overlap each other
|
5
|
+
# (1..5).overlap?(4..6) # => true
|
6
|
+
# (1..5).overlap?(7..9) # => false
|
7
|
+
unless Range.method_defined?(:overlap?)
|
8
|
+
def overlap?(other)
|
9
|
+
raise TypeError unless other.is_a? Range
|
10
|
+
|
11
|
+
self_begin = self.begin
|
12
|
+
other_end = other.end
|
13
|
+
other_excl = other.exclude_end?
|
14
|
+
|
15
|
+
return false if _empty_range?(self_begin, other_end, other_excl)
|
16
|
+
|
17
|
+
other_begin = other.begin
|
18
|
+
self_end = self.end
|
19
|
+
self_excl = self.exclude_end?
|
20
|
+
|
21
|
+
return false if _empty_range?(other_begin, self_end, self_excl)
|
22
|
+
return true if self_begin == other_begin
|
23
|
+
|
24
|
+
return false if _empty_range?(self_begin, self_end, self_excl)
|
25
|
+
return false if _empty_range?(other_begin, other_end, other_excl)
|
26
|
+
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def _empty_range?(b, e, excl)
|
32
|
+
return false if b.nil? || e.nil?
|
33
|
+
|
34
|
+
comp = b <=> e
|
35
|
+
comp.nil? || comp > 0 || (comp == 0 && excl)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
alias :overlaps? :overlap?
|
40
|
+
end
|
@@ -2,6 +2,5 @@
|
|
2
2
|
|
3
3
|
require "active_support/core_ext/range/conversions"
|
4
4
|
require "active_support/core_ext/range/compare_range"
|
5
|
-
require "active_support/core_ext/range/
|
6
|
-
require "active_support/core_ext/range/overlaps"
|
5
|
+
require "active_support/core_ext/range/overlap"
|
7
6
|
require "active_support/core_ext/range/each"
|
@@ -12,16 +12,22 @@ module SecureRandom
|
|
12
12
|
#
|
13
13
|
# If _n_ is not specified or is +nil+, 16 is assumed. It may be larger in the future.
|
14
14
|
#
|
15
|
-
# 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.
|
16
16
|
#
|
17
17
|
# p SecureRandom.base58 # => "4kUgL2pdQMSCQtjE"
|
18
18
|
# p SecureRandom.base58(24) # => "77TMHrHJFvFDwodq8w7Ev2m7"
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
19
|
+
if RUBY_VERSION >= "3.3"
|
20
|
+
def self.base58(n = 16)
|
21
|
+
SecureRandom.alphanumeric(n, chars: BASE58_ALPHABET)
|
22
|
+
end
|
23
|
+
else
|
24
|
+
def self.base58(n = 16)
|
25
|
+
SecureRandom.random_bytes(n).unpack("C*").map do |byte|
|
26
|
+
idx = byte % 64
|
27
|
+
idx = SecureRandom.random_number(58) if idx >= 58
|
28
|
+
BASE58_ALPHABET[idx]
|
29
|
+
end.join
|
30
|
+
end
|
25
31
|
end
|
26
32
|
|
27
33
|
# SecureRandom.base36 generates a random base36 string in lowercase.
|
@@ -35,11 +41,17 @@ module SecureRandom
|
|
35
41
|
#
|
36
42
|
# p SecureRandom.base36 # => "4kugl2pdqmscqtje"
|
37
43
|
# p SecureRandom.base36(24) # => "77tmhrhjfvfdwodq8w7ev2m7"
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
+
if RUBY_VERSION >= "3.3"
|
45
|
+
def self.base36(n = 16)
|
46
|
+
SecureRandom.alphanumeric(n, chars: BASE36_ALPHABET)
|
47
|
+
end
|
48
|
+
else
|
49
|
+
def self.base36(n = 16)
|
50
|
+
SecureRandom.random_bytes(n).unpack("C*").map do |byte|
|
51
|
+
idx = byte % 64
|
52
|
+
idx = SecureRandom.random_number(36) if idx >= 36
|
53
|
+
BASE36_ALPHABET[idx]
|
54
|
+
end.join
|
55
|
+
end
|
44
56
|
end
|
45
57
|
end
|
@@ -5,10 +5,10 @@ require "active_support/core_ext/time/calculations"
|
|
5
5
|
|
6
6
|
class String
|
7
7
|
# Converts a string to a Time value.
|
8
|
-
# The +form+ can be either
|
8
|
+
# The +form+ can be either +:utc+ or +:local+ (default +:local+).
|
9
9
|
#
|
10
10
|
# The time is parsed using Time.parse method.
|
11
|
-
# If +form+ is
|
11
|
+
# If +form+ is +:local+, then the time is in the system timezone.
|
12
12
|
# If the date part is missing then the current date is used and if
|
13
13
|
# the time part is missing then it is assumed to be 00:00:00.
|
14
14
|
#
|
@@ -45,7 +45,7 @@ class String
|
|
45
45
|
self
|
46
46
|
end
|
47
47
|
|
48
|
-
# Truncates a given +text+
|
48
|
+
# Truncates a given +text+ to length <tt>truncate_to</tt> if +text+ is longer than <tt>truncate_to</tt>:
|
49
49
|
#
|
50
50
|
# 'Once upon a time in a world far far away'.truncate(27)
|
51
51
|
# # => "Once upon a time in a wo..."
|
@@ -58,16 +58,20 @@ class String
|
|
58
58
|
# 'Once upon a time in a world far far away'.truncate(27, separator: /\s/)
|
59
59
|
# # => "Once upon a time in a..."
|
60
60
|
#
|
61
|
-
# The last characters will be replaced with the <tt>:omission</tt> string (defaults to "...")
|
62
|
-
#
|
61
|
+
# The last characters will be replaced with the <tt>:omission</tt> string (defaults to "...").
|
62
|
+
# The total length will not exceed <tt>truncate_to</tt> unless both +text+ and <tt>:omission</tt>
|
63
|
+
# are longer than <tt>truncate_to</tt>:
|
63
64
|
#
|
64
65
|
# 'And they found that many people were sleeping better.'.truncate(25, omission: '... (continued)')
|
65
66
|
# # => "And they f... (continued)"
|
66
|
-
|
67
|
-
|
67
|
+
#
|
68
|
+
# 'And they found that many people were sleeping better.'.truncate(4, omission: '... (continued)')
|
69
|
+
# # => "... (continued)"
|
70
|
+
def truncate(truncate_to, options = {})
|
71
|
+
return dup unless length > truncate_to
|
68
72
|
|
69
73
|
omission = options[:omission] || "..."
|
70
|
-
length_with_room_for_omission =
|
74
|
+
length_with_room_for_omission = truncate_to - omission.length
|
71
75
|
stop = \
|
72
76
|
if options[:separator]
|
73
77
|
rindex(options[:separator], length_with_room_for_omission) || length_with_room_for_omission
|
@@ -78,7 +82,7 @@ class String
|
|
78
82
|
+"#{self[0, stop]}#{omission}"
|
79
83
|
end
|
80
84
|
|
81
|
-
# Truncates +text+ to at most <tt>
|
85
|
+
# Truncates +text+ to at most <tt>truncate_to</tt> bytes in length without
|
82
86
|
# breaking string encoding by splitting multibyte characters or breaking
|
83
87
|
# grapheme clusters ("perceptual characters") by truncating at combining
|
84
88
|
# characters.
|
@@ -91,22 +95,24 @@ class String
|
|
91
95
|
# => "🔪🔪🔪🔪…"
|
92
96
|
#
|
93
97
|
# The truncated text ends with the <tt>:omission</tt> string, defaulting
|
94
|
-
# to "…", for a total length not exceeding <tt>
|
95
|
-
|
98
|
+
# to "…", for a total length not exceeding <tt>truncate_to</tt>.
|
99
|
+
#
|
100
|
+
# Raises +ArgumentError+ when the bytesize of <tt>:omission</tt> exceeds <tt>truncate_to</tt>.
|
101
|
+
def truncate_bytes(truncate_to, omission: "…")
|
96
102
|
omission ||= ""
|
97
103
|
|
98
104
|
case
|
99
|
-
when bytesize <=
|
105
|
+
when bytesize <= truncate_to
|
100
106
|
dup
|
101
|
-
when omission.bytesize >
|
102
|
-
raise ArgumentError, "Omission #{omission.inspect} is #{omission.bytesize}, larger than the truncation length of #{
|
103
|
-
when omission.bytesize ==
|
107
|
+
when omission.bytesize > truncate_to
|
108
|
+
raise ArgumentError, "Omission #{omission.inspect} is #{omission.bytesize}, larger than the truncation length of #{truncate_to} bytes"
|
109
|
+
when omission.bytesize == truncate_to
|
104
110
|
omission.dup
|
105
111
|
else
|
106
112
|
self.class.new.tap do |cut|
|
107
|
-
cut_at =
|
113
|
+
cut_at = truncate_to - omission.bytesize
|
108
114
|
|
109
|
-
|
115
|
+
each_grapheme_cluster do |grapheme|
|
110
116
|
if cut.bytesize + grapheme.bytesize <= cut_at
|
111
117
|
cut << grapheme
|
112
118
|
else
|
@@ -24,7 +24,7 @@ class String
|
|
24
24
|
#
|
25
25
|
# The second argument, +indent_string+, specifies which indent string to
|
26
26
|
# use. The default is +nil+, which tells the method to make a guess by
|
27
|
-
# peeking at the first indented line, and
|
27
|
+
# peeking at the first indented line, and fall back to a space if there is
|
28
28
|
# none.
|
29
29
|
#
|
30
30
|
# " foo".indent(2) # => " foo"
|
@@ -97,8 +97,6 @@ class String
|
|
97
97
|
# 'active_record/errors'.camelize # => "ActiveRecord::Errors"
|
98
98
|
# 'active_record/errors'.camelize(:lower) # => "activeRecord::Errors"
|
99
99
|
#
|
100
|
-
# +camelize+ is also aliased as +camelcase+.
|
101
|
-
#
|
102
100
|
# See ActiveSupport::Inflector.camelize.
|
103
101
|
def camelize(first_letter = :upper)
|
104
102
|
case first_letter
|
@@ -114,7 +112,7 @@ class String
|
|
114
112
|
|
115
113
|
# Capitalizes all the words and replaces some characters in the string to create
|
116
114
|
# a nicer looking title. +titleize+ is meant for creating pretty output. It is not
|
117
|
-
# used in the Rails internals.
|
115
|
+
# used in the \Rails internals.
|
118
116
|
#
|
119
117
|
# The trailing '_id','Id'.. can be kept and capitalized by setting the
|
120
118
|
# optional parameter +keep_id_suffix+ to true.
|
@@ -124,8 +122,6 @@ class String
|
|
124
122
|
# 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
|
125
123
|
# 'string_ending_with_id'.titleize(keep_id_suffix: true) # => "String Ending With Id"
|
126
124
|
#
|
127
|
-
# +titleize+ is also aliased as +titlecase+.
|
128
|
-
#
|
129
125
|
# See ActiveSupport::Inflector.titleize.
|
130
126
|
def titleize(keep_id_suffix: false)
|
131
127
|
ActiveSupport::Inflector.titleize(self, keep_id_suffix: keep_id_suffix)
|
@@ -220,7 +216,7 @@ class String
|
|
220
216
|
ActiveSupport::Inflector.parameterize(self, separator: separator, preserve_case: preserve_case, locale: locale)
|
221
217
|
end
|
222
218
|
|
223
|
-
# Creates the name of a table like Rails does for models to table names. This method
|
219
|
+
# Creates the name of a table like \Rails does for models to table names. This method
|
224
220
|
# uses the +pluralize+ method on the last word in the string.
|
225
221
|
#
|
226
222
|
# 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
|
@@ -232,7 +228,7 @@ class String
|
|
232
228
|
ActiveSupport::Inflector.tableize(self)
|
233
229
|
end
|
234
230
|
|
235
|
-
# Creates a class name from a plural table name like Rails does for table names to models.
|
231
|
+
# Creates a class name from a plural table name like \Rails does for table names to models.
|
236
232
|
# Note that this returns a string and not a class. (To convert to an actual class
|
237
233
|
# follow +classify+ with +constantize+.)
|
238
234
|
#
|
@@ -244,7 +240,7 @@ class String
|
|
244
240
|
ActiveSupport::Inflector.classify(self)
|
245
241
|
end
|
246
242
|
|
247
|
-
# Capitalizes the first word, turns underscores into spaces, and (by default)strips a
|
243
|
+
# Capitalizes the first word, turns underscores into spaces, and (by default) strips a
|
248
244
|
# trailing '_id' if present.
|
249
245
|
# Like +titleize+, this is meant for creating pretty output.
|
250
246
|
#
|
@@ -260,14 +256,14 @@ class String
|
|
260
256
|
# 'author_id'.humanize # => "Author"
|
261
257
|
# 'author_id'.humanize(capitalize: false) # => "author"
|
262
258
|
# '_id'.humanize # => "Id"
|
263
|
-
# 'author_id'.humanize(keep_id_suffix: true) # => "Author
|
259
|
+
# 'author_id'.humanize(keep_id_suffix: true) # => "Author id"
|
264
260
|
#
|
265
261
|
# See ActiveSupport::Inflector.humanize.
|
266
262
|
def humanize(capitalize: true, keep_id_suffix: false)
|
267
263
|
ActiveSupport::Inflector.humanize(self, capitalize: capitalize, keep_id_suffix: keep_id_suffix)
|
268
264
|
end
|
269
265
|
|
270
|
-
# Converts
|
266
|
+
# Converts the first character to uppercase.
|
271
267
|
#
|
272
268
|
# 'what a Lovely Day'.upcase_first # => "What a Lovely Day"
|
273
269
|
# 'w'.upcase_first # => "W"
|
@@ -278,6 +274,17 @@ class String
|
|
278
274
|
ActiveSupport::Inflector.upcase_first(self)
|
279
275
|
end
|
280
276
|
|
277
|
+
# Converts the first character to lowercase.
|
278
|
+
#
|
279
|
+
# 'If they enjoyed The Matrix'.downcase_first # => "if they enjoyed The Matrix"
|
280
|
+
# 'I'.downcase_first # => "i"
|
281
|
+
# ''.downcase_first # => ""
|
282
|
+
#
|
283
|
+
# See ActiveSupport::Inflector.downcase_first.
|
284
|
+
def downcase_first
|
285
|
+
ActiveSupport::Inflector.downcase_first(self)
|
286
|
+
end
|
287
|
+
|
281
288
|
# Creates a foreign key name from a class name.
|
282
289
|
# +separate_class_name_and_id_with_underscore+ sets whether
|
283
290
|
# the method should put '_' between the name and 'id'.
|