activesupport 5.1.7 → 6.1.7
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 +434 -490
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -5
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/all.rb +2 -0
- data/lib/active_support/array_inquirer.rb +6 -2
- data/lib/active_support/backtrace_cleaner.rb +31 -3
- data/lib/active_support/benchmarkable.rb +3 -1
- data/lib/active_support/builder.rb +2 -0
- data/lib/active_support/cache/file_store.rb +37 -36
- data/lib/active_support/cache/mem_cache_store.rb +72 -56
- data/lib/active_support/cache/memory_store.rb +61 -33
- data/lib/active_support/cache/null_store.rb +10 -3
- data/lib/active_support/cache/redis_cache_store.rb +493 -0
- data/lib/active_support/cache/strategy/local_cache.rb +67 -21
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +2 -0
- data/lib/active_support/cache.rb +310 -126
- data/lib/active_support/callbacks.rb +106 -100
- data/lib/active_support/concern.rb +79 -6
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
- data/lib/active_support/concurrency/share_lock.rb +2 -1
- data/lib/active_support/configurable.rb +12 -14
- data/lib/active_support/configuration_file.rb +51 -0
- data/lib/active_support/core_ext/array/access.rb +21 -7
- data/lib/active_support/core_ext/array/conversions.rb +7 -5
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- data/lib/active_support/core_ext/array/extract_options.rb +2 -0
- data/lib/active_support/core_ext/array/grouping.rb +2 -0
- data/lib/active_support/core_ext/array/inquiry.rb +2 -0
- data/lib/active_support/core_ext/array/wrap.rb +2 -0
- data/lib/active_support/core_ext/array.rb +3 -1
- data/lib/active_support/core_ext/benchmark.rb +4 -2
- data/lib/active_support/core_ext/big_decimal/conversions.rb +2 -0
- data/lib/active_support/core_ext/big_decimal.rb +2 -0
- data/lib/active_support/core_ext/class/attribute.rb +50 -47
- data/lib/active_support/core_ext/class/attribute_accessors.rb +2 -0
- data/lib/active_support/core_ext/class/subclasses.rb +18 -40
- data/lib/active_support/core_ext/class.rb +2 -0
- data/lib/active_support/core_ext/date/acts_like.rb +2 -0
- data/lib/active_support/core_ext/date/blank.rb +2 -0
- data/lib/active_support/core_ext/date/calculations.rb +8 -5
- data/lib/active_support/core_ext/date/conversions.rb +12 -10
- data/lib/active_support/core_ext/date/zones.rb +2 -0
- data/lib/active_support/core_ext/date.rb +2 -0
- data/lib/active_support/core_ext/date_and_time/calculations.rb +61 -37
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -1
- data/lib/active_support/core_ext/date_and_time/zones.rb +2 -1
- data/lib/active_support/core_ext/date_time/acts_like.rb +2 -0
- data/lib/active_support/core_ext/date_time/blank.rb +2 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +3 -1
- data/lib/active_support/core_ext/date_time/compatibility.rb +7 -5
- data/lib/active_support/core_ext/date_time/conversions.rb +2 -1
- data/lib/active_support/core_ext/date_time.rb +2 -0
- data/lib/active_support/core_ext/digest/uuid.rb +4 -1
- data/lib/active_support/core_ext/digest.rb +3 -0
- data/lib/active_support/core_ext/enumerable.rb +174 -71
- data/lib/active_support/core_ext/file/atomic.rb +3 -1
- data/lib/active_support/core_ext/file.rb +2 -0
- data/lib/active_support/core_ext/hash/conversions.rb +7 -5
- data/lib/active_support/core_ext/hash/deep_merge.rb +8 -12
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +4 -2
- data/lib/active_support/core_ext/hash/indifferent_access.rb +2 -0
- data/lib/active_support/core_ext/hash/keys.rb +3 -30
- data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
- data/lib/active_support/core_ext/hash/slice.rb +8 -29
- data/lib/active_support/core_ext/hash.rb +3 -2
- data/lib/active_support/core_ext/integer/inflections.rb +2 -0
- data/lib/active_support/core_ext/integer/multiple.rb +3 -1
- data/lib/active_support/core_ext/integer/time.rb +7 -14
- data/lib/active_support/core_ext/integer.rb +2 -0
- data/lib/active_support/core_ext/kernel/concern.rb +2 -0
- data/lib/active_support/core_ext/kernel/reporting.rb +2 -0
- data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
- data/lib/active_support/core_ext/kernel.rb +2 -1
- data/lib/active_support/core_ext/load_error.rb +3 -8
- data/lib/active_support/core_ext/marshal.rb +4 -0
- data/lib/active_support/core_ext/module/aliasing.rb +2 -0
- data/lib/active_support/core_ext/module/anonymous.rb +2 -0
- data/lib/active_support/core_ext/module/attr_internal.rb +4 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +44 -56
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +18 -18
- data/lib/active_support/core_ext/module/concerning.rb +15 -10
- data/lib/active_support/core_ext/module/delegation.rb +103 -58
- data/lib/active_support/core_ext/module/deprecation.rb +2 -0
- data/lib/active_support/core_ext/module/introspection.rb +18 -15
- data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
- data/lib/active_support/core_ext/module/remove_method.rb +5 -23
- data/lib/active_support/core_ext/module.rb +3 -1
- data/lib/active_support/core_ext/name_error.rb +36 -2
- data/lib/active_support/core_ext/numeric/bytes.rb +2 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +131 -129
- data/lib/active_support/core_ext/numeric/time.rb +7 -15
- data/lib/active_support/core_ext/numeric.rb +2 -1
- data/lib/active_support/core_ext/object/acts_like.rb +12 -1
- data/lib/active_support/core_ext/object/blank.rb +13 -3
- data/lib/active_support/core_ext/object/conversions.rb +2 -0
- data/lib/active_support/core_ext/object/deep_dup.rb +3 -1
- data/lib/active_support/core_ext/object/duplicable.rb +9 -114
- data/lib/active_support/core_ext/object/inclusion.rb +2 -0
- data/lib/active_support/core_ext/object/instance_variables.rb +2 -0
- data/lib/active_support/core_ext/object/json.rb +22 -2
- data/lib/active_support/core_ext/object/to_param.rb +2 -0
- data/lib/active_support/core_ext/object/to_query.rb +2 -0
- data/lib/active_support/core_ext/object/try.rb +19 -7
- data/lib/active_support/core_ext/object/with_options.rb +4 -2
- data/lib/active_support/core_ext/object.rb +2 -0
- data/lib/active_support/core_ext/range/compare_range.rb +82 -0
- data/lib/active_support/core_ext/range/conversions.rb +35 -25
- data/lib/active_support/core_ext/range/each.rb +5 -2
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +28 -0
- data/lib/active_support/core_ext/range/overlaps.rb +2 -0
- data/lib/active_support/core_ext/range.rb +4 -1
- data/lib/active_support/core_ext/regexp.rb +10 -5
- data/lib/active_support/core_ext/securerandom.rb +25 -3
- data/lib/active_support/core_ext/string/access.rb +7 -16
- data/lib/active_support/core_ext/string/behavior.rb +2 -0
- data/lib/active_support/core_ext/string/conversions.rb +3 -0
- data/lib/active_support/core_ext/string/exclude.rb +2 -0
- data/lib/active_support/core_ext/string/filters.rb +44 -1
- data/lib/active_support/core_ext/string/indent.rb +2 -0
- data/lib/active_support/core_ext/string/inflections.rb +69 -16
- data/lib/active_support/core_ext/string/inquiry.rb +3 -0
- data/lib/active_support/core_ext/string/multibyte.rb +9 -4
- data/lib/active_support/core_ext/string/output_safety.rb +104 -20
- data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
- data/lib/active_support/core_ext/string/strip.rb +5 -1
- data/lib/active_support/core_ext/string/zones.rb +2 -0
- data/lib/active_support/core_ext/string.rb +2 -0
- 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/acts_like.rb +2 -0
- data/lib/active_support/core_ext/time/calculations.rb +76 -18
- data/lib/active_support/core_ext/time/compatibility.rb +4 -2
- data/lib/active_support/core_ext/time/conversions.rb +4 -0
- data/lib/active_support/core_ext/time/zones.rb +6 -4
- data/lib/active_support/core_ext/time.rb +2 -0
- data/lib/active_support/core_ext/uri.rb +11 -6
- data/lib/active_support/core_ext.rb +3 -1
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +210 -0
- data/lib/active_support/dependencies/autoload.rb +2 -0
- data/lib/active_support/dependencies/interlock.rb +2 -0
- data/lib/active_support/dependencies/zeitwerk_integration.rb +120 -0
- data/lib/active_support/dependencies.rb +134 -60
- data/lib/active_support/deprecation/behaviors.rb +43 -11
- data/lib/active_support/deprecation/constant_accessor.rb +4 -2
- data/lib/active_support/deprecation/disallowed.rb +56 -0
- data/lib/active_support/deprecation/instance_delegator.rb +2 -1
- data/lib/active_support/deprecation/method_wrappers.rb +29 -21
- data/lib/active_support/deprecation/proxy_wrappers.rb +32 -6
- data/lib/active_support/deprecation/reporting.rb +54 -9
- data/lib/active_support/deprecation.rb +9 -2
- data/lib/active_support/descendants_tracker.rb +61 -9
- data/lib/active_support/digest.rb +22 -0
- data/lib/active_support/duration/iso8601_parser.rb +6 -6
- data/lib/active_support/duration/iso8601_serializer.rb +20 -14
- data/lib/active_support/duration.rb +102 -45
- data/lib/active_support/encrypted_configuration.rb +45 -0
- data/lib/active_support/encrypted_file.rb +117 -0
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/evented_file_update_checker.rb +84 -117
- data/lib/active_support/execution_wrapper.rb +19 -13
- data/lib/active_support/executor.rb +2 -0
- data/lib/active_support/file_update_checker.rb +2 -1
- data/lib/active_support/fork_tracker.rb +64 -0
- data/lib/active_support/gem_version.rb +3 -1
- data/lib/active_support/gzip.rb +2 -0
- data/lib/active_support/hash_with_indifferent_access.rb +123 -41
- data/lib/active_support/i18n.rb +4 -1
- data/lib/active_support/i18n_railtie.rb +19 -14
- data/lib/active_support/inflections.rb +2 -0
- data/lib/active_support/inflector/inflections.rb +19 -8
- data/lib/active_support/inflector/methods.rb +87 -77
- data/lib/active_support/inflector/transliterate.rb +56 -18
- data/lib/active_support/inflector.rb +2 -0
- data/lib/active_support/json/decoding.rb +27 -26
- data/lib/active_support/json/encoding.rb +13 -3
- data/lib/active_support/json.rb +2 -0
- data/lib/active_support/key_generator.rb +3 -33
- data/lib/active_support/lazy_load_hooks.rb +7 -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/test_helper.rb +2 -0
- data/lib/active_support/log_subscriber.rb +42 -11
- data/lib/active_support/logger.rb +4 -17
- data/lib/active_support/logger_silence.rb +13 -20
- data/lib/active_support/logger_thread_safe_level.rb +54 -7
- data/lib/active_support/message_encryptor.rb +100 -32
- data/lib/active_support/message_verifier.rb +85 -14
- data/lib/active_support/messages/metadata.rb +80 -0
- data/lib/active_support/messages/rotation_configuration.rb +23 -0
- data/lib/active_support/messages/rotator.rb +57 -0
- data/lib/active_support/multibyte/chars.rb +12 -68
- data/lib/active_support/multibyte/unicode.rb +17 -327
- data/lib/active_support/multibyte.rb +2 -0
- data/lib/active_support/notifications/fanout.rb +118 -16
- data/lib/active_support/notifications/instrumenter.rb +73 -9
- data/lib/active_support/notifications.rb +74 -8
- data/lib/active_support/number_helper/number_converter.rb +7 -6
- data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -9
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +5 -2
- data/lib/active_support/number_helper/number_to_human_converter.rb +6 -3
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +6 -3
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +5 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +5 -2
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +14 -27
- data/lib/active_support/number_helper/rounding_helper.rb +16 -30
- data/lib/active_support/number_helper.rb +40 -12
- data/lib/active_support/option_merger.rb +24 -3
- data/lib/active_support/ordered_hash.rb +3 -1
- data/lib/active_support/ordered_options.rb +17 -5
- data/lib/active_support/parameter_filter.rb +133 -0
- data/lib/active_support/per_thread_registry.rb +4 -1
- data/lib/active_support/proxy_object.rb +2 -0
- data/lib/active_support/rails.rb +3 -10
- data/lib/active_support/railtie.rb +60 -9
- data/lib/active_support/reloader.rb +12 -11
- data/lib/active_support/rescuable.rb +7 -6
- data/lib/active_support/secure_compare_rotator.rb +51 -0
- data/lib/active_support/security_utils.rb +26 -15
- data/lib/active_support/string_inquirer.rb +6 -3
- data/lib/active_support/subscriber.rb +74 -24
- data/lib/active_support/tagged_logging.rb +44 -8
- data/lib/active_support/test_case.rb +94 -2
- data/lib/active_support/testing/assertions.rb +58 -20
- data/lib/active_support/testing/autorun.rb +2 -0
- data/lib/active_support/testing/constant_lookup.rb +2 -0
- data/lib/active_support/testing/declarative.rb +2 -0
- data/lib/active_support/testing/deprecation.rb +2 -1
- data/lib/active_support/testing/file_fixtures.rb +4 -0
- data/lib/active_support/testing/isolation.rb +4 -2
- data/lib/active_support/testing/method_call_assertions.rb +30 -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 +12 -7
- data/lib/active_support/testing/stream.rb +3 -2
- data/lib/active_support/testing/tagged_logging.rb +2 -0
- data/lib/active_support/testing/time_helpers.rb +78 -13
- data/lib/active_support/time.rb +2 -0
- data/lib/active_support/time_with_zone.rb +113 -41
- data/lib/active_support/values/time_zone.rb +54 -25
- data/lib/active_support/version.rb +2 -0
- data/lib/active_support/xml_mini/jdom.rb +5 -4
- data/lib/active_support/xml_mini/libxml.rb +4 -2
- data/lib/active_support/xml_mini/libxmlsax.rb +6 -4
- data/lib/active_support/xml_mini/nokogiri.rb +4 -2
- data/lib/active_support/xml_mini/nokogirisax.rb +5 -3
- data/lib/active_support/xml_mini/rexml.rb +12 -3
- data/lib/active_support/xml_mini.rb +5 -11
- data/lib/active_support.rb +18 -13
- metadata +71 -32
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
- data/lib/active_support/core_ext/hash/compact.rb +0 -27
- data/lib/active_support/core_ext/hash/transform_values.rb +0 -30
- data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
- data/lib/active_support/core_ext/module/reachable.rb +0 -8
- data/lib/active_support/core_ext/numeric/inquiry.rb +0 -26
- data/lib/active_support/core_ext/range/include_range.rb +0 -23
- data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
require "active_support/core_ext/regexp"
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
3
|
# Extends the module object with class/module and instance accessors for
|
5
4
|
# class/module attributes, just like the native attr* accessors for instance
|
@@ -26,7 +25,7 @@ class Module
|
|
26
25
|
# end
|
27
26
|
# # => NameError: invalid attribute name: 1_Badname
|
28
27
|
#
|
29
|
-
#
|
28
|
+
# To omit the instance reader method, pass
|
30
29
|
# <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
|
31
30
|
#
|
32
31
|
# class Current
|
@@ -34,9 +33,7 @@ class Module
|
|
34
33
|
# end
|
35
34
|
#
|
36
35
|
# Current.new.user # => NoMethodError
|
37
|
-
def thread_mattr_reader(*syms) # :nodoc:
|
38
|
-
options = syms.extract_options!
|
39
|
-
|
36
|
+
def thread_mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil) # :nodoc:
|
40
37
|
syms.each do |sym|
|
41
38
|
raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
|
42
39
|
|
@@ -48,13 +45,15 @@ class Module
|
|
48
45
|
end
|
49
46
|
EOS
|
50
47
|
|
51
|
-
|
48
|
+
if instance_reader && instance_accessor
|
52
49
|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
53
50
|
def #{sym}
|
54
51
|
self.class.#{sym}
|
55
52
|
end
|
56
53
|
EOS
|
57
54
|
end
|
55
|
+
|
56
|
+
Thread.current["attr_" + name + "_#{sym}"] = default unless default.nil?
|
58
57
|
end
|
59
58
|
end
|
60
59
|
alias :thread_cattr_reader :thread_mattr_reader
|
@@ -69,7 +68,7 @@ class Module
|
|
69
68
|
# Current.user = "DHH"
|
70
69
|
# Thread.current[:attr_Current_user] # => "DHH"
|
71
70
|
#
|
72
|
-
#
|
71
|
+
# To omit the instance writer method, pass
|
73
72
|
# <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
|
74
73
|
#
|
75
74
|
# class Current
|
@@ -77,8 +76,7 @@ class Module
|
|
77
76
|
# end
|
78
77
|
#
|
79
78
|
# Current.new.user = "DHH" # => NoMethodError
|
80
|
-
def thread_mattr_writer(*syms) # :nodoc:
|
81
|
-
options = syms.extract_options!
|
79
|
+
def thread_mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil) # :nodoc:
|
82
80
|
syms.each do |sym|
|
83
81
|
raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
|
84
82
|
|
@@ -90,13 +88,15 @@ class Module
|
|
90
88
|
end
|
91
89
|
EOS
|
92
90
|
|
93
|
-
|
91
|
+
if instance_writer && instance_accessor
|
94
92
|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
95
93
|
def #{sym}=(obj)
|
96
94
|
self.class.#{sym} = obj
|
97
95
|
end
|
98
96
|
EOS
|
99
97
|
end
|
98
|
+
|
99
|
+
public_send("#{sym}=", default) unless default.nil?
|
100
100
|
end
|
101
101
|
end
|
102
102
|
alias :thread_cattr_writer :thread_mattr_writer
|
@@ -122,8 +122,8 @@ class Module
|
|
122
122
|
# Customer.user # => "Rafael"
|
123
123
|
# Account.user # => "DHH"
|
124
124
|
#
|
125
|
-
# To
|
126
|
-
# To
|
125
|
+
# To omit the instance writer method, pass <tt>instance_writer: false</tt>.
|
126
|
+
# To omit the instance reader method, pass <tt>instance_reader: false</tt>.
|
127
127
|
#
|
128
128
|
# class Current
|
129
129
|
# thread_mattr_accessor :user, instance_writer: false, instance_reader: false
|
@@ -132,17 +132,17 @@ class Module
|
|
132
132
|
# Current.new.user = "DHH" # => NoMethodError
|
133
133
|
# Current.new.user # => NoMethodError
|
134
134
|
#
|
135
|
-
# Or pass <tt>instance_accessor: false</tt>, to
|
135
|
+
# Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
|
136
136
|
#
|
137
137
|
# class Current
|
138
|
-
#
|
138
|
+
# thread_mattr_accessor :user, instance_accessor: false
|
139
139
|
# end
|
140
140
|
#
|
141
141
|
# Current.new.user = "DHH" # => NoMethodError
|
142
142
|
# Current.new.user # => NoMethodError
|
143
|
-
def thread_mattr_accessor(*syms)
|
144
|
-
thread_mattr_reader(*syms)
|
145
|
-
thread_mattr_writer(*syms)
|
143
|
+
def thread_mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil)
|
144
|
+
thread_mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default)
|
145
|
+
thread_mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor)
|
146
146
|
end
|
147
147
|
alias :thread_cattr_accessor :thread_mattr_accessor
|
148
148
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/concern"
|
2
4
|
|
3
5
|
class Module
|
@@ -20,7 +22,7 @@ class Module
|
|
20
22
|
#
|
21
23
|
# == Using comments:
|
22
24
|
#
|
23
|
-
# class Todo
|
25
|
+
# class Todo < ApplicationRecord
|
24
26
|
# # Other todo implementation
|
25
27
|
# # ...
|
26
28
|
#
|
@@ -28,7 +30,6 @@ class Module
|
|
28
30
|
# has_many :events
|
29
31
|
#
|
30
32
|
# before_create :track_creation
|
31
|
-
# after_destroy :track_deletion
|
32
33
|
#
|
33
34
|
# private
|
34
35
|
# def track_creation
|
@@ -40,7 +41,7 @@ class Module
|
|
40
41
|
#
|
41
42
|
# Noisy syntax.
|
42
43
|
#
|
43
|
-
# class Todo
|
44
|
+
# class Todo < ApplicationRecord
|
44
45
|
# # Other todo implementation
|
45
46
|
# # ...
|
46
47
|
#
|
@@ -50,7 +51,6 @@ class Module
|
|
50
51
|
# included do
|
51
52
|
# has_many :events
|
52
53
|
# before_create :track_creation
|
53
|
-
# after_destroy :track_deletion
|
54
54
|
# end
|
55
55
|
#
|
56
56
|
# private
|
@@ -68,7 +68,7 @@ class Module
|
|
68
68
|
# increased overhead can be a reasonable tradeoff even if it reduces our
|
69
69
|
# at-a-glance perception of how things work.
|
70
70
|
#
|
71
|
-
# class Todo
|
71
|
+
# class Todo < ApplicationRecord
|
72
72
|
# # Other todo implementation
|
73
73
|
# # ...
|
74
74
|
#
|
@@ -80,7 +80,7 @@ class Module
|
|
80
80
|
# By quieting the mix-in noise, we arrive at a natural, low-ceremony way to
|
81
81
|
# separate bite-sized concerns.
|
82
82
|
#
|
83
|
-
# class Todo
|
83
|
+
# class Todo < ApplicationRecord
|
84
84
|
# # Other todo implementation
|
85
85
|
# # ...
|
86
86
|
#
|
@@ -88,7 +88,6 @@ class Module
|
|
88
88
|
# included do
|
89
89
|
# has_many :events
|
90
90
|
# before_create :track_creation
|
91
|
-
# after_destroy :track_deletion
|
92
91
|
# end
|
93
92
|
#
|
94
93
|
# private
|
@@ -99,16 +98,22 @@ class Module
|
|
99
98
|
# end
|
100
99
|
#
|
101
100
|
# Todo.ancestors
|
102
|
-
# # => [Todo, Todo::EventTracking, Object]
|
101
|
+
# # => [Todo, Todo::EventTracking, ApplicationRecord, Object]
|
103
102
|
#
|
104
103
|
# This small step has some wonderful ripple effects. We can
|
105
104
|
# * grok the behavior of our class in one glance,
|
106
105
|
# * clean up monolithic junk-drawer classes by separating their concerns, and
|
107
106
|
# * stop leaning on protected/private for crude "this is internal stuff" modularity.
|
107
|
+
#
|
108
|
+
# === Prepending concerning
|
109
|
+
#
|
110
|
+
# <tt>concerning</tt> supports a <tt>prepend: true</tt> argument which will <tt>prepend</tt> the
|
111
|
+
# concern instead of using <tt>include</tt> for it.
|
108
112
|
module Concerning
|
109
113
|
# Define a new concern and mix it in.
|
110
|
-
def concerning(topic, &block)
|
111
|
-
|
114
|
+
def concerning(topic, prepend: false, &block)
|
115
|
+
method = prepend ? :prepend : :include
|
116
|
+
__send__(method, concern(topic, &block))
|
112
117
|
end
|
113
118
|
|
114
119
|
# A low-cruft shortcut to define a concern.
|
@@ -1,5 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "set"
|
2
|
-
require "active_support/core_ext/regexp"
|
3
4
|
|
4
5
|
class Module
|
5
6
|
# Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+
|
@@ -18,10 +19,11 @@ class Module
|
|
18
19
|
# public methods as your own.
|
19
20
|
#
|
20
21
|
# ==== Options
|
21
|
-
# * <tt>:to</tt> - Specifies the target object
|
22
|
+
# * <tt>:to</tt> - Specifies the target object name as a symbol or string
|
22
23
|
# * <tt>:prefix</tt> - Prefixes the new method with the target name or a custom prefix
|
23
|
-
# * <tt>:allow_nil</tt> -
|
24
|
+
# * <tt>:allow_nil</tt> - If set to true, prevents a +Module::DelegationError+
|
24
25
|
# from being raised
|
26
|
+
# * <tt>:private</tt> - If set to true, changes method visibility to private
|
25
27
|
#
|
26
28
|
# The macro receives one or more method names (specified as symbols or
|
27
29
|
# strings) and the name of the target object via the <tt>:to</tt> option
|
@@ -112,12 +114,26 @@ class Module
|
|
112
114
|
# invoice.customer_name # => 'John Doe'
|
113
115
|
# invoice.customer_address # => 'Vimmersvej 13'
|
114
116
|
#
|
117
|
+
# The delegated methods are public by default.
|
118
|
+
# Pass <tt>private: true</tt> to change that.
|
119
|
+
#
|
120
|
+
# class User < ActiveRecord::Base
|
121
|
+
# has_one :profile
|
122
|
+
# delegate :first_name, to: :profile
|
123
|
+
# delegate :date_of_birth, to: :profile, private: true
|
124
|
+
#
|
125
|
+
# def age
|
126
|
+
# Date.today.year - date_of_birth.year
|
127
|
+
# end
|
128
|
+
# end
|
129
|
+
#
|
130
|
+
# User.new.first_name # => "Tomas"
|
131
|
+
# User.new.date_of_birth # => NoMethodError: private method `date_of_birth' called for #<User:0x00000008221340>
|
132
|
+
# User.new.age # => 2
|
133
|
+
#
|
115
134
|
# If the target is +nil+ and does not respond to the delegated method a
|
116
|
-
# +Module::DelegationError+ is raised
|
117
|
-
#
|
118
|
-
# purpose of the <tt>:allow_nil</tt> option: If the target is not +nil+, or it
|
119
|
-
# is and responds to the method, everything works as usual. But if it is +nil+
|
120
|
-
# and does not respond to the delegated method, +nil+ is returned.
|
135
|
+
# +Module::DelegationError+ is raised. If you wish to instead return +nil+,
|
136
|
+
# use the <tt>:allow_nil</tt> option.
|
121
137
|
#
|
122
138
|
# class User < ActiveRecord::Base
|
123
139
|
# has_one :profile
|
@@ -152,9 +168,9 @@ class Module
|
|
152
168
|
# Foo.new("Bar").name # raises NoMethodError: undefined method `name'
|
153
169
|
#
|
154
170
|
# The target method must be public, otherwise it will raise +NoMethodError+.
|
155
|
-
def delegate(*methods, to: nil, prefix: nil, allow_nil: nil)
|
171
|
+
def delegate(*methods, to: nil, prefix: nil, allow_nil: nil, private: nil)
|
156
172
|
unless to
|
157
|
-
raise ArgumentError, "Delegation needs a target. Supply
|
173
|
+
raise ArgumentError, "Delegation needs a target. Supply a keyword argument 'to' (e.g. delegate :hello, to: :greeter)."
|
158
174
|
end
|
159
175
|
|
160
176
|
if prefix == true && /^[^a-z_]/.match?(to)
|
@@ -174,10 +190,22 @@ class Module
|
|
174
190
|
to = to.to_s
|
175
191
|
to = "self.#{to}" if DELEGATION_RESERVED_METHOD_NAMES.include?(to)
|
176
192
|
|
177
|
-
|
193
|
+
method_def = []
|
194
|
+
method_names = []
|
195
|
+
|
196
|
+
methods.map do |method|
|
197
|
+
method_name = prefix ? "#{method_prefix}#{method}" : method
|
198
|
+
method_names << method_name.to_sym
|
199
|
+
|
178
200
|
# Attribute writer methods only accept one argument. Makes sure []=
|
179
201
|
# methods still accept two arguments.
|
180
|
-
definition = /[^\]]=$/.match?(method)
|
202
|
+
definition = if /[^\]]=$/.match?(method)
|
203
|
+
"arg"
|
204
|
+
elsif RUBY_VERSION >= "2.7"
|
205
|
+
"..."
|
206
|
+
else
|
207
|
+
"*args, &block"
|
208
|
+
end
|
181
209
|
|
182
210
|
# The following generated method calls the target exactly once, storing
|
183
211
|
# the returned value in a dummy variable.
|
@@ -187,81 +215,84 @@ class Module
|
|
187
215
|
# whereas conceptually, from the user point of view, the delegator should
|
188
216
|
# be doing one call.
|
189
217
|
if allow_nil
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
"
|
194
|
-
" _
|
195
|
-
"
|
196
|
-
|
197
|
-
|
218
|
+
method = method.to_s
|
219
|
+
|
220
|
+
method_def <<
|
221
|
+
"def #{method_name}(#{definition})" <<
|
222
|
+
" _ = #{to}" <<
|
223
|
+
" if !_.nil? || nil.respond_to?(:#{method})" <<
|
224
|
+
" _.#{method}(#{definition})" <<
|
225
|
+
" end" <<
|
226
|
+
"end"
|
198
227
|
else
|
199
|
-
|
228
|
+
method = method.to_s
|
229
|
+
method_name = method_name.to_s
|
200
230
|
|
201
|
-
method_def
|
202
|
-
"def #{
|
203
|
-
"
|
204
|
-
" _.#{method}(#{definition})"
|
205
|
-
"rescue NoMethodError => e"
|
206
|
-
" if _.nil? && e.name == :#{method}"
|
207
|
-
"
|
208
|
-
" else"
|
209
|
-
" raise"
|
210
|
-
" end"
|
231
|
+
method_def <<
|
232
|
+
"def #{method_name}(#{definition})" <<
|
233
|
+
" _ = #{to}" <<
|
234
|
+
" _.#{method}(#{definition})" <<
|
235
|
+
"rescue NoMethodError => e" <<
|
236
|
+
" if _.nil? && e.name == :#{method}" <<
|
237
|
+
%( raise DelegationError, "#{self}##{method_name} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}") <<
|
238
|
+
" else" <<
|
239
|
+
" raise" <<
|
240
|
+
" end" <<
|
211
241
|
"end"
|
212
|
-
].join ";"
|
213
242
|
end
|
214
|
-
|
215
|
-
module_eval(method_def, file, line)
|
216
243
|
end
|
244
|
+
module_eval(method_def.join(";"), file, line)
|
245
|
+
private(*method_names) if private
|
246
|
+
method_names
|
217
247
|
end
|
218
248
|
|
219
249
|
# When building decorators, a common pattern may emerge:
|
220
250
|
#
|
221
251
|
# class Partition
|
222
|
-
# def initialize(
|
223
|
-
# @
|
252
|
+
# def initialize(event)
|
253
|
+
# @event = event
|
224
254
|
# end
|
225
255
|
#
|
226
|
-
# def
|
227
|
-
#
|
228
|
-
# @events.collect { |e| Array(e.detail.people) }.flatten.uniq
|
229
|
-
# else
|
230
|
-
# @events.collect(&:creator).uniq
|
231
|
-
# end
|
256
|
+
# def person
|
257
|
+
# detail.person || creator
|
232
258
|
# end
|
233
259
|
#
|
234
260
|
# private
|
235
261
|
# def respond_to_missing?(name, include_private = false)
|
236
|
-
# @
|
262
|
+
# @event.respond_to?(name, include_private)
|
237
263
|
# end
|
238
264
|
#
|
239
265
|
# def method_missing(method, *args, &block)
|
240
|
-
# @
|
266
|
+
# @event.send(method, *args, &block)
|
241
267
|
# end
|
242
268
|
# end
|
243
269
|
#
|
244
|
-
# With
|
270
|
+
# With <tt>Module#delegate_missing_to</tt>, the above is condensed to:
|
245
271
|
#
|
246
272
|
# class Partition
|
247
|
-
# delegate_missing_to :@
|
273
|
+
# delegate_missing_to :@event
|
248
274
|
#
|
249
|
-
# def initialize(
|
250
|
-
# @
|
275
|
+
# def initialize(event)
|
276
|
+
# @event = event
|
251
277
|
# end
|
252
278
|
#
|
253
|
-
# def
|
254
|
-
#
|
255
|
-
# @events.collect { |e| Array(e.detail.people) }.flatten.uniq
|
256
|
-
# else
|
257
|
-
# @events.collect(&:creator).uniq
|
258
|
-
# end
|
279
|
+
# def person
|
280
|
+
# detail.person || creator
|
259
281
|
# end
|
260
282
|
# end
|
261
283
|
#
|
262
|
-
# The target can be anything callable within the object
|
263
|
-
# variables, methods, constants
|
264
|
-
|
284
|
+
# The target can be anything callable within the object, e.g. instance
|
285
|
+
# variables, methods, constants, etc.
|
286
|
+
#
|
287
|
+
# The delegated method must be public on the target, otherwise it will
|
288
|
+
# raise +DelegationError+. If you wish to instead return +nil+,
|
289
|
+
# use the <tt>:allow_nil</tt> option.
|
290
|
+
#
|
291
|
+
# The <tt>marshal_dump</tt> and <tt>_dump</tt> methods are exempt from
|
292
|
+
# delegation due to possible interference when calling
|
293
|
+
# <tt>Marshal.dump(object)</tt>, should the delegation target method
|
294
|
+
# of <tt>object</tt> add or remove instance variables.
|
295
|
+
def delegate_missing_to(target, allow_nil: nil)
|
265
296
|
target = target.to_s
|
266
297
|
target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target)
|
267
298
|
|
@@ -270,6 +301,7 @@ class Module
|
|
270
301
|
# It may look like an oversight, but we deliberately do not pass
|
271
302
|
# +include_private+, because they do not get delegated.
|
272
303
|
|
304
|
+
return false if name == :marshal_dump || name == :_dump
|
273
305
|
#{target}.respond_to?(name) || super
|
274
306
|
end
|
275
307
|
|
@@ -277,9 +309,22 @@ class Module
|
|
277
309
|
if #{target}.respond_to?(method)
|
278
310
|
#{target}.public_send(method, *args, &block)
|
279
311
|
else
|
280
|
-
|
312
|
+
begin
|
313
|
+
super
|
314
|
+
rescue NoMethodError
|
315
|
+
if #{target}.nil?
|
316
|
+
if #{allow_nil == true}
|
317
|
+
nil
|
318
|
+
else
|
319
|
+
raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
|
320
|
+
end
|
321
|
+
else
|
322
|
+
raise
|
323
|
+
end
|
324
|
+
end
|
281
325
|
end
|
282
326
|
end
|
327
|
+
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
283
328
|
RUBY
|
284
329
|
end
|
285
330
|
end
|
@@ -1,14 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/string/filters"
|
1
4
|
require "active_support/inflector"
|
2
5
|
|
3
6
|
class Module
|
4
7
|
# Returns the name of the module containing this one.
|
5
8
|
#
|
6
|
-
# M::N.
|
7
|
-
def
|
9
|
+
# M::N.module_parent_name # => "M"
|
10
|
+
def module_parent_name
|
8
11
|
if defined?(@parent_name)
|
9
12
|
@parent_name
|
10
13
|
else
|
11
|
-
parent_name = name =~ /::[^:]+\
|
14
|
+
parent_name = name =~ /::[^:]+\z/ ? -$` : nil
|
12
15
|
@parent_name = parent_name unless frozen?
|
13
16
|
parent_name
|
14
17
|
end
|
@@ -22,15 +25,15 @@ class Module
|
|
22
25
|
# end
|
23
26
|
# X = M::N
|
24
27
|
#
|
25
|
-
# M::N.
|
26
|
-
# X.
|
28
|
+
# M::N.module_parent # => M
|
29
|
+
# X.module_parent # => M
|
27
30
|
#
|
28
31
|
# The parent of top-level and anonymous modules is Object.
|
29
32
|
#
|
30
|
-
# M.
|
31
|
-
# Module.new.
|
32
|
-
def
|
33
|
-
|
33
|
+
# M.module_parent # => Object
|
34
|
+
# Module.new.module_parent # => Object
|
35
|
+
def module_parent
|
36
|
+
module_parent_name ? ActiveSupport::Inflector.constantize(module_parent_name) : Object
|
34
37
|
end
|
35
38
|
|
36
39
|
# Returns all the parents of this module according to its name, ordered from
|
@@ -42,13 +45,13 @@ class Module
|
|
42
45
|
# end
|
43
46
|
# X = M::N
|
44
47
|
#
|
45
|
-
# M.
|
46
|
-
# M::N.
|
47
|
-
# X.
|
48
|
-
def
|
48
|
+
# M.module_parents # => [Object]
|
49
|
+
# M::N.module_parents # => [M, Object]
|
50
|
+
# X.module_parents # => [M, Object]
|
51
|
+
def module_parents
|
49
52
|
parents = []
|
50
|
-
if
|
51
|
-
parts =
|
53
|
+
if module_parent_name
|
54
|
+
parts = module_parent_name.split("::")
|
52
55
|
until parts.empty?
|
53
56
|
parents << ActiveSupport::Inflector.constantize(parts * "::")
|
54
57
|
parts.pop
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Module
|
4
|
+
# Marks the named method as intended to be redefined, if it exists.
|
5
|
+
# Suppresses the Ruby method redefinition warning. Prefer
|
6
|
+
# #redefine_method where possible.
|
7
|
+
def silence_redefinition_of_method(method)
|
8
|
+
if method_defined?(method) || private_method_defined?(method)
|
9
|
+
# This suppresses the "method redefined" warning; the self-alias
|
10
|
+
# looks odd, but means we don't need to generate a unique name
|
11
|
+
alias_method method, method
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Replaces the existing method definition, if there is one, with the passed
|
16
|
+
# block as its body.
|
17
|
+
def redefine_method(method, &block)
|
18
|
+
visibility = method_visibility(method)
|
19
|
+
silence_redefinition_of_method(method)
|
20
|
+
define_method(method, &block)
|
21
|
+
send(visibility, method)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Replaces the existing singleton method definition, if there is one, with
|
25
|
+
# the passed block as its body.
|
26
|
+
def redefine_singleton_method(method, &block)
|
27
|
+
singleton_class.redefine_method(method, &block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def method_visibility(method) # :nodoc:
|
31
|
+
case
|
32
|
+
when private_method_defined?(method)
|
33
|
+
:private
|
34
|
+
when protected_method_defined?(method)
|
35
|
+
:protected
|
36
|
+
else
|
37
|
+
:public
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/module/redefine_method"
|
4
|
+
|
1
5
|
class Module
|
2
6
|
# Removes the named method, if it exists.
|
3
7
|
def remove_possible_method(method)
|
@@ -8,28 +12,6 @@ class Module
|
|
8
12
|
|
9
13
|
# Removes the named singleton method, if it exists.
|
10
14
|
def remove_possible_singleton_method(method)
|
11
|
-
singleton_class.
|
12
|
-
remove_possible_method(method)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
# Replaces the existing method definition, if there is one, with the passed
|
17
|
-
# block as its body.
|
18
|
-
def redefine_method(method, &block)
|
19
|
-
visibility = method_visibility(method)
|
20
|
-
remove_possible_method(method)
|
21
|
-
define_method(method, &block)
|
22
|
-
send(visibility, method)
|
23
|
-
end
|
24
|
-
|
25
|
-
def method_visibility(method) # :nodoc:
|
26
|
-
case
|
27
|
-
when private_method_defined?(method)
|
28
|
-
:private
|
29
|
-
when protected_method_defined?(method)
|
30
|
-
:protected
|
31
|
-
else
|
32
|
-
:public
|
33
|
-
end
|
15
|
+
singleton_class.remove_possible_method(method)
|
34
16
|
end
|
35
17
|
end
|
@@ -1,11 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/core_ext/module/aliasing"
|
2
4
|
require "active_support/core_ext/module/introspection"
|
3
5
|
require "active_support/core_ext/module/anonymous"
|
4
|
-
require "active_support/core_ext/module/reachable"
|
5
6
|
require "active_support/core_ext/module/attribute_accessors"
|
6
7
|
require "active_support/core_ext/module/attribute_accessors_per_thread"
|
7
8
|
require "active_support/core_ext/module/attr_internal"
|
8
9
|
require "active_support/core_ext/module/concerning"
|
9
10
|
require "active_support/core_ext/module/delegation"
|
10
11
|
require "active_support/core_ext/module/deprecation"
|
12
|
+
require "active_support/core_ext/module/redefine_method"
|
11
13
|
require "active_support/core_ext/module/remove_method"
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class NameError
|
2
4
|
# Extract the name of the missing constant from the exception message.
|
3
5
|
#
|
@@ -8,8 +10,26 @@ class NameError
|
|
8
10
|
# end
|
9
11
|
# # => "HelloWorld"
|
10
12
|
def missing_name
|
11
|
-
|
12
|
-
|
13
|
+
# Since ruby v2.3.0 `did_you_mean` gem is loaded by default.
|
14
|
+
# It extends NameError#message with spell corrections which are SLOW.
|
15
|
+
# We should use original_message message instead.
|
16
|
+
message = respond_to?(:original_message) ? original_message : self.message
|
17
|
+
return unless message.start_with?("uninitialized constant ")
|
18
|
+
|
19
|
+
receiver = begin
|
20
|
+
self.receiver
|
21
|
+
rescue ArgumentError
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
if receiver == Object
|
26
|
+
name.to_s
|
27
|
+
elsif receiver
|
28
|
+
"#{real_mod_name(receiver)}::#{self.name}"
|
29
|
+
else
|
30
|
+
if match = message.match(/((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/)
|
31
|
+
match[1]
|
32
|
+
end
|
13
33
|
end
|
14
34
|
end
|
15
35
|
|
@@ -28,4 +48,18 @@ class NameError
|
|
28
48
|
missing_name == name.to_s
|
29
49
|
end
|
30
50
|
end
|
51
|
+
|
52
|
+
private
|
53
|
+
UNBOUND_METHOD_MODULE_NAME = Module.instance_method(:name)
|
54
|
+
private_constant :UNBOUND_METHOD_MODULE_NAME
|
55
|
+
|
56
|
+
if UnboundMethod.method_defined?(:bind_call)
|
57
|
+
def real_mod_name(mod)
|
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
|
64
|
+
end
|
31
65
|
end
|