activesupport 6.0.0
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 +572 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +40 -0
- data/lib/active_support.rb +96 -0
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/all.rb +5 -0
- data/lib/active_support/array_inquirer.rb +48 -0
- data/lib/active_support/backtrace_cleaner.rb +132 -0
- data/lib/active_support/benchmarkable.rb +51 -0
- data/lib/active_support/builder.rb +8 -0
- data/lib/active_support/cache.rb +830 -0
- data/lib/active_support/cache/file_store.rb +196 -0
- data/lib/active_support/cache/mem_cache_store.rb +212 -0
- data/lib/active_support/cache/memory_store.rb +174 -0
- data/lib/active_support/cache/null_store.rb +48 -0
- data/lib/active_support/cache/redis_cache_store.rb +488 -0
- data/lib/active_support/cache/strategy/local_cache.rb +194 -0
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
- data/lib/active_support/callbacks.rb +856 -0
- data/lib/active_support/concern.rb +171 -0
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +17 -0
- data/lib/active_support/concurrency/share_lock.rb +227 -0
- data/lib/active_support/configurable.rb +146 -0
- data/lib/active_support/core_ext.rb +5 -0
- data/lib/active_support/core_ext/array.rb +9 -0
- data/lib/active_support/core_ext/array/access.rb +104 -0
- data/lib/active_support/core_ext/array/conversions.rb +213 -0
- 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/prepend_and_append.rb +5 -0
- data/lib/active_support/core_ext/array/wrap.rb +48 -0
- data/lib/active_support/core_ext/benchmark.rb +16 -0
- data/lib/active_support/core_ext/big_decimal.rb +3 -0
- data/lib/active_support/core_ext/big_decimal/conversions.rb +14 -0
- data/lib/active_support/core_ext/class.rb +4 -0
- data/lib/active_support/core_ext/class/attribute.rb +141 -0
- data/lib/active_support/core_ext/class/attribute_accessors.rb +6 -0
- data/lib/active_support/core_ext/class/subclasses.rb +54 -0
- data/lib/active_support/core_ext/date.rb +7 -0
- data/lib/active_support/core_ext/date/acts_like.rb +10 -0
- data/lib/active_support/core_ext/date/blank.rb +14 -0
- data/lib/active_support/core_ext/date/calculations.rb +146 -0
- data/lib/active_support/core_ext/date/conversions.rb +96 -0
- data/lib/active_support/core_ext/date/zones.rb +8 -0
- data/lib/active_support/core_ext/date_and_time/calculations.rb +351 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +41 -0
- data/lib/active_support/core_ext/date_time.rb +7 -0
- data/lib/active_support/core_ext/date_time/acts_like.rb +16 -0
- data/lib/active_support/core_ext/date_time/blank.rb +14 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +211 -0
- data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +107 -0
- data/lib/active_support/core_ext/digest.rb +3 -0
- data/lib/active_support/core_ext/digest/uuid.rb +53 -0
- data/lib/active_support/core_ext/enumerable.rb +188 -0
- data/lib/active_support/core_ext/file.rb +3 -0
- data/lib/active_support/core_ext/file/atomic.rb +70 -0
- data/lib/active_support/core_ext/hash.rb +10 -0
- data/lib/active_support/core_ext/hash/compact.rb +5 -0
- data/lib/active_support/core_ext/hash/conversions.rb +263 -0
- data/lib/active_support/core_ext/hash/deep_merge.rb +34 -0
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +24 -0
- data/lib/active_support/core_ext/hash/indifferent_access.rb +24 -0
- data/lib/active_support/core_ext/hash/keys.rb +143 -0
- data/lib/active_support/core_ext/hash/reverse_merge.rb +25 -0
- data/lib/active_support/core_ext/hash/slice.rb +26 -0
- data/lib/active_support/core_ext/hash/transform_values.rb +5 -0
- data/lib/active_support/core_ext/integer.rb +5 -0
- data/lib/active_support/core_ext/integer/inflections.rb +31 -0
- 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/kernel.rb +5 -0
- 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/load_error.rb +9 -0
- data/lib/active_support/core_ext/marshal.rb +24 -0
- data/lib/active_support/core_ext/module.rb +13 -0
- 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 +38 -0
- data/lib/active_support/core_ext/module/attribute_accessors.rb +212 -0
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +144 -0
- data/lib/active_support/core_ext/module/concerning.rb +134 -0
- data/lib/active_support/core_ext/module/delegation.rb +313 -0
- data/lib/active_support/core_ext/module/deprecation.rb +25 -0
- data/lib/active_support/core_ext/module/introspection.rb +86 -0
- data/lib/active_support/core_ext/module/reachable.rb +6 -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/name_error.rb +38 -0
- data/lib/active_support/core_ext/numeric.rb +5 -0
- data/lib/active_support/core_ext/numeric/bytes.rb +66 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +136 -0
- data/lib/active_support/core_ext/numeric/inquiry.rb +5 -0
- data/lib/active_support/core_ext/numeric/time.rb +66 -0
- data/lib/active_support/core_ext/object.rb +16 -0
- data/lib/active_support/core_ext/object/acts_like.rb +21 -0
- data/lib/active_support/core_ext/object/blank.rb +155 -0
- data/lib/active_support/core_ext/object/conversions.rb +6 -0
- data/lib/active_support/core_ext/object/deep_dup.rb +55 -0
- data/lib/active_support/core_ext/object/duplicable.rb +49 -0
- data/lib/active_support/core_ext/object/inclusion.rb +29 -0
- data/lib/active_support/core_ext/object/instance_variables.rb +30 -0
- data/lib/active_support/core_ext/object/json.rb +228 -0
- data/lib/active_support/core_ext/object/to_param.rb +3 -0
- data/lib/active_support/core_ext/object/to_query.rb +89 -0
- data/lib/active_support/core_ext/object/try.rb +156 -0
- data/lib/active_support/core_ext/object/with_options.rb +82 -0
- data/lib/active_support/core_ext/range.rb +7 -0
- data/lib/active_support/core_ext/range/compare_range.rb +70 -0
- data/lib/active_support/core_ext/range/conversions.rb +41 -0
- data/lib/active_support/core_ext/range/each.rb +25 -0
- data/lib/active_support/core_ext/range/include_range.rb +9 -0
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
- data/lib/active_support/core_ext/range/overlaps.rb +10 -0
- data/lib/active_support/core_ext/regexp.rb +7 -0
- data/lib/active_support/core_ext/securerandom.rb +45 -0
- data/lib/active_support/core_ext/string.rb +15 -0
- data/lib/active_support/core_ext/string/access.rb +114 -0
- data/lib/active_support/core_ext/string/behavior.rb +8 -0
- data/lib/active_support/core_ext/string/conversions.rb +59 -0
- data/lib/active_support/core_ext/string/exclude.rb +13 -0
- data/lib/active_support/core_ext/string/filters.rb +145 -0
- data/lib/active_support/core_ext/string/indent.rb +45 -0
- data/lib/active_support/core_ext/string/inflections.rb +259 -0
- data/lib/active_support/core_ext/string/inquiry.rb +15 -0
- data/lib/active_support/core_ext/string/multibyte.rb +58 -0
- data/lib/active_support/core_ext/string/output_safety.rb +314 -0
- data/lib/active_support/core_ext/string/starts_ends_with.rb +6 -0
- 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/time.rb +7 -0
- data/lib/active_support/core_ext/time/acts_like.rb +10 -0
- data/lib/active_support/core_ext/time/calculations.rb +344 -0
- data/lib/active_support/core_ext/time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/time/conversions.rb +72 -0
- data/lib/active_support/core_ext/time/zones.rb +113 -0
- data/lib/active_support/core_ext/uri.rb +25 -0
- data/lib/active_support/current_attributes.rb +203 -0
- data/lib/active_support/dependencies.rb +806 -0
- data/lib/active_support/dependencies/autoload.rb +79 -0
- data/lib/active_support/dependencies/interlock.rb +57 -0
- data/lib/active_support/dependencies/zeitwerk_integration.rb +110 -0
- data/lib/active_support/deprecation.rb +46 -0
- data/lib/active_support/deprecation/behaviors.rb +109 -0
- data/lib/active_support/deprecation/constant_accessor.rb +52 -0
- data/lib/active_support/deprecation/instance_delegator.rb +39 -0
- data/lib/active_support/deprecation/method_wrappers.rb +78 -0
- data/lib/active_support/deprecation/proxy_wrappers.rb +173 -0
- data/lib/active_support/deprecation/reporting.rb +114 -0
- data/lib/active_support/descendants_tracker.rb +109 -0
- data/lib/active_support/digest.rb +20 -0
- data/lib/active_support/duration.rb +433 -0
- data/lib/active_support/duration/iso8601_parser.rb +124 -0
- data/lib/active_support/duration/iso8601_serializer.rb +54 -0
- data/lib/active_support/encrypted_configuration.rb +45 -0
- data/lib/active_support/encrypted_file.rb +100 -0
- data/lib/active_support/evented_file_update_checker.rb +235 -0
- data/lib/active_support/execution_wrapper.rb +129 -0
- data/lib/active_support/executor.rb +8 -0
- data/lib/active_support/file_update_checker.rb +163 -0
- data/lib/active_support/gem_version.rb +17 -0
- data/lib/active_support/gzip.rb +38 -0
- data/lib/active_support/hash_with_indifferent_access.rb +399 -0
- data/lib/active_support/i18n.rb +16 -0
- data/lib/active_support/i18n_railtie.rb +126 -0
- data/lib/active_support/inflections.rb +72 -0
- data/lib/active_support/inflector.rb +9 -0
- data/lib/active_support/inflector/inflections.rb +257 -0
- data/lib/active_support/inflector/methods.rb +398 -0
- data/lib/active_support/inflector/transliterate.rb +147 -0
- data/lib/active_support/json.rb +4 -0
- data/lib/active_support/json/decoding.rb +76 -0
- data/lib/active_support/json/encoding.rb +134 -0
- data/lib/active_support/key_generator.rb +41 -0
- data/lib/active_support/lazy_load_hooks.rb +82 -0
- data/lib/active_support/locale/en.rb +31 -0
- data/lib/active_support/locale/en.yml +135 -0
- data/lib/active_support/log_subscriber.rb +135 -0
- data/lib/active_support/log_subscriber/test_helper.rb +106 -0
- data/lib/active_support/logger.rb +93 -0
- data/lib/active_support/logger_silence.rb +45 -0
- data/lib/active_support/logger_thread_safe_level.rb +56 -0
- data/lib/active_support/message_encryptor.rb +227 -0
- data/lib/active_support/message_verifier.rb +205 -0
- data/lib/active_support/messages/metadata.rb +71 -0
- data/lib/active_support/messages/rotation_configuration.rb +22 -0
- data/lib/active_support/messages/rotator.rb +56 -0
- data/lib/active_support/multibyte.rb +23 -0
- data/lib/active_support/multibyte/chars.rb +216 -0
- data/lib/active_support/multibyte/unicode.rb +157 -0
- data/lib/active_support/notifications.rb +253 -0
- data/lib/active_support/notifications/fanout.rb +244 -0
- data/lib/active_support/notifications/instrumenter.rb +164 -0
- data/lib/active_support/number_helper.rb +378 -0
- data/lib/active_support/number_helper/number_converter.rb +184 -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 +31 -0
- data/lib/active_support/number_helper/number_to_human_converter.rb +70 -0
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +61 -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 +56 -0
- data/lib/active_support/number_helper/rounding_helper.rb +66 -0
- data/lib/active_support/option_merger.rb +27 -0
- data/lib/active_support/ordered_hash.rb +50 -0
- data/lib/active_support/ordered_options.rb +85 -0
- data/lib/active_support/parameter_filter.rb +129 -0
- data/lib/active_support/per_thread_registry.rb +60 -0
- data/lib/active_support/proxy_object.rb +15 -0
- data/lib/active_support/rails.rb +29 -0
- data/lib/active_support/railtie.rb +80 -0
- data/lib/active_support/reloader.rb +130 -0
- data/lib/active_support/rescuable.rb +174 -0
- data/lib/active_support/security_utils.rb +31 -0
- data/lib/active_support/string_inquirer.rb +34 -0
- data/lib/active_support/subscriber.rb +169 -0
- data/lib/active_support/tagged_logging.rb +88 -0
- data/lib/active_support/test_case.rb +163 -0
- data/lib/active_support/testing/assertions.rb +228 -0
- data/lib/active_support/testing/autorun.rb +7 -0
- data/lib/active_support/testing/constant_lookup.rb +51 -0
- data/lib/active_support/testing/declarative.rb +28 -0
- data/lib/active_support/testing/deprecation.rb +38 -0
- data/lib/active_support/testing/file_fixtures.rb +38 -0
- data/lib/active_support/testing/isolation.rb +110 -0
- data/lib/active_support/testing/method_call_assertions.rb +70 -0
- data/lib/active_support/testing/parallelization.rb +128 -0
- data/lib/active_support/testing/setup_and_teardown.rb +55 -0
- data/lib/active_support/testing/stream.rb +44 -0
- data/lib/active_support/testing/tagged_logging.rb +27 -0
- data/lib/active_support/testing/time_helpers.rb +200 -0
- data/lib/active_support/time.rb +20 -0
- data/lib/active_support/time_with_zone.rb +561 -0
- data/lib/active_support/values/time_zone.rb +570 -0
- data/lib/active_support/version.rb +10 -0
- data/lib/active_support/xml_mini.rb +202 -0
- data/lib/active_support/xml_mini/jdom.rb +183 -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 +130 -0
- metadata +385 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Enumerable
|
|
4
|
+
INDEX_WITH_DEFAULT = Object.new
|
|
5
|
+
private_constant :INDEX_WITH_DEFAULT
|
|
6
|
+
|
|
7
|
+
# Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements
|
|
8
|
+
# when we omit an identity.
|
|
9
|
+
|
|
10
|
+
# :stopdoc:
|
|
11
|
+
|
|
12
|
+
# We can't use Refinements here because Refinements with Module which will be prepended
|
|
13
|
+
# doesn't work well https://bugs.ruby-lang.org/issues/13446
|
|
14
|
+
alias :_original_sum_with_required_identity :sum
|
|
15
|
+
private :_original_sum_with_required_identity
|
|
16
|
+
|
|
17
|
+
# :startdoc:
|
|
18
|
+
|
|
19
|
+
# Calculates a sum from the elements.
|
|
20
|
+
#
|
|
21
|
+
# payments.sum { |p| p.price * p.tax_rate }
|
|
22
|
+
# payments.sum(&:price)
|
|
23
|
+
#
|
|
24
|
+
# The latter is a shortcut for:
|
|
25
|
+
#
|
|
26
|
+
# payments.inject(0) { |sum, p| sum + p.price }
|
|
27
|
+
#
|
|
28
|
+
# It can also calculate the sum without the use of a block.
|
|
29
|
+
#
|
|
30
|
+
# [5, 15, 10].sum # => 30
|
|
31
|
+
# ['foo', 'bar'].sum # => "foobar"
|
|
32
|
+
# [[1, 2], [3, 1, 5]].sum # => [1, 2, 3, 1, 5]
|
|
33
|
+
#
|
|
34
|
+
# The default sum of an empty list is zero. You can override this default:
|
|
35
|
+
#
|
|
36
|
+
# [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
|
|
37
|
+
def sum(identity = nil, &block)
|
|
38
|
+
if identity
|
|
39
|
+
_original_sum_with_required_identity(identity, &block)
|
|
40
|
+
elsif block_given?
|
|
41
|
+
map(&block).sum(identity)
|
|
42
|
+
else
|
|
43
|
+
inject(:+) || 0
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Convert an enumerable to a hash keying it by the block return value.
|
|
48
|
+
#
|
|
49
|
+
# people.index_by(&:login)
|
|
50
|
+
# # => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
|
|
51
|
+
#
|
|
52
|
+
# people.index_by { |person| "#{person.first_name} #{person.last_name}" }
|
|
53
|
+
# # => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
|
|
54
|
+
def index_by
|
|
55
|
+
if block_given?
|
|
56
|
+
result = {}
|
|
57
|
+
each { |elem| result[yield(elem)] = elem }
|
|
58
|
+
result
|
|
59
|
+
else
|
|
60
|
+
to_enum(:index_by) { size if respond_to?(:size) }
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Convert an enumerable to a hash keying it with the enumerable items and with the values returned in the block.
|
|
65
|
+
#
|
|
66
|
+
# post = Post.new(title: "hey there", body: "what's up?")
|
|
67
|
+
#
|
|
68
|
+
# %i( title body ).index_with { |attr_name| post.public_send(attr_name) }
|
|
69
|
+
# # => { title: "hey there", body: "what's up?" }
|
|
70
|
+
def index_with(default = INDEX_WITH_DEFAULT)
|
|
71
|
+
if block_given?
|
|
72
|
+
result = {}
|
|
73
|
+
each { |elem| result[elem] = yield(elem) }
|
|
74
|
+
result
|
|
75
|
+
elsif default != INDEX_WITH_DEFAULT
|
|
76
|
+
result = {}
|
|
77
|
+
each { |elem| result[elem] = default }
|
|
78
|
+
result
|
|
79
|
+
else
|
|
80
|
+
to_enum(:index_with) { size if respond_to?(:size) }
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Returns +true+ if the enumerable has more than 1 element. Functionally
|
|
85
|
+
# equivalent to <tt>enum.to_a.size > 1</tt>. Can be called with a block too,
|
|
86
|
+
# much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns +true+
|
|
87
|
+
# if more than one person is over 26.
|
|
88
|
+
def many?
|
|
89
|
+
cnt = 0
|
|
90
|
+
if block_given?
|
|
91
|
+
any? do |element|
|
|
92
|
+
cnt += 1 if yield element
|
|
93
|
+
cnt > 1
|
|
94
|
+
end
|
|
95
|
+
else
|
|
96
|
+
any? { (cnt += 1) > 1 }
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Returns a new array that includes the passed elements.
|
|
101
|
+
#
|
|
102
|
+
# [ 1, 2, 3 ].including(4, 5)
|
|
103
|
+
# # => [ 1, 2, 3, 4, 5 ]
|
|
104
|
+
#
|
|
105
|
+
# ["David", "Rafael"].including %w[ Aaron Todd ]
|
|
106
|
+
# # => ["David", "Rafael", "Aaron", "Todd"]
|
|
107
|
+
def including(*elements)
|
|
108
|
+
to_a.including(*elements)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the
|
|
112
|
+
# collection does not include the object.
|
|
113
|
+
def exclude?(object)
|
|
114
|
+
!include?(object)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Returns a copy of the enumerable excluding the specified elements.
|
|
118
|
+
#
|
|
119
|
+
# ["David", "Rafael", "Aaron", "Todd"].excluding "Aaron", "Todd"
|
|
120
|
+
# # => ["David", "Rafael"]
|
|
121
|
+
#
|
|
122
|
+
# ["David", "Rafael", "Aaron", "Todd"].excluding %w[ Aaron Todd ]
|
|
123
|
+
# # => ["David", "Rafael"]
|
|
124
|
+
#
|
|
125
|
+
# {foo: 1, bar: 2, baz: 3}.excluding :bar
|
|
126
|
+
# # => {foo: 1, baz: 3}
|
|
127
|
+
def excluding(*elements)
|
|
128
|
+
elements.flatten!(1)
|
|
129
|
+
reject { |element| elements.include?(element) }
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Alias for #excluding.
|
|
133
|
+
def without(*elements)
|
|
134
|
+
excluding(*elements)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Convert an enumerable to an array based on the given key.
|
|
138
|
+
#
|
|
139
|
+
# [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
|
|
140
|
+
# # => ["David", "Rafael", "Aaron"]
|
|
141
|
+
#
|
|
142
|
+
# [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pluck(:id, :name)
|
|
143
|
+
# # => [[1, "David"], [2, "Rafael"]]
|
|
144
|
+
def pluck(*keys)
|
|
145
|
+
if keys.many?
|
|
146
|
+
map { |element| keys.map { |key| element[key] } }
|
|
147
|
+
else
|
|
148
|
+
map { |element| element[keys.first] }
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
class Range #:nodoc:
|
|
154
|
+
# Optimize range sum to use arithmetic progression if a block is not given and
|
|
155
|
+
# we have a range of numeric values.
|
|
156
|
+
def sum(identity = nil)
|
|
157
|
+
if block_given? || !(first.is_a?(Integer) && last.is_a?(Integer))
|
|
158
|
+
super
|
|
159
|
+
else
|
|
160
|
+
actual_last = exclude_end? ? (last - 1) : last
|
|
161
|
+
if actual_last >= first
|
|
162
|
+
sum = identity || 0
|
|
163
|
+
sum + (actual_last - first + 1) * (actual_last + first) / 2
|
|
164
|
+
else
|
|
165
|
+
identity || 0
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Using Refinements here in order not to expose our internal method
|
|
172
|
+
using Module.new {
|
|
173
|
+
refine Array do
|
|
174
|
+
alias :orig_sum :sum
|
|
175
|
+
end
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
class Array #:nodoc:
|
|
179
|
+
# Array#sum was added in Ruby 2.4 but it only works with Numeric elements.
|
|
180
|
+
def sum(init = nil, &block)
|
|
181
|
+
if init.is_a?(Numeric) || first.is_a?(Numeric)
|
|
182
|
+
init ||= 0
|
|
183
|
+
orig_sum(init, &block)
|
|
184
|
+
else
|
|
185
|
+
super
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
5
|
+
class File
|
|
6
|
+
# Write to a file atomically. Useful for situations where you don't
|
|
7
|
+
# want other processes or threads to see half-written files.
|
|
8
|
+
#
|
|
9
|
+
# File.atomic_write('important.file') do |file|
|
|
10
|
+
# file.write('hello')
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
# This method needs to create a temporary file. By default it will create it
|
|
14
|
+
# in the same directory as the destination file. If you don't like this
|
|
15
|
+
# behavior you can provide a different directory but it must be on the
|
|
16
|
+
# same physical filesystem as the file you're trying to write.
|
|
17
|
+
#
|
|
18
|
+
# File.atomic_write('/data/something.important', '/data/tmp') do |file|
|
|
19
|
+
# file.write('hello')
|
|
20
|
+
# end
|
|
21
|
+
def self.atomic_write(file_name, temp_dir = dirname(file_name))
|
|
22
|
+
require "tempfile" unless defined?(Tempfile)
|
|
23
|
+
|
|
24
|
+
Tempfile.open(".#{basename(file_name)}", temp_dir) do |temp_file|
|
|
25
|
+
temp_file.binmode
|
|
26
|
+
return_val = yield temp_file
|
|
27
|
+
temp_file.close
|
|
28
|
+
|
|
29
|
+
old_stat = if exist?(file_name)
|
|
30
|
+
# Get original file permissions
|
|
31
|
+
stat(file_name)
|
|
32
|
+
else
|
|
33
|
+
# If not possible, probe which are the default permissions in the
|
|
34
|
+
# destination directory.
|
|
35
|
+
probe_stat_in(dirname(file_name))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
if old_stat
|
|
39
|
+
# Set correct permissions on new file
|
|
40
|
+
begin
|
|
41
|
+
chown(old_stat.uid, old_stat.gid, temp_file.path)
|
|
42
|
+
# This operation will affect filesystem ACL's
|
|
43
|
+
chmod(old_stat.mode, temp_file.path)
|
|
44
|
+
rescue Errno::EPERM, Errno::EACCES
|
|
45
|
+
# Changing file ownership failed, moving on.
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Overwrite original file with temp file
|
|
50
|
+
rename(temp_file.path, file_name)
|
|
51
|
+
return_val
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Private utility method.
|
|
56
|
+
def self.probe_stat_in(dir) #:nodoc:
|
|
57
|
+
basename = [
|
|
58
|
+
".permissions_check",
|
|
59
|
+
Thread.current.object_id,
|
|
60
|
+
Process.pid,
|
|
61
|
+
rand(1000000)
|
|
62
|
+
].join(".")
|
|
63
|
+
|
|
64
|
+
file_name = join(dir, basename)
|
|
65
|
+
FileUtils.touch(file_name)
|
|
66
|
+
stat(file_name)
|
|
67
|
+
ensure
|
|
68
|
+
FileUtils.rm_f(file_name) if file_name
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/core_ext/hash/conversions"
|
|
4
|
+
require "active_support/core_ext/hash/deep_merge"
|
|
5
|
+
require "active_support/core_ext/hash/deep_transform_values"
|
|
6
|
+
require "active_support/core_ext/hash/except"
|
|
7
|
+
require "active_support/core_ext/hash/indifferent_access"
|
|
8
|
+
require "active_support/core_ext/hash/keys"
|
|
9
|
+
require "active_support/core_ext/hash/reverse_merge"
|
|
10
|
+
require "active_support/core_ext/hash/slice"
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/deprecation"
|
|
4
|
+
|
|
5
|
+
ActiveSupport::Deprecation.warn "Ruby 2.5+ (required by Rails 6) provides Hash#compact and Hash#compact! natively, so requiring active_support/core_ext/hash/compact is no longer necessary. Requiring it will raise LoadError in Rails 6.1."
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/xml_mini"
|
|
4
|
+
require "active_support/time"
|
|
5
|
+
require "active_support/core_ext/object/blank"
|
|
6
|
+
require "active_support/core_ext/object/to_param"
|
|
7
|
+
require "active_support/core_ext/object/to_query"
|
|
8
|
+
require "active_support/core_ext/array/wrap"
|
|
9
|
+
require "active_support/core_ext/hash/reverse_merge"
|
|
10
|
+
require "active_support/core_ext/string/inflections"
|
|
11
|
+
|
|
12
|
+
class Hash
|
|
13
|
+
# Returns a string containing an XML representation of its receiver:
|
|
14
|
+
#
|
|
15
|
+
# { foo: 1, bar: 2 }.to_xml
|
|
16
|
+
# # =>
|
|
17
|
+
# # <?xml version="1.0" encoding="UTF-8"?>
|
|
18
|
+
# # <hash>
|
|
19
|
+
# # <foo type="integer">1</foo>
|
|
20
|
+
# # <bar type="integer">2</bar>
|
|
21
|
+
# # </hash>
|
|
22
|
+
#
|
|
23
|
+
# To do so, the method loops over the pairs and builds nodes that depend on
|
|
24
|
+
# the _values_. Given a pair +key+, +value+:
|
|
25
|
+
#
|
|
26
|
+
# * If +value+ is a hash there's a recursive call with +key+ as <tt>:root</tt>.
|
|
27
|
+
#
|
|
28
|
+
# * If +value+ is an array there's a recursive call with +key+ as <tt>:root</tt>,
|
|
29
|
+
# and +key+ singularized as <tt>:children</tt>.
|
|
30
|
+
#
|
|
31
|
+
# * If +value+ is a callable object it must expect one or two arguments. Depending
|
|
32
|
+
# on the arity, the callable is invoked with the +options+ hash as first argument
|
|
33
|
+
# with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. The
|
|
34
|
+
# callable can add nodes by using <tt>options[:builder]</tt>.
|
|
35
|
+
#
|
|
36
|
+
# {foo: lambda { |options, key| options[:builder].b(key) }}.to_xml
|
|
37
|
+
# # => "<b>foo</b>"
|
|
38
|
+
#
|
|
39
|
+
# * If +value+ responds to +to_xml+ the method is invoked with +key+ as <tt>:root</tt>.
|
|
40
|
+
#
|
|
41
|
+
# class Foo
|
|
42
|
+
# def to_xml(options)
|
|
43
|
+
# options[:builder].bar 'fooing!'
|
|
44
|
+
# end
|
|
45
|
+
# end
|
|
46
|
+
#
|
|
47
|
+
# { foo: Foo.new }.to_xml(skip_instruct: true)
|
|
48
|
+
# # =>
|
|
49
|
+
# # <hash>
|
|
50
|
+
# # <bar>fooing!</bar>
|
|
51
|
+
# # </hash>
|
|
52
|
+
#
|
|
53
|
+
# * Otherwise, a node with +key+ as tag is created with a string representation of
|
|
54
|
+
# +value+ as text node. If +value+ is +nil+ an attribute "nil" set to "true" is added.
|
|
55
|
+
# Unless the option <tt>:skip_types</tt> exists and is true, an attribute "type" is
|
|
56
|
+
# added as well according to the following mapping:
|
|
57
|
+
#
|
|
58
|
+
# XML_TYPE_NAMES = {
|
|
59
|
+
# "Symbol" => "symbol",
|
|
60
|
+
# "Integer" => "integer",
|
|
61
|
+
# "BigDecimal" => "decimal",
|
|
62
|
+
# "Float" => "float",
|
|
63
|
+
# "TrueClass" => "boolean",
|
|
64
|
+
# "FalseClass" => "boolean",
|
|
65
|
+
# "Date" => "date",
|
|
66
|
+
# "DateTime" => "dateTime",
|
|
67
|
+
# "Time" => "dateTime"
|
|
68
|
+
# }
|
|
69
|
+
#
|
|
70
|
+
# By default the root node is "hash", but that's configurable via the <tt>:root</tt> option.
|
|
71
|
+
#
|
|
72
|
+
# The default XML builder is a fresh instance of <tt>Builder::XmlMarkup</tt>. You can
|
|
73
|
+
# configure your own builder with the <tt>:builder</tt> option. The method also accepts
|
|
74
|
+
# options like <tt>:dasherize</tt> and friends, they are forwarded to the builder.
|
|
75
|
+
def to_xml(options = {})
|
|
76
|
+
require "active_support/builder" unless defined?(Builder)
|
|
77
|
+
|
|
78
|
+
options = options.dup
|
|
79
|
+
options[:indent] ||= 2
|
|
80
|
+
options[:root] ||= "hash"
|
|
81
|
+
options[:builder] ||= Builder::XmlMarkup.new(indent: options[:indent])
|
|
82
|
+
|
|
83
|
+
builder = options[:builder]
|
|
84
|
+
builder.instruct! unless options.delete(:skip_instruct)
|
|
85
|
+
|
|
86
|
+
root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options)
|
|
87
|
+
|
|
88
|
+
builder.tag!(root) do
|
|
89
|
+
each { |key, value| ActiveSupport::XmlMini.to_tag(key, value, options) }
|
|
90
|
+
yield builder if block_given?
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
class << self
|
|
95
|
+
# Returns a Hash containing a collection of pairs when the key is the node name and the value is
|
|
96
|
+
# its content
|
|
97
|
+
#
|
|
98
|
+
# xml = <<-XML
|
|
99
|
+
# <?xml version="1.0" encoding="UTF-8"?>
|
|
100
|
+
# <hash>
|
|
101
|
+
# <foo type="integer">1</foo>
|
|
102
|
+
# <bar type="integer">2</bar>
|
|
103
|
+
# </hash>
|
|
104
|
+
# XML
|
|
105
|
+
#
|
|
106
|
+
# hash = Hash.from_xml(xml)
|
|
107
|
+
# # => {"hash"=>{"foo"=>1, "bar"=>2}}
|
|
108
|
+
#
|
|
109
|
+
# +DisallowedType+ is raised if the XML contains attributes with <tt>type="yaml"</tt> or
|
|
110
|
+
# <tt>type="symbol"</tt>. Use <tt>Hash.from_trusted_xml</tt> to
|
|
111
|
+
# parse this XML.
|
|
112
|
+
#
|
|
113
|
+
# Custom +disallowed_types+ can also be passed in the form of an
|
|
114
|
+
# array.
|
|
115
|
+
#
|
|
116
|
+
# xml = <<-XML
|
|
117
|
+
# <?xml version="1.0" encoding="UTF-8"?>
|
|
118
|
+
# <hash>
|
|
119
|
+
# <foo type="integer">1</foo>
|
|
120
|
+
# <bar type="string">"David"</bar>
|
|
121
|
+
# </hash>
|
|
122
|
+
# XML
|
|
123
|
+
#
|
|
124
|
+
# hash = Hash.from_xml(xml, ['integer'])
|
|
125
|
+
# # => ActiveSupport::XMLConverter::DisallowedType: Disallowed type attribute: "integer"
|
|
126
|
+
#
|
|
127
|
+
# Note that passing custom disallowed types will override the default types,
|
|
128
|
+
# which are Symbol and YAML.
|
|
129
|
+
def from_xml(xml, disallowed_types = nil)
|
|
130
|
+
ActiveSupport::XMLConverter.new(xml, disallowed_types).to_h
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Builds a Hash from XML just like <tt>Hash.from_xml</tt>, but also allows Symbol and YAML.
|
|
134
|
+
def from_trusted_xml(xml)
|
|
135
|
+
from_xml xml, []
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
module ActiveSupport
|
|
141
|
+
class XMLConverter # :nodoc:
|
|
142
|
+
# Raised if the XML contains attributes with type="yaml" or
|
|
143
|
+
# type="symbol". Read Hash#from_xml for more details.
|
|
144
|
+
class DisallowedType < StandardError
|
|
145
|
+
def initialize(type)
|
|
146
|
+
super "Disallowed type attribute: #{type.inspect}"
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
DISALLOWED_TYPES = %w(symbol yaml)
|
|
151
|
+
|
|
152
|
+
def initialize(xml, disallowed_types = nil)
|
|
153
|
+
@xml = normalize_keys(XmlMini.parse(xml))
|
|
154
|
+
@disallowed_types = disallowed_types || DISALLOWED_TYPES
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def to_h
|
|
158
|
+
deep_to_h(@xml)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
private
|
|
162
|
+
def normalize_keys(params)
|
|
163
|
+
case params
|
|
164
|
+
when Hash
|
|
165
|
+
Hash[params.map { |k, v| [k.to_s.tr("-", "_"), normalize_keys(v)] } ]
|
|
166
|
+
when Array
|
|
167
|
+
params.map { |v| normalize_keys(v) }
|
|
168
|
+
else
|
|
169
|
+
params
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def deep_to_h(value)
|
|
174
|
+
case value
|
|
175
|
+
when Hash
|
|
176
|
+
process_hash(value)
|
|
177
|
+
when Array
|
|
178
|
+
process_array(value)
|
|
179
|
+
when String
|
|
180
|
+
value
|
|
181
|
+
else
|
|
182
|
+
raise "can't typecast #{value.class.name} - #{value.inspect}"
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def process_hash(value)
|
|
187
|
+
if value.include?("type") && !value["type"].is_a?(Hash) && @disallowed_types.include?(value["type"])
|
|
188
|
+
raise DisallowedType, value["type"]
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
if become_array?(value)
|
|
192
|
+
_, entries = Array.wrap(value.detect { |k, v| not v.is_a?(String) })
|
|
193
|
+
if entries.nil? || value["__content__"].try(:empty?)
|
|
194
|
+
[]
|
|
195
|
+
else
|
|
196
|
+
case entries
|
|
197
|
+
when Array
|
|
198
|
+
entries.collect { |v| deep_to_h(v) }
|
|
199
|
+
when Hash
|
|
200
|
+
[deep_to_h(entries)]
|
|
201
|
+
else
|
|
202
|
+
raise "can't typecast #{entries.inspect}"
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
elsif become_content?(value)
|
|
206
|
+
process_content(value)
|
|
207
|
+
|
|
208
|
+
elsif become_empty_string?(value)
|
|
209
|
+
""
|
|
210
|
+
elsif become_hash?(value)
|
|
211
|
+
xml_value = Hash[value.map { |k, v| [k, deep_to_h(v)] }]
|
|
212
|
+
|
|
213
|
+
# Turn { files: { file: #<StringIO> } } into { files: #<StringIO> } so it is compatible with
|
|
214
|
+
# how multipart uploaded files from HTML appear
|
|
215
|
+
xml_value["file"].is_a?(StringIO) ? xml_value["file"] : xml_value
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def become_content?(value)
|
|
220
|
+
value["type"] == "file" || (value["__content__"] && (value.keys.size == 1 || value["__content__"].present?))
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def become_array?(value)
|
|
224
|
+
value["type"] == "array"
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def become_empty_string?(value)
|
|
228
|
+
# { "string" => true }
|
|
229
|
+
# No tests fail when the second term is removed.
|
|
230
|
+
value["type"] == "string" && value["nil"] != "true"
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def become_hash?(value)
|
|
234
|
+
!nothing?(value) && !garbage?(value)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def nothing?(value)
|
|
238
|
+
# blank or nil parsed values are represented by nil
|
|
239
|
+
value.blank? || value["nil"] == "true"
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def garbage?(value)
|
|
243
|
+
# If the type is the only element which makes it then
|
|
244
|
+
# this still makes the value nil, except if type is
|
|
245
|
+
# an XML node(where type['value'] is a Hash)
|
|
246
|
+
value["type"] && !value["type"].is_a?(::Hash) && value.size == 1
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def process_content(value)
|
|
250
|
+
content = value["__content__"]
|
|
251
|
+
if parser = ActiveSupport::XmlMini::PARSING[value["type"]]
|
|
252
|
+
parser.arity == 1 ? parser.call(content) : parser.call(content, value)
|
|
253
|
+
else
|
|
254
|
+
content
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def process_array(value)
|
|
259
|
+
value.map! { |i| deep_to_h(i) }
|
|
260
|
+
value.length > 1 ? value : value.first
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|