diff options
Diffstat (limited to 'activerecord/lib/active_record/relation')
3 files changed, 102 insertions, 77 deletions
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 7a0c9dc612..f39951e16c 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -87,8 +87,8 @@ module ActiveRecord # person.visits += 1 # person.save! # end - def find(*args, &block) - return to_a.find(&block) if block_given? + def find(*args) + return to_a.find { |*block_args| yield(*block_args) } if block_given? options = args.extract_options! @@ -259,8 +259,8 @@ module ActiveRecord record end - def find_with_ids(*ids, &block) - return to_a.find(&block) if block_given? + def find_with_ids(*ids) + return to_a.find { |*block_args| yield(*block_args) } if block_given? expects_array = ids.first.kind_of?(Array) return ids.first if expects_array && ids.first.empty? diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index d0efa2189d..d853fd63d1 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -28,7 +28,7 @@ module ActiveRecord when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::Relation values = value.to_a attribute.in(values) - when Range + when Range, Arel::Relation attribute.in(value) else attribute.eq(value) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 50e94134f5..015ca8c24c 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -5,79 +5,98 @@ module ActiveRecord module QueryMethods extend ActiveSupport::Concern - included do - (ActiveRecord::Relation::ASSOCIATION_METHODS + ActiveRecord::Relation::MULTI_VALUE_METHODS).each do |query_method| - attr_accessor :"#{query_method}_values" - - next if [:where, :having, :select].include?(query_method) - class_eval <<-CEVAL, __FILE__, __LINE__ + 1 - def #{query_method}(*args, &block) - new_relation = clone - new_relation.send(:apply_modules, Module.new(&block)) if block_given? - value = Array.wrap(args.flatten).reject {|x| x.blank? } - new_relation.#{query_method}_values += value if value.present? - new_relation - end - CEVAL - end + attr_accessor :includes_values, :eager_load_values, :preload_values, + :select_values, :group_values, :order_values, :joins_values, :where_values, :having_values, + :limit_value, :offset_value, :lock_value, :readonly_value, :create_with_value, :from_value - class_eval <<-CEVAL, __FILE__, __LINE__ + 1 - def select(*args, &block) - if block_given? - to_a.select(&block) - else - new_relation = clone - value = Array.wrap(args.flatten).reject {|x| x.blank? } - new_relation.select_values += value if value.present? - new_relation - end - end - CEVAL - - [:where, :having].each do |query_method| - class_eval <<-CEVAL, __FILE__, __LINE__ + 1 - def #{query_method}(*args, &block) - new_relation = clone - new_relation.send(:apply_modules, Module.new(&block)) if block_given? - value = build_where(*args) - new_relation.#{query_method}_values += Array.wrap(value) if value.present? - new_relation - end - CEVAL - end + def includes(*args) + args.reject! { |a| a.blank? } + clone.tap { |r| r.includes_values += args if args.present? } + end - ActiveRecord::Relation::SINGLE_VALUE_METHODS.each do |query_method| - attr_accessor :"#{query_method}_value" + def eager_load(*args) + args.reject! { |a| a.blank? } + clone.tap { |r| r.eager_load_values += args if args.present? } + end - class_eval <<-CEVAL, __FILE__, __LINE__ + 1 - def #{query_method}(value = true, &block) - new_relation = clone - new_relation.send(:apply_modules, Module.new(&block)) if block_given? - new_relation.#{query_method}_value = value - new_relation - end - CEVAL + def preload(*args) + args.reject! { |a| a.blank? } + clone.tap { |r| r.preload_values += args if args.present? } + end + + def select(*args) + if block_given? + to_a.select { |*block_args| yield(*block_args) } + else + args.reject! { |a| a.blank? } + clone.tap { |r| r.select_values += args if args.present? } end end - def extending(*modules) - new_relation = clone - new_relation.send :apply_modules, *modules - new_relation + def group(*args) + args.reject! { |a| a.blank? } + clone.tap { |r| r.group_values += args if args.present? } + end + + def order(*args) + args.reject! { |a| a.blank? } + clone.tap { |r| r.order_values += args if args.present? } + end + + def reorder(*args) + args.reject! { |a| a.blank? } + clone.tap { |r| r.order_values = args if args.present? } + end + + def joins(*args) + args.flatten! + args.reject! { |a| a.blank? } + clone.tap { |r| r.joins_values += args if args.present? } + end + + def where(*args) + value = build_where(*args) + clone.tap { |r| r.where_values += Array.wrap(value) if value.present? } end - def lock(locks = true, &block) - relation = clone - relation.send(:apply_modules, Module.new(&block)) if block_given? + def having(*args) + value = build_where(*args) + clone.tap { |r| r.having_values += Array.wrap(value) if value.present? } + end + + def limit(value = true) + clone.tap { |r| r.limit_value = value } + end + def offset(value = true) + clone.tap { |r| r.offset_value = value } + end + + def lock(locks = true) case locks when String, TrueClass, NilClass - clone.tap {|new_relation| new_relation.lock_value = locks || true } + clone.tap { |r| r.lock_value = locks || true } else - clone.tap {|new_relation| new_relation.lock_value = false } + clone.tap { |r| r.lock_value = false } end end + def readonly(value = true) + clone.tap { |r| r.readonly_value = value } + end + + def create_with(value = true) + clone.tap { |r| r.create_with_value = value } + end + + def from(value = true) + clone.tap { |r| r.from_value = value } + end + + def extending(*modules) + clone.tap { |r| r.send :apply_modules, *modules } + end + def reverse_order order_clause = arel.send(:order_clauses).join(', ') relation = except(:order) @@ -130,27 +149,18 @@ module ActiveRecord end end - arel = arel.having(*@having_values.uniq.select{|h| h.present?}) + arel = arel.having(*@having_values.uniq.select{|h| h.present?}) if @having_values.present? arel = arel.take(@limit_value) if @limit_value.present? arel = arel.skip(@offset_value) if @offset_value.present? - arel = arel.group(*@group_values.uniq.select{|g| g.present?}) + arel = arel.group(*@group_values.uniq.select{|g| g.present?}) if @group_values.present? - arel = arel.order(*@order_values.uniq.select{|o| o.present?}.map(&:to_s)) + arel = arel.order(*@order_values.uniq.select{|o| o.present?}) if @order_values.present? - selects = @select_values.uniq + arel = build_select(arel, @select_values.uniq) - if selects.present? - selects.each do |s| - @implicit_readonly = false - arel = arel.project(s) if s.present? - end - else - arel = arel.project(@klass.quoted_table_name + '.*') - end - - arel = @from_value.present? ? arel.from(@from_value) : arel.from(@klass.quoted_table_name) + arel = arel.from(@from_value) if @from_value.present? case @lock_value when TrueClass @@ -221,6 +231,21 @@ module ActiveRecord relation.join(custom_joins) end + def build_select(arel, selects) + if selects.present? + @implicit_readonly = false + # TODO: fix this ugly hack, we should refactor the callers to get an ARel compatible array. + # Before this change we were passing to ARel the last element only, and ARel is capable of handling an array + if selects.all? { |s| s.is_a?(String) || !s.is_a?(Arel::Expression) } && !(selects.last =~ /^COUNT\(/) + arel.project(*selects) + else + arel.project(selects.last) + end + else + arel.project(@klass.quoted_table_name + '.*') + end + end + def apply_modules(modules) values = Array.wrap(modules) @extensions += values if values.present? |