activesupport 7.0.0 → 7.2.2.1
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 +4 -4
- data/CHANGELOG.md +156 -255
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -6
- data/lib/active_support/actionable_error.rb +3 -1
- data/lib/active_support/array_inquirer.rb +3 -1
- data/lib/active_support/backtrace_cleaner.rb +41 -9
- data/lib/active_support/benchmarkable.rb +1 -0
- data/lib/active_support/broadcast_logger.rb +251 -0
- data/lib/active_support/builder.rb +1 -1
- 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 +49 -17
- data/lib/active_support/cache/mem_cache_store.rb +111 -129
- data/lib/active_support/cache/memory_store.rb +81 -26
- data/lib/active_support/cache/null_store.rb +6 -0
- data/lib/active_support/cache/redis_cache_store.rb +175 -154
- data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
- data/lib/active_support/cache/strategy/local_cache.rb +31 -13
- data/lib/active_support/cache.rb +457 -377
- data/lib/active_support/callbacks.rb +123 -139
- data/lib/active_support/code_generator.rb +15 -10
- data/lib/active_support/concern.rb +4 -2
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +42 -3
- data/lib/active_support/concurrency/null_lock.rb +13 -0
- data/lib/active_support/configurable.rb +12 -2
- data/lib/active_support/core_ext/array/conversions.rb +7 -9
- data/lib/active_support/core_ext/array/inquiry.rb +2 -2
- data/lib/active_support/core_ext/array.rb +0 -1
- data/lib/active_support/core_ext/class/subclasses.rb +4 -15
- data/lib/active_support/core_ext/date/blank.rb +4 -0
- data/lib/active_support/core_ext/date/calculations.rb +20 -5
- data/lib/active_support/core_ext/date/conversions.rb +15 -16
- data/lib/active_support/core_ext/date.rb +0 -1
- data/lib/active_support/core_ext/date_and_time/calculations.rb +14 -4
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +29 -2
- data/lib/active_support/core_ext/date_time/blank.rb +4 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +15 -15
- data/lib/active_support/core_ext/date_time.rb +0 -1
- data/lib/active_support/core_ext/digest/uuid.rb +7 -10
- data/lib/active_support/core_ext/enumerable.rb +51 -101
- data/lib/active_support/core_ext/erb/util.rb +201 -0
- data/lib/active_support/core_ext/file/atomic.rb +2 -0
- data/lib/active_support/core_ext/hash/conversions.rb +1 -2
- data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +3 -3
- data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
- data/lib/active_support/core_ext/hash/keys.rb +7 -7
- data/lib/active_support/core_ext/integer/inflections.rb +12 -12
- data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +17 -6
- data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -0
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +38 -20
- data/lib/active_support/core_ext/module/concerning.rb +6 -6
- data/lib/active_support/core_ext/module/delegation.rb +20 -119
- data/lib/active_support/core_ext/module/deprecation.rb +12 -12
- data/lib/active_support/core_ext/module/introspection.rb +0 -1
- data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +77 -75
- data/lib/active_support/core_ext/numeric.rb +0 -1
- data/lib/active_support/core_ext/object/acts_like.rb +29 -5
- data/lib/active_support/core_ext/object/blank.rb +45 -1
- data/lib/active_support/core_ext/object/deep_dup.rb +16 -0
- data/lib/active_support/core_ext/object/duplicable.rb +25 -16
- data/lib/active_support/core_ext/object/inclusion.rb +13 -5
- data/lib/active_support/core_ext/object/instance_variables.rb +4 -2
- data/lib/active_support/core_ext/object/json.rb +17 -7
- data/lib/active_support/core_ext/object/to_query.rb +0 -2
- data/lib/active_support/core_ext/object/with.rb +46 -0
- data/lib/active_support/core_ext/object/with_options.rb +9 -9
- data/lib/active_support/core_ext/object.rb +1 -0
- data/lib/active_support/core_ext/pathname/blank.rb +20 -0
- data/lib/active_support/core_ext/pathname/existence.rb +2 -0
- data/lib/active_support/core_ext/pathname.rb +1 -0
- data/lib/active_support/core_ext/range/conversions.rb +32 -11
- data/lib/active_support/core_ext/range/overlap.rb +40 -0
- data/lib/active_support/core_ext/range.rb +1 -2
- data/lib/active_support/core_ext/securerandom.rb +2 -6
- data/lib/active_support/core_ext/string/conversions.rb +3 -3
- data/lib/active_support/core_ext/string/filters.rb +21 -15
- data/lib/active_support/core_ext/string/indent.rb +1 -1
- data/lib/active_support/core_ext/string/inflections.rb +16 -9
- data/lib/active_support/core_ext/string/inquiry.rb +1 -1
- data/lib/active_support/core_ext/string/multibyte.rb +1 -1
- data/lib/active_support/core_ext/string/output_safety.rb +39 -150
- data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
- data/lib/active_support/core_ext/time/calculations.rb +42 -32
- data/lib/active_support/core_ext/time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/time/conversions.rb +13 -15
- data/lib/active_support/core_ext/time/zones.rb +8 -9
- data/lib/active_support/core_ext/time.rb +0 -1
- data/lib/active_support/core_ext.rb +0 -1
- data/lib/active_support/current_attributes.rb +53 -46
- data/lib/active_support/deep_mergeable.rb +53 -0
- data/lib/active_support/delegation.rb +202 -0
- data/lib/active_support/dependencies/autoload.rb +9 -16
- data/lib/active_support/deprecation/behaviors.rb +65 -42
- data/lib/active_support/deprecation/constant_accessor.rb +47 -25
- data/lib/active_support/deprecation/deprecators.rb +104 -0
- data/lib/active_support/deprecation/disallowed.rb +6 -8
- data/lib/active_support/deprecation/method_wrappers.rb +6 -23
- data/lib/active_support/deprecation/proxy_wrappers.rb +34 -22
- data/lib/active_support/deprecation/reporting.rb +49 -27
- data/lib/active_support/deprecation.rb +39 -9
- data/lib/active_support/deprecator.rb +7 -0
- data/lib/active_support/descendants_tracker.rb +66 -175
- data/lib/active_support/duration/iso8601_parser.rb +2 -2
- data/lib/active_support/duration/iso8601_serializer.rb +1 -4
- data/lib/active_support/duration.rb +13 -7
- data/lib/active_support/encrypted_configuration.rb +63 -10
- data/lib/active_support/encrypted_file.rb +29 -13
- data/lib/active_support/environment_inquirer.rb +22 -2
- data/lib/active_support/error_reporter/test_helper.rb +15 -0
- data/lib/active_support/error_reporter.rb +160 -36
- data/lib/active_support/evented_file_update_checker.rb +19 -7
- data/lib/active_support/execution_wrapper.rb +23 -28
- data/lib/active_support/file_update_checker.rb +5 -3
- data/lib/active_support/fork_tracker.rb +4 -32
- data/lib/active_support/gem_version.rb +4 -4
- data/lib/active_support/gzip.rb +2 -0
- data/lib/active_support/hash_with_indifferent_access.rb +41 -25
- data/lib/active_support/html_safe_translation.rb +19 -6
- data/lib/active_support/i18n.rb +1 -1
- data/lib/active_support/i18n_railtie.rb +20 -13
- data/lib/active_support/inflector/inflections.rb +2 -0
- data/lib/active_support/inflector/methods.rb +28 -18
- data/lib/active_support/inflector/transliterate.rb +4 -2
- data/lib/active_support/isolated_execution_state.rb +39 -19
- data/lib/active_support/json/decoding.rb +2 -1
- data/lib/active_support/json/encoding.rb +25 -43
- data/lib/active_support/key_generator.rb +13 -5
- data/lib/active_support/lazy_load_hooks.rb +33 -7
- data/lib/active_support/locale/en.yml +2 -0
- data/lib/active_support/log_subscriber/test_helper.rb +2 -2
- data/lib/active_support/log_subscriber.rb +76 -36
- data/lib/active_support/logger.rb +22 -60
- data/lib/active_support/logger_thread_safe_level.rb +10 -32
- data/lib/active_support/message_encryptor.rb +200 -55
- data/lib/active_support/message_encryptors.rb +141 -0
- data/lib/active_support/message_pack/cache_serializer.rb +23 -0
- data/lib/active_support/message_pack/extensions.rb +305 -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 +220 -89
- data/lib/active_support/message_verifiers.rb +135 -0
- data/lib/active_support/messages/codec.rb +65 -0
- data/lib/active_support/messages/metadata.rb +111 -45
- data/lib/active_support/messages/rotation_coordinator.rb +93 -0
- data/lib/active_support/messages/rotator.rb +34 -32
- data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
- data/lib/active_support/multibyte/chars.rb +4 -2
- data/lib/active_support/multibyte/unicode.rb +9 -37
- data/lib/active_support/notifications/fanout.rb +248 -87
- data/lib/active_support/notifications/instrumenter.rb +93 -25
- data/lib/active_support/notifications.rb +38 -31
- data/lib/active_support/number_helper/number_converter.rb +16 -7
- data/lib/active_support/number_helper/number_to_currency_converter.rb +6 -6
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +3 -3
- data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -0
- data/lib/active_support/number_helper.rb +379 -317
- data/lib/active_support/option_merger.rb +4 -4
- data/lib/active_support/ordered_hash.rb +3 -3
- data/lib/active_support/ordered_options.rb +68 -16
- data/lib/active_support/parameter_filter.rb +103 -84
- data/lib/active_support/proxy_object.rb +8 -3
- data/lib/active_support/railtie.rb +30 -25
- data/lib/active_support/reloader.rb +13 -5
- data/lib/active_support/rescuable.rb +12 -10
- data/lib/active_support/secure_compare_rotator.rb +17 -10
- data/lib/active_support/string_inquirer.rb +4 -2
- data/lib/active_support/subscriber.rb +10 -27
- data/lib/active_support/syntax_error_proxy.rb +60 -0
- data/lib/active_support/tagged_logging.rb +64 -25
- data/lib/active_support/test_case.rb +160 -7
- data/lib/active_support/testing/assertions.rb +29 -13
- data/lib/active_support/testing/autorun.rb +0 -2
- data/lib/active_support/testing/constant_stubbing.rb +54 -0
- data/lib/active_support/testing/deprecation.rb +20 -27
- data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
- data/lib/active_support/testing/isolation.rb +46 -33
- data/lib/active_support/testing/method_call_assertions.rb +7 -8
- data/lib/active_support/testing/parallelization/server.rb +3 -0
- data/lib/active_support/testing/parallelize_executor.rb +8 -3
- data/lib/active_support/testing/setup_and_teardown.rb +2 -0
- data/lib/active_support/testing/stream.rb +1 -1
- data/lib/active_support/testing/strict_warnings.rb +43 -0
- data/lib/active_support/testing/tests_without_assertions.rb +19 -0
- data/lib/active_support/testing/time_helpers.rb +38 -16
- data/lib/active_support/time_with_zone.rb +28 -54
- data/lib/active_support/values/time_zone.rb +26 -15
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/xml_mini/jdom.rb +3 -10
- data/lib/active_support/xml_mini/nokogiri.rb +1 -1
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
- data/lib/active_support/xml_mini/rexml.rb +1 -1
- data/lib/active_support/xml_mini.rb +13 -4
- data/lib/active_support.rb +15 -3
- metadata +142 -21
- data/lib/active_support/core_ext/array/deprecated_conversions.rb +0 -25
- data/lib/active_support/core_ext/date/deprecated_conversions.rb +0 -26
- data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +0 -22
- data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +0 -60
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +0 -26
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -7
- data/lib/active_support/core_ext/range/overlaps.rb +0 -10
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +0 -22
- data/lib/active_support/core_ext/uri.rb +0 -5
- data/lib/active_support/deprecation/instance_delegator.rb +0 -38
- data/lib/active_support/per_thread_registry.rb +0 -65
- data/lib/active_support/ruby_features.rb +0 -7
@@ -1,80 +1,46 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module
|
4
|
-
|
5
|
-
|
3
|
+
module ActiveSupport
|
4
|
+
module EnumerableCoreExt # :nodoc:
|
5
|
+
module Constants
|
6
|
+
private
|
7
|
+
def const_missing(name)
|
8
|
+
if name == :SoleItemExpectedError
|
9
|
+
::ActiveSupport::EnumerableCoreExt::SoleItemExpectedError
|
10
|
+
else
|
11
|
+
super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
6
17
|
|
18
|
+
module Enumerable
|
7
19
|
# Error generated by +sole+ when called on an enumerable that doesn't have
|
8
20
|
# exactly one item.
|
9
21
|
class SoleItemExpectedError < StandardError; end
|
10
22
|
|
11
|
-
#
|
12
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
# We can't use Refinements here because Refinements with Module which will be prepended
|
17
|
-
# doesn't work well https://bugs.ruby-lang.org/issues/13446
|
18
|
-
alias :_original_sum_with_required_identity :sum
|
19
|
-
private :_original_sum_with_required_identity
|
20
|
-
|
21
|
-
# :startdoc:
|
23
|
+
# HACK: For performance reasons, Enumerable shouldn't have any constants of its own.
|
24
|
+
# So we move SoleItemExpectedError into ActiveSupport::EnumerableCoreExt.
|
25
|
+
ActiveSupport::EnumerableCoreExt::SoleItemExpectedError = remove_const(:SoleItemExpectedError)
|
26
|
+
singleton_class.prepend(ActiveSupport::EnumerableCoreExt::Constants)
|
22
27
|
|
23
28
|
# Calculates the minimum from the extracted elements.
|
24
29
|
#
|
25
|
-
#
|
26
|
-
#
|
30
|
+
# payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
|
31
|
+
# payments.minimum(:price) # => 5
|
27
32
|
def minimum(key)
|
28
33
|
map(&key).min
|
29
34
|
end
|
30
35
|
|
31
36
|
# Calculates the maximum from the extracted elements.
|
32
37
|
#
|
33
|
-
#
|
34
|
-
#
|
38
|
+
# payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
|
39
|
+
# payments.maximum(:price) # => 15
|
35
40
|
def maximum(key)
|
36
41
|
map(&key).max
|
37
42
|
end
|
38
43
|
|
39
|
-
# Calculates a sum from the elements.
|
40
|
-
#
|
41
|
-
# payments.sum { |p| p.price * p.tax_rate }
|
42
|
-
# payments.sum(&:price)
|
43
|
-
#
|
44
|
-
# The latter is a shortcut for:
|
45
|
-
#
|
46
|
-
# payments.inject(0) { |sum, p| sum + p.price }
|
47
|
-
#
|
48
|
-
# It can also calculate the sum without the use of a block.
|
49
|
-
#
|
50
|
-
# [5, 15, 10].sum # => 30
|
51
|
-
# ['foo', 'bar'].sum('') # => "foobar"
|
52
|
-
# [[1, 2], [3, 1, 5]].sum([]) # => [1, 2, 3, 1, 5]
|
53
|
-
#
|
54
|
-
# The default sum of an empty list is zero. You can override this default:
|
55
|
-
#
|
56
|
-
# [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
|
57
|
-
def sum(identity = nil, &block)
|
58
|
-
if identity
|
59
|
-
_original_sum_with_required_identity(identity, &block)
|
60
|
-
elsif block_given?
|
61
|
-
map(&block).sum
|
62
|
-
# we check `first(1) == []` to check if we have an
|
63
|
-
# empty Enumerable; checking `empty?` would return
|
64
|
-
# true for `[nil]`, which we want to deprecate to
|
65
|
-
# keep consistent with Ruby
|
66
|
-
elsif first.is_a?(Numeric) || first(1) == []
|
67
|
-
identity ||= 0
|
68
|
-
_original_sum_with_required_identity(identity, &block)
|
69
|
-
else
|
70
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
71
|
-
Rails 7.0 has deprecated Enumerable.sum in favor of Ruby's native implementation available since 2.4.
|
72
|
-
Sum of non-numeric elements requires an initial argument.
|
73
|
-
MSG
|
74
|
-
inject(:+) || 0
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
44
|
# Convert an enumerable to a hash, using the block result as the key and the
|
79
45
|
# element as the value.
|
80
46
|
#
|
@@ -106,17 +72,17 @@ module Enumerable
|
|
106
72
|
#
|
107
73
|
# %i( created_at updated_at ).index_with(Time.now)
|
108
74
|
# # => { created_at: 2020-03-09 22:31:47, updated_at: 2020-03-09 22:31:47 }
|
109
|
-
def index_with(default =
|
75
|
+
def index_with(default = (no_default = true))
|
110
76
|
if block_given?
|
111
77
|
result = {}
|
112
78
|
each { |elem| result[elem] = yield(elem) }
|
113
79
|
result
|
114
|
-
elsif
|
80
|
+
elsif no_default
|
81
|
+
to_enum(:index_with) { size if respond_to?(:size) }
|
82
|
+
else
|
115
83
|
result = {}
|
116
84
|
each { |elem| result[elem] = default }
|
117
85
|
result
|
118
|
-
else
|
119
|
-
to_enum(:index_with) { size if respond_to?(:size) }
|
120
86
|
end
|
121
87
|
end
|
122
88
|
|
@@ -127,8 +93,8 @@ module Enumerable
|
|
127
93
|
def many?
|
128
94
|
cnt = 0
|
129
95
|
if block_given?
|
130
|
-
any? do |
|
131
|
-
cnt += 1 if yield
|
96
|
+
any? do |*args|
|
97
|
+
cnt += 1 if yield(*args)
|
132
98
|
cnt > 1
|
133
99
|
end
|
134
100
|
else
|
@@ -205,16 +171,16 @@ module Enumerable
|
|
205
171
|
# Returns a new +Array+ without the blank items.
|
206
172
|
# Uses Object#blank? for determining if an item is blank.
|
207
173
|
#
|
208
|
-
#
|
209
|
-
#
|
174
|
+
# [1, "", nil, 2, " ", [], {}, false, true].compact_blank
|
175
|
+
# # => [1, 2, true]
|
210
176
|
#
|
211
|
-
#
|
212
|
-
#
|
177
|
+
# Set.new([nil, "", 1, false]).compact_blank
|
178
|
+
# # => [1]
|
213
179
|
#
|
214
180
|
# When called on a +Hash+, returns a new +Hash+ without the blank values.
|
215
181
|
#
|
216
|
-
#
|
217
|
-
#
|
182
|
+
# { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
|
183
|
+
# # => { b: 1, f: true }
|
218
184
|
def compact_blank
|
219
185
|
reject(&:blank?)
|
220
186
|
end
|
@@ -223,25 +189,25 @@ module Enumerable
|
|
223
189
|
# objects in the original enumerable.
|
224
190
|
#
|
225
191
|
# [ Person.find(5), Person.find(3), Person.find(1) ].in_order_of(:id, [ 1, 5, 3 ])
|
226
|
-
# => [ Person.find(1), Person.find(5), Person.find(3) ]
|
192
|
+
# # => [ Person.find(1), Person.find(5), Person.find(3) ]
|
227
193
|
#
|
228
194
|
# If the +series+ include keys that have no corresponding element in the Enumerable, these are ignored.
|
229
195
|
# If the Enumerable has additional elements that aren't named in the +series+, these are not included in the result.
|
230
196
|
def in_order_of(key, series)
|
231
|
-
|
197
|
+
group_by(&key).values_at(*series).flatten(1).compact
|
232
198
|
end
|
233
199
|
|
234
200
|
# Returns the sole item in the enumerable. If there are no items, or more
|
235
201
|
# than one item, raises +Enumerable::SoleItemExpectedError+.
|
236
202
|
#
|
237
|
-
#
|
238
|
-
#
|
239
|
-
#
|
203
|
+
# ["x"].sole # => "x"
|
204
|
+
# Set.new.sole # => Enumerable::SoleItemExpectedError: no item found
|
205
|
+
# { a: 1, b: 2 }.sole # => Enumerable::SoleItemExpectedError: multiple items found
|
240
206
|
def sole
|
241
207
|
case count
|
242
208
|
when 1 then return first # rubocop:disable Style/RedundantReturn
|
243
|
-
when 0 then raise SoleItemExpectedError, "no item found"
|
244
|
-
when 2.. then raise SoleItemExpectedError, "multiple items found"
|
209
|
+
when 0 then raise ActiveSupport::EnumerableCoreExt::SoleItemExpectedError, "no item found"
|
210
|
+
when 2.. then raise ActiveSupport::EnumerableCoreExt::SoleItemExpectedError, "multiple items found"
|
245
211
|
end
|
246
212
|
end
|
247
213
|
end
|
@@ -255,9 +221,9 @@ class Hash
|
|
255
221
|
# Removes all blank values from the +Hash+ in place and returns self.
|
256
222
|
# Uses Object#blank? for determining if a value is blank.
|
257
223
|
#
|
258
|
-
#
|
259
|
-
#
|
260
|
-
#
|
224
|
+
# h = { a: "", b: 1, c: nil, d: [], e: false, f: true }
|
225
|
+
# h.compact_blank!
|
226
|
+
# # => { b: 1, f: true }
|
261
227
|
def compact_blank!
|
262
228
|
# use delete_if rather than reject! because it always returns self even if nothing changed
|
263
229
|
delete_if { |_k, v| v.blank? }
|
@@ -267,44 +233,28 @@ end
|
|
267
233
|
class Range # :nodoc:
|
268
234
|
# Optimize range sum to use arithmetic progression if a block is not given and
|
269
235
|
# we have a range of numeric values.
|
270
|
-
def sum(
|
236
|
+
def sum(initial_value = 0)
|
271
237
|
if block_given? || !(first.is_a?(Integer) && last.is_a?(Integer))
|
272
238
|
super
|
273
239
|
else
|
274
240
|
actual_last = exclude_end? ? (last - 1) : last
|
275
241
|
if actual_last >= first
|
276
|
-
sum =
|
242
|
+
sum = initial_value || 0
|
277
243
|
sum + (actual_last - first + 1) * (actual_last + first) / 2
|
278
244
|
else
|
279
|
-
|
245
|
+
initial_value || 0
|
280
246
|
end
|
281
247
|
end
|
282
248
|
end
|
283
249
|
end
|
284
250
|
|
285
|
-
# Using Refinements here in order not to expose our internal method
|
286
|
-
using Module.new {
|
287
|
-
refine Array do
|
288
|
-
alias :orig_sum :sum
|
289
|
-
end
|
290
|
-
}
|
291
|
-
|
292
251
|
class Array # :nodoc:
|
293
|
-
def sum(init = nil, &block)
|
294
|
-
if init.is_a?(Numeric) || first.is_a?(Numeric)
|
295
|
-
init ||= 0
|
296
|
-
orig_sum(init, &block)
|
297
|
-
else
|
298
|
-
super
|
299
|
-
end
|
300
|
-
end
|
301
|
-
|
302
252
|
# Removes all blank elements from the +Array+ in place and returns self.
|
303
253
|
# Uses Object#blank? for determining if an item is blank.
|
304
254
|
#
|
305
|
-
#
|
306
|
-
#
|
307
|
-
#
|
255
|
+
# a = [1, "", nil, 2, " ", [], {}, false, true]
|
256
|
+
# a.compact_blank!
|
257
|
+
# # => [1, 2, true]
|
308
258
|
def compact_blank!
|
309
259
|
# use delete_if rather than reject! because it always returns self even if nothing changed
|
310
260
|
delete_if(&:blank?)
|
@@ -0,0 +1,201 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "erb"
|
4
|
+
|
5
|
+
module ActiveSupport
|
6
|
+
module CoreExt
|
7
|
+
module ERBUtil
|
8
|
+
# HTML escapes strings but doesn't wrap them with an ActiveSupport::SafeBuffer.
|
9
|
+
# This method is not for public consumption! Seriously!
|
10
|
+
def html_escape(s) # :nodoc:
|
11
|
+
s = s.to_s
|
12
|
+
if s.html_safe?
|
13
|
+
s
|
14
|
+
else
|
15
|
+
super(ActiveSupport::Multibyte::Unicode.tidy_bytes(s))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
alias :unwrapped_html_escape :html_escape # :nodoc:
|
19
|
+
|
20
|
+
# A utility method for escaping HTML tag characters.
|
21
|
+
# This method is also aliased as <tt>h</tt>.
|
22
|
+
#
|
23
|
+
# puts html_escape('is a > 0 & a < 10?')
|
24
|
+
# # => is a > 0 & a < 10?
|
25
|
+
def html_escape(s) # rubocop:disable Lint/DuplicateMethods
|
26
|
+
unwrapped_html_escape(s).html_safe
|
27
|
+
end
|
28
|
+
alias h html_escape
|
29
|
+
end
|
30
|
+
|
31
|
+
module ERBUtilPrivate
|
32
|
+
include ERBUtil
|
33
|
+
private :unwrapped_html_escape, :html_escape, :h
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class ERB
|
39
|
+
module Util
|
40
|
+
HTML_ESCAPE = { "&" => "&", ">" => ">", "<" => "<", '"' => """, "'" => "'" }
|
41
|
+
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+)|(#[xX][\dA-Fa-f]+));)/
|
42
|
+
|
43
|
+
# Following XML requirements: https://www.w3.org/TR/REC-xml/#NT-Name
|
44
|
+
TAG_NAME_START_CODEPOINTS = "@:A-Z_a-z\u{C0}-\u{D6}\u{D8}-\u{F6}\u{F8}-\u{2FF}\u{370}-\u{37D}\u{37F}-\u{1FFF}" \
|
45
|
+
"\u{200C}-\u{200D}\u{2070}-\u{218F}\u{2C00}-\u{2FEF}\u{3001}-\u{D7FF}\u{F900}-\u{FDCF}" \
|
46
|
+
"\u{FDF0}-\u{FFFD}\u{10000}-\u{EFFFF}"
|
47
|
+
INVALID_TAG_NAME_START_REGEXP = /[^#{TAG_NAME_START_CODEPOINTS}]/
|
48
|
+
TAG_NAME_FOLLOWING_CODEPOINTS = "#{TAG_NAME_START_CODEPOINTS}\\-.0-9\u{B7}\u{0300}-\u{036F}\u{203F}-\u{2040}"
|
49
|
+
INVALID_TAG_NAME_FOLLOWING_REGEXP = /[^#{TAG_NAME_FOLLOWING_CODEPOINTS}]/
|
50
|
+
SAFE_XML_TAG_NAME_REGEXP = /\A[#{TAG_NAME_START_CODEPOINTS}][#{TAG_NAME_FOLLOWING_CODEPOINTS}]*\z/
|
51
|
+
TAG_NAME_REPLACEMENT_CHAR = "_"
|
52
|
+
|
53
|
+
prepend ActiveSupport::CoreExt::ERBUtilPrivate
|
54
|
+
singleton_class.prepend ActiveSupport::CoreExt::ERBUtil
|
55
|
+
|
56
|
+
# A utility method for escaping HTML without affecting existing escaped entities.
|
57
|
+
#
|
58
|
+
# html_escape_once('1 < 2 & 3')
|
59
|
+
# # => "1 < 2 & 3"
|
60
|
+
#
|
61
|
+
# html_escape_once('<< Accept & Checkout')
|
62
|
+
# # => "<< Accept & Checkout"
|
63
|
+
def html_escape_once(s)
|
64
|
+
ActiveSupport::Multibyte::Unicode.tidy_bytes(s.to_s).gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE).html_safe
|
65
|
+
end
|
66
|
+
|
67
|
+
module_function :html_escape_once
|
68
|
+
|
69
|
+
# A utility method for escaping HTML entities in JSON strings. Specifically, the
|
70
|
+
# &, > and < characters are replaced with their equivalent unicode escaped form -
|
71
|
+
# \u0026, \u003e, and \u003c. The Unicode sequences \u2028 and \u2029 are also
|
72
|
+
# escaped as they are treated as newline characters in some JavaScript engines.
|
73
|
+
# These sequences have identical meaning as the original characters inside the
|
74
|
+
# context of a JSON string, so assuming the input is a valid and well-formed
|
75
|
+
# JSON value, the output will have equivalent meaning when parsed:
|
76
|
+
#
|
77
|
+
# json = JSON.generate({ name: "</script><script>alert('PWNED!!!')</script>"})
|
78
|
+
# # => "{\"name\":\"</script><script>alert('PWNED!!!')</script>\"}"
|
79
|
+
#
|
80
|
+
# json_escape(json)
|
81
|
+
# # => "{\"name\":\"\\u003C/script\\u003E\\u003Cscript\\u003Ealert('PWNED!!!')\\u003C/script\\u003E\"}"
|
82
|
+
#
|
83
|
+
# JSON.parse(json) == JSON.parse(json_escape(json))
|
84
|
+
# # => true
|
85
|
+
#
|
86
|
+
# The intended use case for this method is to escape JSON strings before including
|
87
|
+
# them inside a script tag to avoid XSS vulnerability:
|
88
|
+
#
|
89
|
+
# <script>
|
90
|
+
# var currentUser = <%= raw json_escape(current_user.to_json) %>;
|
91
|
+
# </script>
|
92
|
+
#
|
93
|
+
# It is necessary to +raw+ the result of +json_escape+, so that quotation marks
|
94
|
+
# don't get converted to <tt>"</tt> entities. +json_escape+ doesn't
|
95
|
+
# automatically flag the result as HTML safe, since the raw value is unsafe to
|
96
|
+
# use inside HTML attributes.
|
97
|
+
#
|
98
|
+
# If your JSON is being used downstream for insertion into the DOM, be aware of
|
99
|
+
# whether or not it is being inserted via <tt>html()</tt>. Most jQuery plugins do this.
|
100
|
+
# If that is the case, be sure to +html_escape+ or +sanitize+ any user-generated
|
101
|
+
# content returned by your JSON.
|
102
|
+
#
|
103
|
+
# If you need to output JSON elsewhere in your HTML, you can just do something
|
104
|
+
# like this, as any unsafe characters (including quotation marks) will be
|
105
|
+
# automatically escaped for you:
|
106
|
+
#
|
107
|
+
# <div data-user-info="<%= current_user.to_json %>">...</div>
|
108
|
+
#
|
109
|
+
# WARNING: this helper only works with valid JSON. Using this on non-JSON values
|
110
|
+
# will open up serious XSS vulnerabilities. For example, if you replace the
|
111
|
+
# +current_user.to_json+ in the example above with user input instead, the browser
|
112
|
+
# will happily <tt>eval()</tt> that string as JavaScript.
|
113
|
+
#
|
114
|
+
# The escaping performed in this method is identical to those performed in the
|
115
|
+
# Active Support JSON encoder when +ActiveSupport.escape_html_entities_in_json+ is
|
116
|
+
# set to true. Because this transformation is idempotent, this helper can be
|
117
|
+
# applied even if +ActiveSupport.escape_html_entities_in_json+ is already true.
|
118
|
+
#
|
119
|
+
# Therefore, when you are unsure if +ActiveSupport.escape_html_entities_in_json+
|
120
|
+
# is enabled, or if you are unsure where your JSON string originated from, it
|
121
|
+
# is recommended that you always apply this helper (other libraries, such as the
|
122
|
+
# JSON gem, do not provide this kind of protection by default; also some gems
|
123
|
+
# might override +to_json+ to bypass Active Support's encoder).
|
124
|
+
def json_escape(s)
|
125
|
+
result = s.to_s.dup
|
126
|
+
result.gsub!(">", '\u003e')
|
127
|
+
result.gsub!("<", '\u003c')
|
128
|
+
result.gsub!("&", '\u0026')
|
129
|
+
result.gsub!("\u2028", '\u2028')
|
130
|
+
result.gsub!("\u2029", '\u2029')
|
131
|
+
s.html_safe? ? result.html_safe : result
|
132
|
+
end
|
133
|
+
|
134
|
+
module_function :json_escape
|
135
|
+
|
136
|
+
# A utility method for escaping XML names of tags and names of attributes.
|
137
|
+
#
|
138
|
+
# xml_name_escape('1 < 2 & 3')
|
139
|
+
# # => "1___2___3"
|
140
|
+
#
|
141
|
+
# It follows the requirements of the specification: https://www.w3.org/TR/REC-xml/#NT-Name
|
142
|
+
def xml_name_escape(name)
|
143
|
+
name = name.to_s
|
144
|
+
return "" if name.blank?
|
145
|
+
return name if name.match?(SAFE_XML_TAG_NAME_REGEXP)
|
146
|
+
|
147
|
+
starting_char = name[0]
|
148
|
+
starting_char.gsub!(INVALID_TAG_NAME_START_REGEXP, TAG_NAME_REPLACEMENT_CHAR)
|
149
|
+
|
150
|
+
return starting_char if name.size == 1
|
151
|
+
|
152
|
+
following_chars = name[1..-1]
|
153
|
+
following_chars.gsub!(INVALID_TAG_NAME_FOLLOWING_REGEXP, TAG_NAME_REPLACEMENT_CHAR)
|
154
|
+
|
155
|
+
starting_char << following_chars
|
156
|
+
end
|
157
|
+
module_function :xml_name_escape
|
158
|
+
|
159
|
+
# Tokenizes a line of ERB. This is really just for error reporting and
|
160
|
+
# nobody should use it.
|
161
|
+
def self.tokenize(source) # :nodoc:
|
162
|
+
require "strscan"
|
163
|
+
source = StringScanner.new(source.chomp)
|
164
|
+
tokens = []
|
165
|
+
|
166
|
+
start_re = /<%(?:={1,2}|-|\#|%)?/m
|
167
|
+
finish_re = /(?:[-=])?%>/m
|
168
|
+
|
169
|
+
while !source.eos?
|
170
|
+
pos = source.pos
|
171
|
+
source.scan_until(/(?:#{start_re}|#{finish_re})/)
|
172
|
+
raise NotImplementedError if source.matched.nil?
|
173
|
+
len = source.pos - source.matched.bytesize - pos
|
174
|
+
|
175
|
+
case source.matched
|
176
|
+
when start_re
|
177
|
+
tokens << [:TEXT, source.string[pos, len]] if len > 0
|
178
|
+
tokens << [:OPEN, source.matched]
|
179
|
+
if source.scan(/(.*?)(?=#{finish_re}|\z)/m)
|
180
|
+
tokens << [:CODE, source.matched] unless source.matched.empty?
|
181
|
+
tokens << [:CLOSE, source.scan(finish_re)] unless source.eos?
|
182
|
+
else
|
183
|
+
raise NotImplementedError
|
184
|
+
end
|
185
|
+
when finish_re
|
186
|
+
tokens << [:CODE, source.string[pos, len]] if len > 0
|
187
|
+
tokens << [:CLOSE, source.matched]
|
188
|
+
else
|
189
|
+
raise NotImplementedError, source.matched
|
190
|
+
end
|
191
|
+
|
192
|
+
unless source.eos? || source.exist?(start_re) || source.exist?(finish_re)
|
193
|
+
tokens << [:TEXT, source.rest]
|
194
|
+
source.terminate
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
tokens
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/xml_mini"
|
4
3
|
require "active_support/core_ext/object/blank"
|
5
4
|
require "active_support/core_ext/object/to_param"
|
6
5
|
require "active_support/core_ext/object/to_query"
|
@@ -69,7 +68,7 @@ class Hash
|
|
69
68
|
#
|
70
69
|
# By default the root node is "hash", but that's configurable via the <tt>:root</tt> option.
|
71
70
|
#
|
72
|
-
# The default XML builder is a fresh instance of
|
71
|
+
# The default XML builder is a fresh instance of +Builder::XmlMarkup+. You can
|
73
72
|
# configure your own builder with the <tt>:builder</tt> option. The method also accepts
|
74
73
|
# options like <tt>:dasherize</tt> and friends, they are forwarded to the builder.
|
75
74
|
def to_xml(options = {})
|
@@ -1,6 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/deep_mergeable"
|
4
|
+
|
3
5
|
class Hash
|
6
|
+
include ActiveSupport::DeepMergeable
|
7
|
+
|
8
|
+
##
|
9
|
+
# :method: deep_merge
|
10
|
+
# :call-seq: deep_merge(other_hash, &block)
|
11
|
+
#
|
4
12
|
# Returns a new hash with +self+ and +other_hash+ merged recursively.
|
5
13
|
#
|
6
14
|
# h1 = { a: true, b: { c: [1, 2, 3] } }
|
@@ -15,20 +23,20 @@ class Hash
|
|
15
23
|
# h2 = { b: 250, c: { c1: 200 } }
|
16
24
|
# h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val }
|
17
25
|
# # => { a: 100, b: 450, c: { c1: 300 } }
|
18
|
-
|
19
|
-
|
20
|
-
|
26
|
+
#
|
27
|
+
#--
|
28
|
+
# Implemented by ActiveSupport::DeepMergeable#deep_merge.
|
29
|
+
|
30
|
+
##
|
31
|
+
# :method: deep_merge!
|
32
|
+
# :call-seq: deep_merge!(other_hash, &block)
|
33
|
+
#
|
34
|
+
# Same as #deep_merge, but modifies +self+.
|
35
|
+
#
|
36
|
+
#--
|
37
|
+
# Implemented by ActiveSupport::DeepMergeable#deep_merge!.
|
21
38
|
|
22
|
-
|
23
|
-
|
24
|
-
merge!(other_hash) do |key, this_val, other_val|
|
25
|
-
if this_val.is_a?(Hash) && other_val.is_a?(Hash)
|
26
|
-
this_val.deep_merge(other_val, &block)
|
27
|
-
elsif block_given?
|
28
|
-
block.call(key, this_val, other_val)
|
29
|
-
else
|
30
|
-
other_val
|
31
|
-
end
|
32
|
-
end
|
39
|
+
def deep_merge?(other) # :nodoc:
|
40
|
+
other.is_a?(Hash)
|
33
41
|
end
|
34
42
|
end
|
@@ -5,10 +5,10 @@ class Hash
|
|
5
5
|
# This includes the values from the root hash and from all
|
6
6
|
# nested hashes and arrays.
|
7
7
|
#
|
8
|
-
#
|
8
|
+
# hash = { person: { name: 'Rob', age: '28' } }
|
9
9
|
#
|
10
|
-
#
|
11
|
-
#
|
10
|
+
# hash.deep_transform_values{ |value| value.to_s.upcase }
|
11
|
+
# # => {person: {name: "ROB", age: "28"}}
|
12
12
|
def deep_transform_values(&block)
|
13
13
|
_deep_transform_values_in_object(self, &block)
|
14
14
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require "active_support/hash_with_indifferent_access"
|
4
4
|
|
5
5
|
class Hash
|
6
|
-
# Returns an
|
6
|
+
# Returns an ActiveSupport::HashWithIndifferentAccess out of its receiver:
|
7
7
|
#
|
8
8
|
# { a: 1 }.with_indifferent_access['a'] # => 1
|
9
9
|
def with_indifferent_access
|
@@ -13,8 +13,8 @@ class Hash
|
|
13
13
|
# Called when object is nested under an object that receives
|
14
14
|
# #with_indifferent_access. This method will be called on the current object
|
15
15
|
# by the enclosing object and is aliased to #with_indifferent_access by
|
16
|
-
# default. Subclasses of Hash may
|
17
|
-
# converting to an
|
16
|
+
# default. Subclasses of Hash may override this method to return +self+ if
|
17
|
+
# converting to an ActiveSupport::HashWithIndifferentAccess would not be
|
18
18
|
# desirable.
|
19
19
|
#
|
20
20
|
# b = { b: 1 }
|
@@ -8,13 +8,13 @@ class Hash
|
|
8
8
|
# hash.stringify_keys
|
9
9
|
# # => {"name"=>"Rob", "age"=>"28"}
|
10
10
|
def stringify_keys
|
11
|
-
transform_keys
|
11
|
+
transform_keys { |k| Symbol === k ? k.name : k.to_s }
|
12
12
|
end
|
13
13
|
|
14
14
|
# Destructively converts all keys to strings. Same as
|
15
15
|
# +stringify_keys+, but modifies +self+.
|
16
16
|
def stringify_keys!
|
17
|
-
transform_keys!
|
17
|
+
transform_keys! { |k| Symbol === k ? k.name : k.to_s }
|
18
18
|
end
|
19
19
|
|
20
20
|
# Returns a new hash with all keys converted to symbols, as long as
|
@@ -58,10 +58,10 @@ class Hash
|
|
58
58
|
# This includes the keys from the root hash and from all
|
59
59
|
# nested hashes and arrays.
|
60
60
|
#
|
61
|
-
#
|
61
|
+
# hash = { person: { name: 'Rob', age: '28' } }
|
62
62
|
#
|
63
|
-
#
|
64
|
-
#
|
63
|
+
# hash.deep_transform_keys{ |key| key.to_s.upcase }
|
64
|
+
# # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}}
|
65
65
|
def deep_transform_keys(&block)
|
66
66
|
_deep_transform_keys_in_object(self, &block)
|
67
67
|
end
|
@@ -82,14 +82,14 @@ class Hash
|
|
82
82
|
# hash.deep_stringify_keys
|
83
83
|
# # => {"person"=>{"name"=>"Rob", "age"=>"28"}}
|
84
84
|
def deep_stringify_keys
|
85
|
-
deep_transform_keys
|
85
|
+
deep_transform_keys { |k| Symbol === k ? k.name : k.to_s }
|
86
86
|
end
|
87
87
|
|
88
88
|
# Destructively converts all keys to strings.
|
89
89
|
# This includes the keys from the root hash and from all
|
90
90
|
# nested hashes and arrays.
|
91
91
|
def deep_stringify_keys!
|
92
|
-
deep_transform_keys!
|
92
|
+
deep_transform_keys! { |k| Symbol === k ? k.name : k.to_s }
|
93
93
|
end
|
94
94
|
|
95
95
|
# Returns a new hash with all keys converted to symbols, as long as
|
@@ -6,12 +6,12 @@ class Integer
|
|
6
6
|
# Ordinalize turns a number into an ordinal string used to denote the
|
7
7
|
# position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
|
8
8
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
9
|
+
# 1.ordinalize # => "1st"
|
10
|
+
# 2.ordinalize # => "2nd"
|
11
|
+
# 1002.ordinalize # => "1002nd"
|
12
|
+
# 1003.ordinalize # => "1003rd"
|
13
|
+
# -11.ordinalize # => "-11th"
|
14
|
+
# -1001.ordinalize # => "-1001st"
|
15
15
|
def ordinalize
|
16
16
|
ActiveSupport::Inflector.ordinalize(self)
|
17
17
|
end
|
@@ -19,12 +19,12 @@ class Integer
|
|
19
19
|
# Ordinal returns the suffix used to denote the position
|
20
20
|
# in an ordered sequence such as 1st, 2nd, 3rd, 4th.
|
21
21
|
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
22
|
+
# 1.ordinal # => "st"
|
23
|
+
# 2.ordinal # => "nd"
|
24
|
+
# 1002.ordinal # => "nd"
|
25
|
+
# 1003.ordinal # => "rd"
|
26
|
+
# -11.ordinal # => "th"
|
27
|
+
# -1001.ordinal # => "st"
|
28
28
|
def ordinal
|
29
29
|
ActiveSupport::Inflector.ordinal(self)
|
30
30
|
end
|