aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/relation/query_methods.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/relation/query_methods.rb')
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb208
1 files changed, 132 insertions, 76 deletions
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 525a9cb365..a3ac58bc81 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -1,72 +1,61 @@
module ActiveRecord
module QueryMethods
-
- def preload(*associations)
- spawn.tap {|r| r.preload_associations += Array.wrap(associations) }
- end
-
- def includes(*associations)
- spawn.tap {|r| r.includes_associations += Array.wrap(associations) }
- end
-
- def eager_load(*associations)
- spawn.tap {|r| r.eager_load_associations += Array.wrap(associations) }
- end
-
- def readonly(status = true)
- spawn.tap {|r| r.readonly = status }
- end
-
- def select(selects)
- if selects.present?
- relation = spawn(@relation.project(selects))
- relation.readonly = @relation.joins(relation).present? ? false : @readonly
- relation
- else
- spawn
+ extend ActiveSupport::Concern
+
+ included do
+ (ActiveRecord::Relation::ASSOCIATION_METHODS + ActiveRecord::Relation::MULTI_VALUE_METHODS).each do |query_method|
+ attr_accessor :"#{query_method}_values"
+
+ class_eval <<-CEVAL
+ def #{query_method}(*args)
+ spawn.tap do |new_relation|
+ new_relation.#{query_method}_values ||= []
+ value = Array.wrap(args.flatten).reject {|x| x.blank? }
+ new_relation.#{query_method}_values += value if value.present?
+ end
+ end
+ CEVAL
end
- end
-
- def from(from)
- from.present? ? spawn(@relation.from(from)) : spawn
- end
- def having(*args)
- return spawn if args.blank?
-
- if [String, Hash, Array].include?(args.first.class)
- havings = @klass.send(:merge_conditions, args.size > 1 ? Array.wrap(args) : args.first)
- else
- havings = args.first
+ [:where, :having].each do |query_method|
+ class_eval <<-CEVAL
+ def #{query_method}(*args)
+ spawn.tap do |new_relation|
+ new_relation.#{query_method}_values ||= []
+ value = build_where(*args)
+ new_relation.#{query_method}_values += [*value] if value.present?
+ end
+ end
+ CEVAL
end
- spawn(@relation.having(havings))
- end
+ ActiveRecord::Relation::SINGLE_VALUE_METHODS.each do |query_method|
+ attr_accessor :"#{query_method}_value"
- def group(groups)
- groups.present? ? spawn(@relation.group(groups)) : spawn
- end
-
- def order(orders)
- orders.present? ? spawn(@relation.order(orders)) : spawn
+ class_eval <<-CEVAL
+ def #{query_method}(value = true)
+ spawn.tap do |new_relation|
+ new_relation.#{query_method}_value = value
+ end
+ end
+ CEVAL
+ end
end
def lock(locks = true)
+ relation = spawn
case locks
- when String
- spawn(@relation.lock(locks))
- when TrueClass, NilClass
- spawn(@relation.lock)
+ when String, TrueClass, NilClass
+ spawn.tap {|new_relation| new_relation.lock_value = locks || true }
else
- spawn
+ spawn.tap {|new_relation| new_relation.lock_value = false }
end
end
def reverse_order
- relation = spawn
- relation.instance_variable_set(:@orders, nil)
+ order_clause = arel.send(:order_clauses).join(', ')
+ relation = except(:order)
- order_clause = @relation.send(:order_clauses).join(', ')
if order_clause.present?
relation.order(reverse_sql_order(order_clause))
else
@@ -74,41 +63,108 @@ module ActiveRecord
end
end
- def limit(limits)
- limits.present? ? spawn(@relation.take(limits)) : spawn
+ def arel
+ @arel ||= build_arel
end
- def offset(offsets)
- offsets.present? ? spawn(@relation.skip(offsets)) : spawn
- end
+ def build_arel
+ arel = table
- def on(join)
- spawn(@relation.on(join))
- end
+ joined_associations = []
+ association_joins = []
- def joins(join, join_type = nil)
- return spawn if join.blank?
+ joins = @joins_values.map {|j| j.respond_to?(:strip) ? j.strip : j}.uniq
- join_relation = case join
- when String
- @relation.join(join)
- when Hash, Array, Symbol
- if @klass.send(:array_of_strings?, join)
- @relation.join(join.join(' '))
+ # Build association joins first
+ joins.each do |join|
+ association_joins << join if [Hash, Array, Symbol].include?(join.class) && !@klass.send(:array_of_strings?, join)
+ end
+
+ if association_joins.any?
+ join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins.uniq, nil)
+ to_join = []
+
+ join_dependency.join_associations.each do |association|
+ if (association_relation = association.relation).is_a?(Array)
+ to_join << [association_relation.first, association.association_join.first]
+ to_join << [association_relation.last, association.association_join.last]
+ else
+ to_join << [association_relation, association.association_join]
+ end
+ end
+
+ to_join.each do |tj|
+ unless joined_associations.detect {|ja| ja[0] == tj[0] && ja[1] == tj[1] }
+ joined_associations << tj
+ arel = arel.join(tj[0]).on(*tj[1])
+ end
+ end
+ end
+
+ joins.each do |join|
+ next if join.blank?
+
+ @implicit_readonly = true
+
+ case join
+ when Relation::JoinOperation
+ arel = arel.join(join.relation, join.join_class).on(*join.on)
+ when Hash, Array, Symbol
+ if @klass.send(:array_of_strings?, join)
+ join_string = join.join(' ')
+ arel = arel.join(join_string)
+ end
else
- @relation.join(@klass.send(:build_association_joins, join))
+ arel = arel.join(join)
end
- else
- @relation.join(join, join_type)
end
- spawn(join_relation).tap { |r| r.readonly = true }
+ @where_values.uniq.each do |w|
+ arel = w.is_a?(String) ? arel.where(w) : arel.where(*w)
+ end
+
+ @having_values.uniq.each do |h|
+ arel = h.is_a?(String) ? arel.having(h) : arel.having(*h)
+ end
+
+ arel = arel.take(@limit_value) if @limit_value.present?
+ arel = arel.skip(@offset_value) if @offset_value.present?
+
+ @group_values.uniq.each do |g|
+ arel = arel.group(g) if g.present?
+ end
+
+ @order_values.uniq.each do |o|
+ arel = arel.order(o) if o.present?
+ end
+
+ selects = @select_values.uniq
+
+ if selects.present?
+ selects.each do |s|
+ @implicit_readonly = false
+ arel = arel.project(s) if s.present?
+ end
+ elsif joins.present?
+ arel = arel.project(@klass.quoted_table_name + '.*')
+ end
+
+ arel = arel.from(@from_value) if @from_value.present?
+
+ case @lock_value
+ when TrueClass
+ arel = arel.lock
+ when String
+ arel = arel.lock(@lock_value)
+ end
+
+ arel
end
- def where(*args)
- return spawn if args.blank?
+ def build_where(*args)
+ return if args.blank?
- builder = PredicateBuilder.new(Arel::Sql::Engine.new(@klass))
+ builder = PredicateBuilder.new(table.engine)
conditions = if [String, Array].include?(args.first.class)
merged = @klass.send(:merge_conditions, args.size > 1 ? Array.wrap(args) : args.first)
@@ -120,7 +176,7 @@ module ActiveRecord
args.first
end
- conditions.is_a?(String) ? spawn(@relation.where(conditions)) : spawn(@relation.where(*conditions))
+ conditions
end
private