activesupport 4.0.12 → 7.0.2.4
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 +5 -5
- data/CHANGELOG.md +249 -501
- data/MIT-LICENSE +2 -2
- data/README.rdoc +10 -5
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/all.rb +5 -3
- data/lib/active_support/array_inquirer.rb +48 -0
- data/lib/active_support/backtrace_cleaner.rb +41 -13
- data/lib/active_support/benchmarkable.rb +7 -15
- data/lib/active_support/builder.rb +3 -1
- data/lib/active_support/cache/file_store.rb +96 -74
- data/lib/active_support/cache/mem_cache_store.rb +211 -103
- data/lib/active_support/cache/memory_store.rb +90 -58
- data/lib/active_support/cache/null_store.rb +19 -7
- data/lib/active_support/cache/redis_cache_store.rb +468 -0
- data/lib/active_support/cache/strategy/local_cache.rb +86 -83
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
- data/lib/active_support/cache.rb +580 -241
- data/lib/active_support/callbacks.rb +812 -425
- data/lib/active_support/code_generator.rb +65 -0
- data/lib/active_support/concern.rb +103 -14
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +33 -0
- data/lib/active_support/concurrency/share_lock.rb +226 -0
- data/lib/active_support/configurable.rb +21 -19
- data/lib/active_support/configuration_file.rb +51 -0
- data/lib/active_support/core_ext/array/access.rb +47 -1
- data/lib/active_support/core_ext/array/conversions.rb +35 -44
- 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/extract_options.rb +2 -0
- data/lib/active_support/core_ext/array/grouping.rb +26 -16
- data/lib/active_support/core_ext/array/inquiry.rb +19 -0
- data/lib/active_support/core_ext/array/wrap.rb +7 -4
- data/lib/active_support/core_ext/array.rb +10 -7
- data/lib/active_support/core_ext/benchmark.rb +5 -3
- data/lib/active_support/core_ext/big_decimal/conversions.rb +9 -26
- data/lib/active_support/core_ext/big_decimal.rb +3 -1
- data/lib/active_support/core_ext/class/attribute.rb +52 -49
- data/lib/active_support/core_ext/class/attribute_accessors.rb +5 -169
- data/lib/active_support/core_ext/class/subclasses.rb +25 -26
- data/lib/active_support/core_ext/class.rb +4 -4
- data/lib/active_support/core_ext/date/acts_like.rb +3 -1
- data/lib/active_support/core_ext/date/blank.rb +14 -0
- data/lib/active_support/core_ext/date/calculations.rb +31 -18
- data/lib/active_support/core_ext/date/conversions.rb +43 -32
- data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/date/zones.rb +5 -34
- data/lib/active_support/core_ext/date.rb +7 -4
- data/lib/active_support/core_ext/date_and_time/calculations.rb +198 -66
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +31 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
- data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
- data/lib/active_support/core_ext/date_time/blank.rb +14 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +79 -38
- data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +31 -26
- data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/date_time.rb +8 -4
- data/lib/active_support/core_ext/digest/uuid.rb +79 -0
- data/lib/active_support/core_ext/digest.rb +3 -0
- data/lib/active_support/core_ext/enumerable.rb +249 -17
- data/lib/active_support/core_ext/file/atomic.rb +41 -32
- data/lib/active_support/core_ext/file.rb +3 -1
- data/lib/active_support/core_ext/hash/conversions.rb +71 -49
- data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +14 -5
- data/lib/active_support/core_ext/hash/indifferent_access.rb +5 -3
- data/lib/active_support/core_ext/hash/keys.rb +39 -56
- data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
- data/lib/active_support/core_ext/hash/slice.rb +8 -23
- data/lib/active_support/core_ext/hash.rb +10 -8
- data/lib/active_support/core_ext/integer/inflections.rb +3 -1
- data/lib/active_support/core_ext/integer/multiple.rb +3 -1
- data/lib/active_support/core_ext/integer/time.rb +11 -33
- data/lib/active_support/core_ext/integer.rb +5 -3
- data/lib/active_support/core_ext/kernel/concern.rb +14 -0
- data/lib/active_support/core_ext/kernel/reporting.rb +9 -78
- data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
- data/lib/active_support/core_ext/kernel.rb +5 -4
- data/lib/active_support/core_ext/load_error.rb +5 -21
- data/lib/active_support/core_ext/module/aliasing.rb +6 -44
- data/lib/active_support/core_ext/module/anonymous.rb +12 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +8 -8
- data/lib/active_support/core_ext/module/attribute_accessors.rb +186 -44
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +157 -0
- data/lib/active_support/core_ext/module/concerning.rb +140 -0
- data/lib/active_support/core_ext/module/delegation.rb +172 -45
- data/lib/active_support/core_ext/module/deprecation.rb +3 -3
- data/lib/active_support/core_ext/module/introspection.rb +23 -38
- data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
- data/lib/active_support/core_ext/module/remove_method.rb +8 -3
- data/lib/active_support/core_ext/module.rb +13 -10
- data/lib/active_support/core_ext/name_error.rb +45 -4
- data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +135 -127
- data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
- data/lib/active_support/core_ext/numeric/time.rb +37 -50
- data/lib/active_support/core_ext/numeric.rb +6 -3
- data/lib/active_support/core_ext/object/acts_like.rb +41 -6
- data/lib/active_support/core_ext/object/blank.rb +70 -20
- data/lib/active_support/core_ext/object/conversions.rb +6 -4
- data/lib/active_support/core_ext/object/deep_dup.rb +19 -10
- data/lib/active_support/core_ext/object/duplicable.rb +17 -47
- data/lib/active_support/core_ext/object/inclusion.rb +18 -15
- data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
- data/lib/active_support/core_ext/object/json.rb +244 -0
- data/lib/active_support/core_ext/object/to_param.rb +3 -1
- data/lib/active_support/core_ext/object/to_query.rb +21 -8
- data/lib/active_support/core_ext/object/try.rb +106 -26
- data/lib/active_support/core_ext/object/with_options.rb +64 -5
- data/lib/active_support/core_ext/object.rb +14 -12
- 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 +57 -0
- data/lib/active_support/core_ext/range/conversions.rb +37 -15
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/range/each.rb +18 -17
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +7 -0
- data/lib/active_support/core_ext/range/overlaps.rb +2 -0
- data/lib/active_support/core_ext/range.rb +7 -4
- data/lib/active_support/core_ext/regexp.rb +10 -1
- data/lib/active_support/core_ext/securerandom.rb +45 -0
- data/lib/active_support/core_ext/string/access.rb +42 -51
- data/lib/active_support/core_ext/string/behavior.rb +3 -1
- data/lib/active_support/core_ext/string/conversions.rb +18 -13
- data/lib/active_support/core_ext/string/exclude.rb +5 -3
- data/lib/active_support/core_ext/string/filters.rb +97 -7
- data/lib/active_support/core_ext/string/indent.rb +6 -4
- data/lib/active_support/core_ext/string/inflections.rb +106 -25
- data/lib/active_support/core_ext/string/inquiry.rb +4 -1
- data/lib/active_support/core_ext/string/multibyte.rb +18 -9
- data/lib/active_support/core_ext/string/output_safety.rb +227 -54
- data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
- data/lib/active_support/core_ext/string/strip.rb +6 -5
- data/lib/active_support/core_ext/string/zones.rb +4 -1
- data/lib/active_support/core_ext/string.rb +15 -13
- 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/acts_like.rb +3 -1
- data/lib/active_support/core_ext/time/calculations.rb +178 -116
- data/lib/active_support/core_ext/time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/time/conversions.rb +37 -25
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/time/zones.rb +44 -42
- data/lib/active_support/core_ext/time.rb +8 -5
- data/lib/active_support/core_ext/uri.rb +4 -25
- data/lib/active_support/core_ext.rb +4 -2
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +226 -0
- data/lib/active_support/dependencies/autoload.rb +3 -1
- data/lib/active_support/dependencies/interlock.rb +49 -0
- data/lib/active_support/dependencies/require_dependency.rb +28 -0
- data/lib/active_support/dependencies.rb +71 -696
- data/lib/active_support/deprecation/behaviors.rb +65 -16
- data/lib/active_support/deprecation/constant_accessor.rb +52 -0
- data/lib/active_support/deprecation/disallowed.rb +56 -0
- data/lib/active_support/deprecation/instance_delegator.rb +16 -2
- data/lib/active_support/deprecation/method_wrappers.rb +62 -21
- data/lib/active_support/deprecation/proxy_wrappers.rb +82 -31
- data/lib/active_support/deprecation/reporting.rb +81 -18
- data/lib/active_support/deprecation.rb +19 -11
- data/lib/active_support/descendants_tracker.rb +192 -34
- data/lib/active_support/digest.rb +22 -0
- data/lib/active_support/duration/iso8601_parser.rb +123 -0
- data/lib/active_support/duration/iso8601_serializer.rb +67 -0
- data/lib/active_support/duration.rb +437 -39
- data/lib/active_support/encrypted_configuration.rb +56 -0
- data/lib/active_support/encrypted_file.rb +117 -0
- 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 +170 -0
- 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 +151 -0
- data/lib/active_support/executor/test_helper.rb +7 -0
- data/lib/active_support/executor.rb +8 -0
- data/lib/active_support/file_update_checker.rb +62 -37
- data/lib/active_support/fork_tracker.rb +71 -0
- data/lib/active_support/gem_version.rb +17 -0
- data/lib/active_support/gzip.rb +7 -5
- data/lib/active_support/hash_with_indifferent_access.rb +207 -54
- data/lib/active_support/html_safe_translation.rb +43 -0
- data/lib/active_support/i18n.rb +10 -6
- data/lib/active_support/i18n_railtie.rb +48 -19
- data/lib/active_support/inflections.rb +19 -12
- data/lib/active_support/inflector/inflections.rb +97 -37
- data/lib/active_support/inflector/methods.rb +192 -157
- data/lib/active_support/inflector/transliterate.rb +83 -33
- data/lib/active_support/inflector.rb +7 -5
- data/lib/active_support/isolated_execution_state.rb +64 -0
- data/lib/active_support/json/decoding.rb +37 -42
- data/lib/active_support/json/encoding.rb +93 -293
- data/lib/active_support/json.rb +4 -2
- data/lib/active_support/key_generator.rb +30 -47
- data/lib/active_support/lazy_load_hooks.rb +54 -21
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/locale/en.yml +10 -4
- data/lib/active_support/log_subscriber/test_helper.rb +14 -12
- data/lib/active_support/log_subscriber.rb +61 -18
- data/lib/active_support/logger.rb +40 -4
- data/lib/active_support/logger_silence.rb +17 -20
- data/lib/active_support/logger_thread_safe_level.rb +69 -0
- data/lib/active_support/message_encryptor.rb +178 -55
- data/lib/active_support/message_verifier.rb +195 -26
- 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 +45 -92
- data/lib/active_support/multibyte/unicode.rb +44 -377
- data/lib/active_support/multibyte.rb +5 -3
- data/lib/active_support/notifications/fanout.rb +177 -44
- data/lib/active_support/notifications/instrumenter.rb +117 -17
- data/lib/active_support/notifications.rb +106 -39
- data/lib/active_support/number_helper/number_converter.rb +181 -0
- data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +30 -0
- data/lib/active_support/number_helper/number_to_human_converter.rb +69 -0
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +60 -0
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
- data/lib/active_support/number_helper/number_to_phone_converter.rb +59 -0
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +59 -0
- data/lib/active_support/number_helper/rounding_helper.rb +46 -0
- data/lib/active_support/number_helper.rb +152 -394
- data/lib/active_support/option_merger.rb +18 -5
- data/lib/active_support/ordered_hash.rb +8 -6
- data/lib/active_support/ordered_options.rb +43 -7
- data/lib/active_support/parameter_filter.rb +138 -0
- data/lib/active_support/per_thread_registry.rb +24 -11
- data/lib/active_support/proxy_object.rb +2 -0
- data/lib/active_support/rails.rb +10 -11
- data/lib/active_support/railtie.rb +118 -12
- data/lib/active_support/reloader.rb +130 -0
- data/lib/active_support/rescuable.rb +112 -57
- 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 +38 -0
- data/lib/active_support/string_inquirer.rb +11 -4
- data/lib/active_support/subscriber.rb +109 -39
- data/lib/active_support/tagged_logging.rb +54 -17
- data/lib/active_support/test_case.rb +121 -37
- data/lib/active_support/testing/assertions.rb +177 -39
- data/lib/active_support/testing/autorun.rb +5 -3
- data/lib/active_support/testing/constant_lookup.rb +3 -6
- data/lib/active_support/testing/declarative.rb +10 -22
- data/lib/active_support/testing/deprecation.rb +65 -11
- data/lib/active_support/testing/file_fixtures.rb +38 -0
- data/lib/active_support/testing/isolation.rb +56 -87
- data/lib/active_support/testing/method_call_assertions.rb +70 -0
- 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/setup_and_teardown.rb +30 -10
- data/lib/active_support/testing/stream.rb +41 -0
- data/lib/active_support/testing/tagged_logging.rb +6 -4
- data/lib/active_support/testing/time_helpers.rb +246 -0
- data/lib/active_support/time.rb +13 -13
- data/lib/active_support/time_with_zone.rb +315 -90
- data/lib/active_support/values/time_zone.rb +306 -135
- data/lib/active_support/version.rb +6 -7
- data/lib/active_support/xml_mini/jdom.rb +117 -115
- data/lib/active_support/xml_mini/libxml.rb +22 -21
- data/lib/active_support/xml_mini/libxmlsax.rb +17 -19
- data/lib/active_support/xml_mini/nokogiri.rb +19 -19
- data/lib/active_support/xml_mini/nokogirisax.rb +16 -17
- data/lib/active_support/xml_mini/rexml.rb +25 -17
- data/lib/active_support/xml_mini.rb +67 -56
- data/lib/active_support.rb +58 -3
- metadata +125 -66
- data/lib/active_support/basic_object.rb +0 -11
- data/lib/active_support/buffered_logger.rb +0 -21
- data/lib/active_support/concurrency/latch.rb +0 -27
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
- data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
- data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -40
- data/lib/active_support/core_ext/date_time/zones.rb +0 -24
- data/lib/active_support/core_ext/hash/diff.rb +0 -14
- data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
- data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
- data/lib/active_support/core_ext/logger.rb +0 -67
- data/lib/active_support/core_ext/marshal.rb +0 -21
- data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
- data/lib/active_support/core_ext/module/reachable.rb +0 -8
- data/lib/active_support/core_ext/object/to_json.rb +0 -27
- data/lib/active_support/core_ext/proc.rb +0 -17
- data/lib/active_support/core_ext/range/include_range.rb +0 -23
- data/lib/active_support/core_ext/string/encoding.rb +0 -8
- data/lib/active_support/core_ext/struct.rb +0 -6
- data/lib/active_support/core_ext/thread.rb +0 -79
- data/lib/active_support/core_ext/time/marshal.rb +0 -30
- data/lib/active_support/file_watcher.rb +0 -36
- data/lib/active_support/json/variable.rb +0 -18
- data/lib/active_support/testing/pending.rb +0 -14
- data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -0,0 +1,140 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/concern"
|
4
|
+
|
5
|
+
class Module
|
6
|
+
# = Bite-sized separation of concerns
|
7
|
+
#
|
8
|
+
# We often find ourselves with a medium-sized chunk of behavior that we'd
|
9
|
+
# like to extract, but only mix in to a single class.
|
10
|
+
#
|
11
|
+
# Extracting a plain old Ruby object to encapsulate it and collaborate or
|
12
|
+
# delegate to the original object is often a good choice, but when there's
|
13
|
+
# no additional state to encapsulate or we're making DSL-style declarations
|
14
|
+
# about the parent class, introducing new collaborators can obfuscate rather
|
15
|
+
# than simplify.
|
16
|
+
#
|
17
|
+
# The typical route is to just dump everything in a monolithic class, perhaps
|
18
|
+
# with a comment, as a least-bad alternative. Using modules in separate files
|
19
|
+
# means tedious sifting to get a big-picture view.
|
20
|
+
#
|
21
|
+
# = Dissatisfying ways to separate small concerns
|
22
|
+
#
|
23
|
+
# == Using comments:
|
24
|
+
#
|
25
|
+
# class Todo < ApplicationRecord
|
26
|
+
# # Other todo implementation
|
27
|
+
# # ...
|
28
|
+
#
|
29
|
+
# ## Event tracking
|
30
|
+
# has_many :events
|
31
|
+
#
|
32
|
+
# before_create :track_creation
|
33
|
+
#
|
34
|
+
# private
|
35
|
+
# def track_creation
|
36
|
+
# # ...
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# == With an inline module:
|
41
|
+
#
|
42
|
+
# Noisy syntax.
|
43
|
+
#
|
44
|
+
# class Todo < ApplicationRecord
|
45
|
+
# # Other todo implementation
|
46
|
+
# # ...
|
47
|
+
#
|
48
|
+
# module EventTracking
|
49
|
+
# extend ActiveSupport::Concern
|
50
|
+
#
|
51
|
+
# included do
|
52
|
+
# has_many :events
|
53
|
+
# before_create :track_creation
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# private
|
57
|
+
# def track_creation
|
58
|
+
# # ...
|
59
|
+
# end
|
60
|
+
# end
|
61
|
+
# include EventTracking
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# == Mix-in noise exiled to its own file:
|
65
|
+
#
|
66
|
+
# Once our chunk of behavior starts pushing the scroll-to-understand-it
|
67
|
+
# boundary, we give in and move it to a separate file. At this size, the
|
68
|
+
# increased overhead can be a reasonable tradeoff even if it reduces our
|
69
|
+
# at-a-glance perception of how things work.
|
70
|
+
#
|
71
|
+
# class Todo < ApplicationRecord
|
72
|
+
# # Other todo implementation
|
73
|
+
# # ...
|
74
|
+
#
|
75
|
+
# include TodoEventTracking
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# = Introducing Module#concerning
|
79
|
+
#
|
80
|
+
# By quieting the mix-in noise, we arrive at a natural, low-ceremony way to
|
81
|
+
# separate bite-sized concerns.
|
82
|
+
#
|
83
|
+
# class Todo < ApplicationRecord
|
84
|
+
# # Other todo implementation
|
85
|
+
# # ...
|
86
|
+
#
|
87
|
+
# concerning :EventTracking do
|
88
|
+
# included do
|
89
|
+
# has_many :events
|
90
|
+
# before_create :track_creation
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# private
|
94
|
+
# def track_creation
|
95
|
+
# # ...
|
96
|
+
# end
|
97
|
+
# end
|
98
|
+
# end
|
99
|
+
#
|
100
|
+
# Todo.ancestors
|
101
|
+
# # => [Todo, Todo::EventTracking, ApplicationRecord, Object]
|
102
|
+
#
|
103
|
+
# This small step has some wonderful ripple effects. We can
|
104
|
+
# * grok the behavior of our class in one glance,
|
105
|
+
# * clean up monolithic junk-drawer classes by separating their concerns, and
|
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.
|
112
|
+
module Concerning
|
113
|
+
# Define a new concern and mix it in.
|
114
|
+
def concerning(topic, prepend: false, &block)
|
115
|
+
method = prepend ? :prepend : :include
|
116
|
+
__send__(method, concern(topic, &block))
|
117
|
+
end
|
118
|
+
|
119
|
+
# A low-cruft shortcut to define a concern.
|
120
|
+
#
|
121
|
+
# concern :EventTracking do
|
122
|
+
# ...
|
123
|
+
# end
|
124
|
+
#
|
125
|
+
# is equivalent to
|
126
|
+
#
|
127
|
+
# module EventTracking
|
128
|
+
# extend ActiveSupport::Concern
|
129
|
+
#
|
130
|
+
# ...
|
131
|
+
# end
|
132
|
+
def concern(topic, &module_definition)
|
133
|
+
const_set topic, Module.new {
|
134
|
+
extend ::ActiveSupport::Concern
|
135
|
+
module_eval(&module_definition)
|
136
|
+
}
|
137
|
+
end
|
138
|
+
end
|
139
|
+
include Concerning
|
140
|
+
end
|
@@ -1,7 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "set"
|
4
|
+
|
1
5
|
class Module
|
6
|
+
# Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+
|
7
|
+
# option is not used.
|
8
|
+
class DelegationError < NoMethodError; end
|
9
|
+
|
10
|
+
RUBY_RESERVED_KEYWORDS = %w(alias and BEGIN begin break case class def defined? do
|
11
|
+
else elsif END end ensure false for if in module next nil not or redo rescue retry
|
12
|
+
return self super then true undef unless until when while yield)
|
13
|
+
DELEGATION_RESERVED_KEYWORDS = %w(_ arg args block)
|
14
|
+
DELEGATION_RESERVED_METHOD_NAMES = Set.new(
|
15
|
+
RUBY_RESERVED_KEYWORDS + DELEGATION_RESERVED_KEYWORDS
|
16
|
+
).freeze
|
17
|
+
|
2
18
|
# Provides a +delegate+ class method to easily expose contained objects'
|
3
19
|
# public methods as your own.
|
4
20
|
#
|
21
|
+
# ==== Options
|
22
|
+
# * <tt>:to</tt> - Specifies the target object name as a symbol or string
|
23
|
+
# * <tt>:prefix</tt> - Prefixes the new method with the target name or a custom prefix
|
24
|
+
# * <tt>:allow_nil</tt> - If set to true, prevents a +Module::DelegationError+
|
25
|
+
# from being raised
|
26
|
+
# * <tt>:private</tt> - If set to true, changes method visibility to private
|
27
|
+
#
|
5
28
|
# The macro receives one or more method names (specified as symbols or
|
6
29
|
# strings) and the name of the target object via the <tt>:to</tt> option
|
7
30
|
# (also a symbol or string).
|
@@ -91,19 +114,34 @@ class Module
|
|
91
114
|
# invoice.customer_name # => 'John Doe'
|
92
115
|
# invoice.customer_address # => 'Vimmersvej 13'
|
93
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
|
+
#
|
94
134
|
# If the target is +nil+ and does not respond to the delegated method a
|
95
|
-
# +
|
96
|
-
#
|
97
|
-
# <tt>:allow_nil</tt> option: If the target is not +nil+, or it is and
|
98
|
-
# responds to the method, everything works as usual. But if it is +nil+ and
|
99
|
-
# 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.
|
100
137
|
#
|
101
138
|
# class User < ActiveRecord::Base
|
102
139
|
# has_one :profile
|
103
140
|
# delegate :age, to: :profile
|
104
141
|
# end
|
105
142
|
#
|
106
|
-
# User.new.age
|
143
|
+
# User.new.age
|
144
|
+
# # => Module::DelegationError: User#age delegated to profile.age, but profile is nil
|
107
145
|
#
|
108
146
|
# But if not having a profile yet is fine and should not be an error
|
109
147
|
# condition:
|
@@ -129,69 +167,158 @@ class Module
|
|
129
167
|
#
|
130
168
|
# Foo.new("Bar").name # raises NoMethodError: undefined method `name'
|
131
169
|
#
|
132
|
-
|
133
|
-
|
134
|
-
unless
|
135
|
-
raise ArgumentError,
|
170
|
+
# The target method must be public, otherwise it will raise +NoMethodError+.
|
171
|
+
def delegate(*methods, to: nil, prefix: nil, allow_nil: nil, private: nil)
|
172
|
+
unless to
|
173
|
+
raise ArgumentError, "Delegation needs a target. Supply a keyword argument 'to' (e.g. delegate :hello, to: :greeter)."
|
136
174
|
end
|
137
175
|
|
138
|
-
prefix
|
139
|
-
|
140
|
-
if prefix == true && to =~ /^[^a-z_]/
|
141
|
-
raise ArgumentError, 'Can only automatically set the delegation prefix when delegating to a method.'
|
176
|
+
if prefix == true && /^[^a-z_]/.match?(to)
|
177
|
+
raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."
|
142
178
|
end
|
143
179
|
|
144
180
|
method_prefix = \
|
145
181
|
if prefix
|
146
182
|
"#{prefix == true ? to : prefix}_"
|
147
183
|
else
|
148
|
-
|
184
|
+
""
|
149
185
|
end
|
150
186
|
|
151
|
-
|
152
|
-
line =
|
187
|
+
location = caller_locations(1, 1).first
|
188
|
+
file, line = location.path, location.lineno
|
153
189
|
|
154
190
|
to = to.to_s
|
155
|
-
to =
|
191
|
+
to = "self.#{to}" if DELEGATION_RESERVED_METHOD_NAMES.include?(to)
|
192
|
+
|
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
|
156
199
|
|
157
|
-
methods.each do |method|
|
158
200
|
# Attribute writer methods only accept one argument. Makes sure []=
|
159
201
|
# methods still accept two arguments.
|
160
|
-
definition =
|
202
|
+
definition = /[^\]]=\z/.match?(method) ? "arg" : "..."
|
161
203
|
|
162
|
-
# The following generated
|
204
|
+
# The following generated method calls the target exactly once, storing
|
163
205
|
# the returned value in a dummy variable.
|
164
206
|
#
|
165
207
|
# Reason is twofold: On one hand doing less calls is in general better.
|
166
208
|
# On the other hand it could be that the target has side-effects,
|
167
|
-
# whereas
|
209
|
+
# whereas conceptually, from the user point of view, the delegator should
|
168
210
|
# be doing one call.
|
169
211
|
if allow_nil
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
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"
|
178
221
|
else
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
end
|
193
|
-
EOS
|
222
|
+
method = method.to_s
|
223
|
+
method_name = method_name.to_s
|
224
|
+
|
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" <<
|
235
|
+
"end"
|
194
236
|
end
|
195
237
|
end
|
238
|
+
module_eval(method_def.join(";"), file, line)
|
239
|
+
private(*method_names) if private
|
240
|
+
method_names
|
241
|
+
end
|
242
|
+
|
243
|
+
# When building decorators, a common pattern may emerge:
|
244
|
+
#
|
245
|
+
# class Partition
|
246
|
+
# def initialize(event)
|
247
|
+
# @event = event
|
248
|
+
# end
|
249
|
+
#
|
250
|
+
# def person
|
251
|
+
# detail.person || creator
|
252
|
+
# end
|
253
|
+
#
|
254
|
+
# private
|
255
|
+
# def respond_to_missing?(name, include_private = false)
|
256
|
+
# @event.respond_to?(name, include_private)
|
257
|
+
# end
|
258
|
+
#
|
259
|
+
# def method_missing(method, *args, &block)
|
260
|
+
# @event.send(method, *args, &block)
|
261
|
+
# end
|
262
|
+
# end
|
263
|
+
#
|
264
|
+
# With <tt>Module#delegate_missing_to</tt>, the above is condensed to:
|
265
|
+
#
|
266
|
+
# class Partition
|
267
|
+
# delegate_missing_to :@event
|
268
|
+
#
|
269
|
+
# def initialize(event)
|
270
|
+
# @event = event
|
271
|
+
# end
|
272
|
+
#
|
273
|
+
# def person
|
274
|
+
# detail.person || creator
|
275
|
+
# end
|
276
|
+
# end
|
277
|
+
#
|
278
|
+
# The target can be anything callable within the object, e.g. instance
|
279
|
+
# variables, methods, constants, etc.
|
280
|
+
#
|
281
|
+
# The delegated method must be public on the target, otherwise it will
|
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)
|
290
|
+
target = target.to_s
|
291
|
+
target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target)
|
292
|
+
|
293
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
294
|
+
def respond_to_missing?(name, include_private = false)
|
295
|
+
# It may look like an oversight, but we deliberately do not pass
|
296
|
+
# +include_private+, because they do not get delegated.
|
297
|
+
|
298
|
+
return false if name == :marshal_dump || name == :_dump
|
299
|
+
#{target}.respond_to?(name) || super
|
300
|
+
end
|
301
|
+
|
302
|
+
def method_missing(method, *args, &block)
|
303
|
+
if #{target}.respond_to?(method)
|
304
|
+
#{target}.public_send(method, *args, &block)
|
305
|
+
else
|
306
|
+
begin
|
307
|
+
super
|
308
|
+
rescue NoMethodError
|
309
|
+
if #{target}.nil?
|
310
|
+
if #{allow_nil == true}
|
311
|
+
nil
|
312
|
+
else
|
313
|
+
raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
|
314
|
+
end
|
315
|
+
else
|
316
|
+
raise
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
ruby2_keywords(:method_missing)
|
322
|
+
RUBY
|
196
323
|
end
|
197
324
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Module
|
4
4
|
# deprecate :foo
|
@@ -15,8 +15,8 @@ class Module
|
|
15
15
|
#
|
16
16
|
# class MyLib::Deprecator
|
17
17
|
# def deprecation_warning(deprecated_method_name, message, caller_backtrace = nil)
|
18
|
-
#
|
19
|
-
#
|
18
|
+
# message = "#{deprecated_method_name} is deprecated and will be removed from MyLibrary | #{message}"
|
19
|
+
# Kernel.warn message
|
20
20
|
# end
|
21
21
|
# end
|
22
22
|
def deprecate(*method_names)
|
@@ -1,14 +1,19 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/string/filters"
|
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
|
8
|
-
if defined?
|
9
|
+
# M::N.module_parent_name # => "M"
|
10
|
+
def module_parent_name
|
11
|
+
if defined?(@parent_name)
|
9
12
|
@parent_name
|
10
13
|
else
|
11
|
-
|
14
|
+
parent_name = name =~ /::[^:]+\z/ ? -$` : nil
|
15
|
+
@parent_name = parent_name unless frozen?
|
16
|
+
parent_name
|
12
17
|
end
|
13
18
|
end
|
14
19
|
|
@@ -20,15 +25,15 @@ class Module
|
|
20
25
|
# end
|
21
26
|
# X = M::N
|
22
27
|
#
|
23
|
-
# M::N.
|
24
|
-
# X.
|
28
|
+
# M::N.module_parent # => M
|
29
|
+
# X.module_parent # => M
|
25
30
|
#
|
26
31
|
# The parent of top-level and anonymous modules is Object.
|
27
32
|
#
|
28
|
-
# M.
|
29
|
-
# Module.new.
|
30
|
-
def
|
31
|
-
|
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
|
32
37
|
end
|
33
38
|
|
34
39
|
# Returns all the parents of this module according to its name, ordered from
|
@@ -40,39 +45,19 @@ class Module
|
|
40
45
|
# end
|
41
46
|
# X = M::N
|
42
47
|
#
|
43
|
-
# M.
|
44
|
-
# M::N.
|
45
|
-
# X.
|
46
|
-
def
|
48
|
+
# M.module_parents # => [Object]
|
49
|
+
# M::N.module_parents # => [M, Object]
|
50
|
+
# X.module_parents # => [M, Object]
|
51
|
+
def module_parents
|
47
52
|
parents = []
|
48
|
-
if
|
49
|
-
parts =
|
53
|
+
if module_parent_name
|
54
|
+
parts = module_parent_name.split("::")
|
50
55
|
until parts.empty?
|
51
|
-
parents << ActiveSupport::Inflector.constantize(parts *
|
56
|
+
parents << ActiveSupport::Inflector.constantize(parts * "::")
|
52
57
|
parts.pop
|
53
58
|
end
|
54
59
|
end
|
55
60
|
parents << Object unless parents.include? Object
|
56
61
|
parents
|
57
62
|
end
|
58
|
-
|
59
|
-
def local_constants #:nodoc:
|
60
|
-
constants(false)
|
61
|
-
end
|
62
|
-
|
63
|
-
# *DEPRECATED*: Use +local_constants+ instead.
|
64
|
-
#
|
65
|
-
# Returns the names of the constants defined locally as strings.
|
66
|
-
#
|
67
|
-
# module M
|
68
|
-
# X = 1
|
69
|
-
# end
|
70
|
-
# M.local_constant_names # => ["X"]
|
71
|
-
#
|
72
|
-
# This method is useful for forward compatibility, since Ruby 1.8 returns
|
73
|
-
# constant names as strings, whereas 1.9 returns them as symbols.
|
74
|
-
def local_constant_names
|
75
|
-
ActiveSupport::Deprecation.warn 'Module#local_constant_names is deprecated, use Module#local_constants instead'
|
76
|
-
local_constants.map { |c| c.to_s }
|
77
|
-
end
|
78
63
|
end
|
@@ -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,12 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/module/redefine_method"
|
4
|
+
|
1
5
|
class Module
|
6
|
+
# Removes the named method, if it exists.
|
2
7
|
def remove_possible_method(method)
|
3
8
|
if method_defined?(method) || private_method_defined?(method)
|
4
9
|
undef_method(method)
|
5
10
|
end
|
6
11
|
end
|
7
12
|
|
8
|
-
|
9
|
-
|
10
|
-
|
13
|
+
# Removes the named singleton method, if it exists.
|
14
|
+
def remove_possible_singleton_method(method)
|
15
|
+
singleton_class.remove_possible_method(method)
|
11
16
|
end
|
12
17
|
end
|
@@ -1,10 +1,13 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/module/aliasing"
|
4
|
+
require "active_support/core_ext/module/introspection"
|
5
|
+
require "active_support/core_ext/module/anonymous"
|
6
|
+
require "active_support/core_ext/module/attribute_accessors"
|
7
|
+
require "active_support/core_ext/module/attribute_accessors_per_thread"
|
8
|
+
require "active_support/core_ext/module/attr_internal"
|
9
|
+
require "active_support/core_ext/module/concerning"
|
10
|
+
require "active_support/core_ext/module/delegation"
|
11
|
+
require "active_support/core_ext/module/deprecation"
|
12
|
+
require "active_support/core_ext/module/redefine_method"
|
13
|
+
require "active_support/core_ext/module/remove_method"
|
@@ -1,18 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class NameError
|
2
4
|
# Extract the name of the missing constant from the exception message.
|
5
|
+
#
|
6
|
+
# begin
|
7
|
+
# HelloWorld
|
8
|
+
# rescue NameError => e
|
9
|
+
# e.missing_name
|
10
|
+
# end
|
11
|
+
# # => "HelloWorld"
|
3
12
|
def missing_name
|
4
|
-
|
5
|
-
|
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
|
6
33
|
end
|
7
34
|
end
|
8
35
|
|
9
36
|
# Was this exception raised because the given name was missing?
|
37
|
+
#
|
38
|
+
# begin
|
39
|
+
# HelloWorld
|
40
|
+
# rescue NameError => e
|
41
|
+
# e.missing_name?("HelloWorld")
|
42
|
+
# end
|
43
|
+
# # => true
|
10
44
|
def missing_name?(name)
|
11
45
|
if name.is_a? Symbol
|
12
|
-
|
13
|
-
last_name == name.to_s
|
46
|
+
self.name == name
|
14
47
|
else
|
15
48
|
missing_name == name.to_s
|
16
49
|
end
|
17
50
|
end
|
51
|
+
|
52
|
+
private
|
53
|
+
UNBOUND_METHOD_MODULE_NAME = Module.instance_method(:name)
|
54
|
+
private_constant :UNBOUND_METHOD_MODULE_NAME
|
55
|
+
|
56
|
+
def real_mod_name(mod)
|
57
|
+
UNBOUND_METHOD_MODULE_NAME.bind_call(mod)
|
58
|
+
end
|
18
59
|
end
|