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,176 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/concern"
|
|
4
|
+
require "active_support/core_ext/class/attribute"
|
|
5
|
+
require "active_support/core_ext/string/inflections"
|
|
6
|
+
|
|
7
|
+
module ActiveSupport
|
|
8
|
+
# = Active Support \Rescuable
|
|
9
|
+
#
|
|
10
|
+
# Rescuable module adds support for easier exception handling.
|
|
11
|
+
module Rescuable
|
|
12
|
+
extend Concern
|
|
13
|
+
|
|
14
|
+
included do
|
|
15
|
+
class_attribute :rescue_handlers, default: []
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
module ClassMethods
|
|
19
|
+
# Registers exception classes with a handler to be called by <tt>rescue_with_handler</tt>.
|
|
20
|
+
#
|
|
21
|
+
# <tt>rescue_from</tt> receives a series of exception classes or class
|
|
22
|
+
# names, and an exception handler specified by a trailing <tt>:with</tt>
|
|
23
|
+
# option containing the name of a method or a Proc object. Alternatively, a block
|
|
24
|
+
# can be given as the handler.
|
|
25
|
+
#
|
|
26
|
+
# Handlers that take one argument will be called with the exception, so
|
|
27
|
+
# that the exception can be inspected when dealing with it.
|
|
28
|
+
#
|
|
29
|
+
# Handlers are inherited. They are searched from right to left, from
|
|
30
|
+
# bottom to top, and up the hierarchy. The handler of the first class for
|
|
31
|
+
# which <tt>exception.is_a?(klass)</tt> holds true is the one invoked, if
|
|
32
|
+
# any.
|
|
33
|
+
#
|
|
34
|
+
# class ApplicationController < ActionController::Base
|
|
35
|
+
# rescue_from User::NotAuthorized, with: :deny_access
|
|
36
|
+
# rescue_from ActiveRecord::RecordInvalid, with: :show_record_errors
|
|
37
|
+
#
|
|
38
|
+
# rescue_from "MyApp::BaseError" do |exception|
|
|
39
|
+
# redirect_to root_url, alert: exception.message
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
42
|
+
# private
|
|
43
|
+
# def deny_access
|
|
44
|
+
# head :forbidden
|
|
45
|
+
# end
|
|
46
|
+
#
|
|
47
|
+
# def show_record_errors(exception)
|
|
48
|
+
# redirect_back_or_to root_url, alert: exception.record.errors.full_messages.to_sentence
|
|
49
|
+
# end
|
|
50
|
+
# end
|
|
51
|
+
#
|
|
52
|
+
# Exceptions raised inside exception handlers are not propagated up.
|
|
53
|
+
def rescue_from(*klasses, with: nil, &block)
|
|
54
|
+
unless with
|
|
55
|
+
if block_given?
|
|
56
|
+
with = block
|
|
57
|
+
else
|
|
58
|
+
raise ArgumentError, "Need a handler. Pass the with: keyword argument or provide a block."
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
klasses.each do |klass|
|
|
63
|
+
key = if klass.is_a?(Module) && klass.respond_to?(:===)
|
|
64
|
+
klass.name
|
|
65
|
+
elsif klass.is_a?(String)
|
|
66
|
+
klass
|
|
67
|
+
else
|
|
68
|
+
raise ArgumentError, "#{klass.inspect} must be an Exception class or a String referencing an Exception class"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Put the new handler at the end because the list is read in reverse.
|
|
72
|
+
self.rescue_handlers += [[key, with]]
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Matches an exception to a handler based on the exception class.
|
|
77
|
+
#
|
|
78
|
+
# If no handler matches the exception, check for a handler matching the
|
|
79
|
+
# (optional) +exception.cause+. If no handler matches the exception or its
|
|
80
|
+
# cause, this returns +nil+, so you can deal with unhandled exceptions.
|
|
81
|
+
# Be sure to re-raise unhandled exceptions if this is what you expect.
|
|
82
|
+
#
|
|
83
|
+
# begin
|
|
84
|
+
# # ...
|
|
85
|
+
# rescue => exception
|
|
86
|
+
# rescue_with_handler(exception) || raise
|
|
87
|
+
# end
|
|
88
|
+
#
|
|
89
|
+
# Returns the exception if it was handled and +nil+ if it was not.
|
|
90
|
+
def rescue_with_handler(exception, object: self, visited_exceptions: [])
|
|
91
|
+
visited_exceptions << exception
|
|
92
|
+
|
|
93
|
+
if handler = handler_for_rescue(exception, object: object)
|
|
94
|
+
handler.call exception
|
|
95
|
+
exception
|
|
96
|
+
elsif exception
|
|
97
|
+
if visited_exceptions.include?(exception.cause)
|
|
98
|
+
nil
|
|
99
|
+
else
|
|
100
|
+
rescue_with_handler(exception.cause, object: object, visited_exceptions: visited_exceptions)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def handler_for_rescue(exception, object: self) # :nodoc:
|
|
106
|
+
case rescuer = find_rescue_handler(exception)
|
|
107
|
+
when Symbol
|
|
108
|
+
method = object.method(rescuer)
|
|
109
|
+
if method.arity == 0
|
|
110
|
+
-> e { method.call }
|
|
111
|
+
else
|
|
112
|
+
method
|
|
113
|
+
end
|
|
114
|
+
when Proc
|
|
115
|
+
if rescuer.arity == 0
|
|
116
|
+
-> e { object.instance_exec(&rescuer) }
|
|
117
|
+
else
|
|
118
|
+
-> e { object.instance_exec(e, &rescuer) }
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
private
|
|
124
|
+
def find_rescue_handler(exception)
|
|
125
|
+
if exception
|
|
126
|
+
# Handlers are in order of declaration but the most recently declared
|
|
127
|
+
# is the highest priority match, so we search for matching handlers
|
|
128
|
+
# in reverse.
|
|
129
|
+
_, handler = rescue_handlers.reverse_each.detect do |class_or_name, _|
|
|
130
|
+
if klass = constantize_rescue_handler_class(class_or_name)
|
|
131
|
+
klass === exception
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
handler
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def constantize_rescue_handler_class(class_or_name)
|
|
140
|
+
case class_or_name
|
|
141
|
+
when String, Symbol
|
|
142
|
+
begin
|
|
143
|
+
# Try a lexical lookup first since we support
|
|
144
|
+
#
|
|
145
|
+
# class Super
|
|
146
|
+
# rescue_from 'Error', with: …
|
|
147
|
+
# end
|
|
148
|
+
#
|
|
149
|
+
# class Sub
|
|
150
|
+
# class Error < StandardError; end
|
|
151
|
+
# end
|
|
152
|
+
#
|
|
153
|
+
# so an Error raised in Sub will hit the 'Error' handler.
|
|
154
|
+
const_get class_or_name
|
|
155
|
+
rescue NameError
|
|
156
|
+
class_or_name.safe_constantize
|
|
157
|
+
end
|
|
158
|
+
else
|
|
159
|
+
class_or_name
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Delegates to the class method, but uses the instance as the subject for
|
|
165
|
+
# rescue_from handlers (method calls, +instance_exec+ blocks).
|
|
166
|
+
def rescue_with_handler(exception)
|
|
167
|
+
self.class.rescue_with_handler exception, object: self
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Internal handler lookup. Delegates to class method. Some libraries call
|
|
171
|
+
# this directly, so keeping it around for compatibility.
|
|
172
|
+
def handler_for_rescue(exception) # :nodoc:
|
|
173
|
+
self.class.handler_for_rescue exception, object: self
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/security_utils"
|
|
4
|
+
require "active_support/messages/rotator"
|
|
5
|
+
|
|
6
|
+
module ActiveSupport
|
|
7
|
+
# = Secure Compare Rotator
|
|
8
|
+
#
|
|
9
|
+
# The ActiveSupport::SecureCompareRotator is a wrapper around ActiveSupport::SecurityUtils.secure_compare
|
|
10
|
+
# and allows you to rotate a previously defined value to a new one.
|
|
11
|
+
#
|
|
12
|
+
# It can be used as follow:
|
|
13
|
+
#
|
|
14
|
+
# rotator = ActiveSupport::SecureCompareRotator.new('new_production_value')
|
|
15
|
+
# rotator.rotate('previous_production_value')
|
|
16
|
+
# rotator.secure_compare!('previous_production_value')
|
|
17
|
+
#
|
|
18
|
+
# One real use case example would be to rotate a basic auth credentials:
|
|
19
|
+
#
|
|
20
|
+
# class MyController < ApplicationController
|
|
21
|
+
# def authenticate_request
|
|
22
|
+
# rotator = ActiveSupport::SecureCompareRotator.new('new_password')
|
|
23
|
+
# rotator.rotate('old_password')
|
|
24
|
+
#
|
|
25
|
+
# authenticate_or_request_with_http_basic do |username, password|
|
|
26
|
+
# rotator.secure_compare!(password)
|
|
27
|
+
# rescue ActiveSupport::SecureCompareRotator::InvalidMatch
|
|
28
|
+
# false
|
|
29
|
+
# end
|
|
30
|
+
# end
|
|
31
|
+
# end
|
|
32
|
+
class SecureCompareRotator
|
|
33
|
+
include SecurityUtils
|
|
34
|
+
|
|
35
|
+
InvalidMatch = Class.new(StandardError)
|
|
36
|
+
|
|
37
|
+
def initialize(value, on_rotation: nil)
|
|
38
|
+
@value = value
|
|
39
|
+
@rotate_values = []
|
|
40
|
+
@on_rotation = on_rotation
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def rotate(previous_value)
|
|
44
|
+
@rotate_values << previous_value
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def secure_compare!(other_value, on_rotation: @on_rotation)
|
|
48
|
+
if secure_compare(@value, other_value)
|
|
49
|
+
true
|
|
50
|
+
elsif @rotate_values.any? { |value| secure_compare(value, other_value) }
|
|
51
|
+
on_rotation&.call
|
|
52
|
+
true
|
|
53
|
+
else
|
|
54
|
+
raise InvalidMatch
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveSupport
|
|
4
|
+
module SecurityUtils
|
|
5
|
+
# Constant time string comparison, for fixed length strings.
|
|
6
|
+
#
|
|
7
|
+
# The values compared should be of fixed length, such as strings
|
|
8
|
+
# that have already been processed by HMAC. Raises in case of length mismatch.
|
|
9
|
+
|
|
10
|
+
if defined?(OpenSSL.fixed_length_secure_compare)
|
|
11
|
+
def fixed_length_secure_compare(a, b)
|
|
12
|
+
OpenSSL.fixed_length_secure_compare(a, b)
|
|
13
|
+
end
|
|
14
|
+
else
|
|
15
|
+
def fixed_length_secure_compare(a, b)
|
|
16
|
+
raise ArgumentError, "string length mismatch." unless a.bytesize == b.bytesize
|
|
17
|
+
|
|
18
|
+
l = a.unpack "C#{a.bytesize}"
|
|
19
|
+
|
|
20
|
+
res = 0
|
|
21
|
+
b.each_byte { |byte| res |= byte ^ l.shift }
|
|
22
|
+
res == 0
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
module_function :fixed_length_secure_compare
|
|
26
|
+
|
|
27
|
+
# Secure string comparison for strings of variable length.
|
|
28
|
+
#
|
|
29
|
+
# While a timing attack would not be able to discern the content of
|
|
30
|
+
# a secret compared via secure_compare, it is possible to determine
|
|
31
|
+
# the secret length. This should be considered when using secure_compare
|
|
32
|
+
# to compare weak, short secrets to user input.
|
|
33
|
+
def secure_compare(a, b)
|
|
34
|
+
a.bytesize == b.bytesize && fixed_length_secure_compare(a, b)
|
|
35
|
+
end
|
|
36
|
+
module_function :secure_compare
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveSupport
|
|
4
|
+
# = \String Inquirer
|
|
5
|
+
#
|
|
6
|
+
# Wrapping a string in this class gives you a prettier way to test
|
|
7
|
+
# for equality. The value returned by <tt>Rails.env</tt> is wrapped
|
|
8
|
+
# in a StringInquirer object, so instead of calling this:
|
|
9
|
+
#
|
|
10
|
+
# Rails.env == 'production'
|
|
11
|
+
#
|
|
12
|
+
# you can call this:
|
|
13
|
+
#
|
|
14
|
+
# Rails.env.production?
|
|
15
|
+
#
|
|
16
|
+
# == Instantiating a new \StringInquirer
|
|
17
|
+
#
|
|
18
|
+
# vehicle = ActiveSupport::StringInquirer.new('car')
|
|
19
|
+
# vehicle.car? # => true
|
|
20
|
+
# vehicle.bike? # => false
|
|
21
|
+
class StringInquirer < String
|
|
22
|
+
private
|
|
23
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
24
|
+
method_name.end_with?("?") || super
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def method_missing(method_name, ...)
|
|
28
|
+
if method_name.end_with?("?")
|
|
29
|
+
self == method_name[0..-2]
|
|
30
|
+
else
|
|
31
|
+
super
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/subscriber"
|
|
4
|
+
|
|
5
|
+
module ActiveSupport
|
|
6
|
+
# = Active Support Structured Event \Subscriber
|
|
7
|
+
#
|
|
8
|
+
# +ActiveSupport::StructuredEventSubscriber+ consumes ActiveSupport::Notifications
|
|
9
|
+
# in order to emit structured events via +Rails.event+.
|
|
10
|
+
#
|
|
11
|
+
# An example would be the Action Controller structured event subscriber, responsible for
|
|
12
|
+
# emitting request processing events:
|
|
13
|
+
#
|
|
14
|
+
# module ActionController
|
|
15
|
+
# class StructuredEventSubscriber < ActiveSupport::StructuredEventSubscriber
|
|
16
|
+
# attach_to :action_controller
|
|
17
|
+
#
|
|
18
|
+
# def start_processing(event)
|
|
19
|
+
# emit_event("controller.request_started",
|
|
20
|
+
# controller: event.payload[:controller],
|
|
21
|
+
# action: event.payload[:action],
|
|
22
|
+
# format: event.payload[:format]
|
|
23
|
+
# )
|
|
24
|
+
# end
|
|
25
|
+
# end
|
|
26
|
+
# end
|
|
27
|
+
#
|
|
28
|
+
# After configured, whenever a <tt>"start_processing.action_controller"</tt> notification is published,
|
|
29
|
+
# it will properly dispatch the event (+ActiveSupport::Notifications::Event+) to the +start_processing+ method.
|
|
30
|
+
# The subscriber can then emit a structured event via the +emit_event+ method.
|
|
31
|
+
class StructuredEventSubscriber < Subscriber
|
|
32
|
+
class_attribute :debug_methods, instance_accessor: false, default: [] # :nodoc:
|
|
33
|
+
|
|
34
|
+
DEBUG_CHECK = proc { !ActiveSupport.event_reporter.debug_mode? }
|
|
35
|
+
|
|
36
|
+
class << self
|
|
37
|
+
def attach_to(...) # :nodoc:
|
|
38
|
+
result = super
|
|
39
|
+
set_silenced_events
|
|
40
|
+
result
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
def set_silenced_events
|
|
45
|
+
if subscriber
|
|
46
|
+
subscriber.silenced_events = debug_methods.to_h { |method| ["#{method}.#{namespace}", DEBUG_CHECK] }
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def debug_only(method)
|
|
51
|
+
self.debug_methods << method
|
|
52
|
+
set_silenced_events
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def initialize
|
|
57
|
+
super
|
|
58
|
+
@silenced_events = {}
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def silenced?(event)
|
|
62
|
+
ActiveSupport.event_reporter.subscribers.none? || @silenced_events[event]&.call
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
attr_writer :silenced_events # :nodoc:
|
|
66
|
+
|
|
67
|
+
# Emit a structured event via Rails.event.notify.
|
|
68
|
+
#
|
|
69
|
+
# ==== Arguments
|
|
70
|
+
#
|
|
71
|
+
# * +name+ - The event name as a string or symbol
|
|
72
|
+
# * +payload+ - The event payload as a hash or object
|
|
73
|
+
# * +caller_depth+ - Stack depth for source location (default: 1)
|
|
74
|
+
# * +kwargs+ - Additional payload data merged with the payload hash
|
|
75
|
+
def emit_event(name, payload = nil, caller_depth: 1, **kwargs)
|
|
76
|
+
ActiveSupport.event_reporter.notify(name, payload, caller_depth: caller_depth + 1, **kwargs)
|
|
77
|
+
rescue => e
|
|
78
|
+
handle_event_error(name, e)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Like +emit_event+, but only emits when the event reporter is in debug mode
|
|
82
|
+
def emit_debug_event(name, payload = nil, caller_depth: 1, **kwargs)
|
|
83
|
+
ActiveSupport.event_reporter.debug(name, payload, caller_depth: caller_depth + 1, **kwargs)
|
|
84
|
+
rescue => e
|
|
85
|
+
handle_event_error(name, e)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def call(event)
|
|
89
|
+
super
|
|
90
|
+
rescue => e
|
|
91
|
+
handle_event_error(event.name, e)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
def handle_event_error(name, error)
|
|
96
|
+
ActiveSupport.error_reporter.report(error, source: name)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/notifications"
|
|
4
|
+
|
|
5
|
+
module ActiveSupport
|
|
6
|
+
# = Active Support \Subscriber
|
|
7
|
+
#
|
|
8
|
+
# +ActiveSupport::Subscriber+ is an object set to consume
|
|
9
|
+
# ActiveSupport::Notifications. The subscriber dispatches notifications to
|
|
10
|
+
# a registered object based on its given namespace.
|
|
11
|
+
#
|
|
12
|
+
# An example would be an Active Record subscriber responsible for collecting
|
|
13
|
+
# statistics about queries:
|
|
14
|
+
#
|
|
15
|
+
# module ActiveRecord
|
|
16
|
+
# class StatsSubscriber < ActiveSupport::Subscriber
|
|
17
|
+
# attach_to :active_record
|
|
18
|
+
#
|
|
19
|
+
# def sql(event)
|
|
20
|
+
# Statsd.timing("sql.#{event.payload[:name]}", event.duration)
|
|
21
|
+
# end
|
|
22
|
+
# end
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# After configured, whenever a <tt>"sql.active_record"</tt> notification is
|
|
26
|
+
# published, it will properly dispatch the event
|
|
27
|
+
# (ActiveSupport::Notifications::Event) to the +sql+ method.
|
|
28
|
+
#
|
|
29
|
+
# We can detach a subscriber as well:
|
|
30
|
+
#
|
|
31
|
+
# ActiveRecord::StatsSubscriber.detach_from(:active_record)
|
|
32
|
+
class Subscriber
|
|
33
|
+
class << self
|
|
34
|
+
# Attach the subscriber to a namespace.
|
|
35
|
+
def attach_to(namespace, subscriber = new, notifier = ActiveSupport::Notifications, inherit_all: false)
|
|
36
|
+
@namespace = namespace
|
|
37
|
+
@subscriber = subscriber
|
|
38
|
+
@notifier = notifier
|
|
39
|
+
@inherit_all = inherit_all
|
|
40
|
+
|
|
41
|
+
subscribers << subscriber
|
|
42
|
+
|
|
43
|
+
# Add event subscribers for all existing methods on the class.
|
|
44
|
+
fetch_public_methods(subscriber, inherit_all).each do |event|
|
|
45
|
+
add_event_subscriber(event)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Detach the subscriber from a namespace.
|
|
50
|
+
def detach_from(namespace, notifier = ActiveSupport::Notifications)
|
|
51
|
+
@namespace = namespace
|
|
52
|
+
@subscriber = find_attached_subscriber
|
|
53
|
+
@notifier = notifier
|
|
54
|
+
|
|
55
|
+
return unless subscriber
|
|
56
|
+
|
|
57
|
+
subscribers.delete(subscriber)
|
|
58
|
+
|
|
59
|
+
# Remove event subscribers of all existing methods on the class.
|
|
60
|
+
fetch_public_methods(subscriber, true).each do |event|
|
|
61
|
+
remove_event_subscriber(event)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Reset notifier so that event subscribers will not add for new methods added to the class.
|
|
65
|
+
@notifier = nil
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Adds event subscribers for all new methods added to the class.
|
|
69
|
+
def method_added(event)
|
|
70
|
+
super
|
|
71
|
+
# Only public methods are added as subscribers, and only if a notifier
|
|
72
|
+
# has been set up. This means that subscribers will only be set up for
|
|
73
|
+
# classes that call #attach_to.
|
|
74
|
+
if public_method_defined?(event) && notifier
|
|
75
|
+
add_event_subscriber(event)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def subscribers
|
|
80
|
+
@@subscribers ||= []
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
attr_reader :subscriber, :notifier, :namespace
|
|
85
|
+
|
|
86
|
+
def add_event_subscriber(event) # :doc:
|
|
87
|
+
return if invalid_event?(event)
|
|
88
|
+
|
|
89
|
+
pattern = prepare_pattern(event)
|
|
90
|
+
|
|
91
|
+
# Don't add multiple subscribers (e.g. if methods are redefined).
|
|
92
|
+
return if pattern_subscribed?(pattern)
|
|
93
|
+
|
|
94
|
+
subscriber.patterns[pattern] = notifier.subscribe(pattern, subscriber)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def remove_event_subscriber(event) # :doc:
|
|
98
|
+
return if invalid_event?(event)
|
|
99
|
+
|
|
100
|
+
pattern = prepare_pattern(event)
|
|
101
|
+
|
|
102
|
+
return unless pattern_subscribed?(pattern)
|
|
103
|
+
|
|
104
|
+
notifier.unsubscribe(subscriber.patterns[pattern])
|
|
105
|
+
subscriber.patterns.delete(pattern)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def find_attached_subscriber
|
|
109
|
+
subscribers.find { |attached_subscriber| attached_subscriber.instance_of?(self) }
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def invalid_event?(event)
|
|
113
|
+
%i{ start finish }.include?(event.to_sym)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def prepare_pattern(event)
|
|
117
|
+
"#{event}.#{namespace}"
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def pattern_subscribed?(pattern)
|
|
121
|
+
subscriber.patterns.key?(pattern)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def fetch_public_methods(subscriber, inherit_all)
|
|
125
|
+
subscriber.public_methods(inherit_all) - Subscriber.public_instance_methods(true)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
attr_reader :patterns # :nodoc:
|
|
130
|
+
|
|
131
|
+
def initialize
|
|
132
|
+
@patterns = {}
|
|
133
|
+
super
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def call(event)
|
|
137
|
+
method = event.name[0, event.name.index(".")]
|
|
138
|
+
send(method, event)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "delegate"
|
|
4
|
+
|
|
5
|
+
module ActiveSupport
|
|
6
|
+
# This is a class for wrapping syntax errors. The purpose of this class
|
|
7
|
+
# is to enhance the backtraces on SyntaxError exceptions to include the
|
|
8
|
+
# source location of the syntax error. That way we can display the error
|
|
9
|
+
# source on error pages in development.
|
|
10
|
+
class SyntaxErrorProxy < DelegateClass(SyntaxError) # :nodoc:
|
|
11
|
+
def backtrace
|
|
12
|
+
parse_message_for_trace + super
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class BacktraceLocation < Struct.new(:path, :lineno, :to_s) # :nodoc:
|
|
16
|
+
def spot(_)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def label
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def base_label
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def absolute_path
|
|
26
|
+
path
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class BacktraceLocationProxy < DelegateClass(Thread::Backtrace::Location) # :nodoc:
|
|
31
|
+
def initialize(loc, ex)
|
|
32
|
+
super(loc)
|
|
33
|
+
@ex = ex
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def spot(_)
|
|
37
|
+
super(@ex.__getobj__)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def backtrace_locations
|
|
42
|
+
return nil if super.nil?
|
|
43
|
+
|
|
44
|
+
parse_message_for_trace.map { |trace|
|
|
45
|
+
file, line = trace.match(/^(.+?):(\d+).*$/, &:captures) || trace
|
|
46
|
+
BacktraceLocation.new(file, line.to_i, trace)
|
|
47
|
+
# We have to wrap these backtrace locations because we need the
|
|
48
|
+
# spot information to come from the originating exception, not the
|
|
49
|
+
# proxy object that's generating these
|
|
50
|
+
} + super.map { |loc| BacktraceLocationProxy.new(loc, self) }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
def parse_message_for_trace
|
|
55
|
+
if __getobj__.to_s.start_with?("(eval")
|
|
56
|
+
# If the exception is coming from a call to eval, we need to keep
|
|
57
|
+
# the path of the file in which eval was called to ensure we can
|
|
58
|
+
# return the right source fragment to show the location of the
|
|
59
|
+
# error
|
|
60
|
+
location = __getobj__.backtrace_locations[0]
|
|
61
|
+
["#{location.path}:#{location.lineno}: #{__getobj__}"]
|
|
62
|
+
else
|
|
63
|
+
__getobj__.to_s.split("\n")
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|