diff options
Diffstat (limited to 'activerecord/lib/active_record/relation/query_methods.rb')
-rw-r--r-- | activerecord/lib/active_record/relation/query_methods.rb | 286 |
1 files changed, 177 insertions, 109 deletions
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index d737b34115..855477eaed 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -5,37 +5,67 @@ module ActiveRecord module QueryMethods extend ActiveSupport::Concern - attr_accessor :includes_values, :eager_load_values, :preload_values, - :select_values, :group_values, :order_values, :joins_values, - :where_values, :having_values, :bind_values, - :limit_value, :offset_value, :lock_value, :readonly_value, :create_with_value, - :from_value, :reordering_value, :reverse_order_value, - :uniq_value, :references_values + Relation::MULTI_VALUE_METHODS.each do |name| + class_eval <<-CODE, __FILE__, __LINE__ + 1 + def #{name}_values # def select_values + @values[:#{name}] || [] # @values[:select] || [] + end # end + # + def #{name}_values=(values) # def select_values=(values) + @values[:#{name}] = values # @values[:select] = values + end # end + CODE + end + + (Relation::SINGLE_VALUE_METHODS - [:create_with]).each do |name| + class_eval <<-CODE, __FILE__, __LINE__ + 1 + def #{name}_value # def readonly_value + @values[:#{name}] # @values[:readonly] + end # end + # + def #{name}_value=(value) # def readonly_value=(value) + @values[:#{name}] = value # @values[:readonly] = value + end # end + CODE + end + + def create_with_value + @values[:create_with] || {} + end + + def create_with_value=(value) + @values[:create_with] = value + end + + alias extensions extending_values def includes(*args) - args.reject! {|a| a.blank? } + args.empty? ? self : clone.includes!(*args) + end - return self if args.empty? + def includes!(*args) + args.reject! {|a| a.blank? } - relation = clone - relation.includes_values = (relation.includes_values + args).flatten.uniq - relation + self.includes_values = (includes_values + args).flatten.uniq + self end def eager_load(*args) - return self if args.blank? + args.blank? ? self : clone.eager_load!(*args) + end - relation = clone - relation.eager_load_values += args - relation + def eager_load!(*args) + self.eager_load_values += args + self end def preload(*args) - return self if args.blank? + args.blank? ? self : clone.preload!(*args) + end - relation = clone - relation.preload_values += args - relation + def preload!(*args) + self.preload_values += args + self end # Used to indicate that an association is referenced by an SQL string, and should @@ -49,11 +79,12 @@ module ActiveRecord # User.includes(:posts).where("posts.name = 'foo'").references(:posts) # # => Query now knows the string references posts, so adds a JOIN def references(*args) - return self if args.blank? + args.blank? ? self : clone.references!(*args) + end - relation = clone - relation.references_values = (references_values + args.flatten.map(&:to_s)).uniq - relation + def references!(*args) + self.references_values = (references_values + args.flatten.map(&:to_s)).uniq + self end # Works in two unique ways. @@ -87,34 +118,40 @@ module ActiveRecord # => ActiveModel::MissingAttributeError: missing attribute: other_field def select(value = Proc.new) if block_given? - to_a.select {|*block_args| value.call(*block_args) } + to_a.select { |*block_args| value.call(*block_args) } else - relation = clone - relation.select_values += Array.wrap(value) - relation + clone.select!(value) end end + def select!(value) + self.select_values += Array.wrap(value) + self + end + def group(*args) - return self if args.blank? + args.blank? ? self : clone.group!(*args) + end - relation = clone - relation.group_values += args.flatten - relation + def group!(*args) + self.group_values += args.flatten + self end def order(*args) - return self if args.blank? + args.blank? ? self : clone.order!(*args) + end + def order!(*args) args = args.flatten + references = args.reject { |arg| Arel::Node === arg } .map { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 } .compact + references!(references) if references.any? - relation = clone - relation = relation.references(references) if references.any? - relation.order_values += args - relation + self.order_values += args + self end # Replaces any existing order defined on the relation with the specified order. @@ -128,72 +165,88 @@ module ActiveRecord # generates a query with 'ORDER BY id ASC, name ASC'. # def reorder(*args) - return self if args.blank? + args.blank? ? self : clone.reorder!(*args) + end - relation = clone - relation.reordering_value = true - relation.order_values = args.flatten - relation + def reorder!(*args) + self.reordering_value = true + self.order_values = args.flatten + self end def joins(*args) - return self if args.compact.blank? - - relation = clone + args.compact.blank? ? self : clone.joins!(*args) + end + def joins!(*args) args.flatten! - relation.joins_values += args - relation + self.joins_values += args + self end def bind(value) - relation = clone - relation.bind_values += [value] - relation + clone.bind!(value) + end + + def bind!(value) + self.bind_values += [value] + self end def where(opts, *rest) - return self if opts.blank? + opts.blank? ? self : clone.where!(opts, *rest) + end - relation = clone - relation = relation.references(PredicateBuilder.references(opts)) if Hash === opts - relation.where_values += build_where(opts, rest) - relation + def where!(opts, *rest) + references!(PredicateBuilder.references(opts)) if Hash === opts + + self.where_values += build_where(opts, rest) + self end def having(opts, *rest) - return self if opts.blank? + opts.blank? ? self : clone.having!(opts, *rest) + end - relation = clone - relation = relation.references(PredicateBuilder.references(opts)) if Hash === opts - relation.having_values += build_where(opts, rest) - relation + def having!(opts, *rest) + references!(PredicateBuilder.references(opts)) if Hash === opts + + self.having_values += build_where(opts, rest) + self end def limit(value) - relation = clone - relation.limit_value = value - relation + clone.limit!(value) + end + + def limit!(value) + self.limit_value = value + self end def offset(value) - relation = clone - relation.offset_value = value - relation + clone.offset!(value) + end + + def offset!(value) + self.offset_value = value + self end def lock(locks = true) - relation = clone + clone.lock!(locks) + end + def lock!(locks = true) case locks when String, TrueClass, NilClass - relation.lock_value = locks || true + self.lock_value = locks || true else - relation.lock_value = false + self.lock_value = false end - relation + self end # Returns a chainable relation with zero records, specifically an @@ -230,21 +283,30 @@ module ActiveRecord end def readonly(value = true) - relation = clone - relation.readonly_value = value - relation + clone.readonly!(value) + end + + def readonly!(value = true) + self.readonly_value = value + self end def create_with(value) - relation = clone - relation.create_with_value = value ? create_with_value.merge(value) : {} - relation + clone.create_with!(value) + end + + def create_with!(value) + self.create_with_value = value ? create_with_value.merge(value) : {} + self end def from(value) - relation = clone - relation.from_value = value - relation + clone.from!(value) + end + + def from!(value) + self.from_value = value + self end # Specifies whether the records should be unique or not. For example: @@ -258,9 +320,12 @@ module ActiveRecord # User.select(:name).uniq.uniq(false) # # => You can also remove the uniqueness def uniq(value = true) - relation = clone - relation.uniq_value = value - relation + clone.uniq!(value) + end + + def uniq!(value = true) + self.uniq_value = value + self end # Used to extend a scope with additional methods, either through @@ -299,20 +364,30 @@ module ActiveRecord # # pagination code goes here # end # end - def extending(*modules) - modules << Module.new(&Proc.new) if block_given? + def extending(*modules, &block) + if modules.any? || block + clone.extending!(*modules, &block) + else + self + end + end - return self if modules.empty? + def extending!(*modules, &block) + modules << Module.new(&block) if block_given? - relation = clone - relation.send(:apply_modules, modules.flatten) - relation + self.extending_values = modules.flatten + extend(*extending_values) if extending_values.any? + + self end def reverse_order - relation = clone - relation.reverse_order_value = !relation.reverse_order_value - relation + clone.reverse_order! + end + + def reverse_order! + self.reverse_order_value = !reverse_order_value + self end def arel @@ -322,26 +397,26 @@ module ActiveRecord def build_arel arel = table.from table - build_joins(arel, @joins_values) unless @joins_values.empty? + build_joins(arel, joins_values) unless joins_values.empty? - collapse_wheres(arel, (@where_values - ['']).uniq) + collapse_wheres(arel, (where_values - ['']).uniq) - arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty? + arel.having(*having_values.uniq.reject{|h| h.blank?}) unless having_values.empty? - arel.take(connection.sanitize_limit(@limit_value)) if @limit_value - arel.skip(@offset_value.to_i) if @offset_value + arel.take(connection.sanitize_limit(limit_value)) if limit_value + arel.skip(offset_value.to_i) if offset_value - arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty? + arel.group(*group_values.uniq.reject{|g| g.blank?}) unless group_values.empty? - order = @order_values - order = reverse_sql_order(order) if @reverse_order_value + order = order_values + order = reverse_sql_order(order) if reverse_order_value arel.order(*order.uniq.reject{|o| o.blank?}) unless order.empty? - build_select(arel, @select_values.uniq) + build_select(arel, select_values.uniq) - arel.distinct(@uniq_value) - arel.from(@from_value) if @from_value - arel.lock(@lock_value) if @lock_value + arel.distinct(uniq_value) + arel.from(from_value) if from_value + arel.lock(lock_value) if lock_value arel end @@ -443,13 +518,6 @@ module ActiveRecord end end - def apply_modules(modules) - unless modules.empty? - @extensions += modules - modules.each {|extension| extend(extension) } - end - end - def reverse_sql_order(order_query) order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty? |