activesupport 1.2.4 → 8.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +505 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +40 -0
- data/lib/active_support/actionable_error.rb +50 -0
- data/lib/active_support/all.rb +5 -0
- data/lib/active_support/array_inquirer.rb +50 -0
- data/lib/active_support/backtrace_cleaner.rb +234 -0
- data/lib/active_support/benchmark.rb +21 -0
- data/lib/active_support/benchmarkable.rb +53 -0
- data/lib/active_support/broadcast_logger.rb +238 -0
- data/lib/active_support/builder.rb +8 -0
- data/lib/active_support/cache/coder.rb +153 -0
- data/lib/active_support/cache/entry.rb +134 -0
- data/lib/active_support/cache/file_store.rb +244 -0
- data/lib/active_support/cache/mem_cache_store.rb +288 -0
- data/lib/active_support/cache/memory_store.rb +264 -0
- data/lib/active_support/cache/null_store.rb +62 -0
- data/lib/active_support/cache/redis_cache_store.rb +498 -0
- data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
- data/lib/active_support/cache/strategy/local_cache.rb +246 -0
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
- data/lib/active_support/cache.rb +1170 -0
- data/lib/active_support/callbacks.rb +960 -0
- data/lib/active_support/class_attribute.rb +33 -0
- data/lib/active_support/code_generator.rb +79 -0
- data/lib/active_support/concern.rb +217 -0
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
- data/lib/active_support/concurrency/null_lock.rb +13 -0
- data/lib/active_support/concurrency/share_lock.rb +225 -0
- data/lib/active_support/concurrency/thread_monitor.rb +55 -0
- data/lib/active_support/configurable.rb +193 -0
- data/lib/active_support/configuration_file.rb +60 -0
- data/lib/active_support/continuous_integration.rb +145 -0
- data/lib/active_support/core_ext/array/access.rb +100 -0
- data/lib/active_support/core_ext/array/conversions.rb +209 -26
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- data/lib/active_support/core_ext/array/extract_options.rb +31 -0
- data/lib/active_support/core_ext/array/grouping.rb +109 -0
- data/lib/active_support/core_ext/array/inquiry.rb +19 -0
- data/lib/active_support/core_ext/array/wrap.rb +48 -0
- data/lib/active_support/core_ext/array.rb +8 -4
- data/lib/active_support/core_ext/benchmark.rb +6 -0
- data/lib/active_support/core_ext/big_decimal/conversions.rb +14 -0
- data/lib/active_support/core_ext/big_decimal.rb +3 -0
- data/lib/active_support/core_ext/class/attribute.rb +137 -0
- data/lib/active_support/core_ext/class/attribute_accessors.rb +6 -0
- data/lib/active_support/core_ext/class/subclasses.rb +24 -0
- data/lib/active_support/core_ext/class.rb +4 -0
- data/lib/active_support/core_ext/date/acts_like.rb +10 -0
- data/lib/active_support/core_ext/date/blank.rb +18 -0
- data/lib/active_support/core_ext/date/calculations.rb +161 -0
- data/lib/active_support/core_ext/date/conversions.rb +95 -28
- data/lib/active_support/core_ext/date/zones.rb +8 -0
- data/lib/active_support/core_ext/date.rb +6 -5
- data/lib/active_support/core_ext/date_and_time/calculations.rb +374 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +23 -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 +16 -0
- data/lib/active_support/core_ext/date_time/blank.rb +18 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +215 -0
- data/lib/active_support/core_ext/date_time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +108 -0
- data/lib/active_support/core_ext/date_time.rb +7 -0
- data/lib/active_support/core_ext/digest/uuid.rb +76 -0
- data/lib/active_support/core_ext/digest.rb +3 -0
- data/lib/active_support/core_ext/enumerable.rb +277 -7
- data/lib/active_support/core_ext/erb/util.rb +201 -0
- data/lib/active_support/core_ext/file/atomic.rb +72 -0
- data/lib/active_support/core_ext/file.rb +3 -0
- data/lib/active_support/core_ext/hash/conversions.rb +262 -0
- data/lib/active_support/core_ext/hash/deep_merge.rb +43 -0
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +12 -0
- data/lib/active_support/core_ext/hash/indifferent_access.rb +19 -55
- data/lib/active_support/core_ext/hash/keys.rb +134 -44
- data/lib/active_support/core_ext/hash/reverse_merge.rb +22 -22
- data/lib/active_support/core_ext/hash/slice.rb +27 -0
- data/lib/active_support/core_ext/hash.rb +9 -8
- data/lib/active_support/core_ext/integer/inflections.rb +29 -13
- data/lib/active_support/core_ext/integer/multiple.rb +12 -0
- data/lib/active_support/core_ext/integer/time.rb +22 -0
- data/lib/active_support/core_ext/integer.rb +4 -6
- data/lib/active_support/core_ext/kernel/concern.rb +14 -0
- data/lib/active_support/core_ext/kernel/reporting.rb +45 -0
- data/lib/active_support/core_ext/kernel/singleton_class.rb +8 -0
- data/lib/active_support/core_ext/kernel.rb +4 -78
- data/lib/active_support/core_ext/load_error.rb +6 -35
- data/lib/active_support/core_ext/module/aliasing.rb +31 -0
- data/lib/active_support/core_ext/module/anonymous.rb +30 -0
- data/lib/active_support/core_ext/module/attr_internal.rb +48 -0
- data/lib/active_support/core_ext/module/attribute_accessors.rb +214 -0
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +175 -0
- data/lib/active_support/core_ext/module/concerning.rb +140 -0
- data/lib/active_support/core_ext/module/delegation.rb +225 -0
- data/lib/active_support/core_ext/module/deprecation.rb +25 -0
- data/lib/active_support/core_ext/module/introspection.rb +65 -0
- data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
- data/lib/active_support/core_ext/module/remove_method.rb +17 -0
- data/lib/active_support/core_ext/module.rb +13 -0
- data/lib/active_support/core_ext/name_error.rb +59 -0
- data/lib/active_support/core_ext/numeric/bytes.rb +73 -42
- data/lib/active_support/core_ext/numeric/conversions.rb +145 -0
- data/lib/active_support/core_ext/numeric/time.rb +64 -57
- data/lib/active_support/core_ext/numeric.rb +4 -6
- data/lib/active_support/core_ext/object/acts_like.rb +45 -0
- data/lib/active_support/core_ext/object/blank.rb +199 -0
- data/lib/active_support/core_ext/object/conversions.rb +6 -0
- data/lib/active_support/core_ext/object/deep_dup.rb +71 -0
- data/lib/active_support/core_ext/object/duplicable.rb +69 -0
- data/lib/active_support/core_ext/object/inclusion.rb +37 -0
- data/lib/active_support/core_ext/object/instance_variables.rb +32 -0
- data/lib/active_support/core_ext/object/json.rb +267 -0
- data/lib/active_support/core_ext/object/to_param.rb +3 -0
- data/lib/active_support/core_ext/object/to_query.rb +93 -0
- data/lib/active_support/core_ext/object/try.rb +158 -0
- data/lib/active_support/core_ext/object/with.rb +46 -0
- data/lib/active_support/core_ext/object/with_options.rb +101 -0
- data/lib/active_support/core_ext/object.rb +17 -0
- data/lib/active_support/core_ext/pathname/blank.rb +20 -0
- data/lib/active_support/core_ext/pathname/existence.rb +23 -0
- data/lib/active_support/core_ext/pathname.rb +4 -0
- data/lib/active_support/core_ext/range/compare_range.rb +57 -0
- data/lib/active_support/core_ext/range/conversions.rb +58 -17
- data/lib/active_support/core_ext/range/overlap.rb +40 -0
- data/lib/active_support/core_ext/range/sole.rb +17 -0
- data/lib/active_support/core_ext/range.rb +5 -4
- data/lib/active_support/core_ext/regexp.rb +14 -0
- data/lib/active_support/core_ext/securerandom.rb +57 -0
- data/lib/active_support/core_ext/string/access.rb +93 -56
- data/lib/active_support/core_ext/string/behavior.rb +8 -0
- data/lib/active_support/core_ext/string/conversions.rb +57 -16
- data/lib/active_support/core_ext/string/exclude.rb +13 -0
- data/lib/active_support/core_ext/string/filters.rb +151 -0
- data/lib/active_support/core_ext/string/indent.rb +45 -0
- data/lib/active_support/core_ext/string/inflections.rb +297 -54
- data/lib/active_support/core_ext/string/inquiry.rb +16 -0
- data/lib/active_support/core_ext/string/multibyte.rb +67 -0
- data/lib/active_support/core_ext/string/output_safety.rb +235 -0
- data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -18
- data/lib/active_support/core_ext/string/strip.rb +27 -0
- data/lib/active_support/core_ext/string/zones.rb +16 -0
- data/lib/active_support/core_ext/string.rb +14 -10
- 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/thread/backtrace/location.rb +7 -0
- data/lib/active_support/core_ext/time/acts_like.rb +10 -0
- data/lib/active_support/core_ext/time/calculations.rb +358 -153
- data/lib/active_support/core_ext/time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/time/conversions.rb +69 -30
- data/lib/active_support/core_ext/time/zones.rb +97 -0
- data/lib/active_support/core_ext/time.rb +6 -6
- data/lib/active_support/core_ext.rb +5 -1
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +243 -0
- data/lib/active_support/deep_mergeable.rb +53 -0
- data/lib/active_support/delegation.rb +183 -0
- data/lib/active_support/dependencies/autoload.rb +72 -0
- data/lib/active_support/dependencies/interlock.rb +55 -0
- data/lib/active_support/dependencies/require_dependency.rb +28 -0
- data/lib/active_support/dependencies.rb +84 -222
- data/lib/active_support/deprecation/behaviors.rb +148 -0
- data/lib/active_support/deprecation/constant_accessor.rb +74 -0
- data/lib/active_support/deprecation/deprecators.rb +104 -0
- data/lib/active_support/deprecation/disallowed.rb +54 -0
- data/lib/active_support/deprecation/method_wrappers.rb +68 -0
- data/lib/active_support/deprecation/proxy_wrappers.rb +189 -0
- data/lib/active_support/deprecation/reporting.rb +162 -0
- data/lib/active_support/deprecation.rb +81 -0
- data/lib/active_support/deprecator.rb +7 -0
- data/lib/active_support/descendants_tracker.rb +112 -0
- 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 +64 -0
- data/lib/active_support/duration.rb +524 -0
- data/lib/active_support/editor.rb +70 -0
- data/lib/active_support/encrypted_configuration.rb +126 -0
- data/lib/active_support/encrypted_file.rb +133 -0
- data/lib/active_support/environment_inquirer.rb +40 -0
- data/lib/active_support/error_reporter/test_helper.rb +15 -0
- data/lib/active_support/error_reporter.rb +318 -0
- data/lib/active_support/event_reporter/test_helper.rb +32 -0
- data/lib/active_support/event_reporter.rb +592 -0
- data/lib/active_support/evented_file_update_checker.rb +185 -0
- data/lib/active_support/execution_context/test_helper.rb +13 -0
- data/lib/active_support/execution_context.rb +110 -0
- data/lib/active_support/execution_wrapper.rb +150 -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 +166 -0
- data/lib/active_support/fork_tracker.rb +43 -0
- data/lib/active_support/gem_version.rb +17 -0
- data/lib/active_support/gzip.rb +41 -0
- data/lib/active_support/hash_with_indifferent_access.rb +464 -0
- data/lib/active_support/html_safe_translation.rb +56 -0
- data/lib/active_support/i18n.rb +17 -0
- data/lib/active_support/i18n_railtie.rb +140 -0
- data/lib/active_support/inflections.rb +68 -49
- data/lib/active_support/inflector/inflections.rb +290 -0
- data/lib/active_support/inflector/methods.rb +387 -0
- data/lib/active_support/inflector/transliterate.rb +147 -0
- data/lib/active_support/inflector.rb +7 -164
- data/lib/active_support/isolated_execution_state.rb +76 -0
- data/lib/active_support/json/decoding.rb +78 -0
- data/lib/active_support/json/encoding.rb +256 -0
- data/lib/active_support/json.rb +4 -0
- data/lib/active_support/key_generator.rb +66 -0
- data/lib/active_support/lazy_load_hooks.rb +107 -0
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/locale/en.yml +141 -0
- data/lib/active_support/log_subscriber/test_helper.rb +106 -0
- data/lib/active_support/log_subscriber.rb +188 -0
- data/lib/active_support/logger.rb +55 -0
- data/lib/active_support/logger_silence.rb +21 -0
- data/lib/active_support/logger_thread_safe_level.rb +50 -0
- data/lib/active_support/message_encryptor.rb +374 -0
- data/lib/active_support/message_encryptors.rb +193 -0
- data/lib/active_support/message_pack/cache_serializer.rb +23 -0
- data/lib/active_support/message_pack/extensions.rb +310 -0
- data/lib/active_support/message_pack/serializer.rb +63 -0
- data/lib/active_support/message_pack.rb +50 -0
- data/lib/active_support/message_verifier.rb +377 -0
- data/lib/active_support/message_verifiers.rb +189 -0
- data/lib/active_support/messages/codec.rb +65 -0
- data/lib/active_support/messages/metadata.rb +146 -0
- data/lib/active_support/messages/rotation_configuration.rb +23 -0
- data/lib/active_support/messages/rotation_coordinator.rb +102 -0
- data/lib/active_support/messages/rotator.rb +69 -0
- data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
- data/lib/active_support/multibyte/chars.rb +188 -0
- data/lib/active_support/multibyte/unicode.rb +42 -0
- data/lib/active_support/multibyte.rb +27 -0
- data/lib/active_support/notifications/fanout.rb +467 -0
- data/lib/active_support/notifications/instrumenter.rb +240 -0
- data/lib/active_support/notifications.rb +281 -0
- data/lib/active_support/number_helper/number_converter.rb +190 -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 +60 -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 +479 -0
- data/lib/active_support/option_merger.rb +38 -0
- data/lib/active_support/ordered_hash.rb +50 -0
- data/lib/active_support/ordered_options.rb +141 -25
- data/lib/active_support/parameter_filter.rb +157 -0
- data/lib/active_support/rails.rb +26 -0
- data/lib/active_support/railtie.rb +180 -0
- data/lib/active_support/reloader.rb +138 -0
- data/lib/active_support/rescuable.rb +176 -0
- data/lib/active_support/secure_compare_rotator.rb +58 -0
- data/lib/active_support/security_utils.rb +38 -0
- data/lib/active_support/string_inquirer.rb +35 -0
- data/lib/active_support/structured_event_subscriber.rb +99 -0
- data/lib/active_support/subscriber.rb +141 -0
- data/lib/active_support/syntax_error_proxy.rb +67 -0
- data/lib/active_support/tagged_logging.rb +157 -0
- data/lib/active_support/test_case.rb +365 -0
- data/lib/active_support/testing/assertions.rb +369 -0
- data/lib/active_support/testing/autorun.rb +10 -0
- data/lib/active_support/testing/constant_lookup.rb +51 -0
- data/lib/active_support/testing/constant_stubbing.rb +54 -0
- data/lib/active_support/testing/declarative.rb +28 -0
- data/lib/active_support/testing/deprecation.rb +82 -0
- data/lib/active_support/testing/error_reporter_assertions.rb +124 -0
- data/lib/active_support/testing/event_reporter_assertions.rb +227 -0
- data/lib/active_support/testing/file_fixtures.rb +38 -0
- data/lib/active_support/testing/isolation.rb +121 -0
- data/lib/active_support/testing/method_call_assertions.rb +69 -0
- data/lib/active_support/testing/notification_assertions.rb +92 -0
- data/lib/active_support/testing/parallelization/server.rb +98 -0
- data/lib/active_support/testing/parallelization/worker.rb +107 -0
- data/lib/active_support/testing/parallelization.rb +79 -0
- data/lib/active_support/testing/parallelize_executor.rb +81 -0
- data/lib/active_support/testing/setup_and_teardown.rb +57 -0
- data/lib/active_support/testing/stream.rb +41 -0
- data/lib/active_support/testing/tagged_logging.rb +27 -0
- data/lib/active_support/testing/tests_without_assertions.rb +19 -0
- data/lib/active_support/testing/time_helpers.rb +273 -0
- data/lib/active_support/time.rb +20 -0
- data/lib/active_support/time_with_zone.rb +613 -0
- data/lib/active_support/values/time_zone.rb +599 -158
- data/lib/active_support/version.rb +7 -6
- data/lib/active_support/xml_mini/jdom.rb +175 -0
- data/lib/active_support/xml_mini/libxml.rb +80 -0
- data/lib/active_support/xml_mini/libxmlsax.rb +83 -0
- data/lib/active_support/xml_mini/nokogiri.rb +83 -0
- data/lib/active_support/xml_mini/nokogirisax.rb +86 -0
- data/lib/active_support/xml_mini/rexml.rb +137 -0
- data/lib/active_support/xml_mini.rb +212 -0
- data/lib/active_support.rb +122 -10
- metadata +524 -93
- data/CHANGELOG +0 -283
- data/lib/active_support/binding_of_caller.rb +0 -84
- data/lib/active_support/breakpoint.rb +0 -523
- data/lib/active_support/class_attribute_accessors.rb +0 -57
- data/lib/active_support/class_inheritable_attributes.rb +0 -117
- data/lib/active_support/clean_logger.rb +0 -36
- data/lib/active_support/core_ext/blank.rb +0 -38
- data/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb +0 -14
- data/lib/active_support/core_ext/cgi.rb +0 -5
- data/lib/active_support/core_ext/exception.rb +0 -29
- data/lib/active_support/core_ext/integer/even_odd.rb +0 -24
- data/lib/active_support/core_ext/object_and_class.rb +0 -44
- data/lib/active_support/module_attribute_accessors.rb +0 -57
- data/lib/active_support/whiny_nil.rb +0 -38
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Module
|
|
4
|
+
# Declares an attribute reader backed by an internally-named instance variable.
|
|
5
|
+
def attr_internal_reader(*attrs)
|
|
6
|
+
attrs.each { |attr_name| attr_internal_define(attr_name, :reader) }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Declares an attribute writer backed by an internally-named instance variable.
|
|
10
|
+
def attr_internal_writer(*attrs)
|
|
11
|
+
attrs.each { |attr_name| attr_internal_define(attr_name, :writer) }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Declares an attribute reader and writer backed by an internally-named instance
|
|
15
|
+
# variable.
|
|
16
|
+
def attr_internal_accessor(*attrs)
|
|
17
|
+
attr_internal_reader(*attrs)
|
|
18
|
+
attr_internal_writer(*attrs)
|
|
19
|
+
end
|
|
20
|
+
alias_method :attr_internal, :attr_internal_accessor
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
attr_reader :attr_internal_naming_format
|
|
24
|
+
|
|
25
|
+
def attr_internal_naming_format=(format)
|
|
26
|
+
if format.start_with?("@")
|
|
27
|
+
raise ArgumentError, <<~MESSAGE.squish
|
|
28
|
+
Setting `attr_internal_naming_format` with a `@` prefix is not supported.
|
|
29
|
+
|
|
30
|
+
You can simply replace #{format.inspect} by #{format.delete_prefix("@").inspect}.
|
|
31
|
+
MESSAGE
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
@attr_internal_naming_format = format
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
self.attr_internal_naming_format = "_%s"
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
def attr_internal_define(attr_name, type)
|
|
41
|
+
internal_name = Module.attr_internal_naming_format % attr_name
|
|
42
|
+
# use native attr_* methods as they are faster on some Ruby implementations
|
|
43
|
+
public_send("attr_#{type}", internal_name)
|
|
44
|
+
attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer
|
|
45
|
+
alias_method attr_name, internal_name
|
|
46
|
+
remove_method internal_name
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# == Attribute Accessors
|
|
4
|
+
#
|
|
5
|
+
# Extends the module object with class/module and instance accessors for
|
|
6
|
+
# class/module attributes, just like the native attr* accessors for instance
|
|
7
|
+
# attributes.
|
|
8
|
+
class Module
|
|
9
|
+
# Defines a class attribute and creates a class and instance reader methods.
|
|
10
|
+
# The underlying class variable is set to +nil+, if it is not previously
|
|
11
|
+
# defined. All class and instance methods created will be public, even if
|
|
12
|
+
# this method is called with a private or protected access modifier.
|
|
13
|
+
#
|
|
14
|
+
# module HairColors
|
|
15
|
+
# mattr_reader :hair_colors
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# HairColors.hair_colors # => nil
|
|
19
|
+
# HairColors.class_variable_set("@@hair_colors", [:brown, :black])
|
|
20
|
+
# HairColors.hair_colors # => [:brown, :black]
|
|
21
|
+
#
|
|
22
|
+
# The attribute name must be a valid method name in Ruby.
|
|
23
|
+
#
|
|
24
|
+
# module Foo
|
|
25
|
+
# mattr_reader :"1_Badname"
|
|
26
|
+
# end
|
|
27
|
+
# # => NameError: invalid attribute name: 1_Badname
|
|
28
|
+
#
|
|
29
|
+
# To omit the instance reader method, pass
|
|
30
|
+
# <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
|
|
31
|
+
#
|
|
32
|
+
# module HairColors
|
|
33
|
+
# mattr_reader :hair_colors, instance_reader: false
|
|
34
|
+
# end
|
|
35
|
+
#
|
|
36
|
+
# class Person
|
|
37
|
+
# include HairColors
|
|
38
|
+
# end
|
|
39
|
+
#
|
|
40
|
+
# Person.new.hair_colors # => NoMethodError
|
|
41
|
+
#
|
|
42
|
+
# You can set a default value for the attribute.
|
|
43
|
+
#
|
|
44
|
+
# module HairColors
|
|
45
|
+
# mattr_reader :hair_colors, default: [:brown, :black, :blonde, :red]
|
|
46
|
+
# mattr_reader(:hair_styles) { [:long, :short] }
|
|
47
|
+
# end
|
|
48
|
+
#
|
|
49
|
+
# class Person
|
|
50
|
+
# include HairColors
|
|
51
|
+
# end
|
|
52
|
+
#
|
|
53
|
+
# Person.new.hair_colors # => [:brown, :black, :blonde, :red]
|
|
54
|
+
# Person.new.hair_styles # => [:long, :short]
|
|
55
|
+
def mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil, location: nil)
|
|
56
|
+
raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class?
|
|
57
|
+
location ||= caller_locations(1, 1).first
|
|
58
|
+
|
|
59
|
+
definition = []
|
|
60
|
+
syms.each do |sym|
|
|
61
|
+
raise NameError.new("invalid attribute name: #{sym}") unless /\A[_A-Za-z]\w*\z/.match?(sym)
|
|
62
|
+
|
|
63
|
+
definition << "def self.#{sym}; @@#{sym}; end"
|
|
64
|
+
|
|
65
|
+
if instance_reader && instance_accessor
|
|
66
|
+
definition << "def #{sym}; @@#{sym}; end"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
sym_default_value = (block_given? && default.nil?) ? yield : default
|
|
70
|
+
class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil? && class_variable_defined?("@@#{sym}")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
module_eval(definition.join(";"), location.path, location.lineno)
|
|
74
|
+
end
|
|
75
|
+
alias :cattr_reader :mattr_reader
|
|
76
|
+
|
|
77
|
+
# Defines a class attribute and creates a class and instance writer methods to
|
|
78
|
+
# allow assignment to the attribute. All class and instance methods created
|
|
79
|
+
# will be public, even if this method is called with a private or protected
|
|
80
|
+
# access modifier.
|
|
81
|
+
#
|
|
82
|
+
# module HairColors
|
|
83
|
+
# mattr_writer :hair_colors
|
|
84
|
+
# end
|
|
85
|
+
#
|
|
86
|
+
# class Person
|
|
87
|
+
# include HairColors
|
|
88
|
+
# end
|
|
89
|
+
#
|
|
90
|
+
# HairColors.hair_colors = [:brown, :black]
|
|
91
|
+
# Person.class_variable_get("@@hair_colors") # => [:brown, :black]
|
|
92
|
+
# Person.new.hair_colors = [:blonde, :red]
|
|
93
|
+
# HairColors.class_variable_get("@@hair_colors") # => [:blonde, :red]
|
|
94
|
+
#
|
|
95
|
+
# To omit the instance writer method, pass
|
|
96
|
+
# <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
|
|
97
|
+
#
|
|
98
|
+
# module HairColors
|
|
99
|
+
# mattr_writer :hair_colors, instance_writer: false
|
|
100
|
+
# end
|
|
101
|
+
#
|
|
102
|
+
# class Person
|
|
103
|
+
# include HairColors
|
|
104
|
+
# end
|
|
105
|
+
#
|
|
106
|
+
# Person.new.hair_colors = [:blonde, :red] # => NoMethodError
|
|
107
|
+
#
|
|
108
|
+
# You can set a default value for the attribute.
|
|
109
|
+
#
|
|
110
|
+
# module HairColors
|
|
111
|
+
# mattr_writer :hair_colors, default: [:brown, :black, :blonde, :red]
|
|
112
|
+
# mattr_writer(:hair_styles) { [:long, :short] }
|
|
113
|
+
# end
|
|
114
|
+
#
|
|
115
|
+
# class Person
|
|
116
|
+
# include HairColors
|
|
117
|
+
# end
|
|
118
|
+
#
|
|
119
|
+
# Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
|
|
120
|
+
# Person.class_variable_get("@@hair_styles") # => [:long, :short]
|
|
121
|
+
def mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil, location: nil)
|
|
122
|
+
raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class?
|
|
123
|
+
location ||= caller_locations(1, 1).first
|
|
124
|
+
|
|
125
|
+
definition = []
|
|
126
|
+
syms.each do |sym|
|
|
127
|
+
raise NameError.new("invalid attribute name: #{sym}") unless /\A[_A-Za-z]\w*\z/.match?(sym)
|
|
128
|
+
definition << "def self.#{sym}=(val); @@#{sym} = val; end"
|
|
129
|
+
|
|
130
|
+
if instance_writer && instance_accessor
|
|
131
|
+
definition << "def #{sym}=(val); @@#{sym} = val; end"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
sym_default_value = (block_given? && default.nil?) ? yield : default
|
|
135
|
+
class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil? && class_variable_defined?("@@#{sym}")
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
module_eval(definition.join(";"), location.path, location.lineno)
|
|
139
|
+
end
|
|
140
|
+
alias :cattr_writer :mattr_writer
|
|
141
|
+
|
|
142
|
+
# Defines both class and instance accessors for class attributes.
|
|
143
|
+
# All class and instance methods created will be public, even if
|
|
144
|
+
# this method is called with a private or protected access modifier.
|
|
145
|
+
#
|
|
146
|
+
# module HairColors
|
|
147
|
+
# mattr_accessor :hair_colors
|
|
148
|
+
# end
|
|
149
|
+
#
|
|
150
|
+
# class Person
|
|
151
|
+
# include HairColors
|
|
152
|
+
# end
|
|
153
|
+
#
|
|
154
|
+
# HairColors.hair_colors = [:brown, :black, :blonde, :red]
|
|
155
|
+
# HairColors.hair_colors # => [:brown, :black, :blonde, :red]
|
|
156
|
+
# Person.new.hair_colors # => [:brown, :black, :blonde, :red]
|
|
157
|
+
#
|
|
158
|
+
# If a subclass changes the value then that would also change the value for
|
|
159
|
+
# parent class. Similarly if parent class changes the value then that would
|
|
160
|
+
# change the value of subclasses too.
|
|
161
|
+
#
|
|
162
|
+
# class Citizen < Person
|
|
163
|
+
# end
|
|
164
|
+
#
|
|
165
|
+
# Citizen.new.hair_colors << :blue
|
|
166
|
+
# Person.new.hair_colors # => [:brown, :black, :blonde, :red, :blue]
|
|
167
|
+
#
|
|
168
|
+
# To omit the instance writer method, pass <tt>instance_writer: false</tt>.
|
|
169
|
+
# To omit the instance reader method, pass <tt>instance_reader: false</tt>.
|
|
170
|
+
#
|
|
171
|
+
# module HairColors
|
|
172
|
+
# mattr_accessor :hair_colors, instance_writer: false, instance_reader: false
|
|
173
|
+
# end
|
|
174
|
+
#
|
|
175
|
+
# class Person
|
|
176
|
+
# include HairColors
|
|
177
|
+
# end
|
|
178
|
+
#
|
|
179
|
+
# Person.new.hair_colors = [:brown] # => NoMethodError
|
|
180
|
+
# Person.new.hair_colors # => NoMethodError
|
|
181
|
+
#
|
|
182
|
+
# Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
|
|
183
|
+
#
|
|
184
|
+
# module HairColors
|
|
185
|
+
# mattr_accessor :hair_colors, instance_accessor: false
|
|
186
|
+
# end
|
|
187
|
+
#
|
|
188
|
+
# class Person
|
|
189
|
+
# include HairColors
|
|
190
|
+
# end
|
|
191
|
+
#
|
|
192
|
+
# Person.new.hair_colors = [:brown] # => NoMethodError
|
|
193
|
+
# Person.new.hair_colors # => NoMethodError
|
|
194
|
+
#
|
|
195
|
+
# You can set a default value for the attribute.
|
|
196
|
+
#
|
|
197
|
+
# module HairColors
|
|
198
|
+
# mattr_accessor :hair_colors, default: [:brown, :black, :blonde, :red]
|
|
199
|
+
# mattr_accessor(:hair_styles) { [:long, :short] }
|
|
200
|
+
# end
|
|
201
|
+
#
|
|
202
|
+
# class Person
|
|
203
|
+
# include HairColors
|
|
204
|
+
# end
|
|
205
|
+
#
|
|
206
|
+
# Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
|
|
207
|
+
# Person.class_variable_get("@@hair_styles") # => [:long, :short]
|
|
208
|
+
def mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil, &blk)
|
|
209
|
+
location = caller_locations(1, 1).first
|
|
210
|
+
mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default, location: location, &blk)
|
|
211
|
+
mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor, default: default, location: location)
|
|
212
|
+
end
|
|
213
|
+
alias :cattr_accessor :mattr_accessor
|
|
214
|
+
end
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# == Attribute Accessors per Thread
|
|
4
|
+
#
|
|
5
|
+
# Extends the module object with class/module and instance accessors for
|
|
6
|
+
# class/module attributes, just like the native attr* accessors for instance
|
|
7
|
+
# attributes, but does so on a per-thread basis.
|
|
8
|
+
#
|
|
9
|
+
# So the values are scoped within the Thread.current space under the class name
|
|
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+.
|
|
14
|
+
class Module
|
|
15
|
+
# Defines a per-thread class attribute and creates class and instance reader methods.
|
|
16
|
+
# The underlying per-thread class variable is set to +nil+, if it is not previously defined.
|
|
17
|
+
#
|
|
18
|
+
# module Current
|
|
19
|
+
# thread_mattr_reader :user
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# Current.user = "DHH"
|
|
23
|
+
# Current.user # => "DHH"
|
|
24
|
+
# Thread.new { Current.user }.value # => nil
|
|
25
|
+
#
|
|
26
|
+
# The attribute name must be a valid method name in Ruby.
|
|
27
|
+
#
|
|
28
|
+
# module Foo
|
|
29
|
+
# thread_mattr_reader :"1_Badname"
|
|
30
|
+
# end
|
|
31
|
+
# # => NameError: invalid attribute name: 1_Badname
|
|
32
|
+
#
|
|
33
|
+
# To omit the instance reader method, pass
|
|
34
|
+
# <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
|
|
35
|
+
#
|
|
36
|
+
# class Current
|
|
37
|
+
# thread_mattr_reader :user, instance_reader: false
|
|
38
|
+
# end
|
|
39
|
+
#
|
|
40
|
+
# Current.new.user # => NoMethodError
|
|
41
|
+
def thread_mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil) # :nodoc:
|
|
42
|
+
syms.each do |sym|
|
|
43
|
+
raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
|
|
44
|
+
|
|
45
|
+
# The following generated method concatenates `object_id` because we want
|
|
46
|
+
# subclasses to maintain independent values.
|
|
47
|
+
if default.nil?
|
|
48
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
|
49
|
+
def self.#{sym}
|
|
50
|
+
@__thread_mattr_#{sym} ||= "attr_#{sym}_\#{object_id}"
|
|
51
|
+
::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}]
|
|
52
|
+
end
|
|
53
|
+
EOS
|
|
54
|
+
else
|
|
55
|
+
default = default.dup.freeze unless default.frozen?
|
|
56
|
+
singleton_class.define_method("#{sym}_default_value") { default }
|
|
57
|
+
|
|
58
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
|
59
|
+
def self.#{sym}
|
|
60
|
+
@__thread_mattr_#{sym} ||= "attr_#{sym}_\#{object_id}"
|
|
61
|
+
value = ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}]
|
|
62
|
+
|
|
63
|
+
if value.nil? && !::ActiveSupport::IsolatedExecutionState.key?(@__thread_mattr_#{sym})
|
|
64
|
+
::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}] = #{sym}_default_value
|
|
65
|
+
else
|
|
66
|
+
value
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
EOS
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
if instance_reader && instance_accessor
|
|
73
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
|
74
|
+
def #{sym}
|
|
75
|
+
self.class.#{sym}
|
|
76
|
+
end
|
|
77
|
+
EOS
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
alias :thread_cattr_reader :thread_mattr_reader
|
|
82
|
+
|
|
83
|
+
# Defines a per-thread class attribute and creates a class and instance writer methods to
|
|
84
|
+
# allow assignment to the attribute.
|
|
85
|
+
#
|
|
86
|
+
# module Current
|
|
87
|
+
# thread_mattr_writer :user
|
|
88
|
+
# end
|
|
89
|
+
#
|
|
90
|
+
# Current.user = "DHH"
|
|
91
|
+
# Thread.current[:attr_Current_user] # => "DHH"
|
|
92
|
+
#
|
|
93
|
+
# To omit the instance writer method, pass
|
|
94
|
+
# <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
|
|
95
|
+
#
|
|
96
|
+
# class Current
|
|
97
|
+
# thread_mattr_writer :user, instance_writer: false
|
|
98
|
+
# end
|
|
99
|
+
#
|
|
100
|
+
# Current.new.user = "DHH" # => NoMethodError
|
|
101
|
+
def thread_mattr_writer(*syms, instance_writer: true, instance_accessor: true) # :nodoc:
|
|
102
|
+
syms.each do |sym|
|
|
103
|
+
raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
|
|
104
|
+
|
|
105
|
+
# The following generated method concatenates `object_id` because we want
|
|
106
|
+
# subclasses to maintain independent values.
|
|
107
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
|
108
|
+
def self.#{sym}=(obj)
|
|
109
|
+
@__thread_mattr_#{sym} ||= "attr_#{sym}_\#{object_id}"
|
|
110
|
+
::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}] = obj
|
|
111
|
+
end
|
|
112
|
+
EOS
|
|
113
|
+
|
|
114
|
+
if instance_writer && instance_accessor
|
|
115
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
|
116
|
+
def #{sym}=(obj)
|
|
117
|
+
self.class.#{sym} = obj
|
|
118
|
+
end
|
|
119
|
+
EOS
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
alias :thread_cattr_writer :thread_mattr_writer
|
|
124
|
+
|
|
125
|
+
# Defines both class and instance accessors for class attributes.
|
|
126
|
+
#
|
|
127
|
+
# class Account
|
|
128
|
+
# thread_mattr_accessor :user
|
|
129
|
+
# end
|
|
130
|
+
#
|
|
131
|
+
# Account.user = "DHH"
|
|
132
|
+
# Account.user # => "DHH"
|
|
133
|
+
# Account.new.user # => "DHH"
|
|
134
|
+
#
|
|
135
|
+
# Unlike +mattr_accessor+, values are *not* shared with subclasses or parent classes.
|
|
136
|
+
# If a subclass changes the value, the parent class' value is not changed.
|
|
137
|
+
# If the parent class changes the value, the value of subclasses is not changed.
|
|
138
|
+
#
|
|
139
|
+
# class Customer < Account
|
|
140
|
+
# end
|
|
141
|
+
#
|
|
142
|
+
# Account.user # => "DHH"
|
|
143
|
+
# Customer.user # => nil
|
|
144
|
+
# Customer.user = "Rafael"
|
|
145
|
+
# Customer.user # => "Rafael"
|
|
146
|
+
# Account.user # => "DHH"
|
|
147
|
+
#
|
|
148
|
+
# To omit the instance writer method, pass <tt>instance_writer: false</tt>.
|
|
149
|
+
# To omit the instance reader method, pass <tt>instance_reader: false</tt>.
|
|
150
|
+
#
|
|
151
|
+
# class Current
|
|
152
|
+
# thread_mattr_accessor :user, instance_writer: false, instance_reader: false
|
|
153
|
+
# end
|
|
154
|
+
#
|
|
155
|
+
# Current.new.user = "DHH" # => NoMethodError
|
|
156
|
+
# Current.new.user # => NoMethodError
|
|
157
|
+
#
|
|
158
|
+
# Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
|
|
159
|
+
#
|
|
160
|
+
# class Current
|
|
161
|
+
# thread_mattr_accessor :user, instance_accessor: false
|
|
162
|
+
# end
|
|
163
|
+
#
|
|
164
|
+
# Current.new.user = "DHH" # => NoMethodError
|
|
165
|
+
# Current.new.user # => NoMethodError
|
|
166
|
+
#
|
|
167
|
+
# A default value may be specified using the +:default+ option. Because
|
|
168
|
+
# multiple threads can access the default value, non-frozen default values
|
|
169
|
+
# will be <tt>dup</tt>ed and frozen.
|
|
170
|
+
def thread_mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil)
|
|
171
|
+
thread_mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default)
|
|
172
|
+
thread_mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor)
|
|
173
|
+
end
|
|
174
|
+
alias :thread_cattr_accessor :thread_mattr_accessor
|
|
175
|
+
end
|
|
@@ -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
|