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:
Fumiaki MATSUSHIMA 2017-04-11 23:21:53 +09:00
parent 8776a71397
commit 3adbf14d65

@ -1,4 +1,15 @@
module Enumerable
# Enumerable#sum was added in Ruby 2.4 but it only works with Numeric elements
# when we omit an identity.
#
# 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 }
@ -17,6 +28,16 @@ module Enumerable
# 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)
@ -25,6 +46,7 @@ def sum(identity = nil, &block)
sum || identity || 0
end
end
end
# Convert an enumerable to a hash.
#