activesupport 4.0.12 → 7.0.2.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activesupport might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +249 -501
- data/MIT-LICENSE +2 -2
- data/README.rdoc +10 -5
- data/lib/active_support/actionable_error.rb +48 -0
- data/lib/active_support/all.rb +5 -3
- data/lib/active_support/array_inquirer.rb +48 -0
- data/lib/active_support/backtrace_cleaner.rb +41 -13
- data/lib/active_support/benchmarkable.rb +7 -15
- data/lib/active_support/builder.rb +3 -1
- data/lib/active_support/cache/file_store.rb +96 -74
- data/lib/active_support/cache/mem_cache_store.rb +211 -103
- data/lib/active_support/cache/memory_store.rb +90 -58
- data/lib/active_support/cache/null_store.rb +19 -7
- data/lib/active_support/cache/redis_cache_store.rb +468 -0
- data/lib/active_support/cache/strategy/local_cache.rb +86 -83
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
- data/lib/active_support/cache.rb +580 -241
- data/lib/active_support/callbacks.rb +812 -425
- data/lib/active_support/code_generator.rb +65 -0
- data/lib/active_support/concern.rb +103 -14
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +33 -0
- data/lib/active_support/concurrency/share_lock.rb +226 -0
- data/lib/active_support/configurable.rb +21 -19
- data/lib/active_support/configuration_file.rb +51 -0
- data/lib/active_support/core_ext/array/access.rb +47 -1
- data/lib/active_support/core_ext/array/conversions.rb +35 -44
- data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- data/lib/active_support/core_ext/array/extract_options.rb +2 -0
- data/lib/active_support/core_ext/array/grouping.rb +26 -16
- data/lib/active_support/core_ext/array/inquiry.rb +19 -0
- data/lib/active_support/core_ext/array/wrap.rb +7 -4
- data/lib/active_support/core_ext/array.rb +10 -7
- data/lib/active_support/core_ext/benchmark.rb +5 -3
- data/lib/active_support/core_ext/big_decimal/conversions.rb +9 -26
- data/lib/active_support/core_ext/big_decimal.rb +3 -1
- data/lib/active_support/core_ext/class/attribute.rb +52 -49
- data/lib/active_support/core_ext/class/attribute_accessors.rb +5 -169
- data/lib/active_support/core_ext/class/subclasses.rb +25 -26
- data/lib/active_support/core_ext/class.rb +4 -4
- data/lib/active_support/core_ext/date/acts_like.rb +3 -1
- data/lib/active_support/core_ext/date/blank.rb +14 -0
- data/lib/active_support/core_ext/date/calculations.rb +31 -18
- data/lib/active_support/core_ext/date/conversions.rb +43 -32
- data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/date/zones.rb +5 -34
- data/lib/active_support/core_ext/date.rb +7 -4
- data/lib/active_support/core_ext/date_and_time/calculations.rb +198 -66
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +31 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
- data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
- data/lib/active_support/core_ext/date_time/blank.rb +14 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +79 -38
- data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +31 -26
- data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/date_time.rb +8 -4
- data/lib/active_support/core_ext/digest/uuid.rb +79 -0
- data/lib/active_support/core_ext/digest.rb +3 -0
- data/lib/active_support/core_ext/enumerable.rb +249 -17
- data/lib/active_support/core_ext/file/atomic.rb +41 -32
- data/lib/active_support/core_ext/file.rb +3 -1
- data/lib/active_support/core_ext/hash/conversions.rb +71 -49
- data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +14 -5
- data/lib/active_support/core_ext/hash/indifferent_access.rb +5 -3
- data/lib/active_support/core_ext/hash/keys.rb +39 -56
- data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
- data/lib/active_support/core_ext/hash/slice.rb +8 -23
- data/lib/active_support/core_ext/hash.rb +10 -8
- data/lib/active_support/core_ext/integer/inflections.rb +3 -1
- data/lib/active_support/core_ext/integer/multiple.rb +3 -1
- data/lib/active_support/core_ext/integer/time.rb +11 -33
- data/lib/active_support/core_ext/integer.rb +5 -3
- data/lib/active_support/core_ext/kernel/concern.rb +14 -0
- data/lib/active_support/core_ext/kernel/reporting.rb +9 -78
- data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
- data/lib/active_support/core_ext/kernel.rb +5 -4
- data/lib/active_support/core_ext/load_error.rb +5 -21
- data/lib/active_support/core_ext/module/aliasing.rb +6 -44
- data/lib/active_support/core_ext/module/anonymous.rb +12 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +8 -8
- data/lib/active_support/core_ext/module/attribute_accessors.rb +186 -44
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +157 -0
- data/lib/active_support/core_ext/module/concerning.rb +140 -0
- data/lib/active_support/core_ext/module/delegation.rb +172 -45
- data/lib/active_support/core_ext/module/deprecation.rb +3 -3
- data/lib/active_support/core_ext/module/introspection.rb +23 -38
- data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
- data/lib/active_support/core_ext/module/remove_method.rb +8 -3
- data/lib/active_support/core_ext/module.rb +13 -10
- data/lib/active_support/core_ext/name_error.rb +45 -4
- data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +135 -127
- data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
- data/lib/active_support/core_ext/numeric/time.rb +37 -50
- data/lib/active_support/core_ext/numeric.rb +6 -3
- data/lib/active_support/core_ext/object/acts_like.rb +41 -6
- data/lib/active_support/core_ext/object/blank.rb +70 -20
- data/lib/active_support/core_ext/object/conversions.rb +6 -4
- data/lib/active_support/core_ext/object/deep_dup.rb +19 -10
- data/lib/active_support/core_ext/object/duplicable.rb +17 -47
- data/lib/active_support/core_ext/object/inclusion.rb +18 -15
- data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
- data/lib/active_support/core_ext/object/json.rb +244 -0
- data/lib/active_support/core_ext/object/to_param.rb +3 -1
- data/lib/active_support/core_ext/object/to_query.rb +21 -8
- data/lib/active_support/core_ext/object/try.rb +106 -26
- data/lib/active_support/core_ext/object/with_options.rb +64 -5
- data/lib/active_support/core_ext/object.rb +14 -12
- data/lib/active_support/core_ext/pathname/existence.rb +21 -0
- data/lib/active_support/core_ext/pathname.rb +3 -0
- data/lib/active_support/core_ext/range/compare_range.rb +57 -0
- data/lib/active_support/core_ext/range/conversions.rb +37 -15
- data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
- data/lib/active_support/core_ext/range/each.rb +18 -17
- data/lib/active_support/core_ext/range/include_time_with_zone.rb +7 -0
- data/lib/active_support/core_ext/range/overlaps.rb +2 -0
- data/lib/active_support/core_ext/range.rb +7 -4
- data/lib/active_support/core_ext/regexp.rb +10 -1
- data/lib/active_support/core_ext/securerandom.rb +45 -0
- data/lib/active_support/core_ext/string/access.rb +42 -51
- data/lib/active_support/core_ext/string/behavior.rb +3 -1
- data/lib/active_support/core_ext/string/conversions.rb +18 -13
- data/lib/active_support/core_ext/string/exclude.rb +5 -3
- data/lib/active_support/core_ext/string/filters.rb +97 -7
- data/lib/active_support/core_ext/string/indent.rb +6 -4
- data/lib/active_support/core_ext/string/inflections.rb +106 -25
- data/lib/active_support/core_ext/string/inquiry.rb +4 -1
- data/lib/active_support/core_ext/string/multibyte.rb +18 -9
- data/lib/active_support/core_ext/string/output_safety.rb +227 -54
- data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
- data/lib/active_support/core_ext/string/strip.rb +6 -5
- data/lib/active_support/core_ext/string/zones.rb +4 -1
- data/lib/active_support/core_ext/string.rb +15 -13
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/time/acts_like.rb +3 -1
- data/lib/active_support/core_ext/time/calculations.rb +178 -116
- data/lib/active_support/core_ext/time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/time/conversions.rb +37 -25
- data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
- data/lib/active_support/core_ext/time/zones.rb +44 -42
- data/lib/active_support/core_ext/time.rb +8 -5
- data/lib/active_support/core_ext/uri.rb +4 -25
- data/lib/active_support/core_ext.rb +4 -2
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +226 -0
- data/lib/active_support/dependencies/autoload.rb +3 -1
- data/lib/active_support/dependencies/interlock.rb +49 -0
- data/lib/active_support/dependencies/require_dependency.rb +28 -0
- data/lib/active_support/dependencies.rb +71 -696
- data/lib/active_support/deprecation/behaviors.rb +65 -16
- data/lib/active_support/deprecation/constant_accessor.rb +52 -0
- data/lib/active_support/deprecation/disallowed.rb +56 -0
- data/lib/active_support/deprecation/instance_delegator.rb +16 -2
- data/lib/active_support/deprecation/method_wrappers.rb +62 -21
- data/lib/active_support/deprecation/proxy_wrappers.rb +82 -31
- data/lib/active_support/deprecation/reporting.rb +81 -18
- data/lib/active_support/deprecation.rb +19 -11
- data/lib/active_support/descendants_tracker.rb +192 -34
- data/lib/active_support/digest.rb +22 -0
- data/lib/active_support/duration/iso8601_parser.rb +123 -0
- data/lib/active_support/duration/iso8601_serializer.rb +67 -0
- data/lib/active_support/duration.rb +437 -39
- data/lib/active_support/encrypted_configuration.rb +56 -0
- data/lib/active_support/encrypted_file.rb +117 -0
- data/lib/active_support/environment_inquirer.rb +20 -0
- data/lib/active_support/error_reporter.rb +117 -0
- data/lib/active_support/evented_file_update_checker.rb +170 -0
- data/lib/active_support/execution_context/test_helper.rb +13 -0
- data/lib/active_support/execution_context.rb +53 -0
- data/lib/active_support/execution_wrapper.rb +151 -0
- data/lib/active_support/executor/test_helper.rb +7 -0
- data/lib/active_support/executor.rb +8 -0
- data/lib/active_support/file_update_checker.rb +62 -37
- data/lib/active_support/fork_tracker.rb +71 -0
- data/lib/active_support/gem_version.rb +17 -0
- data/lib/active_support/gzip.rb +7 -5
- data/lib/active_support/hash_with_indifferent_access.rb +207 -54
- data/lib/active_support/html_safe_translation.rb +43 -0
- data/lib/active_support/i18n.rb +10 -6
- data/lib/active_support/i18n_railtie.rb +48 -19
- data/lib/active_support/inflections.rb +19 -12
- data/lib/active_support/inflector/inflections.rb +97 -37
- data/lib/active_support/inflector/methods.rb +192 -157
- data/lib/active_support/inflector/transliterate.rb +83 -33
- data/lib/active_support/inflector.rb +7 -5
- data/lib/active_support/isolated_execution_state.rb +64 -0
- data/lib/active_support/json/decoding.rb +37 -42
- data/lib/active_support/json/encoding.rb +93 -293
- data/lib/active_support/json.rb +4 -2
- data/lib/active_support/key_generator.rb +30 -47
- data/lib/active_support/lazy_load_hooks.rb +54 -21
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/locale/en.yml +10 -4
- data/lib/active_support/log_subscriber/test_helper.rb +14 -12
- data/lib/active_support/log_subscriber.rb +61 -18
- data/lib/active_support/logger.rb +40 -4
- data/lib/active_support/logger_silence.rb +17 -20
- data/lib/active_support/logger_thread_safe_level.rb +69 -0
- data/lib/active_support/message_encryptor.rb +178 -55
- data/lib/active_support/message_verifier.rb +195 -26
- data/lib/active_support/messages/metadata.rb +80 -0
- data/lib/active_support/messages/rotation_configuration.rb +23 -0
- data/lib/active_support/messages/rotator.rb +57 -0
- data/lib/active_support/multibyte/chars.rb +45 -92
- data/lib/active_support/multibyte/unicode.rb +44 -377
- data/lib/active_support/multibyte.rb +5 -3
- data/lib/active_support/notifications/fanout.rb +177 -44
- data/lib/active_support/notifications/instrumenter.rb +117 -17
- data/lib/active_support/notifications.rb +106 -39
- data/lib/active_support/number_helper/number_converter.rb +181 -0
- data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +30 -0
- data/lib/active_support/number_helper/number_to_human_converter.rb +69 -0
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +60 -0
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
- data/lib/active_support/number_helper/number_to_phone_converter.rb +59 -0
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +59 -0
- data/lib/active_support/number_helper/rounding_helper.rb +46 -0
- data/lib/active_support/number_helper.rb +152 -394
- data/lib/active_support/option_merger.rb +18 -5
- data/lib/active_support/ordered_hash.rb +8 -6
- data/lib/active_support/ordered_options.rb +43 -7
- data/lib/active_support/parameter_filter.rb +138 -0
- data/lib/active_support/per_thread_registry.rb +24 -11
- data/lib/active_support/proxy_object.rb +2 -0
- data/lib/active_support/rails.rb +10 -11
- data/lib/active_support/railtie.rb +118 -12
- data/lib/active_support/reloader.rb +130 -0
- data/lib/active_support/rescuable.rb +112 -57
- data/lib/active_support/ruby_features.rb +7 -0
- data/lib/active_support/secure_compare_rotator.rb +51 -0
- data/lib/active_support/security_utils.rb +38 -0
- data/lib/active_support/string_inquirer.rb +11 -4
- data/lib/active_support/subscriber.rb +109 -39
- data/lib/active_support/tagged_logging.rb +54 -17
- data/lib/active_support/test_case.rb +121 -37
- data/lib/active_support/testing/assertions.rb +177 -39
- data/lib/active_support/testing/autorun.rb +5 -3
- data/lib/active_support/testing/constant_lookup.rb +3 -6
- data/lib/active_support/testing/declarative.rb +10 -22
- data/lib/active_support/testing/deprecation.rb +65 -11
- data/lib/active_support/testing/file_fixtures.rb +38 -0
- data/lib/active_support/testing/isolation.rb +56 -87
- data/lib/active_support/testing/method_call_assertions.rb +70 -0
- data/lib/active_support/testing/parallelization/server.rb +82 -0
- data/lib/active_support/testing/parallelization/worker.rb +103 -0
- data/lib/active_support/testing/parallelization.rb +55 -0
- data/lib/active_support/testing/parallelize_executor.rb +76 -0
- data/lib/active_support/testing/setup_and_teardown.rb +30 -10
- data/lib/active_support/testing/stream.rb +41 -0
- data/lib/active_support/testing/tagged_logging.rb +6 -4
- data/lib/active_support/testing/time_helpers.rb +246 -0
- data/lib/active_support/time.rb +13 -13
- data/lib/active_support/time_with_zone.rb +315 -90
- data/lib/active_support/values/time_zone.rb +306 -135
- data/lib/active_support/version.rb +6 -7
- data/lib/active_support/xml_mini/jdom.rb +117 -115
- data/lib/active_support/xml_mini/libxml.rb +22 -21
- data/lib/active_support/xml_mini/libxmlsax.rb +17 -19
- data/lib/active_support/xml_mini/nokogiri.rb +19 -19
- data/lib/active_support/xml_mini/nokogirisax.rb +16 -17
- data/lib/active_support/xml_mini/rexml.rb +25 -17
- data/lib/active_support/xml_mini.rb +67 -56
- data/lib/active_support.rb +58 -3
- metadata +125 -66
- data/lib/active_support/basic_object.rb +0 -11
- data/lib/active_support/buffered_logger.rb +0 -21
- data/lib/active_support/concurrency/latch.rb +0 -27
- data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
- data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
- data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -40
- data/lib/active_support/core_ext/date_time/zones.rb +0 -24
- data/lib/active_support/core_ext/hash/diff.rb +0 -14
- data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
- data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
- data/lib/active_support/core_ext/logger.rb +0 -67
- data/lib/active_support/core_ext/marshal.rb +0 -21
- data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
- data/lib/active_support/core_ext/module/reachable.rb +0 -8
- data/lib/active_support/core_ext/object/to_json.rb +0 -27
- data/lib/active_support/core_ext/proc.rb +0 -17
- data/lib/active_support/core_ext/range/include_range.rb +0 -23
- data/lib/active_support/core_ext/string/encoding.rb +0 -8
- data/lib/active_support/core_ext/struct.rb +0 -6
- data/lib/active_support/core_ext/thread.rb +0 -79
- data/lib/active_support/core_ext/time/marshal.rb +0 -30
- data/lib/active_support/file_watcher.rb +0 -36
- data/lib/active_support/json/variable.rb +0 -18
- data/lib/active_support/testing/pending.rb +0 -14
- data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,4 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Enumerable
|
4
|
+
INDEX_WITH_DEFAULT = Object.new
|
5
|
+
private_constant :INDEX_WITH_DEFAULT
|
6
|
+
|
7
|
+
# Error generated by +sole+ when called on an enumerable that doesn't have
|
8
|
+
# exactly one item.
|
9
|
+
class SoleItemExpectedError < StandardError; end
|
10
|
+
|
11
|
+
# Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements
|
12
|
+
# when we omit an identity.
|
13
|
+
|
14
|
+
# :stopdoc:
|
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:
|
22
|
+
|
23
|
+
# Calculates the minimum from the extracted elements.
|
24
|
+
#
|
25
|
+
# payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
|
26
|
+
# payments.minimum(:price) # => 5
|
27
|
+
def minimum(key)
|
28
|
+
map(&key).min
|
29
|
+
end
|
30
|
+
|
31
|
+
# Calculates the maximum from the extracted elements.
|
32
|
+
#
|
33
|
+
# payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
|
34
|
+
# payments.maximum(:price) # => 15
|
35
|
+
def maximum(key)
|
36
|
+
map(&key).max
|
37
|
+
end
|
38
|
+
|
2
39
|
# Calculates a sum from the elements.
|
3
40
|
#
|
4
41
|
# payments.sum { |p| p.price * p.tax_rate }
|
@@ -10,32 +47,76 @@ module Enumerable
|
|
10
47
|
#
|
11
48
|
# It can also calculate the sum without the use of a block.
|
12
49
|
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
50
|
+
# [5, 15, 10].sum # => 30
|
51
|
+
# ['foo', 'bar'].sum('') # => "foobar"
|
52
|
+
# [[1, 2], [3, 1, 5]].sum([]) # => [1, 2, 3, 1, 5]
|
16
53
|
#
|
17
54
|
# The default sum of an empty list is zero. You can override this default:
|
18
55
|
#
|
19
|
-
#
|
20
|
-
def sum(identity =
|
21
|
-
if
|
22
|
-
|
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)
|
23
69
|
else
|
24
|
-
|
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
|
25
75
|
end
|
26
76
|
end
|
27
77
|
|
28
|
-
# Convert an enumerable to a hash
|
78
|
+
# Convert an enumerable to a hash, using the block result as the key and the
|
79
|
+
# element as the value.
|
29
80
|
#
|
30
81
|
# people.index_by(&:login)
|
31
|
-
#
|
82
|
+
# # => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
|
83
|
+
#
|
32
84
|
# people.index_by { |person| "#{person.first_name} #{person.last_name}" }
|
33
|
-
#
|
85
|
+
# # => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
|
34
86
|
def index_by
|
35
87
|
if block_given?
|
36
|
-
|
88
|
+
result = {}
|
89
|
+
each { |elem| result[yield(elem)] = elem }
|
90
|
+
result
|
37
91
|
else
|
38
|
-
to_enum :
|
92
|
+
to_enum(:index_by) { size if respond_to?(:size) }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Convert an enumerable to a hash, using the element as the key and the block
|
97
|
+
# result as the value.
|
98
|
+
#
|
99
|
+
# post = Post.new(title: "hey there", body: "what's up?")
|
100
|
+
#
|
101
|
+
# %i( title body ).index_with { |attr_name| post.public_send(attr_name) }
|
102
|
+
# # => { title: "hey there", body: "what's up?" }
|
103
|
+
#
|
104
|
+
# If an argument is passed instead of a block, it will be used as the value
|
105
|
+
# for all elements:
|
106
|
+
#
|
107
|
+
# %i( created_at updated_at ).index_with(Time.now)
|
108
|
+
# # => { created_at: 2020-03-09 22:31:47, updated_at: 2020-03-09 22:31:47 }
|
109
|
+
def index_with(default = INDEX_WITH_DEFAULT)
|
110
|
+
if block_given?
|
111
|
+
result = {}
|
112
|
+
each { |elem| result[elem] = yield(elem) }
|
113
|
+
result
|
114
|
+
elsif default != INDEX_WITH_DEFAULT
|
115
|
+
result = {}
|
116
|
+
each { |elem| result[elem] = default }
|
117
|
+
result
|
118
|
+
else
|
119
|
+
to_enum(:index_with) { size if respond_to?(:size) }
|
39
120
|
end
|
40
121
|
end
|
41
122
|
|
@@ -55,26 +136,177 @@ module Enumerable
|
|
55
136
|
end
|
56
137
|
end
|
57
138
|
|
139
|
+
# Returns a new array that includes the passed elements.
|
140
|
+
#
|
141
|
+
# [ 1, 2, 3 ].including(4, 5)
|
142
|
+
# # => [ 1, 2, 3, 4, 5 ]
|
143
|
+
#
|
144
|
+
# ["David", "Rafael"].including %w[ Aaron Todd ]
|
145
|
+
# # => ["David", "Rafael", "Aaron", "Todd"]
|
146
|
+
def including(*elements)
|
147
|
+
to_a.including(*elements)
|
148
|
+
end
|
149
|
+
|
58
150
|
# The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the
|
59
151
|
# collection does not include the object.
|
60
152
|
def exclude?(object)
|
61
153
|
!include?(object)
|
62
154
|
end
|
155
|
+
|
156
|
+
# Returns a copy of the enumerable excluding the specified elements.
|
157
|
+
#
|
158
|
+
# ["David", "Rafael", "Aaron", "Todd"].excluding "Aaron", "Todd"
|
159
|
+
# # => ["David", "Rafael"]
|
160
|
+
#
|
161
|
+
# ["David", "Rafael", "Aaron", "Todd"].excluding %w[ Aaron Todd ]
|
162
|
+
# # => ["David", "Rafael"]
|
163
|
+
#
|
164
|
+
# {foo: 1, bar: 2, baz: 3}.excluding :bar
|
165
|
+
# # => {foo: 1, baz: 3}
|
166
|
+
def excluding(*elements)
|
167
|
+
elements.flatten!(1)
|
168
|
+
reject { |element| elements.include?(element) }
|
169
|
+
end
|
170
|
+
alias :without :excluding
|
171
|
+
|
172
|
+
# Extract the given key from each element in the enumerable.
|
173
|
+
#
|
174
|
+
# [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
|
175
|
+
# # => ["David", "Rafael", "Aaron"]
|
176
|
+
#
|
177
|
+
# [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pluck(:id, :name)
|
178
|
+
# # => [[1, "David"], [2, "Rafael"]]
|
179
|
+
def pluck(*keys)
|
180
|
+
if keys.many?
|
181
|
+
map { |element| keys.map { |key| element[key] } }
|
182
|
+
else
|
183
|
+
key = keys.first
|
184
|
+
map { |element| element[key] }
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# Extract the given key from the first element in the enumerable.
|
189
|
+
#
|
190
|
+
# [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pick(:name)
|
191
|
+
# # => "David"
|
192
|
+
#
|
193
|
+
# [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pick(:id, :name)
|
194
|
+
# # => [1, "David"]
|
195
|
+
def pick(*keys)
|
196
|
+
return if none?
|
197
|
+
|
198
|
+
if keys.many?
|
199
|
+
keys.map { |key| first[key] }
|
200
|
+
else
|
201
|
+
first[keys.first]
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# Returns a new +Array+ without the blank items.
|
206
|
+
# Uses Object#blank? for determining if an item is blank.
|
207
|
+
#
|
208
|
+
# [1, "", nil, 2, " ", [], {}, false, true].compact_blank
|
209
|
+
# # => [1, 2, true]
|
210
|
+
#
|
211
|
+
# Set.new([nil, "", 1, 2])
|
212
|
+
# # => [2, 1] (or [1, 2])
|
213
|
+
#
|
214
|
+
# When called on a +Hash+, returns a new +Hash+ without the blank values.
|
215
|
+
#
|
216
|
+
# { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
|
217
|
+
# # => { b: 1, f: true }
|
218
|
+
def compact_blank
|
219
|
+
reject(&:blank?)
|
220
|
+
end
|
221
|
+
|
222
|
+
# Returns a new +Array+ where the order has been set to that provided in the +series+, based on the +key+ of the
|
223
|
+
# objects in the original enumerable.
|
224
|
+
#
|
225
|
+
# [ 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) ]
|
227
|
+
#
|
228
|
+
# If the +series+ include keys that have no corresponding element in the Enumerable, these are ignored.
|
229
|
+
# If the Enumerable has additional elements that aren't named in the +series+, these are not included in the result.
|
230
|
+
def in_order_of(key, series)
|
231
|
+
index_by(&key).values_at(*series).compact
|
232
|
+
end
|
233
|
+
|
234
|
+
# Returns the sole item in the enumerable. If there are no items, or more
|
235
|
+
# than one item, raises +Enumerable::SoleItemExpectedError+.
|
236
|
+
#
|
237
|
+
# ["x"].sole # => "x"
|
238
|
+
# Set.new.sole # => Enumerable::SoleItemExpectedError: no item found
|
239
|
+
# { a: 1, b: 2 }.sole # => Enumerable::SoleItemExpectedError: multiple items found
|
240
|
+
def sole
|
241
|
+
case count
|
242
|
+
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"
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
class Hash
|
250
|
+
# Hash#reject has its own definition, so this needs one too.
|
251
|
+
def compact_blank # :nodoc:
|
252
|
+
reject { |_k, v| v.blank? }
|
253
|
+
end
|
254
|
+
|
255
|
+
# Removes all blank values from the +Hash+ in place and returns self.
|
256
|
+
# Uses Object#blank? for determining if a value is blank.
|
257
|
+
#
|
258
|
+
# h = { a: "", b: 1, c: nil, d: [], e: false, f: true }
|
259
|
+
# h.compact_blank!
|
260
|
+
# # => { b: 1, f: true }
|
261
|
+
def compact_blank!
|
262
|
+
# use delete_if rather than reject! because it always returns self even if nothing changed
|
263
|
+
delete_if { |_k, v| v.blank? }
|
264
|
+
end
|
63
265
|
end
|
64
266
|
|
65
|
-
class Range
|
267
|
+
class Range # :nodoc:
|
66
268
|
# Optimize range sum to use arithmetic progression if a block is not given and
|
67
269
|
# we have a range of numeric values.
|
68
|
-
def sum(identity =
|
270
|
+
def sum(identity = nil)
|
69
271
|
if block_given? || !(first.is_a?(Integer) && last.is_a?(Integer))
|
70
272
|
super
|
71
273
|
else
|
72
274
|
actual_last = exclude_end? ? (last - 1) : last
|
73
275
|
if actual_last >= first
|
74
|
-
|
276
|
+
sum = identity || 0
|
277
|
+
sum + (actual_last - first + 1) * (actual_last + first) / 2
|
75
278
|
else
|
76
|
-
identity
|
279
|
+
identity || 0
|
77
280
|
end
|
78
281
|
end
|
79
282
|
end
|
80
283
|
end
|
284
|
+
|
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
|
+
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
|
+
# Removes all blank elements from the +Array+ in place and returns self.
|
303
|
+
# Uses Object#blank? for determining if an item is blank.
|
304
|
+
#
|
305
|
+
# a = [1, "", nil, 2, " ", [], {}, false, true]
|
306
|
+
# a.compact_blank!
|
307
|
+
# # => [1, 2, true]
|
308
|
+
def compact_blank!
|
309
|
+
# use delete_if rather than reject! because it always returns self even if nothing changed
|
310
|
+
delete_if(&:blank?)
|
311
|
+
end
|
312
|
+
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fileutils"
|
2
4
|
|
3
5
|
class File
|
4
6
|
# Write to a file atomically. Useful for situations where you don't
|
@@ -8,55 +10,62 @@ class File
|
|
8
10
|
# file.write('hello')
|
9
11
|
# end
|
10
12
|
#
|
11
|
-
#
|
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.
|
13
17
|
#
|
14
18
|
# File.atomic_write('/data/something.important', '/data/tmp') do |file|
|
15
19
|
# file.write('hello')
|
16
20
|
# end
|
17
|
-
def self.atomic_write(file_name, temp_dir =
|
18
|
-
require
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
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
|
34
37
|
|
35
|
-
|
36
|
-
|
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
|
37
48
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
# This operation will affect filesystem ACL's
|
42
|
-
chmod(old_stat.mode, file_name)
|
43
|
-
rescue Errno::EPERM
|
44
|
-
# Changing file ownership failed, moving on.
|
49
|
+
# Overwrite original file with temp file
|
50
|
+
rename(temp_file.path, file_name)
|
51
|
+
return_val
|
45
52
|
end
|
46
53
|
end
|
47
54
|
|
48
55
|
# Private utility method.
|
49
|
-
def self.probe_stat_in(dir)
|
56
|
+
def self.probe_stat_in(dir) # :nodoc:
|
50
57
|
basename = [
|
51
|
-
|
58
|
+
".permissions_check",
|
52
59
|
Thread.current.object_id,
|
53
60
|
Process.pid,
|
54
61
|
rand(1000000)
|
55
|
-
].join(
|
62
|
+
].join(".")
|
56
63
|
|
57
64
|
file_name = join(dir, basename)
|
58
65
|
FileUtils.touch(file_name)
|
59
66
|
stat(file_name)
|
67
|
+
rescue Errno::ENOENT
|
68
|
+
file_name = nil
|
60
69
|
ensure
|
61
70
|
FileUtils.rm_f(file_name) if file_name
|
62
71
|
end
|
@@ -1,16 +1,18 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/xml_mini"
|
4
|
+
require "active_support/core_ext/object/blank"
|
5
|
+
require "active_support/core_ext/object/to_param"
|
6
|
+
require "active_support/core_ext/object/to_query"
|
7
|
+
require "active_support/core_ext/object/try"
|
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"
|
9
11
|
|
10
12
|
class Hash
|
11
13
|
# Returns a string containing an XML representation of its receiver:
|
12
14
|
#
|
13
|
-
# {
|
15
|
+
# { foo: 1, bar: 2 }.to_xml
|
14
16
|
# # =>
|
15
17
|
# # <?xml version="1.0" encoding="UTF-8"?>
|
16
18
|
# # <hash>
|
@@ -31,7 +33,7 @@ class Hash
|
|
31
33
|
# with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. The
|
32
34
|
# callable can add nodes by using <tt>options[:builder]</tt>.
|
33
35
|
#
|
34
|
-
#
|
36
|
+
# {foo: lambda { |options, key| options[:builder].b(key) }}.to_xml
|
35
37
|
# # => "<b>foo</b>"
|
36
38
|
#
|
37
39
|
# * If +value+ responds to +to_xml+ the method is invoked with +key+ as <tt>:root</tt>.
|
@@ -43,7 +45,10 @@ class Hash
|
|
43
45
|
# end
|
44
46
|
#
|
45
47
|
# { foo: Foo.new }.to_xml(skip_instruct: true)
|
46
|
-
# # =>
|
48
|
+
# # =>
|
49
|
+
# # <hash>
|
50
|
+
# # <bar>fooing!</bar>
|
51
|
+
# # </hash>
|
47
52
|
#
|
48
53
|
# * Otherwise, a node with +key+ as tag is created with a string representation of
|
49
54
|
# +value+ as text node. If +value+ is +nil+ an attribute "nil" set to "true" is added.
|
@@ -52,8 +57,7 @@ class Hash
|
|
52
57
|
#
|
53
58
|
# XML_TYPE_NAMES = {
|
54
59
|
# "Symbol" => "symbol",
|
55
|
-
# "
|
56
|
-
# "Bignum" => "integer",
|
60
|
+
# "Integer" => "integer",
|
57
61
|
# "BigDecimal" => "decimal",
|
58
62
|
# "Float" => "float",
|
59
63
|
# "TrueClass" => "boolean",
|
@@ -69,11 +73,11 @@ class Hash
|
|
69
73
|
# configure your own builder with the <tt>:builder</tt> option. The method also accepts
|
70
74
|
# options like <tt>:dasherize</tt> and friends, they are forwarded to the builder.
|
71
75
|
def to_xml(options = {})
|
72
|
-
require
|
76
|
+
require "active_support/builder" unless defined?(Builder::XmlMarkup)
|
73
77
|
|
74
78
|
options = options.dup
|
75
79
|
options[:indent] ||= 2
|
76
|
-
options[:root] ||=
|
80
|
+
options[:root] ||= "hash"
|
77
81
|
options[:builder] ||= Builder::XmlMarkup.new(indent: options[:indent])
|
78
82
|
|
79
83
|
builder = options[:builder]
|
@@ -102,8 +106,26 @@ class Hash
|
|
102
106
|
# hash = Hash.from_xml(xml)
|
103
107
|
# # => {"hash"=>{"foo"=>1, "bar"=>2}}
|
104
108
|
#
|
105
|
-
# DisallowedType is
|
106
|
-
# <tt>type="symbol"</tt>. Use <tt>Hash.from_trusted_xml</tt> to
|
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.
|
107
129
|
def from_xml(xml, disallowed_types = nil)
|
108
130
|
ActiveSupport::XMLConverter.new(xml, disallowed_types).to_h
|
109
131
|
end
|
@@ -117,6 +139,8 @@ end
|
|
117
139
|
|
118
140
|
module ActiveSupport
|
119
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.
|
120
144
|
class DisallowedType < StandardError
|
121
145
|
def initialize(type)
|
122
146
|
super "Disallowed type attribute: #{type.inspect}"
|
@@ -137,36 +161,36 @@ module ActiveSupport
|
|
137
161
|
private
|
138
162
|
def normalize_keys(params)
|
139
163
|
case params
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
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
|
146
170
|
end
|
147
171
|
end
|
148
172
|
|
149
173
|
def deep_to_h(value)
|
150
174
|
case value
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
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}"
|
159
183
|
end
|
160
184
|
end
|
161
185
|
|
162
186
|
def process_hash(value)
|
163
|
-
if value.include?(
|
164
|
-
raise DisallowedType, value[
|
187
|
+
if value.include?("type") && !value["type"].is_a?(Hash) && @disallowed_types.include?(value["type"])
|
188
|
+
raise DisallowedType, value["type"]
|
165
189
|
end
|
166
190
|
|
167
191
|
if become_array?(value)
|
168
|
-
_, entries = Array.wrap(value.detect { |k,v| not v.is_a?(String) })
|
169
|
-
if entries.nil? || value[
|
192
|
+
_, entries = Array.wrap(value.detect { |k, v| not v.is_a?(String) })
|
193
|
+
if entries.nil? || value["__content__"].try(:empty?)
|
170
194
|
[]
|
171
195
|
else
|
172
196
|
case entries
|
@@ -182,28 +206,28 @@ module ActiveSupport
|
|
182
206
|
process_content(value)
|
183
207
|
|
184
208
|
elsif become_empty_string?(value)
|
185
|
-
|
209
|
+
""
|
186
210
|
elsif become_hash?(value)
|
187
|
-
xml_value =
|
211
|
+
xml_value = value.transform_values { |v| deep_to_h(v) }
|
188
212
|
|
189
213
|
# Turn { files: { file: #<StringIO> } } into { files: #<StringIO> } so it is compatible with
|
190
214
|
# how multipart uploaded files from HTML appear
|
191
|
-
xml_value[
|
215
|
+
xml_value["file"].is_a?(StringIO) ? xml_value["file"] : xml_value
|
192
216
|
end
|
193
217
|
end
|
194
218
|
|
195
219
|
def become_content?(value)
|
196
|
-
value[
|
220
|
+
value["type"] == "file" || (value["__content__"] && (value.keys.size == 1 || value["__content__"].present?))
|
197
221
|
end
|
198
222
|
|
199
223
|
def become_array?(value)
|
200
|
-
value[
|
224
|
+
value["type"] == "array"
|
201
225
|
end
|
202
226
|
|
203
227
|
def become_empty_string?(value)
|
204
|
-
# {"string" => true}
|
228
|
+
# { "string" => true }
|
205
229
|
# No tests fail when the second term is removed.
|
206
|
-
value[
|
230
|
+
value["type"] == "string" && value["nil"] != "true"
|
207
231
|
end
|
208
232
|
|
209
233
|
def become_hash?(value)
|
@@ -212,19 +236,19 @@ module ActiveSupport
|
|
212
236
|
|
213
237
|
def nothing?(value)
|
214
238
|
# blank or nil parsed values are represented by nil
|
215
|
-
value.blank? || value[
|
239
|
+
value.blank? || value["nil"] == "true"
|
216
240
|
end
|
217
241
|
|
218
242
|
def garbage?(value)
|
219
243
|
# If the type is the only element which makes it then
|
220
244
|
# this still makes the value nil, except if type is
|
221
|
-
#
|
222
|
-
value[
|
245
|
+
# an XML node(where type['value'] is a Hash)
|
246
|
+
value["type"] && !value["type"].is_a?(::Hash) && value.size == 1
|
223
247
|
end
|
224
248
|
|
225
249
|
def process_content(value)
|
226
|
-
content = value[
|
227
|
-
if parser = ActiveSupport::XmlMini::PARSING[value[
|
250
|
+
content = value["__content__"]
|
251
|
+
if parser = ActiveSupport::XmlMini::PARSING[value["type"]]
|
228
252
|
parser.arity == 1 ? parser.call(content) : parser.call(content, value)
|
229
253
|
else
|
230
254
|
content
|
@@ -235,7 +259,5 @@ module ActiveSupport
|
|
235
259
|
value.map! { |i| deep_to_h(i) }
|
236
260
|
value.length > 1 ? value : value.first
|
237
261
|
end
|
238
|
-
|
239
262
|
end
|
240
263
|
end
|
241
|
-
|