Fix Enumerable#sum redefined warning
If we require 'active_support/core_ext/enumerable' on Ruby 2.4, we'll see following warning because `Enumerable#sum` and `Array#sum` are added in Ruby 2.4. ``` rails/rails/activesupport/lib/active_support/core_ext/enumerable.rb:20: warning: method redefined; discarding old sum ``` The minimal way to fix the warning is `alias sum sum`. ``` $ ruby -v ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-linux] $ ruby -w -e "def a; end; def a; end" -e:1: warning: method redefined; discarding old a -e:1: warning: previous definition of a was here $ ruby -w -e "def a; end; alias a a; def a; end" ``` But this behavior is not intended. (@amatsuda was told by @ko1) So we should use `alias` as a meaningful way. Ruby 2.4's `sum` (`orig_sum`) assumes an `identity` is `0` when we omit `identity` so we can delegate to `orig_sum` with explicit `identity` only. In a strict sense, we can detect `identity` by check instance's class but we don't care at this time about that because calling `Enumerable#sum` is rare. In many cases, we will call `Array#sum`.
This commit is contained in:
parent
8776a71397
commit
3adbf14d65
@ -1,28 +1,50 @@
|
||||
module Enumerable
|
||||
# Calculates a sum from the elements.
|
||||
# Enumerable#sum was added in Ruby 2.4 but it only works with Numeric elements
|
||||
# when we omit an identity.
|
||||
#
|
||||
# payments.sum { |p| p.price * p.tax_rate }
|
||||
# payments.sum(&:price)
|
||||
#
|
||||
# The latter is a shortcut for:
|
||||
#
|
||||
# payments.inject(0) { |sum, p| sum + p.price }
|
||||
#
|
||||
# It can also calculate the sum without the use of a block.
|
||||
#
|
||||
# [5, 15, 10].sum # => 30
|
||||
# ['foo', 'bar'].sum # => "foobar"
|
||||
# [[1, 2], [3, 1, 5]].sum # => [1, 2, 3, 1, 5]
|
||||
#
|
||||
# The default sum of an empty list is zero. You can override this default:
|
||||
#
|
||||
# [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
|
||||
def sum(identity = nil, &block)
|
||||
if block_given?
|
||||
map(&block).sum(identity)
|
||||
else
|
||||
sum = identity ? inject(identity, :+) : inject(:+)
|
||||
sum || identity || 0
|
||||
# We tried shimming it to attempt the fast native method, rescue TypeError,
|
||||
# and fall back to the compatible implementation, but that's much slower than
|
||||
# just calling the compat method in the first place.
|
||||
if Enumerable.instance_methods(false).include?(:sum) && !((?a..?b).sum rescue false)
|
||||
# We can't use Refinements here because Refinements with Module which will be prepended
|
||||
# doesn't work well https://bugs.ruby-lang.org/issues/13446
|
||||
alias :_original_sum_with_required_identity :sum
|
||||
private :_original_sum_with_required_identity
|
||||
# Calculates a sum from the elements.
|
||||
#
|
||||
# payments.sum { |p| p.price * p.tax_rate }
|
||||
# payments.sum(&:price)
|
||||
#
|
||||
# The latter is a shortcut for:
|
||||
#
|
||||
# payments.inject(0) { |sum, p| sum + p.price }
|
||||
#
|
||||
# It can also calculate the sum without the use of a block.
|
||||
#
|
||||
# [5, 15, 10].sum # => 30
|
||||
# ['foo', 'bar'].sum # => "foobar"
|
||||
# [[1, 2], [3, 1, 5]].sum # => [1, 2, 3, 1, 5]
|
||||
#
|
||||
# The default sum of an empty list is zero. You can override this default:
|
||||
#
|
||||
# [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
|
||||
def sum(identity = nil, &block)
|
||||
if identity
|
||||
_original_sum_with_required_identity(identity, &block)
|
||||
elsif block_given?
|
||||
map(&block).sum(identity)
|
||||
else
|
||||
inject(:+) || 0
|
||||
end
|
||||
end
|
||||
else
|
||||
def sum(identity = nil, &block)
|
||||
if block_given?
|
||||
map(&block).sum(identity)
|
||||
else
|
||||
sum = identity ? inject(identity, :+) : inject(:+)
|
||||
sum || identity || 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user