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/calculation_methods.rb5
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb94
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb28
3 files changed, 92 insertions, 35 deletions
diff --git a/activerecord/lib/active_record/relation/calculation_methods.rb b/activerecord/lib/active_record/relation/calculation_methods.rb
index e6f62ee49a..2477481ec8 100644
--- a/activerecord/lib/active_record/relation/calculation_methods.rb
+++ b/activerecord/lib/active_record/relation/calculation_methods.rb
@@ -40,7 +40,7 @@ module ActiveRecord
distinct = options[:distinct] || distinct
column_name = :all if column_name.blank? && operation == "count"
- if arel.send(:groupings).any?
+ if @group_values.any?
return execute_grouped_calculation(operation, column_name)
else
return execute_simple_calculation(operation, column_name, distinct)
@@ -63,7 +63,7 @@ module ActiveRecord
end
def execute_grouped_calculation(operation, column_name) #:nodoc:
- group_attr = arel.send(:groupings).first.value
+ group_attr = @group_values.first
association = @klass.reflect_on_association(group_attr.to_sym)
associated = association && association.macro == :belongs_to # only count belongs_to associations
group_field = associated ? association.primary_key_name : group_attr
@@ -106,7 +106,6 @@ module ActiveRecord
column_name = :all
# Handles count(), count(:column), count(:distinct => true), count(:column, :distinct => true)
- # TODO : relation.projections only works when .select() was last in the chain. Fix it!
case args.size
when 0
select = get_projection_name_from_chained_relations
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 7ceec40954..a3ac58bc81 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -10,8 +10,20 @@ module ActiveRecord
def #{query_method}(*args)
spawn.tap do |new_relation|
new_relation.#{query_method}_values ||= []
- value = args.size > 1 ? [args] : Array.wrap(args)
- new_relation.#{query_method}_values += value
+ value = Array.wrap(args.flatten).reject {|x| x.blank? }
+ new_relation.#{query_method}_values += value if value.present?
+ end
+ end
+ CEVAL
+ end
+
+ [: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
@@ -58,51 +70,83 @@ module ActiveRecord
def build_arel
arel = table
- @joins_values.each do |j|
- next if j.blank?
+ joined_associations = []
+ association_joins = []
+
+ joins = @joins_values.map {|j| j.respond_to?(:strip) ? j.strip : j}.uniq
+
+ # 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 j
+ case join
when Relation::JoinOperation
- arel = arel.join(j.relation, j.join_class).on(j.on)
+ arel = arel.join(join.relation, join.join_class).on(*join.on)
when Hash, Array, Symbol
- if @klass.send(:array_of_strings?, j)
- arel = arel.join(j.join(' '))
- else
- arel = arel.join(@klass.send(:build_association_joins, j))
+ if @klass.send(:array_of_strings?, join)
+ join_string = join.join(' ')
+ arel = arel.join(join_string)
end
else
- arel = arel.join(j)
+ arel = arel.join(join)
end
end
- @where_values.each do |where|
- if conditions = build_where(where)
- arel = conditions.is_a?(String) ? arel.where(conditions) : arel.where(*conditions)
- end
+ @where_values.uniq.each do |w|
+ arel = w.is_a?(String) ? arel.where(w) : arel.where(*w)
end
- @having_values.each do |where|
- if conditions = build_where(where)
- arel = conditions.is_a?(String) ? arel.having(conditions) : arel.having(*conditions)
- 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.each do |g|
+ @group_values.uniq.each do |g|
arel = arel.group(g) if g.present?
end
- @order_values.each do |o|
+ @order_values.uniq.each do |o|
arel = arel.order(o) if o.present?
end
- @select_values.each do |s|
- @implicit_readonly = false
- arel = arel.project(s) if s.present?
+ 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?
@@ -120,7 +164,7 @@ module ActiveRecord
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)
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index 66eae69d92..a248c72715 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -19,21 +19,19 @@ module ActiveRecord
merged_relation = spawn.eager_load(r.eager_load_values).preload(r.preload_values).includes(r.includes_values)
- merged_relation.readonly_value = r.readonly_value unless merged_relation.readonly_value
- merged_relation.limit_value = r.limit_value unless merged_relation.limit_value
+ merged_relation.readonly_value = r.readonly_value unless r.readonly_value.nil?
+ merged_relation.limit_value = r.limit_value if r.limit_value.present?
merged_relation.lock_value = r.lock_value unless merged_relation.lock_value
+ merged_relation.offset_value = r.offset_value if r.offset_value.present?
merged_relation = merged_relation.
joins(r.joins_values).
group(r.group_values).
- offset(r.offset_value).
select(r.select_values).
from(r.from_value).
having(r.having_values)
- relation_order = r.order_values
- merged_order = relation_order.present? ? relation_order : order_values
- merged_relation.order_values = merged_order
+ merged_relation.order_values = Array.wrap(order_values) + Array.wrap(r.order_values)
merged_relation.create_with_value = @create_with_value
@@ -50,7 +48,7 @@ module ActiveRecord
merged_wheres = merged_wheres.reject {|p| p.is_a?(Arel::Predicates::Equality) && p.operand1.name == w.operand1.name }
end
- merged_wheres << w
+ merged_wheres += [w]
end
merged_relation.where_values = merged_wheres
@@ -74,5 +72,21 @@ module ActiveRecord
result
end
+ def only(*onlies)
+ result = Relation.new(@klass, table)
+
+ onlies.each do |only|
+ if (Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS).include?(only)
+ result.send(:"#{only}_values=", send(:"#{only}_values"))
+ elsif Relation::SINGLE_VALUE_METHODS.include?(only)
+ result.send(:"#{only}_value=", send(:"#{only}_value"))
+ else
+ raise "Invalid argument : #{only}"
+ end
+ end
+
+ result
+ end
+
end
end