diff options
Diffstat (limited to 'activerecord/lib/active_record/relation')
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 |