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.rb4
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb37
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb2
-rw-r--r--activerecord/lib/active_record/relation/merger.rb2
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb92
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/array_handler.rb16
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb58
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/base_handler.rb17
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/basic_object_handler.rb17
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/class_handler.rb27
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/range_handler.rb17
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb2
12 files changed, 194 insertions, 97 deletions
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index 20d24b409b..4f0502ae75 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -27,7 +27,7 @@ module ActiveRecord
#
# ==== Options
# * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000.
- # * <tt>:start</tt> - Specifies the starting point for the batch processing.
+ # * <tt>:start</tt> - Specifies the primary key value to start from.
# This is especially useful if you want multiple workers dealing with
# the same processing queue. You can make worker 1 handle all the records
# between id 0 and 10,000 and worker 2 handle from 10,000 and beyond
@@ -77,7 +77,7 @@ module ActiveRecord
#
# ==== Options
# * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000.
- # * <tt>:start</tt> - Specifies the starting point for the batch processing.
+ # * <tt>:start</tt> - Specifies the primary key value to start from.
# This is especially useful if you want multiple workers dealing with
# the same processing queue. You can make worker 1 handle all the records
# between id 0 and 10,000 and worker 2 handle from 10,000 and beyond
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 71673324eb..1d4cb1a83b 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -35,21 +35,16 @@ module ActiveRecord
#
# Note: not all valid +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, options = {})
- # TODO: Remove options argument as soon we remove support to
- # activerecord-deprecated_finders.
- column_name, options = nil, column_name if column_name.is_a?(Hash)
- calculate(:count, column_name, options)
+ def count(column_name = nil)
+ calculate(:count, column_name)
end
# Calculates the average value on a given column. Returns +nil+ if there's
# no row. See +calculate+ for examples with options.
#
# Person.average(:age) # => 35.8
- def average(column_name, options = {})
- # TODO: Remove options argument as soon we remove support to
- # activerecord-deprecated_finders.
- calculate(:average, column_name, options)
+ def average(column_name)
+ calculate(:average, column_name)
end
# Calculates the minimum value on a given column. The value is returned
@@ -57,10 +52,8 @@ module ActiveRecord
# +calculate+ for examples with options.
#
# Person.minimum(:age) # => 7
- def minimum(column_name, options = {})
- # TODO: Remove options argument as soon we remove support to
- # activerecord-deprecated_finders.
- calculate(:minimum, column_name, options)
+ def minimum(column_name)
+ calculate(:minimum, column_name)
end
# Calculates the maximum value on a given column. The value is returned
@@ -68,10 +61,8 @@ module ActiveRecord
# +calculate+ for examples with options.
#
# Person.maximum(:age) # => 93
- def maximum(column_name, options = {})
- # TODO: Remove options argument as soon we remove support to
- # activerecord-deprecated_finders.
- calculate(:maximum, column_name, options)
+ def maximum(column_name)
+ calculate(:maximum, column_name)
end
# Calculates the sum of values on a given column. The value is returned
@@ -114,17 +105,15 @@ module ActiveRecord
# Person.group(:last_name).having("min(age) > 17").minimum(:age)
#
# Person.sum("2 * age")
- def calculate(operation, column_name, options = {})
- # TODO: Remove options argument as soon we remove support to
- # activerecord-deprecated_finders.
+ def calculate(operation, column_name)
if column_name.is_a?(Symbol) && attribute_alias?(column_name)
column_name = attribute_alias(column_name)
end
if has_include?(column_name)
- construct_relation_for_association_calculations.calculate(operation, column_name, options)
+ construct_relation_for_association_calculations.calculate(operation, column_name)
else
- perform_calculation(operation, column_name, options)
+ perform_calculation(operation, column_name)
end
end
@@ -196,9 +185,7 @@ module ActiveRecord
eager_loading? || (includes_values.present? && ((column_name && column_name != :all) || references_eager_loaded_tables?))
end
- def perform_calculation(operation, column_name, options = {})
- # TODO: Remove options argument as soon we remove support to
- # activerecord-deprecated_finders.
+ def perform_calculation(operation, column_name)
operation = operation.to_s.downcase
# If #count is used with #distinct / #uniq it is considered distinct. (eg. relation.distinct.count)
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 357861caaa..088bc203b7 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -397,7 +397,7 @@ module ActiveRecord
else
if relation.limit_value
limited_ids = limited_ids_for(relation)
- limited_ids.empty? ? relation.none! : relation.where!(table[primary_key].in(limited_ids))
+ limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
end
relation.except(:limit, :offset)
end
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb
index a27f990f74..afb0b208c3 100644
--- a/activerecord/lib/active_record/relation/merger.rb
+++ b/activerecord/lib/active_record/relation/merger.rb
@@ -22,7 +22,7 @@ module ActiveRecord
# build a relation to merge in rather than directly merging
# the values.
def other
- other = Relation.create(relation.klass, relation.table)
+ other = Relation.create(relation.klass, relation.table, relation.predicate_builder)
hash.each { |k, v|
if k == :joins
if Hash === v
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index 67e646bf18..567efce8ae 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -1,23 +1,26 @@
module ActiveRecord
class PredicateBuilder # :nodoc:
- @handlers = []
+ 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/class_handler'
+ require 'active_record/relation/predicate_builder/range_handler'
+ require 'active_record/relation/predicate_builder/relation_handler'
- autoload :RelationHandler, 'active_record/relation/predicate_builder/relation_handler'
- autoload :ArrayHandler, 'active_record/relation/predicate_builder/array_handler'
+ delegate :resolve_column_aliases, to: :table
- def initialize(klass, table)
- @klass = klass
+ def initialize(table)
@table = table
- end
-
- def resolve_column_aliases(hash)
- hash = hash.dup
- hash.keys.grep(Symbol) do |key|
- if klass.attribute_alias? key
- hash[klass.attribute_alias(key)] = hash.delete key
- end
- end
- hash
+ @handlers = []
+
+ register_handler(BasicObject, BasicObjectHandler.new(self))
+ register_handler(Class, ClassHandler.new(self))
+ register_handler(Base, BaseHandler.new(self))
+ register_handler(Range, RangeHandler.new(self))
+ register_handler(Relation, RelationHandler.new)
+ register_handler(Array, ArrayHandler.new(self))
+ register_handler(AssociationQueryValue, AssociationQueryHandler.new(self))
end
def build_from_hash(attributes)
@@ -26,35 +29,16 @@ module ActiveRecord
end
def expand(column, value)
- queries = []
-
# Find the foreign key when using queries such as:
# Post.where(author: author)
#
# For polymorphic relationships, find the foreign key and type:
# PriceEstimate.where(estimate_of: treasure)
- if klass && reflection = klass._reflect_on_association(column)
- if reflection.polymorphic? && base_class = polymorphic_base_class_from_value(value)
- queries << self.class.build(table[reflection.foreign_type], base_class.name)
- end
-
- column = reflection.foreign_key
+ if table.associated_with?(column)
+ value = AssociationQueryValue.new(table.associated_table(column), value)
end
- queries << self.class.build(table[column], value)
- queries
- end
-
- def polymorphic_base_class_from_value(value)
- case value
- when Relation
- value.klass.base_class
- when Array
- val = value.compact.first
- val.class.base_class if val.is_a?(Base)
- when Base
- value.class.base_class
- end
+ build(table.arel_attribute(column), value)
end
def self.references(attributes)
@@ -79,46 +63,24 @@ module ActiveRecord
# )
# end
# ActiveRecord::PredicateBuilder.register_handler(MyCustomDateRange, handler)
- def self.register_handler(klass, handler)
+ def register_handler(klass, handler)
@handlers.unshift([klass, handler])
end
- register_handler(BasicObject, ->(attribute, value) { attribute.eq(value) })
- register_handler(Class, ->(attribute, value) { deprecate_class_handler; attribute.eq(value.name) })
- register_handler(Base, ->(attribute, value) { attribute.eq(value.id) })
- register_handler(Range, ->(attribute, value) { attribute.between(value) })
- register_handler(Relation, RelationHandler.new)
- register_handler(Array, ArrayHandler.new)
-
- def self.build(attribute, value)
+ def build(attribute, value)
handler_for(value).call(attribute, value)
end
- def self.handler_for(object)
- @handlers.detect { |klass, _| klass === object }.last
- end
- private_class_method :handler_for
-
- def self.deprecate_class_handler
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- Passing a class as a value in an Active Record query is deprecated and
- will be removed. Pass a string instead.
- MSG
- end
-
protected
- attr_reader :klass, :table
+ attr_reader :table
def expand_from_hash(attributes)
return ["1=0"] if attributes.empty?
attributes.flat_map do |key, value|
if value.is_a?(Hash)
- arel_table = Arel::Table.new(key)
- association = klass._reflect_on_association(key)
- builder = self.class.new(association && association.klass, arel_table)
-
+ builder = self.class.new(table.associated_table(key))
builder.expand_from_hash(value)
else
expand(key, value)
@@ -141,5 +103,9 @@ module ActiveRecord
attributes
end
+
+ def handler_for(object)
+ @handlers.detect { |klass, _| klass === object }.last
+ end
end
end
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 4cba297be5..4b5f5773a0 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
@@ -3,6 +3,10 @@ require 'active_support/core_ext/string/filters'
module ActiveRecord
class PredicateBuilder
class ArrayHandler # :nodoc:
+ def initialize(predicate_builder)
+ @predicate_builder = predicate_builder
+ end
+
def call(attribute, value)
values = value.map { |x| x.is_a?(Base) ? x.id : x }
nils, values = values.partition(&:nil?)
@@ -14,20 +18,24 @@ module ActiveRecord
values_predicate =
case values.length
when 0 then NullPredicate
- when 1 then attribute.eq(values.first)
+ when 1 then predicate_builder.build(attribute, values.first)
else attribute.in(values)
end
unless nils.empty?
- values_predicate = values_predicate.or(attribute.eq(nil))
+ values_predicate = values_predicate.or(predicate_builder.build(attribute, nil))
end
- array_predicates = ranges.map { |range| attribute.between(range) }
+ array_predicates = ranges.map { |range| predicate_builder.build(attribute, range) }
array_predicates.unshift(values_predicate)
array_predicates.inject { |composite, predicate| composite.or(predicate) }
end
- module NullPredicate
+ protected
+
+ attr_reader :predicate_builder
+
+ module NullPredicate # :nodoc:
def self.or(other)
other
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
new file mode 100644
index 0000000000..aabcf20c1d
--- /dev/null
+++ b/activerecord/lib/active_record/relation/predicate_builder/association_query_handler.rb
@@ -0,0 +1,58 @@
+module ActiveRecord
+ class PredicateBuilder
+ class AssociationQueryHandler # :nodoc:
+ 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] = value.base_class.name
+ end
+
+ queries[table.association_foreign_key] = value.ids
+ predicate_builder.build_from_hash(queries)
+ end
+
+ 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
+ value
+ end
+
+ def base_class
+ if associated_table.polymorphic_association?
+ @base_class ||= polymorphic_base_class_from_value
+ end
+ end
+
+ private
+
+ def polymorphic_base_class_from_value
+ case value
+ when Relation
+ value.klass.base_class
+ when Array
+ val = value.compact.first
+ val.class.base_class if val.is_a?(Base)
+ when Base
+ value.class.base_class
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb
new file mode 100644
index 0000000000..6fa5b16f73
--- /dev/null
+++ b/activerecord/lib/active_record/relation/predicate_builder/base_handler.rb
@@ -0,0 +1,17 @@
+module ActiveRecord
+ class PredicateBuilder
+ class BaseHandler # :nodoc:
+ def initialize(predicate_builder)
+ @predicate_builder = predicate_builder
+ end
+
+ def call(attribute, value)
+ predicate_builder.build(attribute, value.id)
+ end
+
+ protected
+
+ attr_reader :predicate_builder
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/relation/predicate_builder/basic_object_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/basic_object_handler.rb
new file mode 100644
index 0000000000..6cec75dc0a
--- /dev/null
+++ b/activerecord/lib/active_record/relation/predicate_builder/basic_object_handler.rb
@@ -0,0 +1,17 @@
+module ActiveRecord
+ class PredicateBuilder
+ class BasicObjectHandler # :nodoc:
+ def initialize(predicate_builder)
+ @predicate_builder = predicate_builder
+ end
+
+ def call(attribute, value)
+ attribute.eq(value)
+ end
+
+ protected
+
+ attr_reader :predicate_builder
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/relation/predicate_builder/class_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/class_handler.rb
new file mode 100644
index 0000000000..ed313fc9d4
--- /dev/null
+++ b/activerecord/lib/active_record/relation/predicate_builder/class_handler.rb
@@ -0,0 +1,27 @@
+module ActiveRecord
+ class PredicateBuilder
+ class ClassHandler # :nodoc:
+ def initialize(predicate_builder)
+ @predicate_builder = predicate_builder
+ end
+
+ def call(attribute, value)
+ print_deprecation_warning
+ predicate_builder.build(attribute, value.name)
+ end
+
+ protected
+
+ attr_reader :predicate_builder
+
+ private
+
+ def print_deprecation_warning
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Passing a class as a value in an Active Record query is deprecated and
+ will be removed. Pass a string instead.
+ MSG
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/relation/predicate_builder/range_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/range_handler.rb
new file mode 100644
index 0000000000..1b3849e3ad
--- /dev/null
+++ b/activerecord/lib/active_record/relation/predicate_builder/range_handler.rb
@@ -0,0 +1,17 @@
+module ActiveRecord
+ class PredicateBuilder
+ class RangeHandler # :nodoc:
+ def initialize(predicate_builder)
+ @predicate_builder = predicate_builder
+ end
+
+ def call(attribute, value)
+ attribute.between(value)
+ end
+
+ protected
+
+ attr_reader :predicate_builder
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index 57d66bce4b..01bddea6c9 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -67,7 +67,7 @@ module ActiveRecord
private
def relation_with(values) # :nodoc:
- result = Relation.create(klass, table, values)
+ result = Relation.create(klass, table, predicate_builder, values)
result.extend(*extending_values) if extending_values.any?
result
end