diff options
Diffstat (limited to 'activerecord/lib/active_record/relation/query_methods.rb')
-rw-r--r-- | activerecord/lib/active_record/relation/query_methods.rb | 113 |
1 files changed, 91 insertions, 22 deletions
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 94aa999715..670ba0987d 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -9,7 +9,7 @@ module ActiveRecord :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, :reorder_value + :from_value, :reorder_value, :reverse_order_value def includes(*args) args.reject! {|a| a.blank? } @@ -37,6 +37,35 @@ module ActiveRecord relation end + # Works in two unique ways. + # + # First: takes a block so it can be used just like Array#select. + # + # Model.scoped.select { |m| m.field == value } + # + # This will build an array of objects from the database for the scope, + # converting them into an array and iterating through them using Array#select. + # + # Second: Modifies the SELECT statement for the query so that only certain + # fields are retrieved: + # + # >> Model.select(:field) + # => [#<Model field:value>] + # + # Although in the above example it looks as though this method returns an + # array, it actually returns a relation object and can have other query + # methods appended to it, such as the other methods in ActiveRecord::QueryMethods. + # + # This method will also take multiple parameters: + # + # >> Model.select(:field, :other_field, :and_one_more) + # => [#<Model field: "value", other_field: "value", and_one_more: "value">] + # + # Any attributes that do not have fields retrieved by a select + # will return `nil` when the getter method for that attribute is used: + # + # >> Model.select(:field).first.other_field + # => nil def select(value = Proc.new) if block_given? to_a.select {|*block_args| value.call(*block_args) } @@ -96,11 +125,11 @@ module ActiveRecord relation end - def having(*args) - return self if args.blank? + def having(opts, *rest) + return self if opts.blank? relation = clone - relation.having_values += build_where(*args) + relation.having_values += build_where(opts, rest) relation end @@ -137,7 +166,7 @@ module ActiveRecord def create_with(value) relation = clone - relation.create_with_value = value && (@create_with_value || {}).merge(value) + relation.create_with_value = value ? create_with_value.merge(value) : {} relation end @@ -147,6 +176,42 @@ module ActiveRecord relation end + # Used to extend a scope with additional methods, either through + # a module or through a block provided. + # + # The object returned is a relation, which can be further extended. + # + # === Using a module + # + # module Pagination + # def page(number) + # # pagination code goes here + # end + # end + # + # scope = Model.scoped.extending(Pagination) + # scope.page(params[:page]) + # + # You can also pass a list of modules: + # + # scope = Model.scoped.extending(Pagination, SomethingElse) + # + # === Using a block + # + # scope = Model.scoped.extending do + # def page(number) + # # pagination code goes here + # end + # end + # scope.page(params[:page]) + # + # You can also use a block and a module list: + # + # scope = Model.scoped.extending(Pagination) do + # def per_page(number) + # # pagination code goes here + # end + # end def extending(*modules) modules << Module.new(&Proc.new) if block_given? @@ -158,13 +223,9 @@ module ActiveRecord end def reverse_order - order_clause = arel.order_clauses - - order = order_clause.empty? ? - "#{table_name}.#{primary_key} DESC" : - reverse_sql_order(order_clause).join(', ') - - except(:order).order(Arel.sql(order)) + relation = clone + relation.reverse_order_value = !relation.reverse_order_value + relation end def arel @@ -186,6 +247,7 @@ module ActiveRecord arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty? order = @reorder_value ? @reorder_value : @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) @@ -257,12 +319,12 @@ module ActiveRecord association_joins = buckets['association_join'] || [] stashed_association_joins = buckets['stashed_join'] || [] - join_nodes = buckets['join_node'] || [] + join_nodes = (buckets['join_node'] || []).uniq string_joins = (buckets['string_join'] || []).map { |x| x.strip }.uniq - join_list = custom_join_ast(manager, string_joins) + join_list = join_nodes + custom_join_ast(manager, string_joins) join_dependency = ActiveRecord::Associations::JoinDependency.new( @klass, @@ -270,10 +332,6 @@ module ActiveRecord join_list ) - join_nodes.each do |join| - join_dependency.alias_tracker.aliased_name_for(join.left.name.downcase) - end - join_dependency.graft(*stashed_association_joins) @implicit_readonly = true unless association_joins.empty? && stashed_association_joins.empty? @@ -283,7 +341,6 @@ module ActiveRecord association.join_to(manager) end - manager.join_sources.concat join_nodes.uniq manager.join_sources.concat join_list manager @@ -306,9 +363,21 @@ module ActiveRecord end def reverse_sql_order(order_query) - order_query.join(', ').split(',').collect do |s| - s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC') - end + order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty? + + order_query.map do |o| + case o + when Arel::Nodes::Ordering + o.reverse + when String, Symbol + o.to_s.split(',').collect do |s| + s.strip! + s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC') + end + else + o + end + end.flatten end def array_of_strings?(o) |