1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Merge pull request #38760 from eugeneius/enumerable_pick

Add Enumerable#pick
This commit is contained in:
Kasper Timm Hansen 2020-03-18 21:40:36 +01:00 committed by GitHub
commit 5c188c12ee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 97 additions and 3 deletions

View file

@ -1,3 +1,7 @@
* `Relation#pick` now uses already loaded results instead of making another query.
*Eugene Kenny*
* Deprecate using `return`, `break` or `throw` to exit a transaction block
*Dylan Thacker-Smith*

View file

@ -181,7 +181,7 @@ module ActiveRecord
# See also #ids.
#
def pluck(*column_names)
if loaded? && (column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
if loaded? && all_attributes?(column_names)
return records.pluck(*column_names)
end
@ -218,6 +218,10 @@ module ActiveRecord
# # SELECT people.name, people.email_address FROM people WHERE id = 1 LIMIT 1
# # => [ 'David', 'david@loudthinking.com' ]
def pick(*column_names)
if loaded? && all_attributes?(column_names)
return records.pick(*column_names)
end
limit(1).pluck(*column_names).first
end
@ -230,6 +234,10 @@ module ActiveRecord
end
private
def all_attributes?(column_names)
(column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
end
def has_include?(column_name)
eager_loading? || (includes_values.present? && column_name && column_name != :all)
end

View file

@ -264,6 +264,13 @@ class AssociationProxyTest < ActiveRecord::TestCase
assert_no_queries { david.first_posts.pluck(:title) }
end
def test_pick_uses_loaded_target
david = authors(:david)
assert_equal david.first_posts.pick(:title), david.first_posts.load.pick(:title)
assert_predicate david.first_posts, :loaded?
assert_no_queries { david.first_posts.pick(:title) }
end
def test_reset_unloads_target
david = authors(:david)
david.posts.reload

View file

@ -924,6 +924,30 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal cool_first.color, Minivan.pick(:color)
end
def test_pick_loaded_relation
companies = Company.order(:id).limit(3).load
assert_no_queries do
assert_equal "37signals", companies.pick(:name)
end
end
def test_pick_loaded_relation_multiple_columns
companies = Company.order(:id).limit(3).load
assert_no_queries do
assert_equal [1, "37signals"], companies.pick(:id, :name)
end
end
def test_pick_loaded_relation_sql_fragment
companies = Company.order(:name).limit(3).load
assert_queries 1 do
assert_equal "37signals", companies.pick(Arel.sql("DISTINCT name"))
end
end
def test_grouped_calculation_with_polymorphic_relation
part = ShipPart.create!(name: "has trinket")
part.trinkets.create!

View file

@ -1,3 +1,7 @@
* Add `Enumerable#pick` to complement `ActiveRecord::Relation#pick`.
*Eugene Kenny*
* [Breaking change] `ActiveSupport::Callbacks#halted_callback_hook` now receive a 2nd argument:
`ActiveSupport::Callbacks#halted_callback_hook` now receive the name of the callback

View file

@ -142,7 +142,7 @@ module Enumerable
excluding(*elements)
end
# Convert an enumerable to an array based on the given key.
# Extract the given key from each element in the enumerable.
#
# [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
# # => ["David", "Rafael", "Aaron"]
@ -157,6 +157,23 @@ module Enumerable
end
end
# Extract the given key from the first element in the enumerable.
#
# [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pick(:name)
# # => "David"
#
# [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pick(:id, :name)
# # => [1, "David"]
def pick(*keys)
return if none?
if keys.many?
keys.map { |key| first[key] }
else
first[keys.first]
end
end
# Returns a new +Array+ without the blank items.
# Uses Object#blank? for determining if an item is blank.
#

View file

@ -241,6 +241,24 @@ class EnumerableTests < ActiveSupport::TestCase
ExpandedPayment.new(10, 50)
])
assert_equal [[5, 99], [15, 0], [10, 50]], payments.pluck(:dollars, :cents)
assert_equal [], [].pluck(:price)
assert_equal [], [].pluck(:dollars, :cents)
end
def test_pick
payments = GenericEnumerable.new([ Payment.new(5), Payment.new(15), Payment.new(10) ])
assert_equal 5, payments.pick(:price)
payments = GenericEnumerable.new([
ExpandedPayment.new(5, 99),
ExpandedPayment.new(15, 0),
ExpandedPayment.new(10, 50)
])
assert_equal [5, 99], payments.pick(:dollars, :cents)
assert_nil [].pick(:price)
assert_nil [].pick(:dollars, :cents)
end
def test_compact_blank

View file

@ -2119,10 +2119,22 @@ NOTE: Defined in `active_support/core_ext/enumerable.rb`.
### `pluck`
The method `pluck` returns an array based on the given key:
The method `pluck` extracts the given key from each element:
```ruby
[{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name) # => ["David", "Rafael", "Aaron"]
[{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pluck(:id, :name) # => [[1, "David"], [2, "Rafael"]]
```
NOTE: Defined in `active_support/core_ext/enumerable.rb`.
### `pick`
The method `pick` extracts the given key from the first element:
```ruby
[{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pick(:name) # => "David"
[{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pick(:id, :name) # => [1, "David"]
```
NOTE: Defined in `active_support/core_ext/enumerable.rb`.