activesupport 5.2.0 → 6.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activesupport might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +362 -333
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -3
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/array_inquirer.rb +4 -2
- data/lib/active_support/backtrace_cleaner.rb +29 -3
- data/lib/active_support/benchmarkable.rb +1 -1
- data/lib/active_support/cache/file_store.rb +33 -33
- data/lib/active_support/cache/mem_cache_store.rb +31 -29
- data/lib/active_support/cache/memory_store.rb +59 -33
- data/lib/active_support/cache/null_store.rb +8 -3
- data/lib/active_support/cache/redis_cache_store.rb +84 -45
- data/lib/active_support/cache/strategy/local_cache.rb +41 -26
- data/lib/active_support/cache.rb +174 -113
- data/lib/active_support/callbacks.rb +81 -64
- data/lib/active_support/concern.rb +76 -5
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
- data/lib/active_support/concurrency/share_lock.rb +0 -1
- data/lib/active_support/configurable.rb +10 -14
- data/lib/active_support/configuration_file.rb +46 -0
- data/lib/active_support/core_ext/array/access.rb +18 -6
- data/lib/active_support/core_ext/array/conversions.rb +5 -5
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- data/lib/active_support/core_ext/array.rb +1 -1
- data/lib/active_support/core_ext/benchmark.rb +2 -2
- data/lib/active_support/core_ext/class/attribute.rb +32 -47
- data/lib/active_support/core_ext/class/subclasses.rb +17 -38
- data/lib/active_support/core_ext/date/calculations.rb +6 -5
- data/lib/active_support/core_ext/date/conversions.rb +2 -1
- data/lib/active_support/core_ext/date_and_time/calculations.rb +37 -47
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
- data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
- data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
- data/lib/active_support/core_ext/digest.rb +3 -0
- data/lib/active_support/core_ext/enumerable.rb +171 -70
- data/lib/active_support/core_ext/file/atomic.rb +1 -1
- data/lib/active_support/core_ext/hash/conversions.rb +3 -3
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +2 -2
- data/lib/active_support/core_ext/hash/keys.rb +1 -30
- data/lib/active_support/core_ext/hash/slice.rb +6 -27
- data/lib/active_support/core_ext/hash.rb +1 -2
- data/lib/active_support/core_ext/integer/multiple.rb +1 -1
- data/lib/active_support/core_ext/kernel.rb +0 -1
- data/lib/active_support/core_ext/load_error.rb +1 -1
- data/lib/active_support/core_ext/marshal.rb +2 -0
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +30 -39
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +17 -19
- data/lib/active_support/core_ext/module/concerning.rb +8 -2
- data/lib/active_support/core_ext/module/delegation.rb +76 -33
- data/lib/active_support/core_ext/module/introspection.rb +16 -15
- data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
- data/lib/active_support/core_ext/module.rb +0 -1
- data/lib/active_support/core_ext/name_error.rb +29 -2
- data/lib/active_support/core_ext/numeric/conversions.rb +129 -129
- data/lib/active_support/core_ext/numeric.rb +0 -1
- data/lib/active_support/core_ext/object/blank.rb +1 -2
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/duplicable.rb +7 -114
- data/lib/active_support/core_ext/object/json.rb +7 -2
- data/lib/active_support/core_ext/object/to_query.rb +5 -2
- data/lib/active_support/core_ext/object/try.rb +17 -7
- data/lib/active_support/core_ext/object/with_options.rb +1 -1
- data/lib/active_support/core_ext/range/compare_range.rb +82 -0
- data/lib/active_support/core_ext/range/conversions.rb +31 -29
- data/lib/active_support/core_ext/range/each.rb +0 -1
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
- data/lib/active_support/core_ext/range.rb +1 -1
- data/lib/active_support/core_ext/regexp.rb +8 -5
- data/lib/active_support/core_ext/securerandom.rb +23 -3
- data/lib/active_support/core_ext/string/access.rb +5 -16
- data/lib/active_support/core_ext/string/conversions.rb +1 -0
- data/lib/active_support/core_ext/string/filters.rb +42 -1
- data/lib/active_support/core_ext/string/inflections.rb +45 -6
- data/lib/active_support/core_ext/string/inquiry.rb +1 -0
- data/lib/active_support/core_ext/string/multibyte.rb +6 -5
- data/lib/active_support/core_ext/string/output_safety.rb +69 -12
- data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
- data/lib/active_support/core_ext/string/strip.rb +3 -1
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/time/calculations.rb +50 -3
- data/lib/active_support/core_ext/time/conversions.rb +1 -0
- data/lib/active_support/core_ext/uri.rb +7 -5
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +15 -2
- data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
- data/lib/active_support/dependencies.rb +118 -35
- data/lib/active_support/deprecation/behaviors.rb +20 -3
- data/lib/active_support/deprecation/disallowed.rb +56 -0
- data/lib/active_support/deprecation/instance_delegator.rb +0 -1
- data/lib/active_support/deprecation/method_wrappers.rb +21 -13
- data/lib/active_support/deprecation/proxy_wrappers.rb +29 -6
- data/lib/active_support/deprecation/reporting.rb +51 -8
- data/lib/active_support/deprecation.rb +6 -1
- data/lib/active_support/descendants_tracker.rb +59 -9
- data/lib/active_support/duration/iso8601_parser.rb +2 -4
- data/lib/active_support/duration/iso8601_serializer.rb +18 -14
- data/lib/active_support/duration.rb +90 -38
- data/lib/active_support/encrypted_configuration.rb +1 -5
- data/lib/active_support/encrypted_file.rb +23 -5
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/evented_file_update_checker.rb +82 -117
- data/lib/active_support/execution_wrapper.rb +1 -0
- data/lib/active_support/file_update_checker.rb +0 -1
- data/lib/active_support/fork_tracker.rb +62 -0
- data/lib/active_support/gem_version.rb +2 -2
- data/lib/active_support/hash_with_indifferent_access.rb +78 -41
- data/lib/active_support/i18n.rb +1 -0
- data/lib/active_support/i18n_railtie.rb +16 -5
- data/lib/active_support/inflector/inflections.rb +2 -7
- data/lib/active_support/inflector/methods.rb +50 -57
- data/lib/active_support/inflector/transliterate.rb +47 -18
- data/lib/active_support/json/decoding.rb +25 -26
- data/lib/active_support/json/encoding.rb +11 -3
- data/lib/active_support/key_generator.rb +1 -33
- data/lib/active_support/lazy_load_hooks.rb +5 -2
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/locale/en.yml +7 -3
- data/lib/active_support/log_subscriber.rb +39 -9
- data/lib/active_support/logger.rb +2 -17
- data/lib/active_support/logger_silence.rb +11 -19
- data/lib/active_support/logger_thread_safe_level.rb +52 -7
- data/lib/active_support/message_encryptor.rb +8 -13
- data/lib/active_support/message_verifier.rb +10 -10
- data/lib/active_support/messages/metadata.rb +11 -2
- data/lib/active_support/messages/rotation_configuration.rb +2 -1
- data/lib/active_support/messages/rotator.rb +10 -9
- data/lib/active_support/multibyte/chars.rb +10 -68
- data/lib/active_support/multibyte/unicode.rb +15 -327
- data/lib/active_support/notifications/fanout.rb +116 -16
- data/lib/active_support/notifications/instrumenter.rb +71 -9
- data/lib/active_support/notifications.rb +72 -8
- data/lib/active_support/number_helper/number_converter.rb +5 -6
- data/lib/active_support/number_helper/number_to_currency_converter.rb +4 -9
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
- data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -3
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +8 -7
- data/lib/active_support/number_helper/rounding_helper.rb +12 -28
- data/lib/active_support/number_helper.rb +38 -12
- data/lib/active_support/option_merger.rb +22 -3
- data/lib/active_support/ordered_hash.rb +1 -1
- data/lib/active_support/ordered_options.rb +13 -3
- data/lib/active_support/parameter_filter.rb +133 -0
- data/lib/active_support/per_thread_registry.rb +1 -1
- data/lib/active_support/rails.rb +1 -10
- data/lib/active_support/railtie.rb +23 -1
- data/lib/active_support/reloader.rb +4 -5
- data/lib/active_support/secure_compare_rotator.rb +51 -0
- data/lib/active_support/security_utils.rb +19 -12
- data/lib/active_support/string_inquirer.rb +4 -3
- data/lib/active_support/subscriber.rb +72 -24
- data/lib/active_support/tagged_logging.rb +42 -8
- data/lib/active_support/test_case.rb +92 -1
- data/lib/active_support/testing/assertions.rb +30 -9
- data/lib/active_support/testing/deprecation.rb +0 -1
- data/lib/active_support/testing/file_fixtures.rb +2 -0
- data/lib/active_support/testing/isolation.rb +2 -2
- data/lib/active_support/testing/method_call_assertions.rb +28 -1
- data/lib/active_support/testing/parallelization/server.rb +78 -0
- data/lib/active_support/testing/parallelization/worker.rb +100 -0
- data/lib/active_support/testing/parallelization.rb +51 -0
- data/lib/active_support/testing/setup_and_teardown.rb +5 -9
- data/lib/active_support/testing/stream.rb +1 -2
- data/lib/active_support/testing/time_helpers.rb +47 -12
- data/lib/active_support/time_with_zone.rb +81 -47
- data/lib/active_support/values/time_zone.rb +34 -18
- data/lib/active_support/xml_mini/jdom.rb +2 -3
- data/lib/active_support/xml_mini/libxml.rb +2 -2
- data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
- data/lib/active_support/xml_mini/nokogiri.rb +2 -2
- data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
- data/lib/active_support/xml_mini/rexml.rb +10 -3
- data/lib/active_support/xml_mini.rb +2 -10
- data/lib/active_support.rb +14 -1
- metadata +57 -30
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
- data/lib/active_support/core_ext/hash/compact.rb +0 -29
- data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
- data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
- data/lib/active_support/core_ext/module/reachable.rb +0 -11
- data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
- data/lib/active_support/core_ext/range/include_range.rb +0 -25
- data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,8 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/array/extract_options"
|
4
|
-
require "active_support/core_ext/regexp"
|
5
|
-
|
6
3
|
# Extends the module object with class/module and instance accessors for
|
7
4
|
# class/module attributes, just like the native attr* accessors for instance
|
8
5
|
# attributes.
|
@@ -27,7 +24,7 @@ class Module
|
|
27
24
|
# end
|
28
25
|
# # => NameError: invalid attribute name: 1_Badname
|
29
26
|
#
|
30
|
-
#
|
27
|
+
# To omit the instance reader method, pass
|
31
28
|
# <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
|
32
29
|
#
|
33
30
|
# module HairColors
|
@@ -51,28 +48,25 @@ class Module
|
|
51
48
|
# end
|
52
49
|
#
|
53
50
|
# Person.new.hair_colors # => [:brown, :black, :blonde, :red]
|
54
|
-
def mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil)
|
51
|
+
def mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil, location: nil)
|
52
|
+
raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class?
|
53
|
+
location ||= caller_locations(1, 1).first
|
54
|
+
|
55
|
+
definition = []
|
55
56
|
syms.each do |sym|
|
56
57
|
raise NameError.new("invalid attribute name: #{sym}") unless /\A[_A-Za-z]\w*\z/.match?(sym)
|
57
|
-
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
58
|
-
@@#{sym} = nil unless defined? @@#{sym}
|
59
58
|
|
60
|
-
|
61
|
-
@@#{sym}
|
62
|
-
end
|
63
|
-
EOS
|
59
|
+
definition << "def self.#{sym}; @@#{sym}; end"
|
64
60
|
|
65
61
|
if instance_reader && instance_accessor
|
66
|
-
|
67
|
-
def #{sym}
|
68
|
-
@@#{sym}
|
69
|
-
end
|
70
|
-
EOS
|
62
|
+
definition << "def #{sym}; @@#{sym}; end"
|
71
63
|
end
|
72
64
|
|
73
65
|
sym_default_value = (block_given? && default.nil?) ? yield : default
|
74
|
-
class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil?
|
66
|
+
class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil? && class_variable_defined?("@@#{sym}")
|
75
67
|
end
|
68
|
+
|
69
|
+
module_eval(definition.join(";"), location.path, location.lineno)
|
76
70
|
end
|
77
71
|
alias :cattr_reader :mattr_reader
|
78
72
|
|
@@ -94,7 +88,7 @@ class Module
|
|
94
88
|
# Person.new.hair_colors = [:blonde, :red]
|
95
89
|
# HairColors.class_variable_get("@@hair_colors") # => [:blonde, :red]
|
96
90
|
#
|
97
|
-
#
|
91
|
+
# To omit the instance writer method, pass
|
98
92
|
# <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
|
99
93
|
#
|
100
94
|
# module HairColors
|
@@ -118,28 +112,24 @@ class Module
|
|
118
112
|
# end
|
119
113
|
#
|
120
114
|
# Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
|
121
|
-
def mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil)
|
115
|
+
def mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil, location: nil)
|
116
|
+
raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class?
|
117
|
+
location ||= caller_locations(1, 1).first
|
118
|
+
|
119
|
+
definition = []
|
122
120
|
syms.each do |sym|
|
123
121
|
raise NameError.new("invalid attribute name: #{sym}") unless /\A[_A-Za-z]\w*\z/.match?(sym)
|
124
|
-
|
125
|
-
@@#{sym} = nil unless defined? @@#{sym}
|
126
|
-
|
127
|
-
def self.#{sym}=(obj)
|
128
|
-
@@#{sym} = obj
|
129
|
-
end
|
130
|
-
EOS
|
122
|
+
definition << "def self.#{sym}=(val); @@#{sym} = val; end"
|
131
123
|
|
132
124
|
if instance_writer && instance_accessor
|
133
|
-
|
134
|
-
def #{sym}=(obj)
|
135
|
-
@@#{sym} = obj
|
136
|
-
end
|
137
|
-
EOS
|
125
|
+
definition << "def #{sym}=(val); @@#{sym} = val; end"
|
138
126
|
end
|
139
127
|
|
140
128
|
sym_default_value = (block_given? && default.nil?) ? yield : default
|
141
|
-
|
129
|
+
class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil? && class_variable_defined?("@@#{sym}")
|
142
130
|
end
|
131
|
+
|
132
|
+
module_eval(definition.join(";"), location.path, location.lineno)
|
143
133
|
end
|
144
134
|
alias :cattr_writer :mattr_writer
|
145
135
|
|
@@ -163,14 +153,14 @@ class Module
|
|
163
153
|
# parent class. Similarly if parent class changes the value then that would
|
164
154
|
# change the value of subclasses too.
|
165
155
|
#
|
166
|
-
# class
|
156
|
+
# class Citizen < Person
|
167
157
|
# end
|
168
158
|
#
|
169
|
-
#
|
159
|
+
# Citizen.new.hair_colors << :blue
|
170
160
|
# Person.new.hair_colors # => [:brown, :black, :blonde, :red, :blue]
|
171
161
|
#
|
172
|
-
# To
|
173
|
-
# To
|
162
|
+
# To omit the instance writer method, pass <tt>instance_writer: false</tt>.
|
163
|
+
# To omit the instance reader method, pass <tt>instance_reader: false</tt>.
|
174
164
|
#
|
175
165
|
# module HairColors
|
176
166
|
# mattr_accessor :hair_colors, instance_writer: false, instance_reader: false
|
@@ -183,7 +173,7 @@ class Module
|
|
183
173
|
# Person.new.hair_colors = [:brown] # => NoMethodError
|
184
174
|
# Person.new.hair_colors # => NoMethodError
|
185
175
|
#
|
186
|
-
# Or pass <tt>instance_accessor: false</tt>, to
|
176
|
+
# Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
|
187
177
|
#
|
188
178
|
# module HairColors
|
189
179
|
# mattr_accessor :hair_colors, instance_accessor: false
|
@@ -208,8 +198,9 @@ class Module
|
|
208
198
|
#
|
209
199
|
# Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
|
210
200
|
def mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil, &blk)
|
211
|
-
|
212
|
-
|
201
|
+
location = caller_locations(1, 1).first
|
202
|
+
mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default, location: location, &blk)
|
203
|
+
mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor, default: default, location: location)
|
213
204
|
end
|
214
205
|
alias :cattr_accessor :mattr_accessor
|
215
206
|
end
|
@@ -1,8 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/array/extract_options"
|
4
|
-
require "active_support/core_ext/regexp"
|
5
|
-
|
6
3
|
# Extends the module object with class/module and instance accessors for
|
7
4
|
# class/module attributes, just like the native attr* accessors for instance
|
8
5
|
# attributes, but does so on a per-thread basis.
|
@@ -28,7 +25,7 @@ class Module
|
|
28
25
|
# end
|
29
26
|
# # => NameError: invalid attribute name: 1_Badname
|
30
27
|
#
|
31
|
-
#
|
28
|
+
# To omit the instance reader method, pass
|
32
29
|
# <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
|
33
30
|
#
|
34
31
|
# class Current
|
@@ -36,9 +33,7 @@ class Module
|
|
36
33
|
# end
|
37
34
|
#
|
38
35
|
# Current.new.user # => NoMethodError
|
39
|
-
def thread_mattr_reader(*syms) # :nodoc:
|
40
|
-
options = syms.extract_options!
|
41
|
-
|
36
|
+
def thread_mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil) # :nodoc:
|
42
37
|
syms.each do |sym|
|
43
38
|
raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
|
44
39
|
|
@@ -50,13 +45,15 @@ class Module
|
|
50
45
|
end
|
51
46
|
EOS
|
52
47
|
|
53
|
-
|
48
|
+
if instance_reader && instance_accessor
|
54
49
|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
55
50
|
def #{sym}
|
56
51
|
self.class.#{sym}
|
57
52
|
end
|
58
53
|
EOS
|
59
54
|
end
|
55
|
+
|
56
|
+
Thread.current["attr_" + name + "_#{sym}"] = default unless default.nil?
|
60
57
|
end
|
61
58
|
end
|
62
59
|
alias :thread_cattr_reader :thread_mattr_reader
|
@@ -71,7 +68,7 @@ class Module
|
|
71
68
|
# Current.user = "DHH"
|
72
69
|
# Thread.current[:attr_Current_user] # => "DHH"
|
73
70
|
#
|
74
|
-
#
|
71
|
+
# To omit the instance writer method, pass
|
75
72
|
# <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
|
76
73
|
#
|
77
74
|
# class Current
|
@@ -79,8 +76,7 @@ class Module
|
|
79
76
|
# end
|
80
77
|
#
|
81
78
|
# Current.new.user = "DHH" # => NoMethodError
|
82
|
-
def thread_mattr_writer(*syms) # :nodoc:
|
83
|
-
options = syms.extract_options!
|
79
|
+
def thread_mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil) # :nodoc:
|
84
80
|
syms.each do |sym|
|
85
81
|
raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
|
86
82
|
|
@@ -92,13 +88,15 @@ class Module
|
|
92
88
|
end
|
93
89
|
EOS
|
94
90
|
|
95
|
-
|
91
|
+
if instance_writer && instance_accessor
|
96
92
|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
97
93
|
def #{sym}=(obj)
|
98
94
|
self.class.#{sym} = obj
|
99
95
|
end
|
100
96
|
EOS
|
101
97
|
end
|
98
|
+
|
99
|
+
public_send("#{sym}=", default) unless default.nil?
|
102
100
|
end
|
103
101
|
end
|
104
102
|
alias :thread_cattr_writer :thread_mattr_writer
|
@@ -124,8 +122,8 @@ class Module
|
|
124
122
|
# Customer.user # => "Rafael"
|
125
123
|
# Account.user # => "DHH"
|
126
124
|
#
|
127
|
-
# To
|
128
|
-
# 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>.
|
129
127
|
#
|
130
128
|
# class Current
|
131
129
|
# thread_mattr_accessor :user, instance_writer: false, instance_reader: false
|
@@ -134,17 +132,17 @@ class Module
|
|
134
132
|
# Current.new.user = "DHH" # => NoMethodError
|
135
133
|
# Current.new.user # => NoMethodError
|
136
134
|
#
|
137
|
-
# Or pass <tt>instance_accessor: false</tt>, to
|
135
|
+
# Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
|
138
136
|
#
|
139
137
|
# class Current
|
140
|
-
#
|
138
|
+
# thread_mattr_accessor :user, instance_accessor: false
|
141
139
|
# end
|
142
140
|
#
|
143
141
|
# Current.new.user = "DHH" # => NoMethodError
|
144
142
|
# Current.new.user # => NoMethodError
|
145
|
-
def thread_mattr_accessor(*syms)
|
146
|
-
thread_mattr_reader(*syms)
|
147
|
-
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)
|
148
146
|
end
|
149
147
|
alias :thread_cattr_accessor :thread_mattr_accessor
|
150
148
|
end
|
@@ -104,10 +104,16 @@ class Module
|
|
104
104
|
# * grok the behavior of our class in one glance,
|
105
105
|
# * clean up monolithic junk-drawer classes by separating their concerns, and
|
106
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.
|
107
112
|
module Concerning
|
108
113
|
# Define a new concern and mix it in.
|
109
|
-
def concerning(topic, &block)
|
110
|
-
|
114
|
+
def concerning(topic, prepend: false, &block)
|
115
|
+
method = prepend ? :prepend : :include
|
116
|
+
__send__(method, concern(topic, &block))
|
111
117
|
end
|
112
118
|
|
113
119
|
# A low-cruft shortcut to define a concern.
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "set"
|
4
|
-
require "active_support/core_ext/regexp"
|
5
4
|
|
6
5
|
class Module
|
7
6
|
# Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+
|
@@ -20,10 +19,11 @@ class Module
|
|
20
19
|
# public methods as your own.
|
21
20
|
#
|
22
21
|
# ==== Options
|
23
|
-
# * <tt>:to</tt> - Specifies the target object
|
22
|
+
# * <tt>:to</tt> - Specifies the target object name as a symbol or string
|
24
23
|
# * <tt>:prefix</tt> - Prefixes the new method with the target name or a custom prefix
|
25
|
-
# * <tt>:allow_nil</tt> -
|
24
|
+
# * <tt>:allow_nil</tt> - If set to true, prevents a +Module::DelegationError+
|
26
25
|
# from being raised
|
26
|
+
# * <tt>:private</tt> - If set to true, changes method visibility to private
|
27
27
|
#
|
28
28
|
# The macro receives one or more method names (specified as symbols or
|
29
29
|
# strings) and the name of the target object via the <tt>:to</tt> option
|
@@ -114,6 +114,23 @@ class Module
|
|
114
114
|
# invoice.customer_name # => 'John Doe'
|
115
115
|
# invoice.customer_address # => 'Vimmersvej 13'
|
116
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
|
+
#
|
117
134
|
# If the target is +nil+ and does not respond to the delegated method a
|
118
135
|
# +Module::DelegationError+ is raised. If you wish to instead return +nil+,
|
119
136
|
# use the <tt>:allow_nil</tt> option.
|
@@ -151,9 +168,9 @@ class Module
|
|
151
168
|
# Foo.new("Bar").name # raises NoMethodError: undefined method `name'
|
152
169
|
#
|
153
170
|
# The target method must be public, otherwise it will raise +NoMethodError+.
|
154
|
-
def delegate(*methods, to: nil, prefix: nil, allow_nil: nil)
|
171
|
+
def delegate(*methods, to: nil, prefix: nil, allow_nil: nil, private: nil)
|
155
172
|
unless to
|
156
|
-
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)."
|
157
174
|
end
|
158
175
|
|
159
176
|
if prefix == true && /^[^a-z_]/.match?(to)
|
@@ -173,10 +190,22 @@ class Module
|
|
173
190
|
to = to.to_s
|
174
191
|
to = "self.#{to}" if DELEGATION_RESERVED_METHOD_NAMES.include?(to)
|
175
192
|
|
193
|
+
method_def = []
|
194
|
+
method_names = []
|
195
|
+
|
176
196
|
methods.map do |method|
|
197
|
+
method_name = prefix ? "#{method_prefix}#{method}" : method
|
198
|
+
method_names << method_name.to_sym
|
199
|
+
|
177
200
|
# Attribute writer methods only accept one argument. Makes sure []=
|
178
201
|
# methods still accept two arguments.
|
179
|
-
definition = /[^\]]=$/.match?(method)
|
202
|
+
definition = if /[^\]]=$/.match?(method)
|
203
|
+
"arg"
|
204
|
+
elsif RUBY_VERSION >= "2.7"
|
205
|
+
"..."
|
206
|
+
else
|
207
|
+
"*args, &block"
|
208
|
+
end
|
180
209
|
|
181
210
|
# The following generated method calls the target exactly once, storing
|
182
211
|
# the returned value in a dummy variable.
|
@@ -186,33 +215,35 @@ class Module
|
|
186
215
|
# whereas conceptually, from the user point of view, the delegator should
|
187
216
|
# be doing one call.
|
188
217
|
if allow_nil
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
"
|
193
|
-
" _
|
194
|
-
"
|
195
|
-
|
196
|
-
|
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"
|
197
227
|
else
|
198
|
-
|
228
|
+
method = method.to_s
|
229
|
+
method_name = method_name.to_s
|
199
230
|
|
200
|
-
method_def
|
201
|
-
"def #{
|
202
|
-
"
|
203
|
-
" _.#{method}(#{definition})"
|
204
|
-
"rescue NoMethodError => e"
|
205
|
-
" if _.nil? && e.name == :#{method}"
|
206
|
-
"
|
207
|
-
" else"
|
208
|
-
" raise"
|
209
|
-
" 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" <<
|
210
241
|
"end"
|
211
|
-
].join ";"
|
212
242
|
end
|
213
|
-
|
214
|
-
module_eval(method_def, file, line)
|
215
243
|
end
|
244
|
+
module_eval(method_def.join(";"), file, line)
|
245
|
+
private(*method_names) if private
|
246
|
+
method_names
|
216
247
|
end
|
217
248
|
|
218
249
|
# When building decorators, a common pattern may emerge:
|
@@ -223,7 +254,7 @@ class Module
|
|
223
254
|
# end
|
224
255
|
#
|
225
256
|
# def person
|
226
|
-
#
|
257
|
+
# detail.person || creator
|
227
258
|
# end
|
228
259
|
#
|
229
260
|
# private
|
@@ -246,7 +277,7 @@ class Module
|
|
246
277
|
# end
|
247
278
|
#
|
248
279
|
# def person
|
249
|
-
#
|
280
|
+
# detail.person || creator
|
250
281
|
# end
|
251
282
|
# end
|
252
283
|
#
|
@@ -254,8 +285,14 @@ class Module
|
|
254
285
|
# variables, methods, constants, etc.
|
255
286
|
#
|
256
287
|
# The delegated method must be public on the target, otherwise it will
|
257
|
-
# raise +
|
258
|
-
|
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)
|
259
296
|
target = target.to_s
|
260
297
|
target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target)
|
261
298
|
|
@@ -264,6 +301,7 @@ class Module
|
|
264
301
|
# It may look like an oversight, but we deliberately do not pass
|
265
302
|
# +include_private+, because they do not get delegated.
|
266
303
|
|
304
|
+
return false if name == :marshal_dump || name == :_dump
|
267
305
|
#{target}.respond_to?(name) || super
|
268
306
|
end
|
269
307
|
|
@@ -275,13 +313,18 @@ class Module
|
|
275
313
|
super
|
276
314
|
rescue NoMethodError
|
277
315
|
if #{target}.nil?
|
278
|
-
|
316
|
+
if #{allow_nil == true}
|
317
|
+
nil
|
318
|
+
else
|
319
|
+
raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
|
320
|
+
end
|
279
321
|
else
|
280
322
|
raise
|
281
323
|
end
|
282
324
|
end
|
283
325
|
end
|
284
326
|
end
|
327
|
+
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
285
328
|
RUBY
|
286
329
|
end
|
287
330
|
end
|
@@ -1,16 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/string/filters"
|
3
4
|
require "active_support/inflector"
|
4
5
|
|
5
6
|
class Module
|
6
7
|
# Returns the name of the module containing this one.
|
7
8
|
#
|
8
|
-
# M::N.
|
9
|
-
def
|
9
|
+
# M::N.module_parent_name # => "M"
|
10
|
+
def module_parent_name
|
10
11
|
if defined?(@parent_name)
|
11
12
|
@parent_name
|
12
13
|
else
|
13
|
-
parent_name = name =~ /::[^:]+\
|
14
|
+
parent_name = name =~ /::[^:]+\z/ ? -$` : nil
|
14
15
|
@parent_name = parent_name unless frozen?
|
15
16
|
parent_name
|
16
17
|
end
|
@@ -24,15 +25,15 @@ class Module
|
|
24
25
|
# end
|
25
26
|
# X = M::N
|
26
27
|
#
|
27
|
-
# M::N.
|
28
|
-
# X.
|
28
|
+
# M::N.module_parent # => M
|
29
|
+
# X.module_parent # => M
|
29
30
|
#
|
30
31
|
# The parent of top-level and anonymous modules is Object.
|
31
32
|
#
|
32
|
-
# M.
|
33
|
-
# Module.new.
|
34
|
-
def
|
35
|
-
|
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
|
36
37
|
end
|
37
38
|
|
38
39
|
# Returns all the parents of this module according to its name, ordered from
|
@@ -44,13 +45,13 @@ class Module
|
|
44
45
|
# end
|
45
46
|
# X = M::N
|
46
47
|
#
|
47
|
-
# M.
|
48
|
-
# M::N.
|
49
|
-
# X.
|
50
|
-
def
|
48
|
+
# M.module_parents # => [Object]
|
49
|
+
# M::N.module_parents # => [M, Object]
|
50
|
+
# X.module_parents # => [M, Object]
|
51
|
+
def module_parents
|
51
52
|
parents = []
|
52
|
-
if
|
53
|
-
parts =
|
53
|
+
if module_parent_name
|
54
|
+
parts = module_parent_name.split("::")
|
54
55
|
until parts.empty?
|
55
56
|
parents << ActiveSupport::Inflector.constantize(parts * "::")
|
56
57
|
parts.pop
|
@@ -1,23 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Module
|
4
|
-
if
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
alias_method method, method
|
13
|
-
end
|
14
|
-
end
|
15
|
-
else
|
16
|
-
def silence_redefinition_of_method(method)
|
17
|
-
if method_defined?(method) || private_method_defined?(method)
|
18
|
-
alias_method :__rails_redefine, method
|
19
|
-
remove_method :__rails_redefine
|
20
|
-
end
|
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
|
21
12
|
end
|
22
13
|
end
|
23
14
|
|
@@ -3,7 +3,6 @@
|
|
3
3
|
require "active_support/core_ext/module/aliasing"
|
4
4
|
require "active_support/core_ext/module/introspection"
|
5
5
|
require "active_support/core_ext/module/anonymous"
|
6
|
-
require "active_support/core_ext/module/reachable"
|
7
6
|
require "active_support/core_ext/module/attribute_accessors"
|
8
7
|
require "active_support/core_ext/module/attribute_accessors_per_thread"
|
9
8
|
require "active_support/core_ext/module/attr_internal"
|
@@ -14,9 +14,22 @@ class NameError
|
|
14
14
|
# It extends NameError#message with spell corrections which are SLOW.
|
15
15
|
# We should use original_message message instead.
|
16
16
|
message = respond_to?(:original_message) ? original_message : self.message
|
17
|
+
return unless message.start_with?("uninitialized constant ")
|
17
18
|
|
18
|
-
|
19
|
-
|
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
|
20
33
|
end
|
21
34
|
end
|
22
35
|
|
@@ -35,4 +48,18 @@ class NameError
|
|
35
48
|
missing_name == name.to_s
|
36
49
|
end
|
37
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
|
38
65
|
end
|