activesupport 6.1.7.2 → 7.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +866 -411
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -6
- 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 +27 -7
- data/lib/active_support/benchmarkable.rb +3 -2
- data/lib/active_support/broadcast_logger.rb +250 -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 +52 -19
- data/lib/active_support/cache/mem_cache_store.rb +195 -60
- 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 +478 -247
- data/lib/active_support/callbacks.rb +227 -105
- data/lib/active_support/code_generator.rb +65 -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 +1 -1
- 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 -14
- 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 +41 -18
- 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 +15 -4
- 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 +40 -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 -193
- 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 +29 -10
- data/lib/active_support/core_ext/time/conversions.rb +14 -13
- data/lib/active_support/core_ext/time/zones.rb +12 -28
- data/lib/active_support/core_ext.rb +1 -0
- data/lib/active_support/current_attributes.rb +46 -21
- 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 +42 -25
- 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 +4 -4
- 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 +79 -49
- 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 +31 -12
- 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 -13
- 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 +38 -18
- data/lib/active_support/html_safe_translation.rb +43 -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 -63
- 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 +3 -1
- data/lib/active_support/log_subscriber/test_helper.rb +2 -2
- data/lib/active_support/log_subscriber.rb +96 -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 +109 -35
- data/lib/active_support/notifications.rb +24 -24
- 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/rounding_helper.rb +1 -5
- 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 +104 -80
- data/lib/active_support/proxy_object.rb +2 -0
- data/lib/active_support/railtie.rb +83 -21
- data/lib/active_support/reloader.rb +12 -4
- data/lib/active_support/rescuable.rb +14 -12
- data/lib/active_support/ruby_features.rb +7 -0
- data/lib/active_support/secure_compare_rotator.rb +18 -11
- 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 +70 -0
- data/lib/active_support/tagged_logging.rb +60 -24
- data/lib/active_support/test_case.rb +166 -27
- data/lib/active_support/testing/assertions.rb +60 -14
- 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/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 +38 -17
- 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 +107 -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 -120
- data/lib/active_support/per_thread_registry.rb +0 -61
@@ -5,10 +5,10 @@ class Hash
|
|
5
5
|
# This includes the values from the root hash and from all
|
6
6
|
# nested hashes and arrays.
|
7
7
|
#
|
8
|
-
#
|
8
|
+
# hash = { person: { name: 'Rob', age: '28' } }
|
9
9
|
#
|
10
|
-
#
|
11
|
-
#
|
10
|
+
# hash.deep_transform_values{ |value| value.to_s.upcase }
|
11
|
+
# # => {person: {name: "ROB", age: "28"}}
|
12
12
|
def deep_transform_values(&block)
|
13
13
|
_deep_transform_values_in_object(self, &block)
|
14
14
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require "active_support/hash_with_indifferent_access"
|
4
4
|
|
5
5
|
class Hash
|
6
|
-
# Returns an
|
6
|
+
# Returns an ActiveSupport::HashWithIndifferentAccess out of its receiver:
|
7
7
|
#
|
8
8
|
# { a: 1 }.with_indifferent_access['a'] # => 1
|
9
9
|
def with_indifferent_access
|
@@ -13,8 +13,8 @@ class Hash
|
|
13
13
|
# Called when object is nested under an object that receives
|
14
14
|
# #with_indifferent_access. This method will be called on the current object
|
15
15
|
# by the enclosing object and is aliased to #with_indifferent_access by
|
16
|
-
# default. Subclasses of Hash may
|
17
|
-
# converting to an
|
16
|
+
# default. Subclasses of Hash may override this method to return +self+ if
|
17
|
+
# converting to an ActiveSupport::HashWithIndifferentAccess would not be
|
18
18
|
# desirable.
|
19
19
|
#
|
20
20
|
# b = { b: 1 }
|
@@ -58,10 +58,10 @@ class Hash
|
|
58
58
|
# This includes the keys from the root hash and from all
|
59
59
|
# nested hashes and arrays.
|
60
60
|
#
|
61
|
-
#
|
61
|
+
# hash = { person: { name: 'Rob', age: '28' } }
|
62
62
|
#
|
63
|
-
#
|
64
|
-
#
|
63
|
+
# hash.deep_transform_keys{ |key| key.to_s.upcase }
|
64
|
+
# # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}}
|
65
65
|
def deep_transform_keys(&block)
|
66
66
|
_deep_transform_keys_in_object(self, &block)
|
67
67
|
end
|
@@ -116,7 +116,7 @@ class Hash
|
|
116
116
|
def _deep_transform_keys_in_object(object, &block)
|
117
117
|
case object
|
118
118
|
when Hash
|
119
|
-
object.each_with_object(
|
119
|
+
object.each_with_object(self.class.new) do |(key, value), result|
|
120
120
|
result[yield(key)] = _deep_transform_keys_in_object(value, &block)
|
121
121
|
end
|
122
122
|
when Array
|
@@ -6,12 +6,12 @@ class Integer
|
|
6
6
|
# Ordinalize turns a number into an ordinal string used to denote the
|
7
7
|
# position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
|
8
8
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
9
|
+
# 1.ordinalize # => "1st"
|
10
|
+
# 2.ordinalize # => "2nd"
|
11
|
+
# 1002.ordinalize # => "1002nd"
|
12
|
+
# 1003.ordinalize # => "1003rd"
|
13
|
+
# -11.ordinalize # => "-11th"
|
14
|
+
# -1001.ordinalize # => "-1001st"
|
15
15
|
def ordinalize
|
16
16
|
ActiveSupport::Inflector.ordinalize(self)
|
17
17
|
end
|
@@ -19,12 +19,12 @@ class Integer
|
|
19
19
|
# Ordinal returns the suffix used to denote the position
|
20
20
|
# in an ordered sequence such as 1st, 2nd, 3rd, 4th.
|
21
21
|
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
22
|
+
# 1.ordinal # => "st"
|
23
|
+
# 2.ordinal # => "nd"
|
24
|
+
# 1002.ordinal # => "nd"
|
25
|
+
# 1003.ordinal # => "rd"
|
26
|
+
# -11.ordinal # => "th"
|
27
|
+
# -1001.ordinal # => "st"
|
28
28
|
def ordinal
|
29
29
|
ActiveSupport::Inflector.ordinal(self)
|
30
30
|
end
|
@@ -11,14 +11,14 @@ module Kernel
|
|
11
11
|
# end
|
12
12
|
#
|
13
13
|
# noisy_call # warning voiced
|
14
|
-
def silence_warnings
|
15
|
-
with_warnings(nil)
|
14
|
+
def silence_warnings(&block)
|
15
|
+
with_warnings(nil, &block)
|
16
16
|
end
|
17
17
|
|
18
18
|
# Sets $VERBOSE to +true+ for the duration of the block and back to its
|
19
19
|
# original value afterwards.
|
20
|
-
def enable_warnings
|
21
|
-
with_warnings(true)
|
20
|
+
def enable_warnings(&block)
|
21
|
+
with_warnings(true, &block)
|
22
22
|
end
|
23
23
|
|
24
24
|
# Sets $VERBOSE for the duration of the block and back to its original
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# == Attribute Accessors
|
4
|
+
#
|
3
5
|
# Extends the module object with class/module and instance accessors for
|
4
6
|
# class/module attributes, just like the native attr* accessors for instance
|
5
7
|
# attributes.
|
@@ -41,6 +43,7 @@ class Module
|
|
41
43
|
#
|
42
44
|
# module HairColors
|
43
45
|
# mattr_reader :hair_colors, default: [:brown, :black, :blonde, :red]
|
46
|
+
# mattr_reader(:hair_styles) { [:long, :short] }
|
44
47
|
# end
|
45
48
|
#
|
46
49
|
# class Person
|
@@ -48,6 +51,7 @@ class Module
|
|
48
51
|
# end
|
49
52
|
#
|
50
53
|
# Person.new.hair_colors # => [:brown, :black, :blonde, :red]
|
54
|
+
# Person.new.hair_styles # => [:long, :short]
|
51
55
|
def mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil, location: nil)
|
52
56
|
raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class?
|
53
57
|
location ||= caller_locations(1, 1).first
|
@@ -105,6 +109,7 @@ class Module
|
|
105
109
|
#
|
106
110
|
# module HairColors
|
107
111
|
# mattr_writer :hair_colors, default: [:brown, :black, :blonde, :red]
|
112
|
+
# mattr_writer(:hair_styles) { [:long, :short] }
|
108
113
|
# end
|
109
114
|
#
|
110
115
|
# class Person
|
@@ -112,6 +117,7 @@ class Module
|
|
112
117
|
# end
|
113
118
|
#
|
114
119
|
# Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
|
120
|
+
# Person.class_variable_get("@@hair_styles") # => [:long, :short]
|
115
121
|
def mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil, location: nil)
|
116
122
|
raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class?
|
117
123
|
location ||= caller_locations(1, 1).first
|
@@ -190,6 +196,7 @@ class Module
|
|
190
196
|
#
|
191
197
|
# module HairColors
|
192
198
|
# mattr_accessor :hair_colors, default: [:brown, :black, :blonde, :red]
|
199
|
+
# mattr_accessor(:hair_styles) { [:long, :short] }
|
193
200
|
# end
|
194
201
|
#
|
195
202
|
# class Person
|
@@ -197,6 +204,7 @@ class Module
|
|
197
204
|
# end
|
198
205
|
#
|
199
206
|
# Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
|
207
|
+
# Person.class_variable_get("@@hair_styles") # => [:long, :short]
|
200
208
|
def mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil, &blk)
|
201
209
|
location = caller_locations(1, 1).first
|
202
210
|
mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default, location: location, &blk)
|
@@ -1,11 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# == Attribute Accessors per Thread
|
4
|
+
#
|
3
5
|
# Extends the module object with class/module and instance accessors for
|
4
6
|
# class/module attributes, just like the native attr* accessors for instance
|
5
7
|
# attributes, but does so on a per-thread basis.
|
6
8
|
#
|
7
9
|
# So the values are scoped within the Thread.current space under the class name
|
8
10
|
# of the module.
|
11
|
+
#
|
12
|
+
# Note that it can also be scoped per-fiber if +Rails.application.config.active_support.isolation_level+
|
13
|
+
# is set to +:fiber+.
|
9
14
|
class Module
|
10
15
|
# Defines a per-thread class attribute and creates class and instance reader methods.
|
11
16
|
# The underlying per-thread class variable is set to +nil+, if it is not previously defined.
|
@@ -14,9 +19,9 @@ class Module
|
|
14
19
|
# thread_mattr_reader :user
|
15
20
|
# end
|
16
21
|
#
|
17
|
-
# Current.user
|
18
|
-
# Thread.current[:attr_Current_user] = "DHH"
|
22
|
+
# Current.user = "DHH"
|
19
23
|
# Current.user # => "DHH"
|
24
|
+
# Thread.new { Current.user }.value # => nil
|
20
25
|
#
|
21
26
|
# The attribute name must be a valid method name in Ruby.
|
22
27
|
#
|
@@ -37,13 +42,32 @@ class Module
|
|
37
42
|
syms.each do |sym|
|
38
43
|
raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
|
39
44
|
|
40
|
-
# The following generated method concatenates `
|
41
|
-
# to
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
45
|
+
# The following generated method concatenates `object_id` because we want
|
46
|
+
# subclasses to maintain independent values.
|
47
|
+
if default.nil?
|
48
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
49
|
+
def self.#{sym}
|
50
|
+
@__thread_mattr_#{sym} ||= "attr_#{sym}_\#{object_id}"
|
51
|
+
::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}]
|
52
|
+
end
|
53
|
+
EOS
|
54
|
+
else
|
55
|
+
default = default.dup.freeze unless default.frozen?
|
56
|
+
singleton_class.define_method("#{sym}_default_value") { default }
|
57
|
+
|
58
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
59
|
+
def self.#{sym}
|
60
|
+
@__thread_mattr_#{sym} ||= "attr_#{sym}_\#{object_id}"
|
61
|
+
value = ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}]
|
62
|
+
|
63
|
+
if value.nil? && !::ActiveSupport::IsolatedExecutionState.key?(@__thread_mattr_#{sym})
|
64
|
+
::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}] = #{sym}_default_value
|
65
|
+
else
|
66
|
+
value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
EOS
|
70
|
+
end
|
47
71
|
|
48
72
|
if instance_reader && instance_accessor
|
49
73
|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
@@ -52,8 +76,6 @@ class Module
|
|
52
76
|
end
|
53
77
|
EOS
|
54
78
|
end
|
55
|
-
|
56
|
-
Thread.current["attr_" + name + "_#{sym}"] = default unless default.nil?
|
57
79
|
end
|
58
80
|
end
|
59
81
|
alias :thread_cattr_reader :thread_mattr_reader
|
@@ -76,15 +98,16 @@ class Module
|
|
76
98
|
# end
|
77
99
|
#
|
78
100
|
# Current.new.user = "DHH" # => NoMethodError
|
79
|
-
def thread_mattr_writer(*syms, instance_writer: true, instance_accessor: true
|
101
|
+
def thread_mattr_writer(*syms, instance_writer: true, instance_accessor: true) # :nodoc:
|
80
102
|
syms.each do |sym|
|
81
103
|
raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
|
82
104
|
|
83
|
-
# The following generated method concatenates `
|
84
|
-
# to
|
105
|
+
# The following generated method concatenates `object_id` because we want
|
106
|
+
# subclasses to maintain independent values.
|
85
107
|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
86
108
|
def self.#{sym}=(obj)
|
87
|
-
|
109
|
+
@__thread_mattr_#{sym} ||= "attr_#{sym}_\#{object_id}"
|
110
|
+
::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}] = obj
|
88
111
|
end
|
89
112
|
EOS
|
90
113
|
|
@@ -95,8 +118,6 @@ class Module
|
|
95
118
|
end
|
96
119
|
EOS
|
97
120
|
end
|
98
|
-
|
99
|
-
public_send("#{sym}=", default) unless default.nil?
|
100
121
|
end
|
101
122
|
end
|
102
123
|
alias :thread_cattr_writer :thread_mattr_writer
|
@@ -111,16 +132,18 @@ class Module
|
|
111
132
|
# Account.user # => "DHH"
|
112
133
|
# Account.new.user # => "DHH"
|
113
134
|
#
|
135
|
+
# Unlike +mattr_accessor+, values are *not* shared with subclasses or parent classes.
|
114
136
|
# If a subclass changes the value, the parent class' value is not changed.
|
115
|
-
#
|
116
|
-
# is not changed.
|
137
|
+
# If the parent class changes the value, the value of subclasses is not changed.
|
117
138
|
#
|
118
139
|
# class Customer < Account
|
119
140
|
# end
|
120
141
|
#
|
121
|
-
#
|
122
|
-
# Customer.user
|
123
|
-
#
|
142
|
+
# Account.user # => "DHH"
|
143
|
+
# Customer.user # => nil
|
144
|
+
# Customer.user = "Rafael"
|
145
|
+
# Customer.user # => "Rafael"
|
146
|
+
# Account.user # => "DHH"
|
124
147
|
#
|
125
148
|
# To omit the instance writer method, pass <tt>instance_writer: false</tt>.
|
126
149
|
# To omit the instance reader method, pass <tt>instance_reader: false</tt>.
|
@@ -140,6 +163,10 @@ class Module
|
|
140
163
|
#
|
141
164
|
# Current.new.user = "DHH" # => NoMethodError
|
142
165
|
# Current.new.user # => NoMethodError
|
166
|
+
#
|
167
|
+
# A default value may be specified using the +:default+ option. Because
|
168
|
+
# multiple threads can access the default value, non-frozen default values
|
169
|
+
# will be <tt>dup</tt>ed and frozen.
|
143
170
|
def thread_mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil)
|
144
171
|
thread_mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default)
|
145
172
|
thread_mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require "active_support/concern"
|
4
4
|
|
5
5
|
class Module
|
6
|
-
#
|
6
|
+
# == Bite-sized separation of concerns
|
7
7
|
#
|
8
8
|
# We often find ourselves with a medium-sized chunk of behavior that we'd
|
9
9
|
# like to extract, but only mix in to a single class.
|
@@ -18,9 +18,9 @@ class Module
|
|
18
18
|
# with a comment, as a least-bad alternative. Using modules in separate files
|
19
19
|
# means tedious sifting to get a big-picture view.
|
20
20
|
#
|
21
|
-
#
|
21
|
+
# == Dissatisfying ways to separate small concerns
|
22
22
|
#
|
23
|
-
#
|
23
|
+
# === Using comments:
|
24
24
|
#
|
25
25
|
# class Todo < ApplicationRecord
|
26
26
|
# # Other todo implementation
|
@@ -37,7 +37,7 @@ class Module
|
|
37
37
|
# end
|
38
38
|
# end
|
39
39
|
#
|
40
|
-
#
|
40
|
+
# === With an inline module:
|
41
41
|
#
|
42
42
|
# Noisy syntax.
|
43
43
|
#
|
@@ -61,7 +61,7 @@ class Module
|
|
61
61
|
# include EventTracking
|
62
62
|
# end
|
63
63
|
#
|
64
|
-
#
|
64
|
+
# === Mix-in noise exiled to its own file:
|
65
65
|
#
|
66
66
|
# Once our chunk of behavior starts pushing the scroll-to-understand-it
|
67
67
|
# boundary, we give in and move it to a separate file. At this size, the
|
@@ -75,7 +75,7 @@ class Module
|
|
75
75
|
# include TodoEventTracking
|
76
76
|
# end
|
77
77
|
#
|
78
|
-
#
|
78
|
+
# == Introducing Module#concerning
|
79
79
|
#
|
80
80
|
# By quieting the mix-in noise, we arrive at a natural, low-ceremony way to
|
81
81
|
# separate bite-sized concerns.
|
@@ -7,9 +7,9 @@ class Module
|
|
7
7
|
# option is not used.
|
8
8
|
class DelegationError < NoMethodError; end
|
9
9
|
|
10
|
-
RUBY_RESERVED_KEYWORDS = %w(alias and BEGIN begin break
|
11
|
-
else elsif END end ensure false for if in module next nil
|
12
|
-
return self super then true undef unless until when while yield)
|
10
|
+
RUBY_RESERVED_KEYWORDS = %w(__ENCODING__ __LINE__ __FILE__ alias and BEGIN begin break
|
11
|
+
case class def defined? do else elsif END end ensure false for if in module next nil
|
12
|
+
not or redo rescue retry return self super then true undef unless until when while yield)
|
13
13
|
DELEGATION_RESERVED_KEYWORDS = %w(_ arg args block)
|
14
14
|
DELEGATION_RESERVED_METHOD_NAMES = Set.new(
|
15
15
|
RUBY_RESERVED_KEYWORDS + DELEGATION_RESERVED_KEYWORDS
|
@@ -187,25 +187,49 @@ class Module
|
|
187
187
|
location = caller_locations(1, 1).first
|
188
188
|
file, line = location.path, location.lineno
|
189
189
|
|
190
|
-
|
191
|
-
|
190
|
+
receiver = to.to_s
|
191
|
+
receiver = "self.#{receiver}" if DELEGATION_RESERVED_METHOD_NAMES.include?(receiver)
|
192
192
|
|
193
193
|
method_def = []
|
194
194
|
method_names = []
|
195
195
|
|
196
|
-
|
196
|
+
method_def << "self.private" if private
|
197
|
+
|
198
|
+
methods.each do |method|
|
197
199
|
method_name = prefix ? "#{method_prefix}#{method}" : method
|
198
200
|
method_names << method_name.to_sym
|
199
201
|
|
200
202
|
# Attribute writer methods only accept one argument. Makes sure []=
|
201
203
|
# methods still accept two arguments.
|
202
|
-
definition =
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
204
|
+
definition = \
|
205
|
+
if /[^\]]=\z/.match?(method)
|
206
|
+
"arg"
|
207
|
+
else
|
208
|
+
method_object =
|
209
|
+
begin
|
210
|
+
if to.is_a?(Module)
|
211
|
+
to.method(method)
|
212
|
+
elsif receiver == "self.class"
|
213
|
+
method(method)
|
214
|
+
end
|
215
|
+
rescue NameError
|
216
|
+
# Do nothing. Fall back to `"..."`
|
217
|
+
end
|
218
|
+
|
219
|
+
if method_object
|
220
|
+
parameters = method_object.parameters
|
221
|
+
|
222
|
+
if (parameters.map(&:first) & [:opt, :rest, :keyreq, :key, :keyrest]).any?
|
223
|
+
"..."
|
224
|
+
else
|
225
|
+
defn = parameters.filter_map { |type, arg| arg if type == :req }
|
226
|
+
defn << "&block"
|
227
|
+
defn.join(", ")
|
228
|
+
end
|
229
|
+
else
|
230
|
+
"..."
|
231
|
+
end
|
232
|
+
end
|
209
233
|
|
210
234
|
# The following generated method calls the target exactly once, storing
|
211
235
|
# the returned value in a dummy variable.
|
@@ -219,7 +243,7 @@ class Module
|
|
219
243
|
|
220
244
|
method_def <<
|
221
245
|
"def #{method_name}(#{definition})" <<
|
222
|
-
" _ = #{
|
246
|
+
" _ = #{receiver}" <<
|
223
247
|
" if !_.nil? || nil.respond_to?(:#{method})" <<
|
224
248
|
" _.#{method}(#{definition})" <<
|
225
249
|
" end" <<
|
@@ -230,11 +254,11 @@ class Module
|
|
230
254
|
|
231
255
|
method_def <<
|
232
256
|
"def #{method_name}(#{definition})" <<
|
233
|
-
" _ = #{
|
257
|
+
" _ = #{receiver}" <<
|
234
258
|
" _.#{method}(#{definition})" <<
|
235
259
|
"rescue NoMethodError => e" <<
|
236
260
|
" if _.nil? && e.name == :#{method}" <<
|
237
|
-
%( raise DelegationError, "#{self}##{method_name} delegated to #{
|
261
|
+
%( raise DelegationError, "#{self}##{method_name} delegated to #{receiver}.#{method}, but #{receiver} is nil: \#{self.inspect}") <<
|
238
262
|
" else" <<
|
239
263
|
" raise" <<
|
240
264
|
" end" <<
|
@@ -242,7 +266,6 @@ class Module
|
|
242
266
|
end
|
243
267
|
end
|
244
268
|
module_eval(method_def.join(";"), file, line)
|
245
|
-
private(*method_names) if private
|
246
269
|
method_names
|
247
270
|
end
|
248
271
|
|
@@ -324,7 +347,7 @@ class Module
|
|
324
347
|
end
|
325
348
|
end
|
326
349
|
end
|
327
|
-
ruby2_keywords(:method_missing)
|
350
|
+
ruby2_keywords(:method_missing)
|
328
351
|
RUBY
|
329
352
|
end
|
330
353
|
end
|
@@ -1,17 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Module
|
4
|
-
# deprecate :foo
|
5
|
-
# deprecate bar:
|
6
|
-
# deprecate :foo, :bar, baz: 'warning!', qux: 'gone!'
|
4
|
+
# deprecate :foo, deprecator: MyLib.deprecator
|
5
|
+
# deprecate :foo, bar: "warning!", deprecator: MyLib.deprecator
|
7
6
|
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# deprecate :foo, bar: "warning!", deprecator: MyLib::Deprecator.new
|
12
|
-
#
|
13
|
-
# \Custom deprecators must respond to <tt>deprecation_warning(deprecated_method_name, message, caller_backtrace)</tt>
|
14
|
-
# method where you can implement your custom warning behavior.
|
7
|
+
# A deprecator is typically an instance of ActiveSupport::Deprecation, but you can also pass any object that responds
|
8
|
+
# to <tt>deprecation_warning(deprecated_method_name, message, caller_backtrace)</tt> where you can implement your
|
9
|
+
# custom warning behavior.
|
15
10
|
#
|
16
11
|
# class MyLib::Deprecator
|
17
12
|
# def deprecation_warning(deprecated_method_name, message, caller_backtrace = nil)
|
@@ -19,7 +14,15 @@ class Module
|
|
19
14
|
# Kernel.warn message
|
20
15
|
# end
|
21
16
|
# end
|
22
|
-
def deprecate(*method_names)
|
23
|
-
ActiveSupport::Deprecation
|
17
|
+
def deprecate(*method_names, deprecator: nil, **options)
|
18
|
+
if deprecator.is_a?(ActiveSupport::Deprecation)
|
19
|
+
deprecator.deprecate_methods(self, *method_names, **options)
|
20
|
+
elsif deprecator
|
21
|
+
# we just need any instance to call deprecate_methods, but the deprecation will be emitted by deprecator
|
22
|
+
ActiveSupport.deprecator.deprecate_methods(self, *method_names, **options, deprecator: deprecator)
|
23
|
+
else
|
24
|
+
ActiveSupport.deprecator.warn("Module.deprecate without a deprecator is deprecated")
|
25
|
+
ActiveSupport::Deprecation._instance.deprecate_methods(self, *method_names, **options)
|
26
|
+
end
|
24
27
|
end
|
25
28
|
end
|
@@ -53,13 +53,7 @@ class NameError
|
|
53
53
|
UNBOUND_METHOD_MODULE_NAME = Module.instance_method(:name)
|
54
54
|
private_constant :UNBOUND_METHOD_MODULE_NAME
|
55
55
|
|
56
|
-
|
57
|
-
|
58
|
-
UNBOUND_METHOD_MODULE_NAME.bind_call(mod)
|
59
|
-
end
|
60
|
-
else
|
61
|
-
def real_mod_name(mod)
|
62
|
-
UNBOUND_METHOD_MODULE_NAME.bind(mod).call
|
63
|
-
end
|
56
|
+
def real_mod_name(mod)
|
57
|
+
UNBOUND_METHOD_MODULE_NAME.bind_call(mod)
|
64
58
|
end
|
65
59
|
end
|
@@ -7,6 +7,7 @@ class Numeric
|
|
7
7
|
TERABYTE = GIGABYTE * 1024
|
8
8
|
PETABYTE = TERABYTE * 1024
|
9
9
|
EXABYTE = PETABYTE * 1024
|
10
|
+
ZETTABYTE = EXABYTE * 1024
|
10
11
|
|
11
12
|
# Enables the use of byte calculations and declarations, like 45.bytes + 2.6.megabytes
|
12
13
|
#
|
@@ -63,4 +64,12 @@ class Numeric
|
|
63
64
|
self * EXABYTE
|
64
65
|
end
|
65
66
|
alias :exabyte :exabytes
|
67
|
+
|
68
|
+
# Returns the number of bytes equivalent to the zettabytes provided.
|
69
|
+
#
|
70
|
+
# 2.zettabytes # => 2_361_183_241_434_822_606_848
|
71
|
+
def zettabytes
|
72
|
+
self * ZETTABYTE
|
73
|
+
end
|
74
|
+
alias :zettabyte :zettabytes
|
66
75
|
end
|