aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/relation
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/relation')
-rw-r--r--activerecord/lib/active_record/relation/batches.rb26
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb27
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb18
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb68
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb53
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/array_handler.rb19
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb88
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/association_query_value.rb44
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb59
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb52
-rw-r--r--activerecord/lib/active_record/relation/query_attribute.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb22
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb2
-rw-r--r--activerecord/lib/active_record/relation/where_clause_factory.rb2
14 files changed, 236 insertions, 246 deletions
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index 76031515fd..ee1f25ec84 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -1,4 +1,4 @@
-require "active_record/relation/batches/batch_enumerator"
+require_relative "batches/batch_enumerator"
module ActiveRecord
module Batches
@@ -30,14 +30,14 @@ module ActiveRecord
# end
#
# ==== Options
- # * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000.
+ # * <tt>:batch_size</tt> - Specifies the size of the batch. Defaults to 1000.
# * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
- # an order is present in the relation.
+ # an order is present in the relation.
#
# Limits are honored, and if present there is no requirement for the batch
- # size, it can be less than, equal, or greater than the limit.
+ # size: it can be less than, equal to, or greater than the limit.
#
# The options +start+ and +finish+ are especially useful if you want
# multiple workers dealing with the same processing queue. You can make
@@ -89,14 +89,14 @@ module ActiveRecord
# To be yielded each record one by one, use #find_each instead.
#
# ==== Options
- # * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000.
+ # * <tt>:batch_size</tt> - Specifies the size of the batch. Defaults to 1000.
# * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
- # an order is present in the relation.
+ # an order is present in the relation.
#
# Limits are honored, and if present there is no requirement for the batch
- # size, it can be less than, equal, or greater than the limit.
+ # size: it can be less than, equal to, or greater than the limit.
#
# The options +start+ and +finish+ are especially useful if you want
# multiple workers dealing with the same processing queue. You can make
@@ -140,9 +140,9 @@ module ActiveRecord
# If you do not provide a block to #in_batches, it will return a
# BatchEnumerator which is enumerable.
#
- # Person.in_batches.with_index do |relation, batch_index|
+ # Person.in_batches.each_with_index do |relation, batch_index|
# puts "Processing relation ##{batch_index}"
- # relation.each { |relation| relation.delete_all }
+ # relation.delete_all
# end
#
# Examples of calling methods on the returned BatchEnumerator object:
@@ -152,12 +152,12 @@ module ActiveRecord
# Person.in_batches.each_record(&:party_all_night!)
#
# ==== Options
- # * <tt>:of</tt> - Specifies the size of the batch. Default to 1000.
- # * <tt>:load</tt> - Specifies if the relation should be loaded. Default to false.
+ # * <tt>:of</tt> - Specifies the size of the batch. Defaults to 1000.
+ # * <tt>:load</tt> - Specifies if the relation should be loaded. Defaults to false.
# * <tt>:start</tt> - Specifies the primary key value to start from, inclusive of the value.
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
- # an order is present in the relation.
+ # an order is present in the relation.
#
# Limits are honored, and if present there is no requirement for the batch
# size, it can be less than, equal, or greater than the limit.
@@ -186,7 +186,7 @@ module ActiveRecord
#
# NOTE: It's not possible to set the order. That is automatically set to
# ascending on the primary key ("id ASC") to make the batch ordering
- # consistent. Therefore the primary key must be orderable, e.g an integer
+ # consistent. Therefore the primary key must be orderable, e.g. an integer
# or a string.
#
# NOTE: By its nature, batch processing is subject to race conditions if
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 9cabd1af13..8a54f8f2c3 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -37,7 +37,16 @@ module ActiveRecord
# Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid #count expressions. The specifics differ
# between databases. In invalid cases, an error from the database is thrown.
def count(column_name = nil)
- return super() if block_given?
+ if block_given?
+ unless column_name.nil?
+ ActiveSupport::Deprecation.warn \
+ "When `count' is called with a block, it ignores other arguments. " \
+ "This behavior is now deprecated and will result in an ArgumentError in Rails 6.0."
+ end
+
+ return super()
+ end
+
calculate(:count, column_name)
end
@@ -73,7 +82,16 @@ module ActiveRecord
#
# Person.sum(:age) # => 4562
def sum(column_name = nil)
- return super() if block_given?
+ if block_given?
+ unless column_name.nil?
+ ActiveSupport::Deprecation.warn \
+ "When `sum' is called with a block, it ignores other arguments. " \
+ "This behavior is now deprecated and will result in an ArgumentError in Rails 6.0."
+ end
+
+ return super()
+ end
+
calculate(:sum, column_name)
end
@@ -293,7 +311,7 @@ module ActiveRecord
relation.group_values = group_fields
relation.select_values = select_values
- calculated_data = @klass.connection.select_all(relation, nil, relation.bound_attributes)
+ calculated_data = @klass.connection.select_all(relation.arel, nil, relation.bound_attributes)
if association
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
@@ -368,9 +386,8 @@ module ActiveRecord
relation.select_values = [aliased_column]
subquery = relation.arel.as(subquery_alias)
- sm = Arel::SelectManager.new relation.engine
select_value = operation_over_aggregate_column(column_alias, "count", distinct)
- sm.project(select_value).from(subquery)
+ Arel::SelectManager.new(subquery).project(select_value)
end
end
end
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
index 50378f9d99..48c4dcdef4 100644
--- a/activerecord/lib/active_record/relation/delegation.rb
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -89,6 +89,8 @@ module ActiveRecord
self.class.delegate_to_scoped_klass(method)
scoping { @klass.public_send(method, *args, &block) }
elsif arel.respond_to?(method)
+ ActiveSupport::Deprecation.warn \
+ "Delegating #{method} to arel is deprecated and will be removed in Rails 6.0."
self.class.delegate method, to: :arel
arel.public_send(method, *args, &block)
else
@@ -109,21 +111,9 @@ module ActiveRecord
end
end
- def respond_to_missing?(method, include_private = false)
- super || @klass.respond_to?(method, include_private) ||
- arel.respond_to?(method, include_private)
- end
-
private
-
- def method_missing(method, *args, &block)
- if @klass.respond_to?(method)
- scoping { @klass.public_send(method, *args, &block) }
- elsif arel.respond_to?(method)
- arel.public_send(method, *args, &block)
- else
- super
- end
+ def respond_to_missing?(method, _)
+ super || @klass.respond_to?(method) || arel.respond_to?(method)
end
end
end
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 5d24f5f5ca..eee0f36f63 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -147,8 +147,7 @@ module ActiveRecord
def last(limit = nil)
return find_last(limit) if loaded? || limit_value
- result = limit(limit)
- result.order!(arel_attribute(primary_key)) if order_values.empty? && primary_key
+ result = ordered_relation.limit(limit)
result = result.reverse_order!
limit ? result.reverse : result.first
@@ -307,23 +306,16 @@ module ActiveRecord
MSG
end
- return false if !conditions
+ return false if !conditions || limit_value == 0
- relation = apply_join_dependency(self, construct_join_dependency(eager_loading: false))
- return false if ActiveRecord::NullRelation === relation
+ relation = self unless eager_loading?
+ relation ||= apply_join_dependency(self, construct_join_dependency(eager_loading: false))
- relation = relation.except(:select, :distinct).select(ONE_AS_ONE).limit(1)
+ return false if ActiveRecord::NullRelation === relation
- case conditions
- when Array, Hash
- relation = relation.where(conditions)
- else
- unless conditions == :none
- relation = relation.where(primary_key => conditions)
- end
- end
+ relation = construct_relation_for_exists(relation, conditions)
- connection.select_value(relation, "#{name} Exists", relation.bound_attributes) ? true : false
+ connection.select_value(relation.arel, "#{name} Exists", relation.bound_attributes) ? true : false
rescue ::RangeError
false
end
@@ -342,14 +334,14 @@ module ActiveRecord
name = @klass.name
if ids.nil?
- error = "Couldn't find #{name}"
+ error = "Couldn't find #{name}".dup
error << " with#{conditions}" if conditions
raise RecordNotFound.new(error, name)
elsif Array(ids).size == 1
error = "Couldn't find #{name} with '#{key}'=#{ids}#{conditions}"
raise RecordNotFound.new(error, name, key, ids)
else
- error = "Couldn't find all #{name.pluralize} with '#{key}': "
+ error = "Couldn't find all #{name.pluralize} with '#{key}': ".dup
error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})"
raise RecordNotFound.new(error, name, primary_key, ids)
@@ -384,13 +376,25 @@ module ActiveRecord
if ActiveRecord::NullRelation === relation
[]
else
- arel = relation.arel
- rows = connection.select_all(arel, "SQL", relation.bound_attributes)
+ rows = connection.select_all(relation.arel, "SQL", relation.bound_attributes)
join_dependency.instantiate(rows, aliases)
end
end
end
+ def construct_relation_for_exists(relation, conditions)
+ relation = relation.except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
+
+ case conditions
+ when Array, Hash
+ relation.where!(conditions)
+ else
+ relation.where!(primary_key => conditions) unless conditions == :none
+ end
+
+ relation
+ end
+
def construct_join_dependency(joins = [], eager_loading: true)
including = eager_load_values + includes_values
ActiveRecord::Associations::JoinDependency.new(@klass, including, joins, eager_loading: eager_loading)
@@ -401,8 +405,7 @@ module ActiveRecord
end
def apply_join_dependency(relation, join_dependency)
- relation = relation.except(:includes, :eager_load, :preload)
- relation = relation.joins join_dependency
+ relation = relation.except(:includes, :eager_load, :preload).joins!(join_dependency)
if using_limitable_reflections?(join_dependency.reflections)
relation
@@ -420,9 +423,8 @@ module ActiveRecord
"#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
relation = relation.except(:select).select(values).distinct!
- arel = relation.arel
- id_rows = @klass.connection.select_all(arel, "SQL", relation.bound_attributes)
+ id_rows = @klass.connection.select_all(relation.arel, "SQL", relation.bound_attributes)
id_rows.map { |row| row[primary_key] }
end
@@ -530,11 +532,7 @@ module ActiveRecord
if loaded?
records[index, limit] || []
else
- relation = if order_values.empty? && primary_key
- order(arel_attribute(primary_key).asc)
- else
- self
- end
+ relation = ordered_relation
if limit_value.nil? || index < limit_value
relation = relation.offset(offset_index + index) unless index.zero?
@@ -549,11 +547,7 @@ module ActiveRecord
if loaded?
records[-index]
else
- relation = if order_values.empty? && primary_key
- order(arel_attribute(primary_key).asc)
- else
- self
- end
+ relation = ordered_relation
relation.to_a[-index]
# TODO: can be made more performant on large result sets by
@@ -567,5 +561,13 @@ module ActiveRecord
def find_last(limit)
limit ? records.last(limit) : records.last
end
+
+ def ordered_relation
+ if order_values.empty? && primary_key
+ order(arel_attribute(primary_key).asc)
+ else
+ self
+ end
+ end
end
end
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index fca914aedd..eb80c9a00d 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -1,13 +1,5 @@
module ActiveRecord
class PredicateBuilder # :nodoc:
- require "active_record/relation/predicate_builder/array_handler"
- require "active_record/relation/predicate_builder/association_query_handler"
- require "active_record/relation/predicate_builder/base_handler"
- require "active_record/relation/predicate_builder/basic_object_handler"
- require "active_record/relation/predicate_builder/polymorphic_array_handler"
- require "active_record/relation/predicate_builder/range_handler"
- require "active_record/relation/predicate_builder/relation_handler"
-
delegate :resolve_column_aliases, to: :table
def initialize(table)
@@ -20,8 +12,6 @@ module ActiveRecord
register_handler(RangeHandler::RangeWithBinds, RangeHandler.new)
register_handler(Relation, RelationHandler.new)
register_handler(Array, ArrayHandler.new(self))
- register_handler(AssociationQueryValue, AssociationQueryHandler.new(self))
- register_handler(PolymorphicArrayValue, PolymorphicArrayHandler.new(self))
end
def build_from_hash(attributes)
@@ -87,7 +77,6 @@ module ActiveRecord
binds = []
attributes.each do |column_name, value|
- binds.concat(value.bound_attributes) if value.is_a?(Relation)
case
when value.is_a?(Hash) && !table.has_column?(column_name)
attrs, bvs = associated_predicate_builder(column_name).create_binds_for_hash(value)
@@ -99,24 +88,45 @@ module ActiveRecord
#
# For polymorphic relationships, find the foreign key and type:
# PriceEstimate.where(estimate_of: treasure)
- result[column_name] = AssociationQueryHandler.value_for(table, column_name, value)
+ associated_table = table.associated_table(column_name)
+ if associated_table.polymorphic_association?
+ case value.is_a?(Array) ? value.first : value
+ when Base, Relation
+ value = [value] unless value.is_a?(Array)
+ klass = PolymorphicArrayValue
+ end
+ end
+
+ klass ||= AssociationQueryValue
+ result[column_name] = klass.new(associated_table, value).queries.map do |query|
+ attrs, bvs = create_binds_for_hash(query)
+ binds.concat(bvs)
+ attrs
+ end
when value.is_a?(Range) && !table.type(column_name).respond_to?(:subtype)
first = value.begin
last = value.end
unless first.respond_to?(:infinite?) && first.infinite?
- binds << build_bind_param(column_name, first)
+ binds << build_bind_attribute(column_name, first)
first = Arel::Nodes::BindParam.new
end
unless last.respond_to?(:infinite?) && last.infinite?
- binds << build_bind_param(column_name, last)
+ binds << build_bind_attribute(column_name, last)
last = Arel::Nodes::BindParam.new
end
result[column_name] = RangeHandler::RangeWithBinds.new(first, last, value.exclude_end?)
+ when value.is_a?(Relation)
+ binds.concat(value.bound_attributes)
else
if can_be_bound?(column_name, value)
- result[column_name] = Arel::Nodes::BindParam.new
- binds << build_bind_param(column_name, value)
+ bind_attribute = build_bind_attribute(column_name, value)
+ if value.is_a?(StatementCache::Substitute) || !bind_attribute.value_for_database.nil?
+ result[column_name] = Arel::Nodes::BindParam.new
+ binds << bind_attribute
+ else
+ result[column_name] = nil
+ end
end
end
end
@@ -159,8 +169,17 @@ module ActiveRecord
end
end
- def build_bind_param(column_name, value)
+ def build_bind_attribute(column_name, value)
Relation::QueryAttribute.new(column_name.to_s, value, table.type(column_name))
end
end
end
+
+require_relative "predicate_builder/array_handler"
+require_relative "predicate_builder/base_handler"
+require_relative "predicate_builder/basic_object_handler"
+require_relative "predicate_builder/range_handler"
+require_relative "predicate_builder/relation_handler"
+
+require_relative "predicate_builder/association_query_value"
+require_relative "predicate_builder/polymorphic_array_value"
diff --git a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
index 88b6c37d43..1068e700e2 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
@@ -6,11 +6,11 @@ module ActiveRecord
end
def call(attribute, value)
+ return attribute.in([]) if value.empty?
+ return queries_predicates(value) if value.all? { |v| v.is_a?(Hash) }
+
values = value.map { |x| x.is_a?(Base) ? x.id : x }
nils, values = values.partition(&:nil?)
-
- return attribute.in([]) if values.empty? && nils.empty?
-
ranges, values = values.partition { |v| v.is_a?(Range) }
values_predicate =
@@ -26,7 +26,7 @@ module ActiveRecord
array_predicates = ranges.map { |range| predicate_builder.build(attribute, range) }
array_predicates.unshift(values_predicate)
- array_predicates.inject { |composite, predicate| composite.or(predicate) }
+ array_predicates.inject(&:or)
end
# TODO Change this to private once we've dropped Ruby 2.2 support.
@@ -40,6 +40,17 @@ module ActiveRecord
other
end
end
+
+ private
+ def queries_predicates(queries)
+ if queries.size > 1
+ queries.map do |query|
+ Arel::Nodes::And.new(predicate_builder.build_from_hash(query))
+ end.inject(&:or)
+ else
+ predicate_builder.build_from_hash(queries.first)
+ end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb
deleted file mode 100644
index 29860ec677..0000000000
--- a/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-module ActiveRecord
- class PredicateBuilder
- class AssociationQueryHandler # :nodoc:
- def self.value_for(table, column, value)
- associated_table = table.associated_table(column)
- klass = if associated_table.polymorphic_association? && ::Array === value && value.first.is_a?(Base)
- PolymorphicArrayValue
- else
- AssociationQueryValue
- end
-
- klass.new(associated_table, value)
- end
-
- def initialize(predicate_builder)
- @predicate_builder = predicate_builder
- end
-
- def call(attribute, value)
- queries = {}
-
- table = value.associated_table
- if value.base_class
- queries[table.association_foreign_type.to_s] = value.base_class.name
- end
-
- queries[table.association_foreign_key.to_s] = value.ids
- predicate_builder.build_from_hash(queries)
- end
-
- # TODO Change this to private once we've dropped Ruby 2.2 support.
- # Workaround for Ruby 2.2 "private attribute?" warning.
- protected
-
- attr_reader :predicate_builder
- end
-
- class AssociationQueryValue # :nodoc:
- attr_reader :associated_table, :value
-
- def initialize(associated_table, value)
- @associated_table = associated_table
- @value = value
- end
-
- def ids
- case value
- when Relation
- value.select(primary_key)
- when Array
- value.map { |v| convert_to_id(v) }
- else
- convert_to_id(value)
- end
- end
-
- def base_class
- if associated_table.polymorphic_association?
- @base_class ||= polymorphic_base_class_from_value
- end
- end
-
- private
-
- def primary_key
- associated_table.association_primary_key(base_class)
- end
-
- def polymorphic_base_class_from_value
- case value
- when Relation
- value.klass.base_class
- when Base
- value.class.base_class
- end
- end
-
- def convert_to_id(value)
- case value
- when Base
- value._read_attribute(primary_key)
- else
- value
- end
- end
- end
- end
-end
diff --git a/activerecord/lib/active_record/relation/predicate_builder/association_query_value.rb b/activerecord/lib/active_record/relation/predicate_builder/association_query_value.rb
new file mode 100644
index 0000000000..3e19646ae5
--- /dev/null
+++ b/activerecord/lib/active_record/relation/predicate_builder/association_query_value.rb
@@ -0,0 +1,44 @@
+module ActiveRecord
+ class PredicateBuilder
+ class AssociationQueryValue # :nodoc:
+ def initialize(associated_table, value)
+ @associated_table = associated_table
+ @value = value
+ end
+
+ def queries
+ [associated_table.association_foreign_key.to_s => ids]
+ end
+
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
+ protected
+ attr_reader :associated_table, :value
+
+ private
+ def ids
+ case value
+ when Relation
+ value.select_values.empty? ? value.select(primary_key) : value
+ when Array
+ value.map { |v| convert_to_id(v) }
+ else
+ convert_to_id(value)
+ end
+ end
+
+ def primary_key
+ associated_table.association_primary_key
+ end
+
+ def convert_to_id(value)
+ case value
+ when Base
+ value._read_attribute(primary_key)
+ else
+ value
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb
deleted file mode 100644
index 335124c952..0000000000
--- a/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-module ActiveRecord
- class PredicateBuilder
- class PolymorphicArrayHandler # :nodoc:
- def initialize(predicate_builder)
- @predicate_builder = predicate_builder
- end
-
- def call(attribute, value)
- table = value.associated_table
- queries = value.type_to_ids_mapping.map do |type, ids|
- { table.association_foreign_type.to_s => type, table.association_foreign_key.to_s => ids }
- end
-
- predicates = queries.map { |query| predicate_builder.build_from_hash(query) }
-
- if predicates.size > 1
- type_and_ids_predicates = predicates.map { |type_predicate, id_predicate| Arel::Nodes::Grouping.new(type_predicate.and(id_predicate)) }
- type_and_ids_predicates.inject(&:or)
- else
- predicates.first
- end
- end
-
- # TODO Change this to private once we've dropped Ruby 2.2 support.
- # Workaround for Ruby 2.2 "private attribute?" warning.
- protected
-
- attr_reader :predicate_builder
- end
-
- class PolymorphicArrayValue # :nodoc:
- attr_reader :associated_table, :values
-
- def initialize(associated_table, values)
- @associated_table = associated_table
- @values = values
- end
-
- def type_to_ids_mapping
- default_hash = Hash.new { |hsh, key| hsh[key] = [] }
- values.each_with_object(default_hash) { |value, hash| hash[base_class(value).name] << convert_to_id(value) }
- end
-
- private
-
- def primary_key(value)
- associated_table.association_primary_key(base_class(value))
- end
-
- def base_class(value)
- value.class.base_class
- end
-
- def convert_to_id(value)
- value._read_attribute(primary_key(value))
- end
- end
- end
-end
diff --git a/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb b/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb
new file mode 100644
index 0000000000..7029ae5f47
--- /dev/null
+++ b/activerecord/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb
@@ -0,0 +1,52 @@
+module ActiveRecord
+ class PredicateBuilder
+ class PolymorphicArrayValue # :nodoc:
+ def initialize(associated_table, values)
+ @associated_table = associated_table
+ @values = values
+ end
+
+ def queries
+ type_to_ids_mapping.map do |type, ids|
+ {
+ associated_table.association_foreign_type.to_s => type,
+ associated_table.association_foreign_key.to_s => ids.size > 1 ? ids : ids.first
+ }
+ end
+ end
+
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
+ # Workaround for Ruby 2.2 "private attribute?" warning.
+ protected
+ attr_reader :associated_table, :values
+
+ private
+ def type_to_ids_mapping
+ default_hash = Hash.new { |hsh, key| hsh[key] = [] }
+ values.each_with_object(default_hash) { |value, hash| hash[base_class(value).name] << convert_to_id(value) }
+ end
+
+ def primary_key(value)
+ associated_table.association_primary_key(base_class(value))
+ end
+
+ def base_class(value)
+ case value
+ when Base
+ value.class.base_class
+ when Relation
+ value.klass.base_class
+ end
+ end
+
+ def convert_to_id(value)
+ case value
+ when Base
+ value._read_attribute(primary_key(value))
+ when Relation
+ value.select(primary_key(value))
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/relation/query_attribute.rb b/activerecord/lib/active_record/relation/query_attribute.rb
index a68e508fcc..0e1f64775d 100644
--- a/activerecord/lib/active_record/relation/query_attribute.rb
+++ b/activerecord/lib/active_record/relation/query_attribute.rb
@@ -1,4 +1,4 @@
-require "active_record/attribute"
+require_relative "../attribute"
module ActiveRecord
class Relation
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 1178dec706..9da8f96337 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -1,7 +1,7 @@
-require "active_record/relation/from_clause"
-require "active_record/relation/query_attribute"
-require "active_record/relation/where_clause"
-require "active_record/relation/where_clause_factory"
+require_relative "from_clause"
+require_relative "query_attribute"
+require_relative "where_clause"
+require_relative "where_clause_factory"
require "active_model/forbidden_attributes_protection"
module ActiveRecord
@@ -248,7 +248,7 @@ module ActiveRecord
return super()
end
- raise ArgumentError, "Call this with at least one field" if fields.empty?
+ raise ArgumentError, "Call `select' with at least one field" if fields.empty?
spawn._select!(*fields)
end
@@ -1100,14 +1100,16 @@ module ActiveRecord
end
VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
- "asc", "desc", "ASC", "DESC"] # :nodoc:
+ "asc", "desc", "ASC", "DESC"].to_set # :nodoc:
def validate_order_args(args)
args.each do |arg|
next unless arg.is_a?(Hash)
arg.each do |_key, value|
- raise ArgumentError, "Direction \"#{value}\" is invalid. Valid " \
- "directions are: #{VALID_DIRECTIONS.inspect}" unless VALID_DIRECTIONS.include?(value)
+ unless VALID_DIRECTIONS.include?(value)
+ raise ArgumentError,
+ "Direction \"#{value}\" is invalid. Valid directions are: #{VALID_DIRECTIONS.to_a.inspect}"
+ end
end
end
end
@@ -1120,7 +1122,7 @@ module ActiveRecord
validate_order_args(order_args)
references = order_args.grep(String)
- references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
+ references.map! { |arg| arg =~ /^\W?(\w+)\W?\./ && $1 }.compact!
references!(references) if references.any?
# if a symbol is given we prepend the quoted table name
@@ -1165,7 +1167,7 @@ module ActiveRecord
end
end
- STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having]
+ STRUCTURAL_OR_METHODS = Relation::VALUE_METHODS - [:extending, :where, :having, :unscope]
def structurally_incompatible_values_for_or(other)
STRUCTURAL_OR_METHODS.reject do |method|
get_value(method) == other.get_value(method)
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index ada89b5ec3..ddf7f825c1 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -1,6 +1,6 @@
require "active_support/core_ext/hash/except"
require "active_support/core_ext/hash/slice"
-require "active_record/relation/merger"
+require_relative "merger"
module ActiveRecord
module SpawnMethods
diff --git a/activerecord/lib/active_record/relation/where_clause_factory.rb b/activerecord/lib/active_record/relation/where_clause_factory.rb
index 04bee73e8f..b862dd56a5 100644
--- a/activerecord/lib/active_record/relation/where_clause_factory.rb
+++ b/activerecord/lib/active_record/relation/where_clause_factory.rb
@@ -57,7 +57,7 @@ module ActiveRecord
else
column = klass.column_for_attribute(attribute)
- binds << predicate_builder.send(:build_bind_param, attribute, value)
+ binds << predicate_builder.send(:build_bind_attribute, attribute, value)
value = Arel::Nodes::BindParam.new
predicate = if options[:case_sensitive]