aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib/active_support/core_ext/enumerable.rb
diff options
context:
space:
mode:
authorFumiaki MATSUSHIMA <mtsmfm@gmail.com>2017-04-11 23:21:53 +0900
committerFumiaki MATSUSHIMA <mtsmfm@gmail.com>2017-04-18 21:37:47 +0900
commit3adbf14d65e6e883ae1d6f8478c9ee858586e758 (patch)
treef86a8ce64641ad773c7b452b2648fba538437f69 /activesupport/lib/active_support/core_ext/enumerable.rb
parent8776a7139757d0b264785c774d4e7f37d4bc1ac7 (diff)
downloadrails-3adbf14d65e6e883ae1d6f8478c9ee858586e758.tar.gz
rails-3adbf14d65e6e883ae1d6f8478c9ee858586e758.tar.bz2
rails-3adbf14d65e6e883ae1d6f8478c9ee858586e758.zip
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`.
Diffstat (limited to 'activesupport/lib/active_support/core_ext/enumerable.rb')
-rw-r--r--activesupport/lib/active_support/core_ext/enumerable.rb68
1 files changed, 45 insertions, 23 deletions
diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb
index 90d7d2947f..4f120d4b45 100644
--- a/activesupport/lib/active_support/core_ext/enumerable.rb
+++ b/activesupport/lib/active_support/core_ext/enumerable.rb
@@ -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