Add `Enumerable#sole` (#40914)
* Add `Enumerable#sole`, from `ActiveRecord::FinderMethods#sole` * distinguish single-item Enumerable and two-item with nil last Add a test for same. * add symmetry, against rubocop's wishes
This commit is contained in:
parent
9a263e9a0f
commit
2e14c53fc6
|
@ -1,3 +1,9 @@
|
|||
* Add `Enumerable#sole`, per `ActiveRecord::FinderMethods#sole`. Returns the
|
||||
sole item of the enumerable, raising if no items are found, or if more than
|
||||
one is.
|
||||
|
||||
*Asherah Connor*
|
||||
|
||||
* Freeze `ActiveSupport::Duration#parts` and remove writer methods.
|
||||
|
||||
Durations are meant to be value objects and should not be mutated.
|
||||
|
|
|
@ -4,6 +4,10 @@ module Enumerable
|
|||
INDEX_WITH_DEFAULT = Object.new
|
||||
private_constant :INDEX_WITH_DEFAULT
|
||||
|
||||
# Error generated by +sole+ when called on an enumerable that doesn't have
|
||||
# exactly one item.
|
||||
class SoleItemExpectedError < StandardError; end
|
||||
|
||||
# Enumerable#sum was added in Ruby 2.4, but it only works with Numeric elements
|
||||
# when we omit an identity.
|
||||
|
||||
|
@ -215,6 +219,20 @@ module Enumerable
|
|||
def in_order_of(key, series)
|
||||
index_by(&key).values_at(*series).compact
|
||||
end
|
||||
|
||||
# Returns the sole item in the enumerable. If there are no items, or more
|
||||
# than one item, raises +Enumerable::SoleItemExpectedError+.
|
||||
#
|
||||
# ["x"].sole # => "x"
|
||||
# Set.new.sole # => Enumerable::SoleItemExpectedError: no item found
|
||||
# { a: 1, b: 2 }.sole # => Enumerable::SoleItemExpectedError: multiple items found
|
||||
def sole
|
||||
case count
|
||||
when 1 then return first # rubocop:disable Style/RedundantReturn
|
||||
when 0 then raise SoleItemExpectedError, "no item found"
|
||||
when 2.. then raise SoleItemExpectedError, "multiple items found"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Hash
|
||||
|
|
|
@ -319,4 +319,13 @@ class EnumerableTests < ActiveSupport::TestCase
|
|||
values = [ Payment.new(5), Payment.new(1), Payment.new(3) ]
|
||||
assert_equal [ Payment.new(1), Payment.new(5) ], values.in_order_of(:price, [ 1, 5 ])
|
||||
end
|
||||
|
||||
def test_sole
|
||||
expected_raise = Enumerable::SoleItemExpectedError
|
||||
|
||||
assert_raise(expected_raise) { GenericEnumerable.new([]).sole }
|
||||
assert_equal 1, GenericEnumerable.new([1]).sole
|
||||
assert_raise(expected_raise) { GenericEnumerable.new([1, 2]).sole }
|
||||
assert_raise(expected_raise) { GenericEnumerable.new([1, nil]).sole }
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue