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,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pathname"
|
4
|
+
require "tmpdir"
|
5
|
+
require "active_support/message_encryptor"
|
6
|
+
|
7
|
+
module ActiveSupport
|
8
|
+
class EncryptedFile
|
9
|
+
class MissingContentError < RuntimeError
|
10
|
+
def initialize(content_path)
|
11
|
+
super "Missing encrypted content file in #{content_path}."
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class MissingKeyError < RuntimeError
|
16
|
+
def initialize(key_path:, env_key:)
|
17
|
+
super \
|
18
|
+
"Missing encryption key to decrypt file with. " +
|
19
|
+
"Ask your team for your master key and write it to #{key_path} or put it in the ENV['#{env_key}']."
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class InvalidKeyLengthError < RuntimeError
|
24
|
+
def initialize
|
25
|
+
super "Encryption key must be exactly #{EncryptedFile.expected_key_length} characters."
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
CIPHER = "aes-128-gcm"
|
30
|
+
|
31
|
+
def self.generate_key
|
32
|
+
SecureRandom.hex(ActiveSupport::MessageEncryptor.key_len(CIPHER))
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.expected_key_length # :nodoc:
|
36
|
+
@expected_key_length ||= generate_key.length
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
attr_reader :content_path, :key_path, :env_key, :raise_if_missing_key
|
41
|
+
|
42
|
+
def initialize(content_path:, key_path:, env_key:, raise_if_missing_key:)
|
43
|
+
@content_path = Pathname.new(content_path).yield_self { |path| path.symlink? ? path.realpath : path }
|
44
|
+
@key_path = Pathname.new(key_path)
|
45
|
+
@env_key, @raise_if_missing_key = env_key, raise_if_missing_key
|
46
|
+
end
|
47
|
+
|
48
|
+
def key
|
49
|
+
read_env_key || read_key_file || handle_missing_key
|
50
|
+
end
|
51
|
+
|
52
|
+
def read
|
53
|
+
if !key.nil? && content_path.exist?
|
54
|
+
decrypt content_path.binread
|
55
|
+
else
|
56
|
+
raise MissingContentError, content_path
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def write(contents)
|
61
|
+
IO.binwrite "#{content_path}.tmp", encrypt(contents)
|
62
|
+
FileUtils.mv "#{content_path}.tmp", content_path
|
63
|
+
end
|
64
|
+
|
65
|
+
def change(&block)
|
66
|
+
writing read, &block
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
private
|
71
|
+
def writing(contents)
|
72
|
+
tmp_file = "#{Process.pid}.#{content_path.basename.to_s.chomp('.enc')}"
|
73
|
+
tmp_path = Pathname.new File.join(Dir.tmpdir, tmp_file)
|
74
|
+
tmp_path.binwrite contents
|
75
|
+
|
76
|
+
yield tmp_path
|
77
|
+
|
78
|
+
updated_contents = tmp_path.binread
|
79
|
+
|
80
|
+
write(updated_contents) if updated_contents != contents
|
81
|
+
ensure
|
82
|
+
FileUtils.rm(tmp_path) if tmp_path&.exist?
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
def encrypt(contents)
|
87
|
+
check_key_length
|
88
|
+
encryptor.encrypt_and_sign contents
|
89
|
+
end
|
90
|
+
|
91
|
+
def decrypt(contents)
|
92
|
+
encryptor.decrypt_and_verify contents
|
93
|
+
end
|
94
|
+
|
95
|
+
def encryptor
|
96
|
+
@encryptor ||= ActiveSupport::MessageEncryptor.new([ key ].pack("H*"), cipher: CIPHER)
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
def read_env_key
|
101
|
+
ENV[env_key].presence
|
102
|
+
end
|
103
|
+
|
104
|
+
def read_key_file
|
105
|
+
return @key_file_contents if defined?(@key_file_contents)
|
106
|
+
@key_file_contents = (key_path.binread.strip if key_path.exist?)
|
107
|
+
end
|
108
|
+
|
109
|
+
def handle_missing_key
|
110
|
+
raise MissingKeyError.new(key_path: key_path, env_key: env_key) if raise_if_missing_key
|
111
|
+
end
|
112
|
+
|
113
|
+
def check_key_length
|
114
|
+
raise InvalidKeyLengthError if key&.length != self.class.expected_key_length
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/string_inquirer"
|
4
|
+
|
5
|
+
module ActiveSupport
|
6
|
+
class EnvironmentInquirer < StringInquirer # :nodoc:
|
7
|
+
DEFAULT_ENVIRONMENTS = ["development", "test", "production"]
|
8
|
+
def initialize(env)
|
9
|
+
super(env)
|
10
|
+
|
11
|
+
DEFAULT_ENVIRONMENTS.each do |default|
|
12
|
+
instance_variable_set :"@#{default}", env == default
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
DEFAULT_ENVIRONMENTS.each do |env|
|
17
|
+
class_eval "def #{env}?; @#{env}; end"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveSupport
|
4
|
+
# +ActiveSupport::ErrorReporter+ is a common interface for error reporting services.
|
5
|
+
#
|
6
|
+
# To rescue and report any unhandled error, you can use the +handle+ method:
|
7
|
+
#
|
8
|
+
# Rails.error.handle do
|
9
|
+
# do_something!
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# If an error is raised, it will be reported and swallowed.
|
13
|
+
#
|
14
|
+
# Alternatively if you want to report the error but not swallow it, you can use +record+
|
15
|
+
#
|
16
|
+
# Rails.error.record do
|
17
|
+
# do_something!
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# Both methods can be restricted to only handle a specific exception class
|
21
|
+
#
|
22
|
+
# maybe_tags = Rails.error.handle(Redis::BaseError) { redis.get("tags") }
|
23
|
+
#
|
24
|
+
# You can also pass some extra context information that may be used by the error subscribers:
|
25
|
+
#
|
26
|
+
# Rails.error.handle(context: { section: "admin" }) do
|
27
|
+
# # ...
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# Additionally a +severity+ can be passed along to communicate how important the error report is.
|
31
|
+
# +severity+ can be one of +:error+, +:warning+ or +:info+. Handled errors default to the +:warning+
|
32
|
+
# severity, and unhandled ones to +error+.
|
33
|
+
#
|
34
|
+
# Both +handle+ and +record+ pass through the return value from the block. In the case of +handle+
|
35
|
+
# rescuing an error, a fallback can be provided. The fallback must be a callable whose result will
|
36
|
+
# be returned when the block raises and is handled:
|
37
|
+
#
|
38
|
+
# user = Rails.error.handle(fallback: -> { User.anonymous }) do
|
39
|
+
# User.find_by(params)
|
40
|
+
# end
|
41
|
+
class ErrorReporter
|
42
|
+
SEVERITIES = %i(error warning info)
|
43
|
+
|
44
|
+
attr_accessor :logger
|
45
|
+
|
46
|
+
def initialize(*subscribers, logger: nil)
|
47
|
+
@subscribers = subscribers.flatten
|
48
|
+
@logger = logger
|
49
|
+
end
|
50
|
+
|
51
|
+
# Report any unhandled exception, and swallow it.
|
52
|
+
#
|
53
|
+
# Rails.error.handle do
|
54
|
+
# 1 + '1'
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
def handle(error_class = StandardError, severity: :warning, context: {}, fallback: nil)
|
58
|
+
yield
|
59
|
+
rescue error_class => error
|
60
|
+
report(error, handled: true, severity: severity, context: context)
|
61
|
+
fallback.call if fallback
|
62
|
+
end
|
63
|
+
|
64
|
+
def record(error_class = StandardError, severity: :error, context: {})
|
65
|
+
yield
|
66
|
+
rescue error_class => error
|
67
|
+
report(error, handled: false, severity: severity, context: context)
|
68
|
+
raise
|
69
|
+
end
|
70
|
+
|
71
|
+
# Register a new error subscriber. The subscriber must respond to
|
72
|
+
#
|
73
|
+
# report(Exception, handled: Boolean, context: Hash)
|
74
|
+
#
|
75
|
+
# The +report+ method +should+ never raise an error.
|
76
|
+
def subscribe(subscriber)
|
77
|
+
unless subscriber.respond_to?(:report)
|
78
|
+
raise ArgumentError, "Error subscribers must respond to #report"
|
79
|
+
end
|
80
|
+
@subscribers << subscriber
|
81
|
+
end
|
82
|
+
|
83
|
+
# Update the execution context that is accessible to error subscribers
|
84
|
+
#
|
85
|
+
# Rails.error.set_context(section: "checkout", user_id: @user.id)
|
86
|
+
#
|
87
|
+
# See +ActiveSupport::ExecutionContext.set+
|
88
|
+
def set_context(...)
|
89
|
+
ActiveSupport::ExecutionContext.set(...)
|
90
|
+
end
|
91
|
+
|
92
|
+
# When the block based +handle+ and +record+ methods are not suitable, you can directly use +report+
|
93
|
+
#
|
94
|
+
# Rails.error.report(error, handled: true)
|
95
|
+
def report(error, handled:, severity: handled ? :warning : :error, context: {})
|
96
|
+
unless SEVERITIES.include?(severity)
|
97
|
+
raise ArgumentError, "severity must be one of #{SEVERITIES.map(&:inspect).join(", ")}, got: #{severity.inspect}"
|
98
|
+
end
|
99
|
+
|
100
|
+
full_context = ActiveSupport::ExecutionContext.to_h.merge(context)
|
101
|
+
@subscribers.each do |subscriber|
|
102
|
+
subscriber.report(error, handled: handled, severity: severity, context: full_context)
|
103
|
+
rescue => subscriber_error
|
104
|
+
if logger
|
105
|
+
logger.fatal(
|
106
|
+
"Error subscriber raised an error: #{subscriber_error.message} (#{subscriber_error.class})\n" +
|
107
|
+
subscriber_error.backtrace.join("\n")
|
108
|
+
)
|
109
|
+
else
|
110
|
+
raise
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
nil
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "set"
|
4
|
+
require "pathname"
|
5
|
+
require "concurrent/atomic/atomic_boolean"
|
6
|
+
require "listen"
|
7
|
+
require "active_support/fork_tracker"
|
8
|
+
|
9
|
+
module ActiveSupport
|
10
|
+
# Allows you to "listen" to changes in a file system.
|
11
|
+
# The evented file updater does not hit disk when checking for updates
|
12
|
+
# instead it uses platform specific file system events to trigger a change
|
13
|
+
# in state.
|
14
|
+
#
|
15
|
+
# The file checker takes an array of files to watch or a hash specifying directories
|
16
|
+
# and file extensions to watch. It also takes a block that is called when
|
17
|
+
# EventedFileUpdateChecker#execute is run or when EventedFileUpdateChecker#execute_if_updated
|
18
|
+
# is run and there have been changes to the file system.
|
19
|
+
#
|
20
|
+
# Note: Forking will cause the first call to `updated?` to return `true`.
|
21
|
+
#
|
22
|
+
# Example:
|
23
|
+
#
|
24
|
+
# checker = ActiveSupport::EventedFileUpdateChecker.new(["/tmp/foo"]) { puts "changed" }
|
25
|
+
# checker.updated?
|
26
|
+
# # => false
|
27
|
+
# checker.execute_if_updated
|
28
|
+
# # => nil
|
29
|
+
#
|
30
|
+
# FileUtils.touch("/tmp/foo")
|
31
|
+
#
|
32
|
+
# checker.updated?
|
33
|
+
# # => true
|
34
|
+
# checker.execute_if_updated
|
35
|
+
# # => "changed"
|
36
|
+
#
|
37
|
+
class EventedFileUpdateChecker # :nodoc: all
|
38
|
+
def initialize(files, dirs = {}, &block)
|
39
|
+
unless block
|
40
|
+
raise ArgumentError, "A block is required to initialize an EventedFileUpdateChecker"
|
41
|
+
end
|
42
|
+
|
43
|
+
@block = block
|
44
|
+
@core = Core.new(files, dirs)
|
45
|
+
ObjectSpace.define_finalizer(self, @core.finalizer)
|
46
|
+
end
|
47
|
+
|
48
|
+
def updated?
|
49
|
+
if @core.restart?
|
50
|
+
@core.thread_safely(&:restart)
|
51
|
+
@core.updated.make_true
|
52
|
+
end
|
53
|
+
|
54
|
+
@core.updated.true?
|
55
|
+
end
|
56
|
+
|
57
|
+
def execute
|
58
|
+
@core.updated.make_false
|
59
|
+
@block.call
|
60
|
+
end
|
61
|
+
|
62
|
+
def execute_if_updated
|
63
|
+
if updated?
|
64
|
+
yield if block_given?
|
65
|
+
execute
|
66
|
+
true
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Core
|
71
|
+
attr_reader :updated
|
72
|
+
|
73
|
+
def initialize(files, dirs)
|
74
|
+
@files = files.map { |file| Pathname(file).expand_path }.to_set
|
75
|
+
|
76
|
+
@dirs = dirs.each_with_object({}) do |(dir, exts), hash|
|
77
|
+
hash[Pathname(dir).expand_path] = Array(exts).map { |ext| ext.to_s.sub(/\A\.?/, ".") }.to_set
|
78
|
+
end
|
79
|
+
|
80
|
+
@common_path = common_path(@dirs.keys)
|
81
|
+
|
82
|
+
@dtw = directories_to_watch
|
83
|
+
@missing = []
|
84
|
+
|
85
|
+
@updated = Concurrent::AtomicBoolean.new(false)
|
86
|
+
@mutex = Mutex.new
|
87
|
+
|
88
|
+
start
|
89
|
+
@after_fork = ActiveSupport::ForkTracker.after_fork { start }
|
90
|
+
end
|
91
|
+
|
92
|
+
def finalizer
|
93
|
+
proc do
|
94
|
+
stop
|
95
|
+
ActiveSupport::ForkTracker.unregister(@after_fork)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def thread_safely
|
100
|
+
@mutex.synchronize do
|
101
|
+
yield self
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def start
|
106
|
+
normalize_dirs!
|
107
|
+
@dtw, @missing = [*@dtw, *@missing].partition(&:exist?)
|
108
|
+
@listener = @dtw.any? ? Listen.to(*@dtw, &method(:changed)) : nil
|
109
|
+
@listener&.start
|
110
|
+
end
|
111
|
+
|
112
|
+
def stop
|
113
|
+
@listener&.stop
|
114
|
+
end
|
115
|
+
|
116
|
+
def restart
|
117
|
+
stop
|
118
|
+
start
|
119
|
+
end
|
120
|
+
|
121
|
+
def restart?
|
122
|
+
@missing.any?(&:exist?)
|
123
|
+
end
|
124
|
+
|
125
|
+
def normalize_dirs!
|
126
|
+
@dirs.transform_keys! do |dir|
|
127
|
+
dir.exist? ? dir.realpath : dir
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def changed(modified, added, removed)
|
132
|
+
unless @updated.true?
|
133
|
+
@updated.make_true if (modified + added + removed).any? { |f| watching?(f) }
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def watching?(file)
|
138
|
+
file = Pathname(file)
|
139
|
+
|
140
|
+
if @files.member?(file)
|
141
|
+
true
|
142
|
+
elsif file.directory?
|
143
|
+
false
|
144
|
+
else
|
145
|
+
ext = file.extname
|
146
|
+
|
147
|
+
file.dirname.ascend do |dir|
|
148
|
+
matching = @dirs[dir]
|
149
|
+
|
150
|
+
if matching && (matching.empty? || matching.include?(ext))
|
151
|
+
break true
|
152
|
+
elsif dir == @common_path || dir.root?
|
153
|
+
break false
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def directories_to_watch
|
160
|
+
dtw = @dirs.keys | @files.map(&:dirname)
|
161
|
+
accounted_for = dtw.to_set + Gem.path.map { |path| Pathname(path) }
|
162
|
+
dtw.reject { |dir| dir.ascend.drop(1).any? { |parent| accounted_for.include?(parent) } }
|
163
|
+
end
|
164
|
+
|
165
|
+
def common_path(paths)
|
166
|
+
paths.map { |path| path.ascend.to_a }.reduce(&:&)&.first
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveSupport
|
4
|
+
module ExecutionContext # :nodoc:
|
5
|
+
@after_change_callbacks = []
|
6
|
+
class << self
|
7
|
+
def after_change(&block)
|
8
|
+
@after_change_callbacks << block
|
9
|
+
end
|
10
|
+
|
11
|
+
# Updates the execution context. If a block is given, it resets the provided keys to their
|
12
|
+
# previous value once the block exits.
|
13
|
+
def set(**options)
|
14
|
+
options.symbolize_keys!
|
15
|
+
keys = options.keys
|
16
|
+
|
17
|
+
store = self.store
|
18
|
+
|
19
|
+
previous_context = keys.zip(store.values_at(*keys)).to_h
|
20
|
+
|
21
|
+
store.merge!(options)
|
22
|
+
@after_change_callbacks.each(&:call)
|
23
|
+
|
24
|
+
if block_given?
|
25
|
+
begin
|
26
|
+
yield
|
27
|
+
ensure
|
28
|
+
store.merge!(previous_context)
|
29
|
+
@after_change_callbacks.each(&:call)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def []=(key, value)
|
35
|
+
store[key.to_sym] = value
|
36
|
+
@after_change_callbacks.each(&:call)
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_h
|
40
|
+
store.dup
|
41
|
+
end
|
42
|
+
|
43
|
+
def clear
|
44
|
+
store.clear
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def store
|
49
|
+
IsolatedExecutionState[:active_support_execution_context] ||= {}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/error_reporter"
|
4
|
+
require "active_support/callbacks"
|
5
|
+
require "concurrent/hash"
|
6
|
+
|
7
|
+
module ActiveSupport
|
8
|
+
class ExecutionWrapper
|
9
|
+
include ActiveSupport::Callbacks
|
10
|
+
|
11
|
+
Null = Object.new # :nodoc:
|
12
|
+
def Null.complete! # :nodoc:
|
13
|
+
end
|
14
|
+
|
15
|
+
define_callbacks :run
|
16
|
+
define_callbacks :complete
|
17
|
+
|
18
|
+
def self.to_run(*args, &block)
|
19
|
+
set_callback(:run, *args, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.to_complete(*args, &block)
|
23
|
+
set_callback(:complete, *args, &block)
|
24
|
+
end
|
25
|
+
|
26
|
+
RunHook = Struct.new(:hook) do # :nodoc:
|
27
|
+
def before(target)
|
28
|
+
hook_state = target.send(:hook_state)
|
29
|
+
hook_state[hook] = hook.run
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
CompleteHook = Struct.new(:hook) do # :nodoc:
|
34
|
+
def before(target)
|
35
|
+
hook_state = target.send(:hook_state)
|
36
|
+
if hook_state.key?(hook)
|
37
|
+
hook.complete hook_state[hook]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
alias after before
|
41
|
+
end
|
42
|
+
|
43
|
+
# Register an object to be invoked during both the +run+ and
|
44
|
+
# +complete+ steps.
|
45
|
+
#
|
46
|
+
# +hook.complete+ will be passed the value returned from +hook.run+,
|
47
|
+
# and will only be invoked if +run+ has previously been called.
|
48
|
+
# (Mostly, this means it won't be invoked if an exception occurs in
|
49
|
+
# a preceding +to_run+ block; all ordinary +to_complete+ blocks are
|
50
|
+
# invoked in that situation.)
|
51
|
+
def self.register_hook(hook, outer: false)
|
52
|
+
if outer
|
53
|
+
to_run RunHook.new(hook), prepend: true
|
54
|
+
to_complete :after, CompleteHook.new(hook)
|
55
|
+
else
|
56
|
+
to_run RunHook.new(hook)
|
57
|
+
to_complete CompleteHook.new(hook)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Run this execution.
|
62
|
+
#
|
63
|
+
# Returns an instance, whose +complete!+ method *must* be invoked
|
64
|
+
# after the work has been performed.
|
65
|
+
#
|
66
|
+
# Where possible, prefer +wrap+.
|
67
|
+
def self.run!(reset: false)
|
68
|
+
if reset
|
69
|
+
lost_instance = IsolatedExecutionState.delete(active_key)
|
70
|
+
lost_instance&.complete!
|
71
|
+
else
|
72
|
+
return Null if active?
|
73
|
+
end
|
74
|
+
|
75
|
+
new.tap do |instance|
|
76
|
+
success = nil
|
77
|
+
begin
|
78
|
+
instance.run!
|
79
|
+
success = true
|
80
|
+
ensure
|
81
|
+
instance.complete! unless success
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Perform the work in the supplied block as an execution.
|
87
|
+
def self.wrap
|
88
|
+
return yield if active?
|
89
|
+
|
90
|
+
instance = run!
|
91
|
+
begin
|
92
|
+
yield
|
93
|
+
rescue => error
|
94
|
+
error_reporter.report(error, handled: false)
|
95
|
+
raise
|
96
|
+
ensure
|
97
|
+
instance.complete!
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.perform # :nodoc:
|
102
|
+
instance = new
|
103
|
+
instance.run
|
104
|
+
begin
|
105
|
+
yield
|
106
|
+
ensure
|
107
|
+
instance.complete
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.error_reporter
|
112
|
+
@error_reporter ||= ActiveSupport::ErrorReporter.new
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.active_key # :nodoc:
|
116
|
+
@active_key ||= :"active_execution_wrapper_#{object_id}"
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.active? # :nodoc:
|
120
|
+
IsolatedExecutionState.key?(active_key)
|
121
|
+
end
|
122
|
+
|
123
|
+
def run! # :nodoc:
|
124
|
+
IsolatedExecutionState[self.class.active_key] = self
|
125
|
+
run
|
126
|
+
end
|
127
|
+
|
128
|
+
def run # :nodoc:
|
129
|
+
run_callbacks(:run)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Complete this in-flight execution. This method *must* be called
|
133
|
+
# exactly once on the result of any call to +run!+.
|
134
|
+
#
|
135
|
+
# Where possible, prefer +wrap+.
|
136
|
+
def complete!
|
137
|
+
complete
|
138
|
+
ensure
|
139
|
+
IsolatedExecutionState.delete(self.class.active_key)
|
140
|
+
end
|
141
|
+
|
142
|
+
def complete # :nodoc:
|
143
|
+
run_callbacks(:complete)
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
def hook_state
|
148
|
+
@_hook_state ||= {}
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|