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:
commit
5c188c12ee
8 changed files with 97 additions and 3 deletions
|
@ -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*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
#
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`.
|
||||
|
|
Loading…
Reference in a new issue