activesupport 5.2.4.3 → 7.0.3
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 +244 -459
- 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 +2 -2
- data/lib/active_support/backtrace_cleaner.rb +31 -5
- data/lib/active_support/benchmarkable.rb +3 -3
- data/lib/active_support/cache/file_store.rb +47 -41
- data/lib/active_support/cache/mem_cache_store.rb +151 -40
- data/lib/active_support/cache/memory_store.rb +68 -34
- data/lib/active_support/cache/null_store.rb +16 -3
- data/lib/active_support/cache/redis_cache_store.rb +103 -101
- data/lib/active_support/cache/strategy/local_cache.rb +56 -64
- data/lib/active_support/cache.rb +333 -116
- data/lib/active_support/callbacks.rb +244 -128
- data/lib/active_support/code_generator.rb +65 -0
- data/lib/active_support/concern.rb +72 -5
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +16 -0
- data/lib/active_support/concurrency/share_lock.rb +2 -3
- data/lib/active_support/configurable.rb +15 -16
- data/lib/active_support/configuration_file.rb +51 -0
- data/lib/active_support/core_ext/array/access.rb +15 -7
- data/lib/active_support/core_ext/array/conversions.rb +18 -17
- data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- 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/array.rb +2 -1
- data/lib/active_support/core_ext/benchmark.rb +2 -2
- data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
- data/lib/active_support/core_ext/class/attribute.rb +32 -47
- data/lib/active_support/core_ext/class/subclasses.rb +9 -22
- data/lib/active_support/core_ext/date/blank.rb +1 -1
- data/lib/active_support/core_ext/date/calculations.rb +15 -14
- data/lib/active_support/core_ext/date/conversions.rb +16 -15
- data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/date.rb +1 -0
- data/lib/active_support/core_ext/date_and_time/calculations.rb +41 -51
- 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/blank.rb +1 -1
- data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
- data/lib/active_support/core_ext/date_time/conversions.rb +13 -14
- data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/date_time.rb +1 -0
- data/lib/active_support/core_ext/digest/uuid.rb +39 -13
- data/lib/active_support/core_ext/enumerable.rb +241 -76
- data/lib/active_support/core_ext/file/atomic.rb +3 -1
- data/lib/active_support/core_ext/hash/conversions.rb +3 -4
- 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/indifferent_access.rb +3 -3
- data/lib/active_support/core_ext/hash/keys.rb +2 -31
- 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/reporting.rb +4 -4
- data/lib/active_support/core_ext/kernel/singleton_class.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/module/attr_internal.rb +2 -2
- data/lib/active_support/core_ext/module/attribute_accessors.rb +32 -39
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +35 -28
- data/lib/active_support/core_ext/module/concerning.rb +8 -2
- data/lib/active_support/core_ext/module/delegation.rb +70 -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 +23 -2
- data/lib/active_support/core_ext/numeric/conversions.rb +132 -129
- data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
- data/lib/active_support/core_ext/numeric.rb +1 -1
- data/lib/active_support/core_ext/object/acts_like.rb +29 -5
- data/lib/active_support/core_ext/object/blank.rb +3 -4
- data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
- data/lib/active_support/core_ext/object/duplicable.rb +14 -110
- data/lib/active_support/core_ext/object/json.rb +44 -27
- data/lib/active_support/core_ext/object/to_query.rb +2 -2
- data/lib/active_support/core_ext/object/try.rb +24 -14
- data/lib/active_support/core_ext/object/with_options.rb +21 -2
- data/lib/active_support/core_ext/pathname/existence.rb +21 -0
- data/lib/active_support/core_ext/pathname.rb +3 -0
- data/lib/active_support/core_ext/range/compare_range.rb +23 -27
- data/lib/active_support/core_ext/range/conversions.rb +32 -30
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/range/each.rb +1 -2
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -20
- data/lib/active_support/core_ext/range/overlaps.rb +1 -1
- 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 +3 -2
- data/lib/active_support/core_ext/string/filters.rb +42 -1
- data/lib/active_support/core_ext/string/inflections.rb +46 -7
- data/lib/active_support/core_ext/string/inquiry.rb +2 -1
- data/lib/active_support/core_ext/string/multibyte.rb +6 -5
- data/lib/active_support/core_ext/string/output_safety.rb +129 -20
- 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 +6 -0
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/time/calculations.rb +59 -10
- data/lib/active_support/core_ext/time/conversions.rb +15 -12
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/time/zones.rb +7 -22
- data/lib/active_support/core_ext/time.rb +1 -0
- data/lib/active_support/core_ext/uri.rb +3 -22
- data/lib/active_support/core_ext.rb +2 -1
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +47 -16
- 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 +60 -715
- data/lib/active_support/deprecation/behaviors.rb +21 -5
- 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 +18 -23
- data/lib/active_support/deprecation/proxy_wrappers.rb +31 -8
- data/lib/active_support/deprecation/reporting.rb +50 -7
- data/lib/active_support/deprecation.rb +7 -2
- data/lib/active_support/descendants_tracker.rb +190 -34
- data/lib/active_support/digest.rb +5 -3
- data/lib/active_support/duration/iso8601_parser.rb +5 -7
- data/lib/active_support/duration/iso8601_serializer.rb +27 -15
- data/lib/active_support/duration.rb +149 -67
- data/lib/active_support/encrypted_configuration.rb +12 -5
- data/lib/active_support/encrypted_file.rb +23 -5
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/error_reporter.rb +117 -0
- data/lib/active_support/evented_file_update_checker.rb +85 -122
- data/lib/active_support/execution_context/test_helper.rb +13 -0
- data/lib/active_support/execution_context.rb +53 -0
- data/lib/active_support/execution_wrapper.rb +44 -21
- data/lib/active_support/executor/test_helper.rb +7 -0
- data/lib/active_support/file_update_checker.rb +0 -1
- data/lib/active_support/fork_tracker.rb +71 -0
- data/lib/active_support/gem_version.rb +5 -5
- data/lib/active_support/hash_with_indifferent_access.rb +73 -43
- data/lib/active_support/html_safe_translation.rb +43 -0
- data/lib/active_support/i18n.rb +2 -0
- data/lib/active_support/i18n_railtie.rb +15 -8
- data/lib/active_support/inflector/inflections.rb +25 -14
- data/lib/active_support/inflector/methods.rb +38 -71
- data/lib/active_support/inflector/transliterate.rb +47 -18
- data/lib/active_support/isolated_execution_state.rb +72 -0
- data/lib/active_support/json/decoding.rb +25 -26
- data/lib/active_support/json/encoding.rb +14 -6
- data/lib/active_support/key_generator.rb +23 -38
- data/lib/active_support/lazy_load_hooks.rb +19 -5
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/locale/en.yml +8 -4
- data/lib/active_support/log_subscriber/test_helper.rb +2 -2
- data/lib/active_support/log_subscriber.rb +51 -11
- data/lib/active_support/logger.rb +6 -22
- data/lib/active_support/logger_silence.rb +11 -19
- data/lib/active_support/logger_thread_safe_level.rb +45 -10
- data/lib/active_support/message_encryptor.rb +20 -19
- data/lib/active_support/message_verifier.rb +53 -21
- data/lib/active_support/messages/metadata.rb +13 -4
- 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 +17 -76
- data/lib/active_support/multibyte/unicode.rb +7 -331
- data/lib/active_support/multibyte.rb +1 -1
- data/lib/active_support/notifications/fanout.rb +163 -37
- data/lib/active_support/notifications/instrumenter.rb +90 -11
- data/lib/active_support/notifications.rb +88 -30
- data/lib/active_support/number_helper/number_converter.rb +6 -9
- data/lib/active_support/number_helper/number_to_currency_converter.rb +12 -12
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +4 -3
- 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 +5 -4
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
- data/lib/active_support/number_helper/number_to_phone_converter.rb +3 -2
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +12 -7
- data/lib/active_support/number_helper/rounding_helper.rb +12 -32
- data/lib/active_support/number_helper.rb +36 -12
- data/lib/active_support/option_merger.rb +15 -4
- data/lib/active_support/ordered_hash.rb +2 -2
- data/lib/active_support/ordered_options.rb +14 -4
- data/lib/active_support/parameter_filter.rb +138 -0
- data/lib/active_support/per_thread_registry.rb +6 -1
- data/lib/active_support/rails.rb +1 -10
- data/lib/active_support/railtie.rb +77 -5
- data/lib/active_support/reloader.rb +5 -6
- data/lib/active_support/rescuable.rb +8 -8
- data/lib/active_support/ruby_features.rb +7 -0
- 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 +2 -3
- data/lib/active_support/subscriber.rb +79 -46
- data/lib/active_support/tagged_logging.rb +58 -9
- data/lib/active_support/test_case.rb +79 -0
- data/lib/active_support/testing/assertions.rb +62 -11
- data/lib/active_support/testing/deprecation.rb +52 -2
- data/lib/active_support/testing/file_fixtures.rb +2 -0
- data/lib/active_support/testing/isolation.rb +4 -4
- data/lib/active_support/testing/method_call_assertions.rb +32 -5
- data/lib/active_support/testing/parallelization/server.rb +82 -0
- data/lib/active_support/testing/parallelization/worker.rb +103 -0
- data/lib/active_support/testing/parallelization.rb +55 -0
- data/lib/active_support/testing/parallelize_executor.rb +76 -0
- data/lib/active_support/testing/stream.rb +4 -7
- data/lib/active_support/testing/tagged_logging.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +60 -14
- data/lib/active_support/time_with_zone.rb +139 -64
- data/lib/active_support/values/time_zone.rb +66 -30
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/xml_mini/jdom.rb +3 -4
- data/lib/active_support/xml_mini/libxml.rb +7 -7
- data/lib/active_support/xml_mini/libxmlsax.rb +5 -5
- data/lib/active_support/xml_mini/nokogiri.rb +6 -6
- data/lib/active_support/xml_mini/nokogirisax.rb +4 -4
- data/lib/active_support/xml_mini/rexml.rb +11 -4
- data/lib/active_support/xml_mini.rb +7 -14
- data/lib/active_support.rb +30 -1
- metadata +64 -35
- 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/marshal.rb +0 -24
- 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 -3
- data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,34 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Hash
|
4
|
-
# Slices a hash to include only the given keys. Returns a hash containing
|
5
|
-
# the given keys.
|
6
|
-
#
|
7
|
-
# { a: 1, b: 2, c: 3, d: 4 }.slice(:a, :b)
|
8
|
-
# # => {:a=>1, :b=>2}
|
9
|
-
#
|
10
|
-
# This is useful for limiting an options hash to valid keys before
|
11
|
-
# passing to a method:
|
12
|
-
#
|
13
|
-
# def search(criteria = {})
|
14
|
-
# criteria.assert_valid_keys(:mass, :velocity, :time)
|
15
|
-
# end
|
16
|
-
#
|
17
|
-
# search(options.slice(:mass, :velocity, :time))
|
18
|
-
#
|
19
|
-
# If you have an array of keys you want to limit to, you should splat them:
|
20
|
-
#
|
21
|
-
# valid_keys = [:mass, :velocity, :time]
|
22
|
-
# search(options.slice(*valid_keys))
|
23
|
-
def slice(*keys)
|
24
|
-
keys.each_with_object(Hash.new) { |k, hash| hash[k] = self[k] if has_key?(k) }
|
25
|
-
end unless method_defined?(:slice)
|
26
|
-
|
27
4
|
# Replaces the hash with only the given keys.
|
28
5
|
# Returns a hash containing the removed key/value pairs.
|
29
6
|
#
|
30
|
-
# { a: 1, b: 2, c: 3, d: 4 }
|
31
|
-
# # => {:c=>3, :d=>4}
|
7
|
+
# hash = { a: 1, b: 2, c: 3, d: 4 }
|
8
|
+
# hash.slice!(:a, :b) # => {:c=>3, :d=>4}
|
9
|
+
# hash # => {:a=>1, :b=>2}
|
32
10
|
def slice!(*keys)
|
33
11
|
omit = slice(*self.keys - keys)
|
34
12
|
hash = slice(*keys)
|
@@ -40,8 +18,9 @@ class Hash
|
|
40
18
|
|
41
19
|
# Removes and returns the key/value pairs matching the given keys.
|
42
20
|
#
|
43
|
-
# { a: 1, b: 2, c: 3, d: 4 }
|
44
|
-
#
|
21
|
+
# hash = { a: 1, b: 2, c: 3, d: 4 }
|
22
|
+
# hash.extract!(:a, :b) # => {:a=>1, :b=>2}
|
23
|
+
# hash # => {:c=>3, :d=>4}
|
45
24
|
def extract!(*keys)
|
46
25
|
keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if has_key?(key) }
|
47
26
|
end
|
@@ -1,11 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/hash/compact"
|
4
3
|
require "active_support/core_ext/hash/conversions"
|
5
4
|
require "active_support/core_ext/hash/deep_merge"
|
5
|
+
require "active_support/core_ext/hash/deep_transform_values"
|
6
6
|
require "active_support/core_ext/hash/except"
|
7
7
|
require "active_support/core_ext/hash/indifferent_access"
|
8
8
|
require "active_support/core_ext/hash/keys"
|
9
9
|
require "active_support/core_ext/hash/reverse_merge"
|
10
10
|
require "active_support/core_ext/hash/slice"
|
11
|
-
require "active_support/core_ext/hash/transform_values"
|
@@ -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
|
@@ -4,6 +4,6 @@ class LoadError
|
|
4
4
|
# Returns true if the given path name (except perhaps for the ".rb"
|
5
5
|
# extension) is the missing file which caused the exception to be raised.
|
6
6
|
def is_missing?(location)
|
7
|
-
location.
|
7
|
+
location.delete_suffix(".rb") == path.to_s.delete_suffix(".rb")
|
8
8
|
end
|
9
9
|
end
|
@@ -28,9 +28,9 @@ class Module
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def attr_internal_define(attr_name, type)
|
31
|
-
internal_name = attr_internal_ivar_name(attr_name).
|
31
|
+
internal_name = attr_internal_ivar_name(attr_name).delete_prefix("@")
|
32
32
|
# use native attr_* methods as they are faster on some Ruby implementations
|
33
|
-
|
33
|
+
public_send("attr_#{type}", internal_name)
|
34
34
|
attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer
|
35
35
|
alias_method attr_name, internal_name
|
36
36
|
remove_method internal_name
|
@@ -1,8 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
# == Attribute Accessors
|
4
|
+
#
|
6
5
|
# Extends the module object with class/module and instance accessors for
|
7
6
|
# class/module attributes, just like the native attr* accessors for instance
|
8
7
|
# attributes.
|
@@ -27,7 +26,7 @@ class Module
|
|
27
26
|
# end
|
28
27
|
# # => NameError: invalid attribute name: 1_Badname
|
29
28
|
#
|
30
|
-
#
|
29
|
+
# To omit the instance reader method, pass
|
31
30
|
# <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
|
32
31
|
#
|
33
32
|
# module HairColors
|
@@ -51,28 +50,25 @@ class Module
|
|
51
50
|
# end
|
52
51
|
#
|
53
52
|
# Person.new.hair_colors # => [:brown, :black, :blonde, :red]
|
54
|
-
def mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil)
|
53
|
+
def mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil, location: nil)
|
54
|
+
raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class?
|
55
|
+
location ||= caller_locations(1, 1).first
|
56
|
+
|
57
|
+
definition = []
|
55
58
|
syms.each do |sym|
|
56
59
|
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
60
|
|
60
|
-
|
61
|
-
@@#{sym}
|
62
|
-
end
|
63
|
-
EOS
|
61
|
+
definition << "def self.#{sym}; @@#{sym}; end"
|
64
62
|
|
65
63
|
if instance_reader && instance_accessor
|
66
|
-
|
67
|
-
def #{sym}
|
68
|
-
@@#{sym}
|
69
|
-
end
|
70
|
-
EOS
|
64
|
+
definition << "def #{sym}; @@#{sym}; end"
|
71
65
|
end
|
72
66
|
|
73
67
|
sym_default_value = (block_given? && default.nil?) ? yield : default
|
74
|
-
class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil?
|
68
|
+
class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil? && class_variable_defined?("@@#{sym}")
|
75
69
|
end
|
70
|
+
|
71
|
+
module_eval(definition.join(";"), location.path, location.lineno)
|
76
72
|
end
|
77
73
|
alias :cattr_reader :mattr_reader
|
78
74
|
|
@@ -94,7 +90,7 @@ class Module
|
|
94
90
|
# Person.new.hair_colors = [:blonde, :red]
|
95
91
|
# HairColors.class_variable_get("@@hair_colors") # => [:blonde, :red]
|
96
92
|
#
|
97
|
-
#
|
93
|
+
# To omit the instance writer method, pass
|
98
94
|
# <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
|
99
95
|
#
|
100
96
|
# module HairColors
|
@@ -118,28 +114,24 @@ class Module
|
|
118
114
|
# end
|
119
115
|
#
|
120
116
|
# Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
|
121
|
-
def mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil)
|
117
|
+
def mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil, location: nil)
|
118
|
+
raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class?
|
119
|
+
location ||= caller_locations(1, 1).first
|
120
|
+
|
121
|
+
definition = []
|
122
122
|
syms.each do |sym|
|
123
123
|
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
|
124
|
+
definition << "def self.#{sym}=(val); @@#{sym} = val; end"
|
131
125
|
|
132
126
|
if instance_writer && instance_accessor
|
133
|
-
|
134
|
-
def #{sym}=(obj)
|
135
|
-
@@#{sym} = obj
|
136
|
-
end
|
137
|
-
EOS
|
127
|
+
definition << "def #{sym}=(val); @@#{sym} = val; end"
|
138
128
|
end
|
139
129
|
|
140
130
|
sym_default_value = (block_given? && default.nil?) ? yield : default
|
141
|
-
|
131
|
+
class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil? && class_variable_defined?("@@#{sym}")
|
142
132
|
end
|
133
|
+
|
134
|
+
module_eval(definition.join(";"), location.path, location.lineno)
|
143
135
|
end
|
144
136
|
alias :cattr_writer :mattr_writer
|
145
137
|
|
@@ -163,14 +155,14 @@ class Module
|
|
163
155
|
# parent class. Similarly if parent class changes the value then that would
|
164
156
|
# change the value of subclasses too.
|
165
157
|
#
|
166
|
-
# class
|
158
|
+
# class Citizen < Person
|
167
159
|
# end
|
168
160
|
#
|
169
|
-
#
|
161
|
+
# Citizen.new.hair_colors << :blue
|
170
162
|
# Person.new.hair_colors # => [:brown, :black, :blonde, :red, :blue]
|
171
163
|
#
|
172
|
-
# To
|
173
|
-
# To
|
164
|
+
# To omit the instance writer method, pass <tt>instance_writer: false</tt>.
|
165
|
+
# To omit the instance reader method, pass <tt>instance_reader: false</tt>.
|
174
166
|
#
|
175
167
|
# module HairColors
|
176
168
|
# mattr_accessor :hair_colors, instance_writer: false, instance_reader: false
|
@@ -183,7 +175,7 @@ class Module
|
|
183
175
|
# Person.new.hair_colors = [:brown] # => NoMethodError
|
184
176
|
# Person.new.hair_colors # => NoMethodError
|
185
177
|
#
|
186
|
-
# Or pass <tt>instance_accessor: false</tt>, to
|
178
|
+
# Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
|
187
179
|
#
|
188
180
|
# module HairColors
|
189
181
|
# mattr_accessor :hair_colors, instance_accessor: false
|
@@ -208,8 +200,9 @@ class Module
|
|
208
200
|
#
|
209
201
|
# Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
|
210
202
|
def mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil, &blk)
|
211
|
-
|
212
|
-
|
203
|
+
location = caller_locations(1, 1).first
|
204
|
+
mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default, location: location, &blk)
|
205
|
+
mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor, default: default, location: location)
|
213
206
|
end
|
214
207
|
alias :cattr_accessor :mattr_accessor
|
215
208
|
end
|
@@ -1,14 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
# == Attribute Accessors per Thread
|
4
|
+
#
|
6
5
|
# Extends the module object with class/module and instance accessors for
|
7
6
|
# class/module attributes, just like the native attr* accessors for instance
|
8
7
|
# attributes, but does so on a per-thread basis.
|
9
8
|
#
|
10
9
|
# So the values are scoped within the Thread.current space under the class name
|
11
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+.
|
12
14
|
class Module
|
13
15
|
# Defines a per-thread class attribute and creates class and instance reader methods.
|
14
16
|
# The underlying per-thread class variable is set to +nil+, if it is not previously defined.
|
@@ -17,9 +19,9 @@ class Module
|
|
17
19
|
# thread_mattr_reader :user
|
18
20
|
# end
|
19
21
|
#
|
20
|
-
# Current.user
|
21
|
-
# Thread.current[:attr_Current_user] = "DHH"
|
22
|
+
# Current.user = "DHH"
|
22
23
|
# Current.user # => "DHH"
|
24
|
+
# Thread.new { Current.user }.value # => nil
|
23
25
|
#
|
24
26
|
# The attribute name must be a valid method name in Ruby.
|
25
27
|
#
|
@@ -28,7 +30,7 @@ class Module
|
|
28
30
|
# end
|
29
31
|
# # => NameError: invalid attribute name: 1_Badname
|
30
32
|
#
|
31
|
-
#
|
33
|
+
# To omit the instance reader method, pass
|
32
34
|
# <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
|
33
35
|
#
|
34
36
|
# class Current
|
@@ -36,9 +38,7 @@ class Module
|
|
36
38
|
# end
|
37
39
|
#
|
38
40
|
# Current.new.user # => NoMethodError
|
39
|
-
def thread_mattr_reader(*syms) # :nodoc:
|
40
|
-
options = syms.extract_options!
|
41
|
-
|
41
|
+
def thread_mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil) # :nodoc:
|
42
42
|
syms.each do |sym|
|
43
43
|
raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
|
44
44
|
|
@@ -46,17 +46,20 @@ class Module
|
|
46
46
|
# to work with inheritance via polymorphism.
|
47
47
|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
48
48
|
def self.#{sym}
|
49
|
-
|
49
|
+
@__thread_mattr_#{sym} ||= "attr_\#{name}_#{sym}"
|
50
|
+
::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}]
|
50
51
|
end
|
51
52
|
EOS
|
52
53
|
|
53
|
-
|
54
|
+
if instance_reader && instance_accessor
|
54
55
|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
55
56
|
def #{sym}
|
56
57
|
self.class.#{sym}
|
57
58
|
end
|
58
59
|
EOS
|
59
60
|
end
|
61
|
+
|
62
|
+
::ActiveSupport::IsolatedExecutionState["attr_#{name}_#{sym}"] = default unless default.nil?
|
60
63
|
end
|
61
64
|
end
|
62
65
|
alias :thread_cattr_reader :thread_mattr_reader
|
@@ -71,7 +74,7 @@ class Module
|
|
71
74
|
# Current.user = "DHH"
|
72
75
|
# Thread.current[:attr_Current_user] # => "DHH"
|
73
76
|
#
|
74
|
-
#
|
77
|
+
# To omit the instance writer method, pass
|
75
78
|
# <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
|
76
79
|
#
|
77
80
|
# class Current
|
@@ -79,8 +82,7 @@ class Module
|
|
79
82
|
# end
|
80
83
|
#
|
81
84
|
# Current.new.user = "DHH" # => NoMethodError
|
82
|
-
def thread_mattr_writer(*syms) # :nodoc:
|
83
|
-
options = syms.extract_options!
|
85
|
+
def thread_mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil) # :nodoc:
|
84
86
|
syms.each do |sym|
|
85
87
|
raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
|
86
88
|
|
@@ -88,17 +90,20 @@ class Module
|
|
88
90
|
# to work with inheritance via polymorphism.
|
89
91
|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
90
92
|
def self.#{sym}=(obj)
|
91
|
-
|
93
|
+
@__thread_mattr_#{sym} ||= "attr_\#{name}_#{sym}"
|
94
|
+
::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}] = obj
|
92
95
|
end
|
93
96
|
EOS
|
94
97
|
|
95
|
-
|
98
|
+
if instance_writer && instance_accessor
|
96
99
|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
97
100
|
def #{sym}=(obj)
|
98
101
|
self.class.#{sym} = obj
|
99
102
|
end
|
100
103
|
EOS
|
101
104
|
end
|
105
|
+
|
106
|
+
public_send("#{sym}=", default) unless default.nil?
|
102
107
|
end
|
103
108
|
end
|
104
109
|
alias :thread_cattr_writer :thread_mattr_writer
|
@@ -113,19 +118,21 @@ class Module
|
|
113
118
|
# Account.user # => "DHH"
|
114
119
|
# Account.new.user # => "DHH"
|
115
120
|
#
|
121
|
+
# Unlike +mattr_accessor+, values are *not* shared with subclasses or parent classes.
|
116
122
|
# If a subclass changes the value, the parent class' value is not changed.
|
117
|
-
#
|
118
|
-
# is not changed.
|
123
|
+
# If the parent class changes the value, the value of subclasses is not changed.
|
119
124
|
#
|
120
125
|
# class Customer < Account
|
121
126
|
# end
|
122
127
|
#
|
123
|
-
#
|
124
|
-
# Customer.user
|
125
|
-
#
|
128
|
+
# Account.user # => "DHH"
|
129
|
+
# Customer.user # => nil
|
130
|
+
# Customer.user = "Rafael"
|
131
|
+
# Customer.user # => "Rafael"
|
132
|
+
# Account.user # => "DHH"
|
126
133
|
#
|
127
|
-
# To
|
128
|
-
# To
|
134
|
+
# To omit the instance writer method, pass <tt>instance_writer: false</tt>.
|
135
|
+
# To omit the instance reader method, pass <tt>instance_reader: false</tt>.
|
129
136
|
#
|
130
137
|
# class Current
|
131
138
|
# thread_mattr_accessor :user, instance_writer: false, instance_reader: false
|
@@ -134,17 +141,17 @@ class Module
|
|
134
141
|
# Current.new.user = "DHH" # => NoMethodError
|
135
142
|
# Current.new.user # => NoMethodError
|
136
143
|
#
|
137
|
-
# Or pass <tt>instance_accessor: false</tt>, to
|
144
|
+
# Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
|
138
145
|
#
|
139
146
|
# class Current
|
140
|
-
#
|
147
|
+
# thread_mattr_accessor :user, instance_accessor: false
|
141
148
|
# end
|
142
149
|
#
|
143
150
|
# Current.new.user = "DHH" # => NoMethodError
|
144
151
|
# Current.new.user # => NoMethodError
|
145
|
-
def thread_mattr_accessor(*syms)
|
146
|
-
thread_mattr_reader(*syms)
|
147
|
-
thread_mattr_writer(*syms)
|
152
|
+
def thread_mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil)
|
153
|
+
thread_mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default)
|
154
|
+
thread_mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor)
|
148
155
|
end
|
149
156
|
alias :thread_cattr_accessor :thread_mattr_accessor
|
150
157
|
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,16 @@ 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 = /[^\]]
|
202
|
+
definition = /[^\]]=\z/.match?(method) ? "arg" : "..."
|
180
203
|
|
181
204
|
# The following generated method calls the target exactly once, storing
|
182
205
|
# the returned value in a dummy variable.
|
@@ -186,33 +209,35 @@ class Module
|
|
186
209
|
# whereas conceptually, from the user point of view, the delegator should
|
187
210
|
# be doing one call.
|
188
211
|
if allow_nil
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
"
|
193
|
-
" _
|
194
|
-
"
|
195
|
-
|
196
|
-
|
212
|
+
method = method.to_s
|
213
|
+
|
214
|
+
method_def <<
|
215
|
+
"def #{method_name}(#{definition})" <<
|
216
|
+
" _ = #{to}" <<
|
217
|
+
" if !_.nil? || nil.respond_to?(:#{method})" <<
|
218
|
+
" _.#{method}(#{definition})" <<
|
219
|
+
" end" <<
|
220
|
+
"end"
|
197
221
|
else
|
198
|
-
|
222
|
+
method = method.to_s
|
223
|
+
method_name = method_name.to_s
|
199
224
|
|
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"
|
225
|
+
method_def <<
|
226
|
+
"def #{method_name}(#{definition})" <<
|
227
|
+
" _ = #{to}" <<
|
228
|
+
" _.#{method}(#{definition})" <<
|
229
|
+
"rescue NoMethodError => e" <<
|
230
|
+
" if _.nil? && e.name == :#{method}" <<
|
231
|
+
%( raise DelegationError, "#{self}##{method_name} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}") <<
|
232
|
+
" else" <<
|
233
|
+
" raise" <<
|
234
|
+
" end" <<
|
210
235
|
"end"
|
211
|
-
].join ";"
|
212
236
|
end
|
213
|
-
|
214
|
-
module_eval(method_def, file, line)
|
215
237
|
end
|
238
|
+
module_eval(method_def.join(";"), file, line)
|
239
|
+
private(*method_names) if private
|
240
|
+
method_names
|
216
241
|
end
|
217
242
|
|
218
243
|
# When building decorators, a common pattern may emerge:
|
@@ -223,7 +248,7 @@ class Module
|
|
223
248
|
# end
|
224
249
|
#
|
225
250
|
# def person
|
226
|
-
#
|
251
|
+
# detail.person || creator
|
227
252
|
# end
|
228
253
|
#
|
229
254
|
# private
|
@@ -246,7 +271,7 @@ class Module
|
|
246
271
|
# end
|
247
272
|
#
|
248
273
|
# def person
|
249
|
-
#
|
274
|
+
# detail.person || creator
|
250
275
|
# end
|
251
276
|
# end
|
252
277
|
#
|
@@ -254,8 +279,14 @@ class Module
|
|
254
279
|
# variables, methods, constants, etc.
|
255
280
|
#
|
256
281
|
# The delegated method must be public on the target, otherwise it will
|
257
|
-
# raise +
|
258
|
-
|
282
|
+
# raise +DelegationError+. If you wish to instead return +nil+,
|
283
|
+
# use the <tt>:allow_nil</tt> option.
|
284
|
+
#
|
285
|
+
# The <tt>marshal_dump</tt> and <tt>_dump</tt> methods are exempt from
|
286
|
+
# delegation due to possible interference when calling
|
287
|
+
# <tt>Marshal.dump(object)</tt>, should the delegation target method
|
288
|
+
# of <tt>object</tt> add or remove instance variables.
|
289
|
+
def delegate_missing_to(target, allow_nil: nil)
|
259
290
|
target = target.to_s
|
260
291
|
target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target)
|
261
292
|
|
@@ -264,6 +295,7 @@ class Module
|
|
264
295
|
# It may look like an oversight, but we deliberately do not pass
|
265
296
|
# +include_private+, because they do not get delegated.
|
266
297
|
|
298
|
+
return false if name == :marshal_dump || name == :_dump
|
267
299
|
#{target}.respond_to?(name) || super
|
268
300
|
end
|
269
301
|
|
@@ -275,13 +307,18 @@ class Module
|
|
275
307
|
super
|
276
308
|
rescue NoMethodError
|
277
309
|
if #{target}.nil?
|
278
|
-
|
310
|
+
if #{allow_nil == true}
|
311
|
+
nil
|
312
|
+
else
|
313
|
+
raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
|
314
|
+
end
|
279
315
|
else
|
280
316
|
raise
|
281
317
|
end
|
282
318
|
end
|
283
319
|
end
|
284
320
|
end
|
321
|
+
ruby2_keywords(:method_missing)
|
285
322
|
RUBY
|
286
323
|
end
|
287
324
|
end
|