From 2fcafee250ee24224b8fb8c1d884a48770fe08b3 Mon Sep 17 00:00:00 2001 From: Ben Woosley Date: Fri, 10 May 2013 19:24:56 +0200 Subject: Fix that #pluck wasn't rescuing ThrowResult, meaning it would blow up when failing to construct_limited_ids_condition. --- activerecord/lib/active_record/relation/calculations.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 7239270c4d..308db8227f 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -189,6 +189,8 @@ module ActiveRecord end columns.one? ? result.map!(&:first) : result end + rescue ThrowResult + [] end # Pluck all the ID's for the relation using the table's primary key -- cgit v1.2.3 From 118147af53bebf71bf2a1d3ded4fc6491a708697 Mon Sep 17 00:00:00 2001 From: Ben Woosley Date: Fri, 10 May 2013 20:57:12 +0200 Subject: Rather than raising ThrowResult when construct_limited_ids_conditions comes up empty, set the relation to NullRelation and rely on its results. This will help avoid errors like 2fcafee250ee2, because in most cases NullRelation will do the right thing. Minor bonus is avoiding the use of exceptions for flow control. --- .../lib/active_record/relation/calculations.rb | 4 ---- .../lib/active_record/relation/finder_methods.rb | 28 ++++++++++++---------- 2 files changed, 15 insertions(+), 17 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 308db8227f..7371fe370e 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -114,8 +114,6 @@ module ActiveRecord else relation.calculate(operation, column_name, options) end - rescue ThrowResult - 0 end # Use pluck as a shortcut to select one or more attributes without @@ -189,8 +187,6 @@ module ActiveRecord end columns.one? ? result.map!(&:first) : result end - rescue ThrowResult - [] end # Pluck all the ID's for the relation using the table's primary key diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index ba222aac93..f0edef58bf 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -160,8 +160,9 @@ module ActiveRecord conditions = conditions.id if Base === conditions return false if !conditions - join_dependency = construct_join_dependency - relation = construct_relation_for_association_find(join_dependency) + relation = construct_relation_for_association_find(construct_join_dependency) + return false if ActiveRecord::NullRelation === relation + relation = relation.except(:select, :order).select("1 AS one").limit(1) case conditions @@ -172,8 +173,6 @@ module ActiveRecord end connection.select_value(relation, "#{name} Exists", relation.bind_values) - rescue ThrowResult - false end # This method is called whenever no records are found with either a single @@ -203,10 +202,12 @@ module ActiveRecord def find_with_associations join_dependency = construct_join_dependency relation = construct_relation_for_association_find(join_dependency) - rows = connection.select_all(relation, 'SQL', relation.bind_values.dup) - join_dependency.instantiate(rows) - rescue ThrowResult - [] + if ActiveRecord::NullRelation === relation + [] + else + rows = connection.select_all(relation, 'SQL', relation.bind_values.dup) + join_dependency.instantiate(rows) + end end def construct_join_dependency(joins = []) @@ -230,21 +231,22 @@ module ActiveRecord if using_limitable_reflections?(join_dependency.reflections) relation else - relation.where!(construct_limited_ids_condition(relation)) if relation.limit_value + if relation.limit_value + limited_ids = limited_ids_for(relation) + limited_ids.empty? ? relation.none! : relation.where!(table[primary_key].in(limited_ids)) + end relation.except(:limit, :offset) end end - def construct_limited_ids_condition(relation) + def limited_ids_for(relation) values = @klass.connection.columns_for_distinct( "#{quoted_table_name}.#{quoted_primary_key}", relation.order_values) relation = relation.except(:select).select(values).distinct! id_rows = @klass.connection.select_all(relation.arel, 'SQL', relation.bind_values) - ids_array = id_rows.map {|row| row[primary_key]} - - ids_array.empty? ? raise(ThrowResult) : table[primary_key].in(ids_array) + id_rows.map {|row| row[primary_key]} end def find_with_ids(*ids) -- cgit v1.2.3 From aff928bacfb26f164c3f0ccbee7bcfa8228941cb Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Mon, 27 May 2013 16:52:42 +0200 Subject: `implicit_readonly` is being removed in favor of calling `readonly` explicitly --- activerecord/lib/active_record/relation/query_methods.rb | 5 ----- 1 file changed, 5 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index d020f1ba52..fe8fec4dfa 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -864,8 +864,6 @@ module ActiveRecord return [] if joins.empty? - @implicit_readonly = true - joins.map do |join| case join when Array @@ -947,8 +945,6 @@ module ActiveRecord join_dependency.graft(*stashed_association_joins) - @implicit_readonly = true unless association_joins.empty? && stashed_association_joins.empty? - # FIXME: refactor this to build an AST join_dependency.join_associations.each do |association| association.join_to(manager) @@ -961,7 +957,6 @@ module ActiveRecord def build_select(arel, selects) unless selects.empty? - @implicit_readonly = false arel.project(*selects) else arel.project(@klass.arel_table[Arel.star]) -- cgit v1.2.3 From c3ec0dbdd4279cb9273194a8ed8f8d9dcdf54816 Mon Sep 17 00:00:00 2001 From: John Gesimondo Date: Sun, 26 May 2013 20:27:49 -0700 Subject: use grep over select for consistency and efficiency pass block directly to grep --- activerecord/lib/active_record/relation/query_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index d020f1ba52..0292d363d4 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -1015,7 +1015,7 @@ module ActiveRecord end def validate_order_args(args) - args.select { |a| Hash === a }.each do |h| + args.grep(Hash) do |h| unless (h.values - [:asc, :desc]).empty? raise ArgumentError, 'Direction should be :asc or :desc' end -- cgit v1.2.3 From 5d75579eec90e4fe0f3d33adbf5e7065a7cdcfa3 Mon Sep 17 00:00:00 2001 From: kennyj Date: Sat, 1 Jun 2013 21:36:50 +0900 Subject: Remove #sum with a block was deprecated. --- activerecord/lib/active_record/relation/calculations.rb | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 7239270c4d..e234e02032 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -56,15 +56,7 @@ module ActiveRecord # # Person.sum(:age) # => 4562 def sum(*args) - if block_given? - ActiveSupport::Deprecation.warn( - "Calling #sum with a block is deprecated and will be removed in Rails 4.1. " \ - "If you want to perform sum calculation over the array of elements, use `to_a.sum(&block)`." - ) - self.to_a.sum(*args) {|*block_args| yield(*block_args)} - else - calculate(:sum, *args) - end + calculate(:sum, *args) end # This calculates aggregate values in the given column. Methods for count, sum, average, -- cgit v1.2.3 From 348b9824ed2882f400a5e8f966fceba7caa689d2 Mon Sep 17 00:00:00 2001 From: Thiago Pinto Date: Fri, 7 Jun 2013 00:08:36 -0400 Subject: using Model.all.find_each in rails 3 raises an error and should not be recommended --- activerecord/lib/active_record/relation/batches.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb index b921f2eddb..2f21b65ee4 100644 --- a/activerecord/lib/active_record/relation/batches.rb +++ b/activerecord/lib/active_record/relation/batches.rb @@ -11,7 +11,7 @@ module ActiveRecord # The #find_each method uses #find_in_batches with a batch size of 1000 (or as # specified by the +:batch_size+ option). # - # Person.all.find_each do |person| + # Person.find_each do |person| # person.do_awesome_stuff # end # @@ -50,7 +50,7 @@ module ActiveRecord # end # # # Let's process the next 2000 records - # Person.all.find_in_batches(start: 2000, batch_size: 2000) do |group| + # Person.find_in_batches(start: 2000, batch_size: 2000) do |group| # group.each { |person| person.party_all_night! } # end def find_in_batches(options = {}) -- cgit v1.2.3 From 3f18fa008c352a2f3dbfcda6d5044225050b891c Mon Sep 17 00:00:00 2001 From: Thiago Pinto Date: Fri, 7 Jun 2013 00:37:13 -0400 Subject: lists the options for find_each and find_in_batches --- activerecord/lib/active_record/relation/batches.rb | 54 +++++++++++++++------- 1 file changed, 37 insertions(+), 17 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb index 2f21b65ee4..41291844fc 100644 --- a/activerecord/lib/active_record/relation/batches.rb +++ b/activerecord/lib/active_record/relation/batches.rb @@ -19,8 +19,26 @@ module ActiveRecord # person.party_all_night! # end # - # You can also pass the +:start+ option to specify - # an offset to control the starting point. + # ==== Options + # * :batch_size - Specifies the size of the batch. Default to 1000. + # * :start - Specifies the starting point for the batch processing. + # This is especially useful if you want multiple workers dealing with + # the same processing queue. You can make worker 1 handle all the records + # between id 0 and 10,000 and worker 2 handle from 10,000 and beyond + # (by setting the +:start+ option on that worker). + # + # # Let's process for a batch of 2000 records, skiping the first 2000 rows + # Person.find_each(start: 2000, batch_size: 2000) do |person| + # person.party_all_night! + # end + # + # NOTE: It's not possible to set the order. That is automatically set to + # ascending on the primary key ("id ASC") to make the batch ordering + # work. This also means that this method only works with integer-based + # primary keys. + # + # NOTE: You can't set the limit either, that's used to control + # the batch sizes. def find_each(options = {}) find_in_batches(options) do |records| records.each { |record| yield record } @@ -28,31 +46,33 @@ module ActiveRecord end # Yields each batch of records that was found by the find +options+ as - # an array. The size of each batch is set by the +:batch_size+ - # option; the default is 1000. - # - # You can control the starting point for the batch processing by - # supplying the +:start+ option. This is especially useful if you - # want multiple workers dealing with the same processing queue. You can - # make worker 1 handle all the records between id 0 and 10,000 and - # worker 2 handle from 10,000 and beyond (by setting the +:start+ - # option on that worker). - # - # It's not possible to set the order. That is automatically set to - # ascending on the primary key ("id ASC") to make the batch ordering - # work. This also means that this method only works with integer-based - # primary keys. You can't set the limit either, that's used to control - # the batch sizes. + # an array. # # Person.where("age > 21").find_in_batches do |group| # sleep(50) # Make sure it doesn't get too crowded in there! # group.each { |person| person.party_all_night! } # end # + # ==== Options + # * :batch_size - Specifies the size of the batch. Default to 1000. + # * :start - Specifies the starting point for the batch processing. + # This is especially useful if you want multiple workers dealing with + # the same processing queue. You can make worker 1 handle all the records + # between id 0 and 10,000 and worker 2 handle from 10,000 and beyond + # (by setting the +:start+ option on that worker). + # # # Let's process the next 2000 records # Person.find_in_batches(start: 2000, batch_size: 2000) do |group| # group.each { |person| person.party_all_night! } # end + # + # NOTE: It's not possible to set the order. That is automatically set to + # ascending on the primary key ("id ASC") to make the batch ordering + # work. This also means that this method only works with integer-based + # primary keys. + # + # NOTE: You can't set the limit either, that's used to control + # the batch sizes. def find_in_batches(options = {}) options.assert_valid_keys(:start, :batch_size) -- cgit v1.2.3 From b8640d47907876d43335628fea40da03df0e84cd Mon Sep 17 00:00:00 2001 From: Thiago Pinto Date: Sat, 8 Jun 2013 13:35:47 -0400 Subject: explaining ActiveRecord#first in rails 3 and 4 --- activerecord/lib/active_record/relation/finder_methods.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index ba222aac93..86976077d6 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -79,6 +79,19 @@ module ActiveRecord # Person.where(["user_name = :u", { u: user_name }]).first # Person.order("created_on DESC").offset(5).first # Person.first(3) # returns the first three objects fetched by SELECT * FROM people LIMIT 3 + # + # ==== Rails 3 + # + # Person.first # SELECT "users".* FROM "users" LIMIT 1 + # + # NOTE: Rails 3 may not +order+ this query by be the primary key. + # The order will depend on the database implementation. + # In order to ensure that behavior use User.order(:id).first instead. + # + # ==== Rails 4 + # + # Person.first # SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1 + # def first(limit = nil) if limit if order_values.empty? && primary_key -- cgit v1.2.3 From 667569ab04159b144d799f5fc97c3d862b2c30aa Mon Sep 17 00:00:00 2001 From: Thiago Pinto Date: Sat, 8 Jun 2013 15:01:15 -0400 Subject: instructions for variations and alternatives for ActiveRecord#find --- .../lib/active_record/relation/finder_methods.rb | 36 +++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 86976077d6..e716ce9675 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -11,7 +11,9 @@ module ActiveRecord # Person.find([1]) # returns an array for the object with ID = 1 # Person.where("administrator = 1").order("created_on DESC").find(1) # - # Note that returned records may not be in the same order as the ids you + # NOTE: An RecordNotFound will be raised if one or more ids are not returned. + # + # NOTE: that returned records may not be in the same order as the ids you # provide since database rows are unordered. Give an explicit order # to ensure the results are sorted. # @@ -28,6 +30,38 @@ module ActiveRecord # person.visits += 1 # person.save! # end + # + # ==== Variations of +find+ + # + # Person.where(name: 'Spartacus', rating: 4) + # # returns a chainable list (which can be empty) + # + # Person.find_by(name: 'Spartacus', rating: 4) + # # returns the first item or nil + # + # Person.where(name: 'Spartacus', rating: 4).first_or_initialize + # # returns the first item or returns a new instance (requires you call .save to persist against the database) + # + # Person.where(name: 'Spartacus', rating: 4).first_or_create + # # returns the first item or creates it and returns it, available since rails 3.2.1 + # + # + # ==== Alternatives for +find+ + # + # Person.where(name: 'Spartacus', rating: 4).exists?(conditions = :none) + # # returns true or false + # + # Person.where(name: 'Spartacus', rating: 4).select("field1, field2, field3") + # # returns a chainable list of instances with only the mentioned fields + # + # Person.where(name: 'Spartacus', rating: 4).ids + # # returns an Array of ids, available since rails 3.2.1 + # + # Person.where(name: 'Spartacus', rating: 4).pluck(:field1, :field2) + # # returns an Array of the required fields, available since rails 3.1 + # + # Person.arel_table + # # returns an instance of Arel::Table, which allows a comprehensive variety of filters def find(*args) if block_given? to_a.find { |*block_args| yield(*block_args) } -- cgit v1.2.3 From 763e5a866283c83453de0c728e1b973afd41f934 Mon Sep 17 00:00:00 2001 From: Thiago Pinto Date: Sat, 8 Jun 2013 15:02:30 -0400 Subject: doc: renaming table name to follow the file's standards --- activerecord/lib/active_record/relation/finder_methods.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index e716ce9675..f373714007 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -116,7 +116,7 @@ module ActiveRecord # # ==== Rails 3 # - # Person.first # SELECT "users".* FROM "users" LIMIT 1 + # Person.first # SELECT "people".* FROM "people" LIMIT 1 # # NOTE: Rails 3 may not +order+ this query by be the primary key. # The order will depend on the database implementation. @@ -124,7 +124,7 @@ module ActiveRecord # # ==== Rails 4 # - # Person.first # SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1 + # Person.first # SELECT "people".* FROM "people" ORDER BY "people"."id" ASC LIMIT 1 # def first(limit = nil) if limit -- cgit v1.2.3 From da9b5d4a8435b744fcf278fffd6d7f1e36d4a4f2 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Mon, 13 May 2013 18:31:00 +0100 Subject: Remove fall back and column restrictions for `count`. --- activerecord/lib/active_record/relation/calculations.rb | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index ccb48247b7..4becf3980d 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -207,15 +207,18 @@ module ActiveRecord end if operation == "count" - column_name ||= (select_for_count || :all) + if select_values.present? + column_name ||= select_values.join(", ") + else + column_name ||= :all + end unless arel.ast.grep(Arel::Nodes::OuterJoin).empty? distinct = true end column_name = primary_key if column_name == :all && distinct - - distinct = nil if column_name =~ /\s*DISTINCT\s+/i + distinct = nil if column_name =~ /\s*DISTINCT[\s(]+/i end if group_values.any? @@ -376,13 +379,6 @@ module ActiveRecord column ? column.type_cast(value) : value end - def select_for_count - if select_values.present? - select = select_values.join(", ") - select if select !~ /[,*]/ - end - end - def build_count_subquery(relation, column_name, distinct) column_alias = Arel.sql('count_column') subquery_alias = Arel.sql('subquery_for_count') -- cgit v1.2.3 From d5450a6659a33ef08a170afcaae084023bcd275b Mon Sep 17 00:00:00 2001 From: Dmitry Polushkin Date: Sun, 9 Jun 2013 19:36:39 +0200 Subject: Refactored ActiveRecord `first` method to get rid of duplication. --- .../lib/active_record/relation/finder_methods.rb | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 7ddaea1bb0..cf71df8fba 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -81,11 +81,7 @@ module ActiveRecord # Person.first(3) # returns the first three objects fetched by SELECT * FROM people LIMIT 3 def first(limit = nil) if limit - if order_values.empty? && primary_key - order(arel_table[primary_key].asc).limit(limit).to_a - else - limit(limit).to_a - end + find_first_records(order_values, limit) else find_first end @@ -307,12 +303,15 @@ module ActiveRecord if loaded? @records.first else - @first ||= - if with_default_scope.order_values.empty? && primary_key - order(arel_table[primary_key].asc).limit(1).to_a.first - else - limit(1).to_a.first - end + @first ||= find_first_records(with_default_scope.order_values, 1).first + end + end + + def find_first_records(order_values, limit) + if order_values.empty? && primary_key + order(arel_table[primary_key].asc).limit(limit).to_a + else + limit(limit).to_a end end -- cgit v1.2.3 From e720b50fb1ff841970b2f1198996144c35b48461 Mon Sep 17 00:00:00 2001 From: Dmitry Polushkin Date: Mon, 10 Jun 2013 14:38:32 +0200 Subject: rename method `find_first_records` to `find_first_with_limit` --- activerecord/lib/active_record/relation/finder_methods.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index cf71df8fba..f240d0aaa9 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -81,7 +81,7 @@ module ActiveRecord # Person.first(3) # returns the first three objects fetched by SELECT * FROM people LIMIT 3 def first(limit = nil) if limit - find_first_records(order_values, limit) + find_first_with_limit(order_values, limit) else find_first end @@ -303,11 +303,11 @@ module ActiveRecord if loaded? @records.first else - @first ||= find_first_records(with_default_scope.order_values, 1).first + @first ||= find_first_with_limit(with_default_scope.order_values, 1).first end end - def find_first_records(order_values, limit) + def find_first_with_limit(order_values, limit) if order_values.empty? && primary_key order(arel_table[primary_key].asc).limit(limit).to_a else -- cgit v1.2.3 From d7e23109136733995bfadbe461ae18680a7426b5 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 12 Jun 2013 11:47:02 -0700 Subject: we should apply the default scope before querying --- activerecord/lib/active_record/relation/finder_methods.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index f0edef58bf..cbb2803593 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -172,7 +172,8 @@ module ActiveRecord relation = relation.where(table[primary_key].eq(conditions)) if conditions != :none end - connection.select_value(relation, "#{name} Exists", relation.bind_values) + relation = relation.with_default_scope + connection.select_value(relation.arel, "#{name} Exists", relation.bind_values) end # This method is called whenever no records are found with either a single -- cgit v1.2.3 From 7663149f72b5dba15a8e850e4072a016341e541c Mon Sep 17 00:00:00 2001 From: Vijay Dev Date: Fri, 14 Jun 2013 00:58:11 +0530 Subject: copy edits [ci skip] --- .../lib/active_record/relation/finder_methods.rb | 34 ++++++++++------------ 1 file changed, 15 insertions(+), 19 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index f373714007..f1c4b5392f 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -11,11 +11,11 @@ module ActiveRecord # Person.find([1]) # returns an array for the object with ID = 1 # Person.where("administrator = 1").order("created_on DESC").find(1) # - # NOTE: An RecordNotFound will be raised if one or more ids are not returned. + # ActiveRecord::RecordNotFound will be raised if one or more ids are not found. # - # NOTE: that returned records may not be in the same order as the ids you - # provide since database rows are unordered. Give an explicit order - # to ensure the results are sorted. + # NOTE: The returned records may not be in the same order as the ids you + # provide since database rows are unordered. You'd need to provide an explicit order + # option if you want the results are sorted. # # ==== Find with lock # @@ -34,34 +34,30 @@ module ActiveRecord # ==== Variations of +find+ # # Person.where(name: 'Spartacus', rating: 4) - # # returns a chainable list (which can be empty) + # # returns a chainable list (which can be empty). # # Person.find_by(name: 'Spartacus', rating: 4) - # # returns the first item or nil + # # returns the first item or nil. # # Person.where(name: 'Spartacus', rating: 4).first_or_initialize - # # returns the first item or returns a new instance (requires you call .save to persist against the database) + # # returns the first item or returns a new instance (requires you call .save to persist against the database). # # Person.where(name: 'Spartacus', rating: 4).first_or_create - # # returns the first item or creates it and returns it, available since rails 3.2.1 + # # returns the first item or creates it and returns it, available since Rails 3.2.1. # - # # ==== Alternatives for +find+ # # Person.where(name: 'Spartacus', rating: 4).exists?(conditions = :none) - # # returns true or false + # # returns a boolean indicating if any record with the given conditions exist. # # Person.where(name: 'Spartacus', rating: 4).select("field1, field2, field3") - # # returns a chainable list of instances with only the mentioned fields + # # returns a chainable list of instances with only the mentioned fields. # # Person.where(name: 'Spartacus', rating: 4).ids - # # returns an Array of ids, available since rails 3.2.1 + # # returns an Array of ids, available since Rails 3.2.1. # # Person.where(name: 'Spartacus', rating: 4).pluck(:field1, :field2) - # # returns an Array of the required fields, available since rails 3.1 - # - # Person.arel_table - # # returns an instance of Arel::Table, which allows a comprehensive variety of filters + # # returns an Array of the required fields, available since Rails 3.1. def find(*args) if block_given? to_a.find { |*block_args| yield(*block_args) } @@ -118,9 +114,9 @@ module ActiveRecord # # Person.first # SELECT "people".* FROM "people" LIMIT 1 # - # NOTE: Rails 3 may not +order+ this query by be the primary key. - # The order will depend on the database implementation. - # In order to ensure that behavior use User.order(:id).first instead. + # NOTE: Rails 3 may not order this query by the primary key and the order + # will depend on the database implementation. In order to ensure that behavior, + # use User.order(:id).first instead. # # ==== Rails 4 # -- cgit v1.2.3 From 25042359b388a73bae798e023276df59e53c74e2 Mon Sep 17 00:00:00 2001 From: Ben Woosley Date: Tue, 18 Jun 2013 02:21:52 -0700 Subject: When .find_each is called without a block, return an Enumerator. This lets us do things like call: .find_each.with_index --- activerecord/lib/active_record/relation/batches.rb | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb index 41291844fc..91ea1756c4 100644 --- a/activerecord/lib/active_record/relation/batches.rb +++ b/activerecord/lib/active_record/relation/batches.rb @@ -19,6 +19,13 @@ module ActiveRecord # person.party_all_night! # end # + # If you do not provide a block to #find_each, it will return an Enumerator + # for chaining with other methods: + # + # Person.find_each.with_index do |person, index| + # person.award_trophy(index + 1) + # end + # # ==== Options # * :batch_size - Specifies the size of the batch. Default to 1000. # * :start - Specifies the starting point for the batch processing. @@ -40,8 +47,12 @@ module ActiveRecord # NOTE: You can't set the limit either, that's used to control # the batch sizes. def find_each(options = {}) - find_in_batches(options) do |records| - records.each { |record| yield record } + if block_given? + find_in_batches(options) do |records| + records.each { |record| yield record } + end + else + enum_for :find_each, options end end -- cgit v1.2.3 From 32420bd4bcf20dc21e925474cbb0ac2b46722b80 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Wed, 29 May 2013 23:36:19 -0400 Subject: flatten merged join_values before building the joins fixes #10669 While joining_values special treatment is given to string values. By flattening the array it ensures that string values are detected as strings and not arrays. --- activerecord/lib/active_record/relation/query_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index ca1de2d4dc..0200fcf69b 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -802,7 +802,7 @@ module ActiveRecord def build_arel arel = Arel::SelectManager.new(table.engine, table) - build_joins(arel, joins_values) unless joins_values.empty? + build_joins(arel, joins_values.flatten) unless joins_values.empty? collapse_wheres(arel, (where_values - ['']).uniq) -- cgit v1.2.3 From 94924dc32baf78f13e289172534c2e71c9c8cade Mon Sep 17 00:00:00 2001 From: Jon Leighton Date: Fri, 28 Jun 2013 13:32:54 +0100 Subject: Simplify/fix implementation of default scopes The previous implementation was necessary in order to support stuff like: class Post < ActiveRecord::Base default_scope where(published: true) scope :ordered, order("created_at") end If we didn't evaluate the default scope at the last possible moment before sending the SQL to the database, it would become impossible to do: Post.unscoped.ordered This is because the default scope would already be bound up in the "ordered" scope, and therefore wouldn't be removed by the "Post.unscoped" part. In 4.0, we have deprecated all "eager" forms of scopes. So now you must write: class Post < ActiveRecord::Base default_scope { where(published: true) } scope :ordered, -> { order("created_at") } end This prevents the default scope getting bound up inside the "ordered" scope, which means we can now have a simpler/better/more natural implementation of default scoping. A knock on effect is that some things that didn't work properly now do. For example it was previously impossible to use #except to remove a part of the default scope, since the default scope was evaluated after the call to #except. --- activerecord/lib/active_record/relation/calculations.rb | 12 +++--------- activerecord/lib/active_record/relation/finder_methods.rb | 3 +-- activerecord/lib/active_record/relation/merger.rb | 4 ---- activerecord/lib/active_record/relation/query_methods.rb | 2 +- activerecord/lib/active_record/relation/spawn_methods.rb | 1 - 5 files changed, 5 insertions(+), 17 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 4becf3980d..f2d28db923 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -91,20 +91,14 @@ module ActiveRecord # # Person.sum("2 * age") def calculate(operation, column_name, options = {}) - relation = with_default_scope - if column_name.is_a?(Symbol) && attribute_aliases.key?(column_name.to_s) column_name = attribute_aliases[column_name.to_s].to_sym end - if relation.equal?(self) - if has_include?(column_name) - construct_relation_for_association_calculations.calculate(operation, column_name, options) - else - perform_calculation(operation, column_name, options) - end + if has_include?(column_name) + construct_relation_for_association_calculations.calculate(operation, column_name, options) else - relation.calculate(operation, column_name, options) + perform_calculation(operation, column_name, options) end end diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 3ea3c33fcc..4ea13e2585 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -211,7 +211,6 @@ module ActiveRecord relation = relation.where(table[primary_key].eq(conditions)) if conditions != :none end - relation = relation.with_default_scope connection.select_value(relation.arel, "#{name} Exists", relation.bind_values) end @@ -354,7 +353,7 @@ module ActiveRecord if loaded? @records.first else - @first ||= find_first_with_limit(with_default_scope.order_values, 1).first + @first ||= find_first_with_limit(order_values, 1).first end end diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb index c114ea0c0d..6d047e0331 100644 --- a/activerecord/lib/active_record/relation/merger.rb +++ b/activerecord/lib/active_record/relation/merger.rb @@ -42,10 +42,6 @@ module ActiveRecord attr_reader :relation, :values, :other def initialize(relation, other) - if other.default_scoped? && other.klass != relation.klass - other = other.with_default_scope - end - @relation = relation @values = other.values @other = other diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 0200fcf69b..05e92bea33 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -795,7 +795,7 @@ module ActiveRecord # Returns the Arel object associated with the relation. def arel - @arel ||= with_default_scope.build_arel + @arel ||= build_arel end # Like #arel, but ignores the default scope of the model. diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index de784f9f57..c63ae9c9fb 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -65,7 +65,6 @@ module ActiveRecord def relation_with(values) # :nodoc: result = Relation.new(klass, table, values) - result.default_scoped = default_scoped result.extend(*extending_values) if extending_values.any? result end -- cgit v1.2.3 From 1cf6871a9e3124ff6b74a315c4bef9dc427da5f9 Mon Sep 17 00:00:00 2001 From: Dmitry Polushkin Date: Fri, 28 Jun 2013 12:07:34 +0100 Subject: find_in_batches should work without logger --- activerecord/lib/active_record/relation/batches.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb index 41291844fc..341769b5c4 100644 --- a/activerecord/lib/active_record/relation/batches.rb +++ b/activerecord/lib/active_record/relation/batches.rb @@ -78,8 +78,8 @@ module ActiveRecord relation = self - unless arel.orders.blank? && arel.taken.blank? - ActiveRecord::Base.logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size") + if logger && (arel.orders.present? || arel.taken.present?) + logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size") end start = options.delete(:start) -- cgit v1.2.3 From 5e6de3942ffbc667d1f43860a0c80dd8031a0c60 Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Fri, 28 Jun 2013 09:51:13 -0300 Subject: Remove order_values argument now that default_scope is simplified In 94924dc32baf78f13e289172534c2e71c9c8cade the internal default_scope implementation has changed making it simpler to follow, meaning that the old usage of with_default_scope has been removed. With that, order_values was the same argument for both calls to find_first_with_limit, so remove it and use the existent attribute for the sake of clarity/simplification. --- activerecord/lib/active_record/relation/finder_methods.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 4ea13e2585..bad5886cde 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -32,7 +32,7 @@ module ActiveRecord # end # # ==== Variations of +find+ - # + # # Person.where(name: 'Spartacus', rating: 4) # # returns a chainable list (which can be empty). # @@ -49,7 +49,7 @@ module ActiveRecord # # Person.where(name: 'Spartacus', rating: 4).exists?(conditions = :none) # # returns a boolean indicating if any record with the given conditions exist. - # + # # Person.where(name: 'Spartacus', rating: 4).select("field1, field2, field3") # # returns a chainable list of instances with only the mentioned fields. # @@ -124,7 +124,7 @@ module ActiveRecord # def first(limit = nil) if limit - find_first_with_limit(order_values, limit) + find_first_with_limit(limit) else find_first end @@ -353,11 +353,11 @@ module ActiveRecord if loaded? @records.first else - @first ||= find_first_with_limit(order_values, 1).first + @first ||= find_first_with_limit(1).first end end - def find_first_with_limit(order_values, limit) + def find_first_with_limit(limit) if order_values.empty? && primary_key order(arel_table[primary_key].asc).limit(limit).to_a else -- cgit v1.2.3 From 2181832fca6171631f6c7ff700e14676c6d331e8 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Mon, 1 Jul 2013 22:11:20 +0200 Subject: Remove deprecated `:distinct` option from `Relation#count`. --- activerecord/lib/active_record/relation/calculations.rb | 5 ----- 1 file changed, 5 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index f2d28db923..68968e42a5 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -194,11 +194,6 @@ module ActiveRecord # If #count is used with #distinct / #uniq it is considered distinct. (eg. relation.distinct.count) distinct = self.distinct_value - if options.has_key?(:distinct) - ActiveSupport::Deprecation.warn "The :distinct option for `Relation#count` is deprecated. " \ - "Please use `Relation#distinct` instead. (eg. `relation.distinct.count`)" - distinct = options[:distinct] - end if operation == "count" if select_values.present? -- cgit v1.2.3 From d094aaad19ab0d79d0e74747ad1a7141f914b431 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 1 Jul 2013 16:23:08 -0700 Subject: the data structure used to store attribute aliases should not be exposed --- activerecord/lib/active_record/relation/predicate_builder.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index b7609c97b5..57b7e8d13d 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -6,8 +6,8 @@ module ActiveRecord attributes.each do |column, value| table = default_table - if column.is_a?(Symbol) && klass.attribute_aliases.key?(column.to_s) - column = klass.attribute_aliases[column.to_s] + if column.is_a?(Symbol) && klass.attribute_alias?(column) + column = klass.attribute_alias(column.to_s) end if value.is_a?(Hash) -- cgit v1.2.3 From b9d98597cc3506bb1485f04ffd416e51c0e188bc Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 1 Jul 2013 16:55:10 -0700 Subject: we don't need to to_s the column --- activerecord/lib/active_record/relation/predicate_builder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 57b7e8d13d..59a3130939 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -7,7 +7,7 @@ module ActiveRecord table = default_table if column.is_a?(Symbol) && klass.attribute_alias?(column) - column = klass.attribute_alias(column.to_s) + column = klass.attribute_alias(column) end if value.is_a?(Hash) -- cgit v1.2.3 From 5cba5530ac5df837c55e93c85a3548667f1a9f14 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 1 Jul 2013 17:33:47 -0700 Subject: stop exposing the underlying alias datastructure --- activerecord/lib/active_record/relation/calculations.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 68968e42a5..48a96fec64 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -91,8 +91,8 @@ module ActiveRecord # # Person.sum("2 * age") def calculate(operation, column_name, options = {}) - if column_name.is_a?(Symbol) && attribute_aliases.key?(column_name.to_s) - column_name = attribute_aliases[column_name.to_s].to_sym + if column_name.is_a?(Symbol) && attribute_alias?(column_name) + column_name = attribute_alias(column_name).to_sym end if has_include?(column_name) @@ -138,8 +138,8 @@ module ActiveRecord def pluck(*column_names) column_names.map! do |column_name| if column_name.is_a?(Symbol) - if attribute_aliases.key?(column_name.to_s) - column_name = attribute_aliases[column_name.to_s].to_sym + if attribute_alias?(column_name) + column_name = attribute_alias(column_name).to_sym end if self.columns_hash.key?(column_name.to_s) -- cgit v1.2.3 From 6518f12b73546ee566ecef76f8d13da6f16272a0 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 1 Jul 2013 17:54:30 -0700 Subject: build an AST rather than slapping strings together --- .../lib/active_record/relation/calculations.rb | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 48a96fec64..7d745c757b 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -137,24 +137,20 @@ module ActiveRecord # def pluck(*column_names) column_names.map! do |column_name| - if column_name.is_a?(Symbol) - if attribute_alias?(column_name) - column_name = attribute_alias(column_name).to_sym - end - - if self.columns_hash.key?(column_name.to_s) - column_name = "#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(column_name)}" - end + if column_name.is_a?(Symbol) && attribute_alias?(column_name) + attribute_alias(column_name).to_sym + else + column_name end - - column_name end if has_include?(column_names.first) construct_relation_for_association_calculations.pluck(*column_names) else relation = spawn - relation.select_values = column_names + relation.select_values = column_names.map { |cn| + columns_hash.key?(cn.to_s) ? arel_table[cn] : cn + } result = klass.connection.select_all(relation.arel, nil, bind_values) columns = result.columns.map do |key| klass.column_types.fetch(key) { -- cgit v1.2.3 From b7aba569c871e09fa8227175626ff09e7e6638de Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 1 Jul 2013 17:56:27 -0700 Subject: only deal with strings internally --- activerecord/lib/active_record/relation/calculations.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 7d745c757b..07410884a7 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -138,9 +138,9 @@ module ActiveRecord def pluck(*column_names) column_names.map! do |column_name| if column_name.is_a?(Symbol) && attribute_alias?(column_name) - attribute_alias(column_name).to_sym + attribute_alias(column_name) else - column_name + column_name.to_s end end @@ -149,7 +149,7 @@ module ActiveRecord else relation = spawn relation.select_values = column_names.map { |cn| - columns_hash.key?(cn.to_s) ? arel_table[cn] : cn + columns_hash.key?(cn) ? arel_table[cn] : cn } result = klass.connection.select_all(relation.arel, nil, bind_values) columns = result.columns.map do |key| -- cgit v1.2.3 From 6cd8e3f58ed2d28d07e341793b9e9c00421540a5 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 1 Jul 2013 18:00:52 -0700 Subject: make the identity type a singleton to save on object creation --- activerecord/lib/active_record/relation/calculations.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 07410884a7..fe1480d8c2 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -154,9 +154,7 @@ module ActiveRecord result = klass.connection.select_all(relation.arel, nil, bind_values) columns = result.columns.map do |key| klass.column_types.fetch(key) { - result.column_types.fetch(key) { - Class.new { def type_cast(v); v; end }.new - } + result.column_types.fetch(key) { result.identity_type } } end -- cgit v1.2.3 From 1f75319a9af595d5de3dca55e26547c7f1b166fa Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 1 Jul 2013 18:06:25 -0700 Subject: avoid intermediate zipped array --- activerecord/lib/active_record/relation/calculations.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index fe1480d8c2..477da618df 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -161,9 +161,8 @@ module ActiveRecord result = result.map do |attributes| values = klass.initialize_attributes(attributes).values - columns.zip(values).map do |column, value| - column.type_cast(value) - end + iter = columns.each + values.map { |value| iter.next.type_cast value } end columns.one? ? result.map!(&:first) : result end -- cgit v1.2.3 From 8b9f16fcb350006a8e903bd52be04f30ae8b8d89 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 2 Jul 2013 11:12:17 -0700 Subject: resolve aliases before passing the hash to the predicate builder --- .../lib/active_record/relation/predicate_builder.rb | 14 ++++++++++---- activerecord/lib/active_record/relation/query_methods.rb | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 59a3130939..08ef899f68 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -1,15 +1,21 @@ module ActiveRecord class PredicateBuilder # :nodoc: + def self.resolve_column_aliases(klass, hash) + hash = hash.dup + hash.keys.grep(Symbol) do |key| + if klass.attribute_alias? key + hash[klass.attribute_alias(key)] = hash.delete key + end + end + hash + end + def self.build_from_hash(klass, attributes, default_table) queries = [] attributes.each do |column, value| table = default_table - if column.is_a?(Symbol) && klass.attribute_alias?(column) - column = klass.attribute_alias(column) - end - if value.is_a?(Hash) if value.empty? queries << '1=0' diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 05e92bea33..1327cc3c34 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -891,6 +891,7 @@ module ActiveRecord when String, Array [@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))] when Hash + opts = PredicateBuilder.resolve_column_aliases klass, opts attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts) attributes.values.grep(ActiveRecord::Relation) do |rel| -- cgit v1.2.3 From f98d47b1abff6263c3bf0514c64742e15f9a02b3 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 2 Jul 2013 11:17:20 -0700 Subject: no need to to_sym the column name, leave it as-is --- activerecord/lib/active_record/relation/calculations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 477da618df..3b24ed6754 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -92,7 +92,7 @@ module ActiveRecord # Person.sum("2 * age") def calculate(operation, column_name, options = {}) if column_name.is_a?(Symbol) && attribute_alias?(column_name) - column_name = attribute_alias(column_name).to_sym + column_name = attribute_alias(column_name) end if has_include?(column_name) -- cgit v1.2.3 From e7e28a71be68271c7892477c1b9336316a1871bc Mon Sep 17 00:00:00 2001 From: Vipul A M Date: Sat, 6 Jul 2013 19:51:15 +0530 Subject: Extract common query to a constant. Perf ref: https://gist.github.com/vipulnsward/8209749201dfdd678c97 --- activerecord/lib/active_record/relation/finder_methods.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index bad5886cde..9186b33bd2 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -1,5 +1,7 @@ module ActiveRecord module FinderMethods + ONE_AS_ONE = '1 AS one' + # Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]). # If no record can be found for all of the listed ids, then RecordNotFound will be raised. If the primary key # is an integer, find by id coerces its arguments using +to_i+. @@ -202,7 +204,7 @@ module ActiveRecord relation = construct_relation_for_association_find(construct_join_dependency) return false if ActiveRecord::NullRelation === relation - relation = relation.except(:select, :order).select("1 AS one").limit(1) + relation = relation.except(:select, :order).select(ONE_AS_ONE).limit(1) case conditions when Array, Hash -- cgit v1.2.3 From 8004c91eb67b5d690a4eb8ed4ee475a6d2dc0d6c Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 9 Jul 2013 17:18:52 -0700 Subject: pass arel to select_all rather than depend on method_missing --- activerecord/lib/active_record/relation/finder_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 9186b33bd2..5ce889ce50 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -246,7 +246,7 @@ module ActiveRecord if ActiveRecord::NullRelation === relation [] else - rows = connection.select_all(relation, 'SQL', relation.bind_values.dup) + rows = connection.select_all(relation.arel, 'SQL', relation.bind_values.dup) join_dependency.instantiate(rows) end end -- cgit v1.2.3 From a4c0281dada222d350dc337c3b283e4254b07f1b Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 9 Jul 2013 17:24:10 -0700 Subject: fix visibility of the relation construction methods --- activerecord/lib/active_record/relation/finder_methods.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 5ce889ce50..2d3bd563ac 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -238,7 +238,7 @@ module ActiveRecord raise RecordNotFound, error end - protected + private def find_with_associations join_dependency = construct_join_dependency @@ -290,6 +290,12 @@ module ActiveRecord id_rows.map {|row| row[primary_key]} end + def using_limitable_reflections?(reflections) + reflections.none? { |r| r.collection? } + end + + protected + def find_with_ids(*ids) expects_array = ids.first.kind_of?(Array) return ids.first if expects_array && ids.first.empty? @@ -379,9 +385,5 @@ module ActiveRecord end end end - - def using_limitable_reflections?(reflections) - reflections.none? { |r| r.collection? } - end end end -- cgit v1.2.3 From 9f85c41eec2ffcb2477bd4d66854d168634c5eaf Mon Sep 17 00:00:00 2001 From: Ankit Gupta Date: Fri, 12 Jul 2013 21:29:31 +0100 Subject: Typo fix [skip ci] --- activerecord/lib/active_record/relation/merger.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb index 6d047e0331..f5d668dae8 100644 --- a/activerecord/lib/active_record/relation/merger.rb +++ b/activerecord/lib/active_record/relation/merger.rb @@ -109,7 +109,7 @@ module ActiveRecord # override any order specified in the original relation relation.reorder! values[:order] elsif values[:order] - # merge in order_values from r + # merge in order_values from relation relation.order! values[:order] end -- cgit v1.2.3 From e2d82508148fc4e3d507f70ed236414a75c58672 Mon Sep 17 00:00:00 2001 From: Ernie Miller Date: Sun, 14 Jul 2013 13:53:38 -0400 Subject: Blacklist->whitelist for reference scans in order! Stop special-casing Arel::Nodes as exempt from reference scanning in order. Instead, only scan order values that are strings for a table reference. --- activerecord/lib/active_record/relation/query_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 1327cc3c34..a27b6abe2e 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -280,7 +280,7 @@ module ActiveRecord args.flatten! validate_order_args args - references = args.reject { |arg| Arel::Node === arg } + references = args.grep(String) references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact! references!(references) if references.any? -- cgit v1.2.3 From 648afea0e4addcaaf6da31cba78b94d7681706a4 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Sun, 14 Jul 2013 18:27:00 +0200 Subject: re-introduce `select_for_count` private method. See https://github.com/rails/rails/commit/da9b5d4a8435b744fcf278fffd6d7f1e36d4a4f2#commitcomment-3630064 for discussion. --- activerecord/lib/active_record/relation/calculations.rb | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 3b24ed6754..52a538e5b5 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -189,11 +189,7 @@ module ActiveRecord distinct = self.distinct_value if operation == "count" - if select_values.present? - column_name ||= select_values.join(", ") - else - column_name ||= :all - end + column_name ||= select_for_count unless arel.ast.grep(Arel::Nodes::OuterJoin).empty? distinct = true @@ -361,6 +357,15 @@ module ActiveRecord column ? column.type_cast(value) : value end + # TODO: refactor to allow non-string `select_values` (eg. Arel nodes). + def select_for_count + if select_values.present? + select_values.join(", ") + else + :all + end + end + def build_count_subquery(relation, column_name, distinct) column_alias = Arel.sql('count_column') subquery_alias = Arel.sql('subquery_for_count') -- cgit v1.2.3 From d345ed40b5783ec2cb43f4434872ea5b2d57d203 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 15 Jul 2013 16:16:18 -0700 Subject: use arel rather than slapping together SQL strings --- activerecord/lib/active_record/relation/query_methods.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index a27b6abe2e..f146c1fdb2 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -285,8 +285,10 @@ module ActiveRecord references!(references) if references.any? # if a symbol is given we prepend the quoted table name - args = args.map { |arg| - arg.is_a?(Symbol) ? "#{quoted_table_name}.#{arg} ASC" : arg + args = args.map! { |arg| + arg.is_a?(Symbol) ? + Arel::Nodes::Ascending.new(klass.arel_table[arg]) : + arg } self.order_values = args + self.order_values -- cgit v1.2.3 From 9675c7da28758432ec7bd6e7ca1814801b2591f5 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 15 Jul 2013 16:29:01 -0700 Subject: reorder bind parameters when merging relations --- activerecord/lib/active_record/relation/merger.rb | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb index f5d668dae8..da13152e01 100644 --- a/activerecord/lib/active_record/relation/merger.rb +++ b/activerecord/lib/active_record/relation/merger.rb @@ -97,13 +97,29 @@ module ActiveRecord def merge_multi_values lhs_wheres = relation.where_values rhs_wheres = values[:where] || [] + lhs_binds = relation.bind_values rhs_binds = values[:bind] || [] removed, kept = partition_overwrites(lhs_wheres, rhs_wheres) - relation.where_values = kept + rhs_wheres - relation.bind_values = filter_binds(lhs_binds, removed) + rhs_binds + where_values = kept + rhs_wheres + bind_values = filter_binds(lhs_binds, removed) + rhs_binds + + conn = relation.klass.connection + bviter = bind_values.each.with_index + where_values.map! do |node| + if Arel::Nodes::Equality === node && Arel::Nodes::BindParam === node.right + (column, _), i = bviter.next + substitute = conn.substitute_at column, i + Arel::Nodes::Equality.new(node.left, substitute) + else + node + end + end + + relation.where_values = where_values + relation.bind_values = bind_values if values[:reordering] # override any order specified in the original relation -- cgit v1.2.3 From d9a0587d3ea707aa6ab3a61b54b417aff2101933 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 15 Jul 2013 16:29:21 -0700 Subject: removing useless assingment --- activerecord/lib/active_record/relation/query_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index f146c1fdb2..b5ca117f3d 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -285,7 +285,7 @@ module ActiveRecord references!(references) if references.any? # if a symbol is given we prepend the quoted table name - args = args.map! { |arg| + args.map! { |arg| arg.is_a?(Symbol) ? Arel::Nodes::Ascending.new(klass.arel_table[arg]) : arg -- cgit v1.2.3 From a929b4d4c59551189b8162f30b061c6ed5755bd5 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 15 Jul 2013 16:29:45 -0700 Subject: save another array allocation --- activerecord/lib/active_record/relation/query_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index b5ca117f3d..612c7d17a3 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -291,7 +291,7 @@ module ActiveRecord arg } - self.order_values = args + self.order_values + self.order_values = args.concat self.order_values self end -- cgit v1.2.3 From 85fe5edcec77f96923ed4f4e8b63a247ebe6b055 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 16 Jul 2013 13:41:24 -0700 Subject: decouple the manager class from building join constraints --- activerecord/lib/active_record/relation/query_methods.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 612c7d17a3..fb585ab8ab 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -948,10 +948,11 @@ module ActiveRecord join_dependency.graft(*stashed_association_joins) - # FIXME: refactor this to build an AST - join_dependency.join_associations.each do |association| - association.join_to(manager) - end + joins = join_dependency.join_associations.map { |association| + association.join_constraints + }.flatten + + joins.each { |join| manager.from join } manager.join_sources.concat join_list -- cgit v1.2.3 From ef350c9f2e35fc7e16c74158686c9ad2805cb465 Mon Sep 17 00:00:00 2001 From: Henrik Hodne Date: Wed, 17 Jul 2013 11:10:35 -0700 Subject: Improve ActiveRecord::QueryMethods#includes docs It's not immediately clear whether you can pass in multiple relations or not. After going through the code a bit, I saw that the arguments are just appended to an array. Also, added nested relations example. [ci skip] --- activerecord/lib/active_record/relation/query_methods.rb | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index fb585ab8ab..d0c56ac3d0 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -100,6 +100,14 @@ module ActiveRecord # firing an additional query. This will often result in a # performance improvement over a simple +join+. # + # You can also specify multiple relationships, like this: + # + # users = User.includes(:address, :friends) + # + # Loading nested relationships is possible using a Hash: + # + # users = User.includes(:address, friends: [:address, :followers]) + # # === conditions # # If you want to add conditions to your included models you'll have -- cgit v1.2.3 From f38b5444428f418c4e6377bbb40d7518ea0c61a7 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Jul 2013 14:43:17 -0700 Subject: add a specific factory method rather than using new --- activerecord/lib/active_record/relation/delegation.rb | 6 ++---- activerecord/lib/active_record/relation/merger.rb | 2 +- activerecord/lib/active_record/relation/spawn_methods.rb | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index 8d6740246c..7ed65a548c 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -73,10 +73,8 @@ module ActiveRecord module ClassMethods # :nodoc: @@subclasses = ThreadSafe::Cache.new(:initial_capacity => 2) - def new(klass, *args) - relation = relation_class_for(klass).allocate - relation.__send__(:initialize, klass, *args) - relation + def create(klass, *args) + relation_class_for(klass).new(klass, *args) end # This doesn't have to be thread-safe. relation_class_for guarantees that this will only be diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb index da13152e01..c08158d38b 100644 --- a/activerecord/lib/active_record/relation/merger.rb +++ b/activerecord/lib/active_record/relation/merger.rb @@ -22,7 +22,7 @@ module ActiveRecord # build a relation to merge in rather than directly merging # the values. def other - other = Relation.new(relation.klass, relation.table) + other = Relation.create(relation.klass, relation.table) hash.each { |k, v| if k == :joins if Hash === v diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index c63ae9c9fb..2552cbd234 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -64,7 +64,7 @@ module ActiveRecord private def relation_with(values) # :nodoc: - result = Relation.new(klass, table, values) + result = Relation.create(klass, table, values) result.extend(*extending_values) if extending_values.any? result end -- cgit v1.2.3 From 844efb2bb0d6e40a2d830727f6bc235b37c3a1b1 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 23 Jul 2013 15:02:43 -0700 Subject: stop relying on side effects of const_missing --- activerecord/lib/active_record/relation/delegation.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index 7ed65a548c..b6f80ac5c7 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -77,12 +77,6 @@ module ActiveRecord relation_class_for(klass).new(klass, *args) end - # This doesn't have to be thread-safe. relation_class_for guarantees that this will only be - # called exactly once for a given const name. - def const_missing(name) - const_set(name, Class.new(self) { include ClassSpecificRelation }) - end - private # Cache the constants in @@subclasses because looking them up via const_get # make instantiation significantly slower. @@ -92,7 +86,13 @@ module ActiveRecord # This hash is keyed by klass.name to avoid memory leaks in development mode my_cache.compute_if_absent(klass_name) do # Cache#compute_if_absent guarantees that the block will only executed once for the given klass_name - const_get("#{name.gsub('::', '_')}_#{klass_name.gsub('::', '_')}", false) + subclass_name = "#{name.gsub('::', '_')}_#{klass_name.gsub('::', '_')}" + + if const_defined?(subclass_name) + const_get(subclass_name) + else + const_set(subclass_name, Class.new(self) { include ClassSpecificRelation }) + end end else ActiveRecord::Relation -- cgit v1.2.3 From 92a603387c084f13a36bbf3844d89029bb73a753 Mon Sep 17 00:00:00 2001 From: sgrif Date: Fri, 17 May 2013 18:23:57 -0600 Subject: Add ability to specify how a class is converted to Arel predicate This adds the ability for rails apps or gems to have granular control over how a domain object is converted to sql. One simple use case would be to add support for Regexp. Another simple case would be something like the following: class DateRange < Struct.new(:start, :end) def include?(date) (start..end).cover?(date) end end class DateRangePredicate def call(attribute, range) attribute.in(range.start..range.end) end end ActiveRecord::PredicateBuilder.register_handler(DateRange, DateRangePredicate.new) More complex cases might include taking a currency object and converting it from EUR to USD before performing the query. By moving the existing handlers to this format, we were also able to nicely refactor a rather nasty method in PredicateBuilder. --- .../active_record/relation/predicate_builder.rb | 67 +++++++++++----------- .../relation/predicate_builder/array_handler.rb | 29 ++++++++++ .../relation/predicate_builder/relation_handler.rb | 13 +++++ 3 files changed, 74 insertions(+), 35 deletions(-) create mode 100644 activerecord/lib/active_record/relation/predicate_builder/array_handler.rb create mode 100644 activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 08ef899f68..8948f2bba5 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -1,5 +1,10 @@ module ActiveRecord class PredicateBuilder # :nodoc: + @handlers = [] + + autoload :RelationHandler, 'active_record/relation/predicate_builder/relation_handler' + autoload :ArrayHandler, 'active_record/relation/predicate_builder/array_handler' + def self.resolve_column_aliases(klass, hash) hash = hash.dup hash.keys.grep(Symbol) do |key| @@ -73,44 +78,36 @@ module ActiveRecord end.compact end + # Define how a class is converted to Arel nodes when passed to +where+. + # The handler can be any object that responds to +call+, and will be used + # for any value that +===+ the class given. For example: + # + # MyCustomDateRange = Struct.new(:start, :end) + # handler = proc do |column, range| + # Arel::Nodes::Between.new(column, + # Arel::Nodes::And.new([range.start, range.end]) + # ) + # end + # ActiveRecord::PredicateBuilder.register_handler(MyCustomDateRange, handler) + def self.register_handler(klass, handler) + @handlers.unshift([klass, handler]) + end + + register_handler(BasicObject, ->(attribute, value) { attribute.eq(value) }) + # FIXME: I think we need to deprecate this behavior + register_handler(Class, ->(attribute, value) { attribute.eq(value.name) }) + register_handler(Base, ->(attribute, value) { attribute.eq(value.id) }) + register_handler(Range, ->(attribute, value) { attribute.in(value) }) + register_handler(Relation, RelationHandler.new) + register_handler(Array, ArrayHandler.new) + private def self.build(attribute, value) - case value - when Array - values = value.to_a.map {|x| x.is_a?(Base) ? x.id : x} - ranges, values = values.partition {|v| v.is_a?(Range)} - - values_predicate = if values.include?(nil) - values = values.compact - - case values.length - when 0 - attribute.eq(nil) - when 1 - attribute.eq(values.first).or(attribute.eq(nil)) - else - attribute.in(values).or(attribute.eq(nil)) - end - else - attribute.in(values) - end + handler_for(value).call(attribute, value) + end - array_predicates = ranges.map { |range| attribute.in(range) } - array_predicates << values_predicate - array_predicates.inject { |composite, predicate| composite.or(predicate) } - when ActiveRecord::Relation - value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty? - attribute.in(value.arel.ast) - when Range - attribute.in(value) - when ActiveRecord::Base - attribute.eq(value.id) - when Class - # FIXME: I think we need to deprecate this behavior - attribute.eq(value.name) - else - attribute.eq(value) - end + def self.handler_for(object) + @handlers.detect { |klass, _| klass === object }.last end end end diff --git a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb new file mode 100644 index 0000000000..2f6c34ac08 --- /dev/null +++ b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb @@ -0,0 +1,29 @@ +module ActiveRecord + class PredicateBuilder + class ArrayHandler # :nodoc: + def call(attribute, value) + values = value.map { |x| x.is_a?(Base) ? x.id : x } + ranges, values = values.partition { |v| v.is_a?(Range) } + + values_predicate = if values.include?(nil) + values = values.compact + + case values.length + when 0 + attribute.eq(nil) + when 1 + attribute.eq(values.first).or(attribute.eq(nil)) + else + attribute.in(values).or(attribute.eq(nil)) + end + else + attribute.in(values) + end + + array_predicates = ranges.map { |range| attribute.in(range) } + array_predicates << values_predicate + array_predicates.inject { |composite, predicate| composite.or(predicate) } + end + end + end +end diff --git a/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb new file mode 100644 index 0000000000..618fa3cdd9 --- /dev/null +++ b/activerecord/lib/active_record/relation/predicate_builder/relation_handler.rb @@ -0,0 +1,13 @@ +module ActiveRecord + class PredicateBuilder + class RelationHandler # :nodoc: + def call(attribute, value) + if value.select_values.empty? + value = value.select(value.klass.arel_table[value.klass.primary_key]) + end + + attribute.in(value.arel.ast) + end + end + end +end -- cgit v1.2.3 From 92c5a2244ec3e58fd5e70e7dd2d7882b80183c80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Mon, 29 Jul 2013 23:18:33 -0300 Subject: Revert change on ActiveRecord::Relation#order method that prepends new order on the old ones The previous behavior added a major backward incompatibility since it impossible to have a upgrade path without major changes on the application code. We are taking the most conservative path to be consistent with the idea of having a smoother upgrade on Rails 4. We are reverting the behavior for what was in Rails 3.x and, if needed, we will implement a new API to prepend the order clauses in Rails 4.1. --- activerecord/lib/active_record/relation/query_methods.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index d0c56ac3d0..49b632c4c7 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -299,7 +299,7 @@ module ActiveRecord arg } - self.order_values = args.concat self.order_values + self.order_values += args self end @@ -311,7 +311,7 @@ module ActiveRecord # # User.order('email DESC').reorder('id ASC').order('name ASC') # - # generates a query with 'ORDER BY name ASC, id ASC'. + # generates a query with 'ORDER BY id ASC, name ASC'. def reorder(*args) check_if_method_has_arguments!("reorder", args) spawn.reorder!(*args) -- cgit v1.2.3 From 1e583f81d209ef097e040a6a7f2145ab717153b6 Mon Sep 17 00:00:00 2001 From: Eugene Gilburg Date: Wed, 31 Jul 2013 22:20:19 -0700 Subject: Minor optimization and code cleanup in query_methods. - Use symbols rather than strings where possible to avoid extra object construction - Use destructive methods where possible to avoid extra object construction - Use array union rather than concat followed by uniq - Use shorthand block syntax where possible - Use consistent multiline block styles, method names, method parenteses style, and spacing --- .../lib/active_record/relation/query_methods.rb | 83 ++++++++++++---------- 1 file changed, 45 insertions(+), 38 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 49b632c4c7..9f2a039d94 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -119,14 +119,15 @@ module ActiveRecord # # User.includes(:posts).where('posts.name = ?', 'example').references(:posts) def includes(*args) - check_if_method_has_arguments!("includes", args) + check_if_method_has_arguments!(:includes, args) spawn.includes!(*args) end def includes!(*args) # :nodoc: - args.reject! {|a| a.blank? } + args.reject!(&:blank?) + args.flatten! - self.includes_values = (includes_values + args).flatten.uniq + self.includes_values |= args self end @@ -137,7 +138,7 @@ module ActiveRecord # FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = # "users"."id" def eager_load(*args) - check_if_method_has_arguments!("eager_load", args) + check_if_method_has_arguments!(:eager_load, args) spawn.eager_load!(*args) end @@ -151,7 +152,7 @@ module ActiveRecord # User.preload(:posts) # => SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1, 2, 3) def preload(*args) - check_if_method_has_arguments!("preload", args) + check_if_method_has_arguments!(:preload, args) spawn.preload!(*args) end @@ -169,14 +170,15 @@ 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) - check_if_method_has_arguments!("references", args) + check_if_method_has_arguments!(:references, args) spawn.references!(*args) end def references!(*args) # :nodoc: args.flatten! + args.map!(&:to_s) - self.references_values = (references_values + args.map!(&:to_s)).uniq + self.references_values |= args self end @@ -229,7 +231,9 @@ module ActiveRecord end def select!(*fields) # :nodoc: - self.select_values += fields.flatten + fields.flatten! + + self.select_values += fields self end @@ -249,7 +253,7 @@ module ActiveRecord # User.group('name AS grouped_name, age') # => [#, #, #] def group(*args) - check_if_method_has_arguments!("group", args) + check_if_method_has_arguments!(:group, args) spawn.group!(*args) end @@ -280,24 +284,22 @@ module ActiveRecord # User.order(:name, email: :desc) # => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC def order(*args) - check_if_method_has_arguments!("order", args) + check_if_method_has_arguments!(:order, args) spawn.order!(*args) end def order!(*args) # :nodoc: args.flatten! - validate_order_args args + validate_order_args(args) references = args.grep(String) references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact! references!(references) if references.any? # if a symbol is given we prepend the quoted table name - args.map! { |arg| - arg.is_a?(Symbol) ? - Arel::Nodes::Ascending.new(klass.arel_table[arg]) : - arg - } + args.map! do |arg| + arg.is_a?(Symbol) ? Arel::Nodes::Ascending.new(klass.arel_table[arg]) : arg + end self.order_values += args self @@ -313,13 +315,13 @@ module ActiveRecord # # generates a query with 'ORDER BY id ASC, name ASC'. def reorder(*args) - check_if_method_has_arguments!("reorder", args) + check_if_method_has_arguments!(:reorder, args) spawn.reorder!(*args) end def reorder!(*args) # :nodoc: args.flatten! - validate_order_args args + validate_order_args(args) self.reordering_value = true self.order_values = args @@ -361,7 +363,7 @@ module ActiveRecord # # will still have an order if it comes from the default_scope on Comment. def unscope(*args) - check_if_method_has_arguments!("unscope", args) + check_if_method_has_arguments!(:unscope, args) spawn.unscope!(*args) end @@ -400,8 +402,12 @@ module ActiveRecord # User.joins("LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id") # => SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id def joins(*args) - check_if_method_has_arguments!("joins", args) - spawn.joins!(*args.compact.flatten) + check_if_method_has_arguments!(:joins, args) + + args.compact! + args.flatten! + + spawn.joins!(*args) end def joins!(*args) # :nodoc: @@ -783,9 +789,10 @@ module ActiveRecord end def extending!(*modules, &block) # :nodoc: - modules << Module.new(&block) if block_given? + modules << Module.new(&block) if block + modules.flatten! - self.extending_values += modules.flatten + self.extending_values += modules extend(*extending_values) if extending_values.any? self @@ -816,12 +823,12 @@ module ActiveRecord 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(&: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.group(*group_values.uniq.reject{|g| g.blank?}) unless group_values.empty? + arel.group(*group_values.uniq.reject(&:blank?)) unless group_values.empty? build_order(arel) @@ -870,11 +877,11 @@ module ActiveRecord end def custom_join_ast(table, joins) - joins = joins.reject { |join| join.blank? } + joins = joins.reject(&:blank?) return [] if joins.empty? - joins.map do |join| + joins.map! do |join| case join when Array join = Arel.sql(join.join(' ')) if array_of_strings?(join) @@ -901,7 +908,7 @@ module ActiveRecord when String, Array [@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))] when Hash - opts = PredicateBuilder.resolve_column_aliases klass, opts + opts = PredicateBuilder.resolve_column_aliases(klass, opts) attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts) attributes.values.grep(ActiveRecord::Relation) do |rel| @@ -944,7 +951,7 @@ module ActiveRecord association_joins = buckets[:association_join] || [] stashed_association_joins = buckets[:stashed_join] || [] join_nodes = (buckets[:join_node] || []).uniq - string_joins = (buckets[:string_join] || []).map { |x| x.strip }.uniq + string_joins = (buckets[:string_join] || []).map(&:strip).uniq join_list = join_nodes + custom_join_ast(manager, string_joins) @@ -956,13 +963,12 @@ module ActiveRecord join_dependency.graft(*stashed_association_joins) - joins = join_dependency.join_associations.map { |association| - association.join_constraints - }.flatten + joins = join_dependency.join_associations.map!(&:join_constraints) + joins.flatten! - joins.each { |join| manager.from join } + joins.each { |join| manager.from(join) } - manager.join_sources.concat join_list + manager.join_sources.concat(join_list) manager end @@ -983,7 +989,7 @@ module ActiveRecord when Arel::Nodes::Ordering o.reverse when String - o.to_s.split(',').collect do |s| + o.to_s.split(',').map! do |s| s.strip! s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC') end @@ -1000,14 +1006,15 @@ module ActiveRecord end def array_of_strings?(o) - o.is_a?(Array) && o.all?{|obj| obj.is_a?(String)} + o.is_a?(Array) && o.all? { |obj| obj.is_a?(String) } end def build_order(arel) - orders = order_values + orders = order_values.uniq + orders.reject!(&:blank?) orders = reverse_sql_order(orders) if reverse_order_value - orders = orders.uniq.reject(&:blank?).flat_map do |order| + orders = orders.flat_map do |order| case order when Symbol table[order].asc -- cgit v1.2.3 From 565c367d344228211f03d7736de79d1cfff26422 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Mon, 19 Aug 2013 14:17:49 +0200 Subject: let AR::FinderMethods#exists? return singletons in all cases [closes #11592] This fixes a regression. The documentation said in its introduction paragraph that the method returns truthy/falsy, but then below it was said that if there were no arguments you'd get `true` or `false`. Also when the argument is exactly `false` a singleton is documented to be returned. The method was not returning the singletons so it didn't conform to those special cases. The best solution here seems to be to just return singletons in all cases. This solution is backwards compatible. Also, the contract has been revised because it has no sense that the predicate varies that way depending on the input. I bet the previous contract was just an accident, not something mixed on purpose. Conflicts: activerecord/lib/active_record/relation/finder_methods.rb activerecord/test/cases/finder_test.rb --- activerecord/lib/active_record/relation/finder_methods.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 2d3bd563ac..0132a02f83 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -171,21 +171,21 @@ module ActiveRecord last or raise RecordNotFound end - # Returns truthy if a record exists in the table that matches the +id+ or - # conditions given, or falsy otherwise. The argument can take six forms: + # Returns +true+ if a record exists in the table that matches the +id+ or + # conditions given, or +false+ otherwise. The argument can take six forms: # # * Integer - Finds the record with this primary key. # * String - Finds the record with a primary key corresponding to this # string (such as '5'). # * Array - Finds the record that matches these +find+-style conditions - # (such as ['color = ?', 'red']). + # (such as ['name LIKE ?', "%#{query}%"]). # * Hash - Finds the record that matches these +find+-style conditions - # (such as {color: 'red'}). + # (such as {name: 'David'}). # * +false+ - Returns always +false+. # * No args - Returns +false+ if the table is empty, +true+ otherwise. # - # For more information about specifying conditions as a Hash or Array, - # see the Conditions section in the introduction to ActiveRecord::Base. + # For more information about specifying conditions as a hash or array, + # see the Conditions section in the introduction to ActiveRecord::Base. # # Note: You can't pass in a condition as a string (like name = # 'Jamie'), since it would be sanitized and then queried against @@ -213,7 +213,7 @@ module ActiveRecord relation = relation.where(table[primary_key].eq(conditions)) if conditions != :none end - connection.select_value(relation.arel, "#{name} Exists", relation.bind_values) + connection.select_value(relation, "#{name} Exists", relation.bind_values) ? true : false end # This method is called whenever no records are found with either a single -- cgit v1.2.3 From c2e084ac486a715fb04d49e4c2c33f62b07535dd Mon Sep 17 00:00:00 2001 From: Mikhail Dieterle Date: Tue, 20 Aug 2013 14:21:23 +0300 Subject: check class hierarchy with is_a? in PredicateBuilder.expand add changelog entry for #11945 --- activerecord/lib/active_record/relation/predicate_builder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 8948f2bba5..c60cd27a83 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -55,7 +55,7 @@ module ActiveRecord # # For polymorphic relationships, find the foreign key and type: # PriceEstimate.where(estimate_of: treasure) - if klass && value.class < Base && reflection = klass.reflect_on_association(column.to_sym) + if klass && value.is_a?(Base) && reflection = klass.reflect_on_association(column.to_sym) if reflection.polymorphic? queries << build(table[reflection.foreign_type], value.class.base_class) end -- cgit v1.2.3 From 9b2cce3660dd91215afe9d26fa29c8f69965e9a5 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 30 Aug 2013 14:16:35 -0700 Subject: cache misses should return self --- activerecord/lib/active_record/relation/delegation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index b6f80ac5c7..1d87b028ec 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -95,7 +95,7 @@ module ActiveRecord end end else - ActiveRecord::Relation + self end end end -- cgit v1.2.3 From 7db835c9f98ee6cc536d0304a5f7a9965efc86cb Mon Sep 17 00:00:00 2001 From: Nicholas Jakobsen Date: Fri, 30 Aug 2013 13:02:45 -0700 Subject: Don't create fibers just to iterate --- activerecord/lib/active_record/relation/merger.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb index c08158d38b..530c47d0d0 100644 --- a/activerecord/lib/active_record/relation/merger.rb +++ b/activerecord/lib/active_record/relation/merger.rb @@ -107,11 +107,11 @@ module ActiveRecord bind_values = filter_binds(lhs_binds, removed) + rhs_binds conn = relation.klass.connection - bviter = bind_values.each.with_index + bv_index = 0 where_values.map! do |node| if Arel::Nodes::Equality === node && Arel::Nodes::BindParam === node.right - (column, _), i = bviter.next - substitute = conn.substitute_at column, i + substitute = conn.substitute_at(bind_values[bv_index].first, bv_index) + bv_index += 1 Arel::Nodes::Equality.new(node.left, substitute) else node -- cgit v1.2.3 From 64669c11174162560aa490b57f2f8b0e45620fc7 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 30 Aug 2013 15:07:52 -0700 Subject: require a class for cache computations --- activerecord/lib/active_record/relation/delegation.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index 1d87b028ec..dd7d2582a0 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -81,7 +81,9 @@ module ActiveRecord # Cache the constants in @@subclasses because looking them up via const_get # make instantiation significantly slower. def relation_class_for(klass) - if klass && (klass_name = klass.name) + klass_name = klass.name + + if klass_name my_cache = @@subclasses.compute_if_absent(self) { ThreadSafe::Cache.new } # This hash is keyed by klass.name to avoid memory leaks in development mode my_cache.compute_if_absent(klass_name) do -- cgit v1.2.3 From c2be6ace40f016001043b84fe80170068eacee78 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 30 Aug 2013 15:15:17 -0700 Subject: no need for the const_get since we lock --- activerecord/lib/active_record/relation/delegation.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index dd7d2582a0..2b132d517d 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -90,11 +90,7 @@ module ActiveRecord # Cache#compute_if_absent guarantees that the block will only executed once for the given klass_name subclass_name = "#{name.gsub('::', '_')}_#{klass_name.gsub('::', '_')}" - if const_defined?(subclass_name) - const_get(subclass_name) - else - const_set(subclass_name, Class.new(self) { include ClassSpecificRelation }) - end + const_set(subclass_name, Class.new(self) { include ClassSpecificRelation }) end else self -- cgit v1.2.3 From 74abdb1eab2dffea6247af7653b994c1f1bfe2ef Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 30 Aug 2013 16:48:20 -0700 Subject: move the cache to the AR models and populate it on inherited --- .../lib/active_record/relation/delegation.rb | 49 +++++++++++++--------- 1 file changed, 29 insertions(+), 20 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index 2b132d517d..31f924ba52 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -1,8 +1,33 @@ -require 'thread' -require 'thread_safe' +require 'active_support/concern' module ActiveRecord module Delegation # :nodoc: + module DelegateCache + def relation_delegate_class(klass) # :nodoc: + @relation_delegate_cache[klass] + end + + def initialize_relation_delegate_cache # :nodoc: + @relation_delegate_cache = cache = {} + [ + ActiveRecord::Relation, + ActiveRecord::Associations::CollectionProxy, + ActiveRecord::AssociationRelation + ].each do |klass| + delegate = Class.new(klass) { + include ActiveRecord::Relation::ClassSpecificRelation + } + const_set klass.name.gsub('::', '_'), delegate + cache[klass] = delegate + end + end + + def inherited(child_class) + child_class.initialize_relation_delegate_cache + super + end + end + extend ActiveSupport::Concern # This module creates compiled delegation methods dynamically at runtime, which makes @@ -71,30 +96,14 @@ module ActiveRecord end module ClassMethods # :nodoc: - @@subclasses = ThreadSafe::Cache.new(:initial_capacity => 2) - def create(klass, *args) relation_class_for(klass).new(klass, *args) end private - # Cache the constants in @@subclasses because looking them up via const_get - # make instantiation significantly slower. - def relation_class_for(klass) - klass_name = klass.name - - if klass_name - my_cache = @@subclasses.compute_if_absent(self) { ThreadSafe::Cache.new } - # This hash is keyed by klass.name to avoid memory leaks in development mode - my_cache.compute_if_absent(klass_name) do - # Cache#compute_if_absent guarantees that the block will only executed once for the given klass_name - subclass_name = "#{name.gsub('::', '_')}_#{klass_name.gsub('::', '_')}" - const_set(subclass_name, Class.new(self) { include ClassSpecificRelation }) - end - else - self - end + def relation_class_for(klass) + klass.relation_delegate_class(self) end end -- cgit v1.2.3 From 12577dba0ef29c0199a88a29bc497812402c46f5 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 30 Aug 2013 17:17:17 -0700 Subject: no need to fully qualify --- activerecord/lib/active_record/relation/delegation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index 31f924ba52..e28938f9d0 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -15,7 +15,7 @@ module ActiveRecord ActiveRecord::AssociationRelation ].each do |klass| delegate = Class.new(klass) { - include ActiveRecord::Relation::ClassSpecificRelation + include ClassSpecificRelation } const_set klass.name.gsub('::', '_'), delegate cache[klass] = delegate -- cgit v1.2.3 From 8467be9e142b0eb75b9007ca9510592ab11368b5 Mon Sep 17 00:00:00 2001 From: Ryan Wallace Date: Fri, 30 Aug 2013 16:16:38 -0700 Subject: Don't use Enumerable#next in pluck since it is very slow --- activerecord/lib/active_record/relation/calculations.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 52a538e5b5..27c04b0952 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -161,8 +161,7 @@ module ActiveRecord result = result.map do |attributes| values = klass.initialize_attributes(attributes).values - iter = columns.each - values.map { |value| iter.next.type_cast value } + columns.zip(values).map { |column, value| column.type_cast value } end columns.one? ? result.map!(&:first) : result end -- cgit v1.2.3 From 1a40be02113287d9a003f8224e91b9f623857f4b Mon Sep 17 00:00:00 2001 From: Ben Woosley Date: Tue, 3 Sep 2013 21:46:33 -0700 Subject: Deprecate the delegation of Array bang methods in ActiveRecord::Delegation The primary means of returning results for Array bang methods is to modify the array in-place. When you call these methods on a relation, that array is created, modified, and then thrown away. Only the secondary return value is exposed to the caller. Removing this delegation is a straight-forward way to reduce user error by forcing callers to first explicitly call #to_a in order to expose the array to be acted on by the bang method. --- activerecord/lib/active_record/relation/delegation.rb | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index e28938f9d0..1e15bddcdf 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -1,4 +1,5 @@ require 'active_support/concern' +require 'active_support/deprecation' module ActiveRecord module Delegation # :nodoc: @@ -83,7 +84,7 @@ module ActiveRecord if @klass.respond_to?(method) self.class.delegate_to_scoped_klass(method) scoping { @klass.send(method, *args, &block) } - elsif Array.method_defined?(method) + elsif array_delegable?(method) self.class.delegate method, :to => :to_a to_a.send(method, *args, &block) elsif arel.respond_to?(method) @@ -108,17 +109,27 @@ module ActiveRecord end def respond_to?(method, include_private = false) - super || Array.method_defined?(method) || + super || array_delegable?(method) || @klass.respond_to?(method, include_private) || arel.respond_to?(method, include_private) end protected + def array_delegable?(method) + defined = Array.method_defined?(method) + if defined && method.to_s.ends_with?('!') + ActiveSupport::Deprecation.warn( + "Association will no longer delegate #{method} to #to_a as of Rails 4.2. You instead must first call #to_a on the association to expose the array to be acted on." + ) + end + defined + end + def method_missing(method, *args, &block) if @klass.respond_to?(method) scoping { @klass.send(method, *args, &block) } - elsif Array.method_defined?(method) + elsif array_delegable?(method) to_a.send(method, *args, &block) elsif arel.respond_to?(method) arel.send(method, *args, &block) -- cgit v1.2.3 From 3f1c0c2bd0b89255b0d7d8d6fe45ac2d50b05076 Mon Sep 17 00:00:00 2001 From: Paul Nikitochkin Date: Wed, 21 Aug 2013 10:49:18 +0300 Subject: Extracted from `order` processing of arguments, and use it for `reorder` to be consistent. --- .../lib/active_record/relation/query_methods.rb | 29 ++++++++++++---------- 1 file changed, 16 insertions(+), 13 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 9f2a039d94..1c6ea94c0b 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -289,17 +289,7 @@ module ActiveRecord end def order!(*args) # :nodoc: - args.flatten! - validate_order_args(args) - - references = args.grep(String) - references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact! - references!(references) if references.any? - - # if a symbol is given we prepend the quoted table name - args.map! do |arg| - arg.is_a?(Symbol) ? Arel::Nodes::Ascending.new(klass.arel_table[arg]) : arg - end + preprocess_order_args(args) self.order_values += args self @@ -320,8 +310,7 @@ module ActiveRecord end def reorder!(*args) # :nodoc: - args.flatten! - validate_order_args(args) + preprocess_order_args(args) self.reordering_value = true self.order_values = args @@ -1036,6 +1025,20 @@ module ActiveRecord end end + def preprocess_order_args(order_args) + order_args.flatten! + validate_order_args(order_args) + + references = order_args.grep(String) + references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact! + references!(references) if references.any? + + # if a symbol is given we prepend the quoted table name + order_args.map! do |arg| + arg.is_a?(Symbol) ? Arel::Nodes::Ascending.new(klass.arel_table[arg]) : arg + end + end + # Checks to make sure that the arguments are not blank. Note that if some # blank-like object were initially passed into the query method, then this # method will not raise an error. -- cgit v1.2.3 From 87a84d3263c8712f85e70c45803203fa54658a9e Mon Sep 17 00:00:00 2001 From: Ryan Wallace Date: Fri, 6 Sep 2013 15:16:39 -0700 Subject: Allow Relation#from to accept other relations with bind values. --- activerecord/lib/active_record/relation/query_methods.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 1c6ea94c0b..9916c597ee 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -915,6 +915,7 @@ module ActiveRecord case opts when Relation name ||= 'subquery' + self.bind_values = opts.bind_values + self.bind_values opts.arel.as(name.to_s) else opts -- cgit v1.2.3 From 5cbed2bc606750c71ee231d2b816471fe241459e Mon Sep 17 00:00:00 2001 From: thedarkone Date: Wed, 11 Sep 2013 14:18:31 +0200 Subject: Relation#merge should not lose readonly(false) flag. The original code ignores the `false` value because `false.blank? # => true`. --- activerecord/lib/active_record/relation/merger.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb index 530c47d0d0..c05632e688 100644 --- a/activerecord/lib/active_record/relation/merger.rb +++ b/activerecord/lib/active_record/relation/merger.rb @@ -58,7 +58,11 @@ module ActiveRecord def merge normal_values.each do |name| value = values[name] - relation.send("#{name}!", *value) unless value.blank? + # The unless clause is here mostly for performance reasons (since the `send` call might be moderately + # expensive), most of the time the value is going to be `nil` or `.blank?`, the only catch is that + # `false.blank?` returns `true`, so there needs to be an extra check so that explicit `false` values + # don't fall through the cracks. + relation.send("#{name}!", *value) unless value.nil? || (value.blank? && false != value) end merge_multi_values -- cgit v1.2.3 From fbbb6c87fd5e8fcd913c0dbf518024edd7538072 Mon Sep 17 00:00:00 2001 From: Paul Nikitochkin Date: Sat, 24 Aug 2013 19:12:05 +0300 Subject: Collapse where constraints to one where constraint In order to remove duplication with joining arel where constraints with `AND`, all constraints on `build_arel` are collapsed into one head node: `Arel::Nodes::And` Closes: #11963 --- activerecord/lib/active_record/relation/query_methods.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 1c6ea94c0b..5d0df32582 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -882,14 +882,13 @@ module ActiveRecord end def collapse_wheres(arel, wheres) - equalities = wheres.grep(Arel::Nodes::Equality) - - arel.where(Arel::Nodes::And.new(equalities)) unless equalities.empty? - - (wheres - equalities).each do |where| + predicates = wheres.map do |where| + next where if ::Arel::Nodes::Equality === where where = Arel.sql(where) if String === where - arel.where(Arel::Nodes::Grouping.new(where)) + Arel::Nodes::Grouping.new(where) end + + arel.where(Arel::Nodes::And.new(predicates)) if predicates.present? end def build_where(opts, other = []) -- cgit v1.2.3 From a594817dad144156c94d1ff5a06eb5ce1d9bd74c Mon Sep 17 00:00:00 2001 From: Vipul A M Date: Tue, 17 Sep 2013 11:51:44 +0530 Subject: `skiping` => `skipping` --- activerecord/lib/active_record/relation/batches.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb index fd8496442e..49b01909c6 100644 --- a/activerecord/lib/active_record/relation/batches.rb +++ b/activerecord/lib/active_record/relation/batches.rb @@ -34,7 +34,7 @@ module ActiveRecord # between id 0 and 10,000 and worker 2 handle from 10,000 and beyond # (by setting the +:start+ option on that worker). # - # # Let's process for a batch of 2000 records, skiping the first 2000 rows + # # Let's process for a batch of 2000 records, skipping the first 2000 rows # Person.find_each(start: 2000, batch_size: 2000) do |person| # person.party_all_night! # end -- cgit v1.2.3 From 7cab255a97fceb48e3b59b3d39b5fe58c6dad54b Mon Sep 17 00:00:00 2001 From: Edo Balvers Date: Fri, 9 Aug 2013 12:58:46 +0200 Subject: Fixes #11773 when using includes combined with select, the select statement was overwritten. --- activerecord/lib/active_record/relation/finder_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 0132a02f83..fb8f44b188 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -261,7 +261,7 @@ module ActiveRecord end def construct_relation_for_association_find(join_dependency) - relation = except(:select).select(join_dependency.columns) + relation = except(:select).select(join_dependency.columns + select_values) apply_join_dependency(relation, join_dependency) end -- cgit v1.2.3 From 752a06ea8d76a072a4cf51594f47d8a4c6c2edd6 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 9 Oct 2013 15:21:34 -0700 Subject: hide join_constraints inside the JoinDependency object --- activerecord/lib/active_record/relation/query_methods.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 9fcb6db726..e1efc6a189 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -952,8 +952,7 @@ module ActiveRecord join_dependency.graft(*stashed_association_joins) - joins = join_dependency.join_associations.map!(&:join_constraints) - joins.flatten! + joins = join_dependency.join_constraints joins.each { |join| manager.from(join) } -- cgit v1.2.3 From e4ec9ce78d879fc20bae0c5c7886a3bdbfab096c Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 10 Oct 2013 10:11:18 -0700 Subject: stop splatting things back and forth --- activerecord/lib/active_record/relation/query_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index e1efc6a189..776360fc55 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -950,7 +950,7 @@ module ActiveRecord join_list ) - join_dependency.graft(*stashed_association_joins) + join_dependency.graft(stashed_association_joins) joins = join_dependency.join_constraints -- cgit v1.2.3 From d20ccb7e632665b6661e82ae450e8180e3c085f9 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 10 Oct 2013 14:12:04 -0700 Subject: stuff the join dependency object in the "anything goes" hash. --- activerecord/lib/active_record/relation/finder_methods.rb | 2 +- activerecord/lib/active_record/relation/merger.rb | 2 +- activerecord/lib/active_record/relation/query_methods.rb | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 0132a02f83..d5eb1edf77 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -267,7 +267,7 @@ module ActiveRecord def apply_join_dependency(relation, join_dependency) relation = relation.except(:includes, :eager_load, :preload) - relation = join_dependency.join_relation(relation) + relation = relation.joins join_dependency if using_limitable_reflections?(join_dependency.reflections) relation diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb index c05632e688..182b9ed89c 100644 --- a/activerecord/lib/active_record/relation/merger.rb +++ b/activerecord/lib/active_record/relation/merger.rb @@ -94,7 +94,7 @@ module ActiveRecord []) relation.joins! rest - @relation = join_dependency.join_relation(relation) + @relation = relation.joins join_dependency end end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 776360fc55..364c7ec418 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -928,7 +928,7 @@ module ActiveRecord :string_join when Hash, Symbol, Array :association_join - when ActiveRecord::Associations::JoinDependency::JoinAssociation + when ActiveRecord::Associations::JoinDependency :stashed_join when Arel::Nodes::Join :join_node @@ -950,7 +950,9 @@ module ActiveRecord join_list ) - join_dependency.graft(stashed_association_joins) + stashed_association_joins.each do |dep| + join_dependency.graft dep.outer_joins + end joins = join_dependency.join_constraints -- cgit v1.2.3 From 223082e588af59cb35108ce2a2d742dcecf72cc6 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 10 Oct 2013 14:46:15 -0700 Subject: merge JoinDependency as outer joins Merge JoinDependency objects as outer joins --- activerecord/lib/active_record/relation/query_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 364c7ec418..9c9690215a 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -951,7 +951,7 @@ module ActiveRecord ) stashed_association_joins.each do |dep| - join_dependency.graft dep.outer_joins + join_dependency.merge_outer_joins! dep end joins = join_dependency.join_constraints -- cgit v1.2.3 From 4ce643dbb5bd37e1f4662b37abc80a18078feba1 Mon Sep 17 00:00:00 2001 From: Vipul A M Date: Sun, 13 Oct 2013 21:27:21 +0530 Subject: `Relation#count` doesn't use options anymore. --- activerecord/lib/active_record/relation/calculations.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 27c04b0952..a4fe3bd3b7 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -19,9 +19,8 @@ module ActiveRecord # # Person.group(:city).count # # => { 'Rome' => 5, 'Paris' => 3 } - def count(column_name = nil, options = {}) - column_name, options = nil, column_name if column_name.is_a?(Hash) - calculate(:count, column_name, options) + def count(column_name = nil) + calculate(:count, column_name) end # Calculates the average value on a given column. Returns +nil+ if there's -- cgit v1.2.3 From 42d3d3c217d158873409c50c429ae711a0306d99 Mon Sep 17 00:00:00 2001 From: Vipul A M Date: Sun, 13 Oct 2013 23:55:25 +0530 Subject: Stop accepting `options` for `Relation#average`, `Relation#minimum`, `Relation#maximum`, `Relation#calculate`, `perform_calculation`, `NullRelation#calculate` as they isn't used anymore. --- .../lib/active_record/relation/calculations.rb | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index a4fe3bd3b7..2d267183ce 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -27,8 +27,8 @@ module ActiveRecord # no row. See +calculate+ for examples with options. # # Person.average(:age) # => 35.8 - def average(column_name, options = {}) - calculate(:average, column_name, options) + def average(column_name) + calculate(:average, column_name) end # Calculates the minimum value on a given column. The value is returned @@ -36,8 +36,8 @@ module ActiveRecord # +calculate+ for examples with options. # # Person.minimum(:age) # => 7 - def minimum(column_name, options = {}) - calculate(:minimum, column_name, options) + def minimum(column_name) + calculate(:minimum, column_name) end # Calculates the maximum value on a given column. The value is returned @@ -45,8 +45,8 @@ module ActiveRecord # +calculate+ for examples with options. # # Person.maximum(:age) # => 93 - def maximum(column_name, options = {}) - calculate(:maximum, column_name, options) + def maximum(column_name) + calculate(:maximum, column_name) end # Calculates the sum of values on a given column. The value is returned @@ -89,15 +89,15 @@ module ActiveRecord # Person.group(:last_name).having("min(age) > 17").minimum(:age) # # Person.sum("2 * age") - def calculate(operation, column_name, options = {}) + def calculate(operation, column_name) if column_name.is_a?(Symbol) && attribute_alias?(column_name) column_name = attribute_alias(column_name) end if has_include?(column_name) - construct_relation_for_association_calculations.calculate(operation, column_name, options) + construct_relation_for_association_calculations.calculate(operation, column_name) else - perform_calculation(operation, column_name, options) + perform_calculation(operation, column_name) end end @@ -180,7 +180,7 @@ module ActiveRecord eager_loading? || (includes_values.present? && (column_name || references_eager_loaded_tables?)) end - def perform_calculation(operation, column_name, options = {}) + def perform_calculation(operation, column_name) operation = operation.to_s.downcase # If #count is used with #distinct / #uniq it is considered distinct. (eg. relation.distinct.count) -- cgit v1.2.3 From 5e4031ef3d2c1b60bab1f8733ae995fa1305ec22 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sun, 13 Oct 2013 16:42:49 -0700 Subject: JoinDependency will take care of making things unique --- activerecord/lib/active_record/relation/finder_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index d5eb1edf77..6ac803afc2 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -252,7 +252,7 @@ module ActiveRecord end def construct_join_dependency(joins = []) - including = (eager_load_values + includes_values).uniq + including = eager_load_values + includes_values ActiveRecord::Associations::JoinDependency.new(@klass, including, joins) end -- cgit v1.2.3 From 7fe6d2450695fdd8c2597acca404b7724ec48507 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sun, 13 Oct 2013 16:51:10 -0700 Subject: push up `select` exclusion --- activerecord/lib/active_record/relation/finder_methods.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 6ac803afc2..b575d9ad61 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -242,7 +242,8 @@ module ActiveRecord def find_with_associations join_dependency = construct_join_dependency - relation = construct_relation_for_association_find(join_dependency) + relation = except :select + relation = construct_relation_for_association_find(join_dependency, relation) if ActiveRecord::NullRelation === relation [] else @@ -260,8 +261,8 @@ module ActiveRecord apply_join_dependency(self, construct_join_dependency(arel.froms.first)) end - def construct_relation_for_association_find(join_dependency) - relation = except(:select).select(join_dependency.columns) + def construct_relation_for_association_find(join_dependency, relation = self) + relation = relation.select(join_dependency.columns) apply_join_dependency(relation, join_dependency) end -- cgit v1.2.3 From d00f9692b82deb8f90c4b3fb56f9e22cceb0e470 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sun, 13 Oct 2013 16:53:28 -0700 Subject: calling construct_relation_for_association_find is no longer necessary --- activerecord/lib/active_record/relation/finder_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index b575d9ad61..3d687b927d 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -201,7 +201,7 @@ module ActiveRecord conditions = conditions.id if Base === conditions return false if !conditions - relation = construct_relation_for_association_find(construct_join_dependency) + relation = apply_join_dependency(self, construct_join_dependency) return false if ActiveRecord::NullRelation === relation relation = relation.except(:select, :order).select(ONE_AS_ONE).limit(1) -- cgit v1.2.3 From 158197b91d34b5ef2a4c06fb12a440d0b88d693d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sun, 13 Oct 2013 16:56:30 -0700 Subject: eliminate duplicate code from to_sql I don't really like passing the block, but this seems easiest for now --- activerecord/lib/active_record/relation/finder_methods.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 3d687b927d..b2d770ba93 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -244,11 +244,15 @@ module ActiveRecord join_dependency = construct_join_dependency relation = except :select relation = construct_relation_for_association_find(join_dependency, relation) - if ActiveRecord::NullRelation === relation - [] + if block_given? + yield relation else - rows = connection.select_all(relation.arel, 'SQL', relation.bind_values.dup) - join_dependency.instantiate(rows) + if ActiveRecord::NullRelation === relation + [] + else + rows = connection.select_all(relation.arel, 'SQL', relation.bind_values.dup) + join_dependency.instantiate(rows) + end end end -- cgit v1.2.3 From 66a6a6b45a4e3cee8a33f43111abafb46969ff94 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sun, 13 Oct 2013 17:03:48 -0700 Subject: eliminate single use method --- activerecord/lib/active_record/relation/finder_methods.rb | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index b2d770ba93..92969cfb30 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -243,7 +243,9 @@ module ActiveRecord def find_with_associations join_dependency = construct_join_dependency relation = except :select - relation = construct_relation_for_association_find(join_dependency, relation) + relation = relation.select(join_dependency.columns) + relation = apply_join_dependency(relation, join_dependency) + if block_given? yield relation else @@ -265,11 +267,6 @@ module ActiveRecord apply_join_dependency(self, construct_join_dependency(arel.froms.first)) end - def construct_relation_for_association_find(join_dependency, relation = self) - relation = relation.select(join_dependency.columns) - apply_join_dependency(relation, join_dependency) - end - def apply_join_dependency(relation, join_dependency) relation = relation.except(:includes, :eager_load, :preload) relation = relation.joins join_dependency -- cgit v1.2.3 From fc61df5a62df7f873122d70827c1a7e37c2aa22f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 14 Oct 2013 18:10:43 -0700 Subject: store aliases in a better structure --- activerecord/lib/active_record/relation/finder_methods.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 92969cfb30..5e11ba0325 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -243,7 +243,9 @@ module ActiveRecord def find_with_associations join_dependency = construct_join_dependency relation = except :select - relation = relation.select(join_dependency.columns) + + aliases = join_dependency.aliases + relation = relation.select aliases.columns relation = apply_join_dependency(relation, join_dependency) if block_given? -- cgit v1.2.3 From 621c24323ea3226206ed65a16070b97a24a5bc2f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 14 Oct 2013 18:37:18 -0700 Subject: keep a cache on the alias object --- activerecord/lib/active_record/relation/finder_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 5e11ba0325..f7f60635d3 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -255,7 +255,7 @@ module ActiveRecord [] else rows = connection.select_all(relation.arel, 'SQL', relation.bind_values.dup) - join_dependency.instantiate(rows) + join_dependency.instantiate(rows, aliases) end end end -- cgit v1.2.3 From ab9475bccf1107919a255be3f5f7363f189e3621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Tue, 15 Oct 2013 17:58:03 -0300 Subject: Don't remove the select values to add they back again Conflicts: activerecord/lib/active_record/relation/finder_methods.rb --- activerecord/lib/active_record/relation/finder_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 8583286de5..fe75a32545 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -261,7 +261,7 @@ module ActiveRecord end def construct_relation_for_association_find(join_dependency) - relation = except(:select).select(join_dependency.columns + select_values) + relation = select(join_dependency.columns) apply_join_dependency(relation, join_dependency) end -- cgit v1.2.3 From a2ed5d2381079716cf6224bc7b0538d318b264a2 Mon Sep 17 00:00:00 2001 From: Paul Nikitochkin Date: Fri, 18 Oct 2013 23:26:39 +0300 Subject: Process sub-query relation's binding values Generated sub-query for Relation as array condition for `where` method did not take in account its bind values, in result generates invalid SQL query. Fixed by adding sub-query relation's binding values to base relation Closes: #12586 --- activerecord/lib/active_record/relation/query_methods.rb | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 9c9690215a..f680d94215 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -894,6 +894,13 @@ module ActiveRecord def build_where(opts, other = []) case opts when String, Array + #TODO: Remove duplication with: /activerecord/lib/active_record/sanitization.rb:113 + values = Hash === other.first ? other.first.values : other + + values.grep(ActiveRecord::Relation) do |rel| + self.bind_values += rel.bind_values + end + [@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))] when Hash opts = PredicateBuilder.resolve_column_aliases(klass, opts) -- cgit v1.2.3 From 796c0fc1b065bc4248a410110e728e5b2c6db19e Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Sun, 20 Oct 2013 13:48:44 -0700 Subject: pass the outer joins to join_constraints --- activerecord/lib/active_record/relation/query_methods.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 9c9690215a..328f23d125 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -950,11 +950,7 @@ module ActiveRecord join_list ) - stashed_association_joins.each do |dep| - join_dependency.merge_outer_joins! dep - end - - joins = join_dependency.join_constraints + joins = join_dependency.join_constraints stashed_association_joins joins.each { |join| manager.from(join) } -- cgit v1.2.3 From e2419a451aaeafc166b8677540bc29cd8b0c97fd Mon Sep 17 00:00:00 2001 From: Shimpei Makimoto Date: Wed, 16 Oct 2013 03:58:45 +0900 Subject: Raise an exception when model without primary key calls .find_with_ids --- activerecord/lib/active_record/relation/finder_methods.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index fe75a32545..ae9717e783 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -297,6 +297,8 @@ module ActiveRecord protected def find_with_ids(*ids) + raise UnknownPrimaryKey.new(@klass) if primary_key.nil? + expects_array = ids.first.kind_of?(Array) return ids.first if expects_array && ids.first.empty? -- cgit v1.2.3 From b05776581747057c162207d0a857c98fa326c11d Mon Sep 17 00:00:00 2001 From: Eric Hankins Date: Wed, 16 Oct 2013 13:39:43 -0500 Subject: Allow unscope to work with `where.not` Allows you to call #unscope on a relation with negative equality operators, i.e. Arel::Nodes::NotIn and Arel::Nodes::NotEqual that have been generated through the use of where.not. --- activerecord/lib/active_record/relation/query_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 9c9690215a..a53f034345 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -856,7 +856,7 @@ module ActiveRecord where_values.reject! do |rel| case rel - when Arel::Nodes::In, Arel::Nodes::Equality + when Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual subrelation = (rel.left.kind_of?(Arel::Attributes::Attribute) ? rel.left : rel.right) subrelation.name.to_sym == target_value_sym else -- cgit v1.2.3 From 4d7080f80c1ec4792730943b33b6eac2a1562b2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Sat, 2 Nov 2013 22:09:10 -0200 Subject: unscope now works on default_scope after 94924dc32baf78f13e289172534c2e71c9c8cade --- activerecord/lib/active_record/relation/query_methods.rb | 3 --- 1 file changed, 3 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index bffd8b5d0f..14d8671161 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -341,9 +341,6 @@ module ActiveRecord # User.where(name: "John", active: true).unscope(where: :name) # == User.where(active: true) # - # This method is applied before the default_scope is applied. So the conditions - # specified in default_scope will not be removed. - # # Note that this method is more generalized than ActiveRecord::SpawnMethods#except # because #except will only affect a particular relation's values. It won't wipe # the order, grouping, etc. when that relation is merged. For example: -- cgit v1.2.3 From f950b2699f97749ef706c6939a84dfc85f0b05f2 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sat, 2 Nov 2013 17:35:34 -0700 Subject: Added ActiveRecord::QueryMethods#rewhere which will overwrite an existing, named where condition. --- activerecord/lib/active_record/relation/query_methods.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 14d8671161..62c555f6d6 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -550,6 +550,18 @@ module ActiveRecord end end + # Allows you to change a previously set where condition for a given attribute, instead of appending to that condition. + # + # Post.where(trashed: true).where(trashed: false) #=> WHERE `trashed` = 1 AND `trashed` = 0 + # Post.where(trashed: true).rewhere(trashed: false) #=> WHERE `trashed` = 0 + # Post.where(active: true).where(trashed: true).rewhere(trashed: false) #=> WHERE `active` = 1 AND `trashed` = 0 + # + # This is short-hand for unscope(where: conditions.keys).where(conditions). Note that unlike reorder, we're only unscoping + # the named conditions -- not the entire where statement. + def rewhere(conditions) + unscope(where: conditions.keys).where(conditions) + end + # Allows to specify a HAVING clause. Note that you can't use HAVING # without also specifying a GROUP clause. # -- cgit v1.2.3 From efff6c1fd4b9e2e4c9f705a45879373cb34a5b0e Mon Sep 17 00:00:00 2001 From: Prem Sichanugrist Date: Mon, 11 Nov 2013 13:53:54 -0500 Subject: Change syntax format for example returned values According to our guideline, we leave 1 space between `#` and `=>`, so we want `# =>` instead of `#=>`. Thanks to @fxn for the suggestion. [ci skip] --- activerecord/lib/active_record/relation/query_methods.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 62c555f6d6..a9dbc14d6e 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -552,9 +552,9 @@ module ActiveRecord # Allows you to change a previously set where condition for a given attribute, instead of appending to that condition. # - # Post.where(trashed: true).where(trashed: false) #=> WHERE `trashed` = 1 AND `trashed` = 0 - # Post.where(trashed: true).rewhere(trashed: false) #=> WHERE `trashed` = 0 - # Post.where(active: true).where(trashed: true).rewhere(trashed: false) #=> WHERE `active` = 1 AND `trashed` = 0 + # Post.where(trashed: true).where(trashed: false) # => WHERE `trashed` = 1 AND `trashed` = 0 + # Post.where(trashed: true).rewhere(trashed: false) # => WHERE `trashed` = 0 + # Post.where(active: true).where(trashed: true).rewhere(trashed: false) # => WHERE `active` = 1 AND `trashed` = 0 # # This is short-hand for unscope(where: conditions.keys).where(conditions). Note that unlike reorder, we're only unscoping # the named conditions -- not the entire where statement. @@ -701,7 +701,7 @@ module ActiveRecord # Specifies table from which the records will be fetched. For example: # # Topic.select('title').from('posts') - # #=> SELECT title FROM posts + # # => SELECT title FROM posts # # Can accept other relation objects. For example: # -- cgit v1.2.3 From 77ed4d98a7147fcfa1c340385f767a0010abe82a Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Fri, 15 Nov 2013 10:10:09 +0100 Subject: document id prefixed String usage of `.find`. refs #12891 [ci skip] --- activerecord/lib/active_record/relation/finder_methods.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 3a02bf90e9..d91d6367a3 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -6,11 +6,12 @@ module ActiveRecord # If no record can be found for all of the listed ids, then RecordNotFound will be raised. If the primary key # is an integer, find by id coerces its arguments using +to_i+. # - # Person.find(1) # returns the object for ID = 1 - # Person.find("1") # returns the object for ID = 1 - # Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6) - # Person.find([7, 17]) # returns an array for objects with IDs in (7, 17) - # Person.find([1]) # returns an array for the object with ID = 1 + # Person.find(1) # returns the object for ID = 1 + # Person.find("1") # returns the object for ID = 1 + # Person.find("31-sarah") # returns the object for ID = 31 + # Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6) + # Person.find([7, 17]) # returns an array for objects with IDs in (7, 17) + # Person.find([1]) # returns an array for the object with ID = 1 # Person.where("administrator = 1").order("created_on DESC").find(1) # # ActiveRecord::RecordNotFound will be raised if one or more ids are not found. -- cgit v1.2.3 From f83c9b10b4c92b0d8deacb30d6fdfa2b1252d6dd Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Wed, 13 Nov 2013 17:26:55 +0100 Subject: use arel nodes to represent non-string `order_values`. This fixes a bug when merging relations of different classes. ``` Given: Post.joins(:author).merge(Author.order(name: :desc)).to_sql Before: SELECT "posts".* FROM "posts" INNER JOIN "authors" ON "authors"."id" = "posts"."author_id" ORDER BY "posts"."name" DESC After: SELECT "posts".* FROM "posts" INNER JOIN "authors" ON "authors"."id" = "posts"."author_id" ORDER BY "authors"."name" DESC ``` --- .../lib/active_record/relation/query_methods.rb | 30 ++++++++-------------- 1 file changed, 11 insertions(+), 19 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 62c555f6d6..b6a7c25b4b 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -995,12 +995,6 @@ module ActiveRecord s.strip! s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC') end - when Symbol - { o => :desc } - when Hash - o.each_with_object({}) do |(field, dir), memo| - memo[field] = (dir == :asc ? :desc : :asc ) - end else o end @@ -1016,17 +1010,6 @@ module ActiveRecord orders.reject!(&:blank?) orders = reverse_sql_order(orders) if reverse_order_value - orders = orders.flat_map do |order| - case order - when Symbol - table[order].asc - when Hash - order.map { |field, dir| table[field].send(dir) } - else - order - end - end - arel.order(*orders) unless orders.empty? end @@ -1048,8 +1031,17 @@ module ActiveRecord # if a symbol is given we prepend the quoted table name order_args.map! do |arg| - arg.is_a?(Symbol) ? Arel::Nodes::Ascending.new(klass.arel_table[arg]) : arg - end + case arg + when Symbol + table[arg].asc + when Hash + arg.map { |field, dir| + table[field].send(dir) + } + else + arg + end + end.flatten! end # Checks to make sure that the arguments are not blank. Note that if some -- cgit v1.2.3 From 64b9e93bb571160315987862583a83222e506734 Mon Sep 17 00:00:00 2001 From: Jon Leighton Date: Wed, 20 Nov 2013 21:51:35 +0000 Subject: Fix ActiveRecord::Relation#unscope I'm pretty confused about the addition of this method. The documentation says that it was intended to allow the removal of values from the default scope (in contrast to #except). However it behaves exactly the same as except: https://gist.github.com/jonleighton/7537008 (other than having a slightly enhanced syntax). The removal of the default scope is allowed by 94924dc32baf78f13e289172534c2e71c9c8cade, which was not a change we could make until 4.1 due to the need to deprecate things. However after that change #unscope still gives us nothing that #except doesn't already give us. However there *is* a desire to be able to unscope stuff in a way that persists across merges, which would allow associations to be defined which unscope stuff from the default scope of the associated model. E.g. has_many :comments, -> { unscope where: :trashed } So that's what this change implements. I've also corrected the documentation. I removed the guide references to #except as I think unscope really supercedes #except now. While we're here, there's also a potential desire to be able to write this: has_many :comments, -> { unscoped } However, it doesn't make sense and would not be straightforward to implement. While with #unscope we're specifying exactly what we want to be removed from the relation, with "unscoped" we're just saying that we want it to not have some things which were added earlier on by the default scope. However in the case of an association, we surely don't want *all* conditions to be removed, otherwise the above would just become "SELECT * FROM comments" with no foreign key constraint. To make the above work, we'd have to somehow tag the relation values which get added when evaluating the default scope in order to differentiate them from other relation values. Which is way too much complexity and therefore not worth it when most use cases can be satisfied with unscope. Closes #10643, #11061. --- .../lib/active_record/relation/query_methods.rb | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index b6a7c25b4b..473762011b 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -341,13 +341,19 @@ module ActiveRecord # User.where(name: "John", active: true).unscope(where: :name) # == User.where(active: true) # - # Note that this method is more generalized than ActiveRecord::SpawnMethods#except - # because #except will only affect a particular relation's values. It won't wipe - # the order, grouping, etc. when that relation is merged. For example: + # This method is similar to except, but unlike + # except, it persists across merges: # - # Post.comments.except(:order) + # User.order('email').merge(User.except(:order)) + # == User.order('email') + # + # User.order('email').merge(User.unscope(:order)) + # == User.all + # + # This means it can be used in association definitions: + # + # has_many :comments, -> { unscope where: :trashed } # - # will still have an order if it comes from the default_scope on Comment. def unscope(*args) check_if_method_has_arguments!(:unscope, args) spawn.unscope!(*args) @@ -355,6 +361,7 @@ module ActiveRecord def unscope!(*args) # :nodoc: args.flatten! + self.unscope_values += args args.each do |scope| case scope -- cgit v1.2.3 From 2a517e7291d6f93b6ca3d8c56fc8559c48089c28 Mon Sep 17 00:00:00 2001 From: Lauro Caetano Date: Tue, 3 Dec 2013 16:33:34 -0200 Subject: Fix offset with last. Closes #7441 --- activerecord/lib/active_record/relation/finder_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index d91d6367a3..3963f2b3e0 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -384,7 +384,7 @@ module ActiveRecord @records.last else @last ||= - if offset_value || limit_value + if limit_value to_a.last else reverse_order.limit(1).to_a.first -- cgit v1.2.3 From 2a7fe7ae9b09024acea3c5d525c89b91bdb264a1 Mon Sep 17 00:00:00 2001 From: Paul Nikitochkin Date: Mon, 9 Dec 2013 01:23:38 +0200 Subject: Fix type cast on group sum with custom expression For PG adapters with custom expression and grouped result of aggregate functions have not found correct column type for it. Extract column type from query result. Closes: #13230 --- activerecord/lib/active_record/relation/calculations.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 2d267183ce..cf24d10a8d 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -311,7 +311,9 @@ module ActiveRecord } key = key.first if key.size == 1 key = key_records[key] if associated - [key, type_cast_calculated_value(row[aggregate_alias], column_for(column_name), operation)] + + column_type = calculated_data.column_types.fetch(aggregate_alias) { column_for(column_name) } + [key, type_cast_calculated_value(row[aggregate_alias], column_type, operation)] end] end -- cgit v1.2.3 From e1ce005942f20c4f64cf78a0dc15e662e0353f3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 11 Dec 2013 13:29:21 -0200 Subject: Revert "Merge pull request #12518 from vipulnsward/remove_count_options" It is needed for activerecord-depecated_finders This reverts commit dcff027a5242b20c0c90eb062dddb22ccf51aed9, reversing changes made to 3a2093984ff49d86db1efeff0c7581e788ecfb9f. --- .../lib/active_record/relation/calculations.rb | 25 +++++++++++----------- 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index cf24d10a8d..1d81b50abb 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -19,16 +19,17 @@ module ActiveRecord # # Person.group(:city).count # # => { 'Rome' => 5, 'Paris' => 3 } - def count(column_name = nil) - calculate(:count, column_name) + def count(column_name = nil, options = {}) + column_name, options = nil, column_name if column_name.is_a?(Hash) + calculate(:count, column_name, options) end # Calculates the average value on a given column. Returns +nil+ if there's # no row. See +calculate+ for examples with options. # # Person.average(:age) # => 35.8 - def average(column_name) - calculate(:average, column_name) + def average(column_name, options = {}) + calculate(:average, column_name, options) end # Calculates the minimum value on a given column. The value is returned @@ -36,8 +37,8 @@ module ActiveRecord # +calculate+ for examples with options. # # Person.minimum(:age) # => 7 - def minimum(column_name) - calculate(:minimum, column_name) + def minimum(column_name, options = {}) + calculate(:minimum, column_name, options) end # Calculates the maximum value on a given column. The value is returned @@ -45,8 +46,8 @@ module ActiveRecord # +calculate+ for examples with options. # # Person.maximum(:age) # => 93 - def maximum(column_name) - calculate(:maximum, column_name) + def maximum(column_name, options = {}) + calculate(:maximum, column_name, options) end # Calculates the sum of values on a given column. The value is returned @@ -89,15 +90,15 @@ module ActiveRecord # Person.group(:last_name).having("min(age) > 17").minimum(:age) # # Person.sum("2 * age") - def calculate(operation, column_name) + def calculate(operation, column_name, options = {}) if column_name.is_a?(Symbol) && attribute_alias?(column_name) column_name = attribute_alias(column_name) end if has_include?(column_name) - construct_relation_for_association_calculations.calculate(operation, column_name) + construct_relation_for_association_calculations.calculate(operation, column_name, options) else - perform_calculation(operation, column_name) + perform_calculation(operation, column_name, options) end end @@ -180,7 +181,7 @@ module ActiveRecord eager_loading? || (includes_values.present? && (column_name || references_eager_loaded_tables?)) end - def perform_calculation(operation, column_name) + def perform_calculation(operation, column_name, options = {}) operation = operation.to_s.downcase # If #count is used with #distinct / #uniq it is considered distinct. (eg. relation.distinct.count) -- cgit v1.2.3 From 929c2261cdb279d6184cc288d7ab014c1414799c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 11 Dec 2013 13:35:34 -0200 Subject: Mark the arguments needed by activerecord-deprecated_finders with a TODO --- activerecord/lib/active_record/relation/calculations.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 1d81b50abb..45ffb99868 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -20,6 +20,8 @@ module ActiveRecord # Person.group(:city).count # # => { 'Rome' => 5, 'Paris' => 3 } def count(column_name = nil, options = {}) + # TODO: Remove options argument as soon we remove support to + # activerecord-deprecated_finders. column_name, options = nil, column_name if column_name.is_a?(Hash) calculate(:count, column_name, options) end @@ -29,6 +31,8 @@ module ActiveRecord # # Person.average(:age) # => 35.8 def average(column_name, options = {}) + # TODO: Remove options argument as soon we remove support to + # activerecord-deprecated_finders. calculate(:average, column_name, options) end @@ -38,6 +42,8 @@ module ActiveRecord # # Person.minimum(:age) # => 7 def minimum(column_name, options = {}) + # TODO: Remove options argument as soon we remove support to + # activerecord-deprecated_finders. calculate(:minimum, column_name, options) end @@ -47,6 +53,8 @@ module ActiveRecord # # Person.maximum(:age) # => 93 def maximum(column_name, options = {}) + # TODO: Remove options argument as soon we remove support to + # activerecord-deprecated_finders. calculate(:maximum, column_name, options) end @@ -91,6 +99,8 @@ module ActiveRecord # # Person.sum("2 * age") def calculate(operation, column_name, options = {}) + # TODO: Remove options argument as soon we remove support to + # activerecord-deprecated_finders. if column_name.is_a?(Symbol) && attribute_alias?(column_name) column_name = attribute_alias(column_name) end @@ -182,6 +192,8 @@ module ActiveRecord end def perform_calculation(operation, column_name, options = {}) + # TODO: Remove options argument as soon we remove support to + # activerecord-deprecated_finders. operation = operation.to_s.downcase # If #count is used with #distinct / #uniq it is considered distinct. (eg. relation.distinct.count) -- cgit v1.2.3 From aa85bdba68eb981588cecfef9dea0c0fa3e1c673 Mon Sep 17 00:00:00 2001 From: Lauro Caetano Date: Tue, 3 Dec 2013 18:30:13 -0200 Subject: Use a whitelist to delegate methods to array --- .../lib/active_record/relation/delegation.rb | 24 ++++++---------------- 1 file changed, 6 insertions(+), 18 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index 1e15bddcdf..af6be24351 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -36,7 +36,11 @@ module ActiveRecord # may vary depending on the klass of a relation, so we create a subclass of Relation # for each different klass, and the delegations are compiled into that subclass only. - delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :to => :to_a + delegate :&, :+, :[], :all?, :collect, :detect, :each, :each_cons, + :each_with_index, :flat_map, :group_by, :include?, :length, + :map, :none?, :one?, :reverse, :sample, :second, :sort, :sort_by, + :to_ary, :to_set, :to_xml, :to_yaml, :to => :to_a + delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :connection, :columns_hash, :to => :klass @@ -84,9 +88,6 @@ module ActiveRecord if @klass.respond_to?(method) self.class.delegate_to_scoped_klass(method) scoping { @klass.send(method, *args, &block) } - elsif array_delegable?(method) - self.class.delegate method, :to => :to_a - to_a.send(method, *args, &block) elsif arel.respond_to?(method) self.class.delegate method, :to => :arel arel.send(method, *args, &block) @@ -109,28 +110,15 @@ module ActiveRecord end def respond_to?(method, include_private = false) - super || array_delegable?(method) || - @klass.respond_to?(method, include_private) || + super || @klass.respond_to?(method, include_private) || arel.respond_to?(method, include_private) end protected - def array_delegable?(method) - defined = Array.method_defined?(method) - if defined && method.to_s.ends_with?('!') - ActiveSupport::Deprecation.warn( - "Association will no longer delegate #{method} to #to_a as of Rails 4.2. You instead must first call #to_a on the association to expose the array to be acted on." - ) - end - defined - end - def method_missing(method, *args, &block) if @klass.respond_to?(method) scoping { @klass.send(method, *args, &block) } - elsif array_delegable?(method) - to_a.send(method, *args, &block) elsif arel.respond_to?(method) arel.send(method, *args, &block) else -- cgit v1.2.3 From 1244aa7c5f871da5e907a39242b63a7aee3308a3 Mon Sep 17 00:00:00 2001 From: Lauro Caetano Date: Thu, 12 Dec 2013 20:19:04 -0200 Subject: Use `public_send` instead of just use `send`. --- activerecord/lib/active_record/relation/delegation.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index af6be24351..603e5a9df5 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -68,7 +68,7 @@ module ActiveRecord RUBY else define_method method do |*args, &block| - scoping { @klass.send(method, *args, &block) } + scoping { @klass.public_send(method, *args, &block) } end end end @@ -87,10 +87,10 @@ module ActiveRecord def method_missing(method, *args, &block) if @klass.respond_to?(method) self.class.delegate_to_scoped_klass(method) - scoping { @klass.send(method, *args, &block) } + scoping { @klass.public_send(method, *args, &block) } elsif arel.respond_to?(method) self.class.delegate method, :to => :arel - arel.send(method, *args, &block) + arel.public_send(method, *args, &block) else super end @@ -118,9 +118,9 @@ module ActiveRecord def method_missing(method, *args, &block) if @klass.respond_to?(method) - scoping { @klass.send(method, *args, &block) } + scoping { @klass.public_send(method, *args, &block) } elsif arel.respond_to?(method) - arel.send(method, *args, &block) + arel.public_send(method, *args, &block) else super end -- cgit v1.2.3 From 0b142a6f842051e3f1f3c146d1e1318050274352 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 12 Dec 2013 21:10:03 -0700 Subject: Add a bunch of Relation -> Array delegate methods to the whitelist. This won't last - aim to switch back to a blacklist for mutator methods. --- activerecord/lib/active_record/relation/delegation.rb | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index 603e5a9df5..87f5e8e684 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -36,10 +36,18 @@ module ActiveRecord # may vary depending on the klass of a relation, so we create a subclass of Relation # for each different klass, and the delegations are compiled into that subclass only. - delegate :&, :+, :[], :all?, :collect, :detect, :each, :each_cons, - :each_with_index, :flat_map, :group_by, :include?, :length, - :map, :none?, :one?, :reverse, :sample, :second, :sort, :sort_by, - :to_ary, :to_set, :to_xml, :to_yaml, :to => :to_a + # TODO: This is not going to work. Brittle, painful. We'll switch to a blacklist + # to disallow mutator methods like map!, pop, and delete_if instead. + ARRAY_DELEGATES = [ + :+, :-, :|, :&, :[], + :all?, :collect, :detect, :each, :each_cons, :each_with_index, + :exclude?, :find_all, :flat_map, :group_by, :include?, :length, + :map, :none?, :one?, :partition, :reject, :reverse, + :sample, :second, :sort, :sort_by, :third, + :to_ary, :to_set, :to_xml, :to_yaml + ] + + delegate *ARRAY_DELEGATES, to: :to_a delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :connection, :columns_hash, :to => :klass -- cgit v1.2.3 From c2f1796c6dfa1f37581471dea0a126d3efa2a7fe Mon Sep 17 00:00:00 2001 From: Arun Agrawal Date: Fri, 13 Dec 2013 17:19:20 +0100 Subject: argument prefix warning removed * interpreted as a argument prefix --- activerecord/lib/active_record/relation/delegation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index 87f5e8e684..246c5db5bd 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -47,7 +47,7 @@ module ActiveRecord :to_ary, :to_set, :to_xml, :to_yaml ] - delegate *ARRAY_DELEGATES, to: :to_a + delegate(*ARRAY_DELEGATES, to: :to_a) delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :connection, :columns_hash, :to => :klass -- cgit v1.2.3 From c758093eca303704f52b45fd0660e13600b9b315 Mon Sep 17 00:00:00 2001 From: Akshay Vishnoi Date: Mon, 16 Dec 2013 00:44:37 +0530 Subject: Spelling and Grammar check [ci skip] --- activerecord/lib/active_record/relation/query_methods.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index cacc787eba..4f4961d273 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -427,7 +427,7 @@ module ActiveRecord # === string # # A single string, without additional arguments, is passed to the query - # constructor as a SQL fragment, and used in the where clause of the query. + # constructor as an SQL fragment, and used in the where clause of the query. # # Client.where("orders_count = '2'") # # SELECT * from clients where orders_count = '2'; @@ -656,7 +656,7 @@ module ActiveRecord # when 'Reviewer' # Post.published # when 'Bad User' - # Post.none # => returning [] instead breaks the previous code + # Post.none # It can't be chained if [] is returned. # end # end # -- cgit v1.2.3 From 8062a307942cb3f7a83bfc1a8cd81e3a1f8edc5b Mon Sep 17 00:00:00 2001 From: Martin Emde Date: Mon, 16 Dec 2013 14:16:15 -0800 Subject: Better support for `where()` conditions that use an association name. Using the name of an association in `where` previously worked only if the value was a single `ActiveRecrd::Base` object. e.g. Post.where(author: Author.first) Any other values, including `nil`, would cause invalid SQL to be generated. This change supports arguments in the `where` query conditions where the key is a `belongs_to` association name and the value is `nil`, an `Array` of `ActiveRecord::Base` objects, or an `ActiveRecord::Relation` object. # Given the Post model class Post < ActiveRecord::Base belongs_to :author end # nil value finds records where the association is not set Post.where(author: nil) # SELECT "posts".* FROM "posts" WHERE "posts"."author_id" IS NULL # Array values find records where the association foreign key # matches the ids of the passed ActiveRecord models, resulting # in the same query as Post.where(author_id: [1,2]) authors_array = [Author.find(1), Author.find(2)] Post.where(author: authors_array) # ActiveRecord::Relation values find records using the same # query as Post.where(author_id: Author.where(last_name: "Emde")) Post.where(author: Author.where(last_name: "Emde")) Polymorphic `belongs_to` associations will continue to be handled appropriately, with the polymorphic `association_type` field added to the query to match the base class of the value. This feature previously only worked when the value was a single `ActveRecord::Base`. class Post < ActiveRecord::Base belongs_to :author, polymorphic: true end Post.where(author: Author.where(last_name: "Emde")) # Generates a query similar to: Post.where(author_id: Author.where(last_name: "Emde"), author_type: "Author") --- .../lib/active_record/relation/predicate_builder.rb | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index c60cd27a83..1252af7635 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -55,9 +55,9 @@ module ActiveRecord # # For polymorphic relationships, find the foreign key and type: # PriceEstimate.where(estimate_of: treasure) - if klass && value.is_a?(Base) && reflection = klass.reflect_on_association(column.to_sym) - if reflection.polymorphic? - queries << build(table[reflection.foreign_type], value.class.base_class) + if klass && reflection = klass.reflect_on_association(column.to_sym) + if reflection.polymorphic? && base_class = polymorphic_base_class_from_value(value) + queries << build(table[reflection.foreign_type], base_class) end column = reflection.foreign_key @@ -67,6 +67,18 @@ module ActiveRecord queries end + def self.polymorphic_base_class_from_value(value) + case value + when Relation + value.klass.base_class + when Array + val = value.compact.first + val.class.base_class if val.is_a?(Base) + when Base + value.class.base_class + end + end + def self.references(attributes) attributes.map do |key, value| if value.is_a?(Hash) -- cgit v1.2.3 From d4ee09cda135da9c36a5ddadd2d8c2d35c116be3 Mon Sep 17 00:00:00 2001 From: Lauro Caetano Date: Fri, 13 Dec 2013 22:11:39 -0200 Subject: Create a blacklist to disallow mutator methods to be delegated to `Array`. This change was necessary because the whitelist wouldn't work. It would be painful for users trying to update their applications. This blacklist intent to prevent odd bugs and confusion in code that call mutator methods directely on the `Relation`. --- .../lib/active_record/relation/delegation.rb | 25 ++++++++++++---------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index 246c5db5bd..21beed332f 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -1,3 +1,4 @@ +require 'set' require 'active_support/concern' require 'active_support/deprecation' @@ -36,18 +37,13 @@ module ActiveRecord # may vary depending on the klass of a relation, so we create a subclass of Relation # for each different klass, and the delegations are compiled into that subclass only. - # TODO: This is not going to work. Brittle, painful. We'll switch to a blacklist - # to disallow mutator methods like map!, pop, and delete_if instead. - ARRAY_DELEGATES = [ - :+, :-, :|, :&, :[], - :all?, :collect, :detect, :each, :each_cons, :each_with_index, - :exclude?, :find_all, :flat_map, :group_by, :include?, :length, - :map, :none?, :one?, :partition, :reject, :reverse, - :sample, :second, :sort, :sort_by, :third, - :to_ary, :to_set, :to_xml, :to_yaml - ] + BLACKLISTED_ARRAY_METHODS = [ + :compact!, :flatten!, :reject!, :reverse!, :rotate!, :map!, + :shuffle!, :slice!, :sort!, :sort_by!, :delete_if, + :keep_if, :pop, :shift, :delete_at, :compact + ].to_set # :nodoc: - delegate(*ARRAY_DELEGATES, to: :to_a) + delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, to: :to_a delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :connection, :columns_hash, :to => :klass @@ -119,14 +115,21 @@ module ActiveRecord def respond_to?(method, include_private = false) super || @klass.respond_to?(method, include_private) || + array_delegable?(method) || arel.respond_to?(method, include_private) end protected + def array_delegable?(method) + Array.method_defined?(method) && BLACKLISTED_ARRAY_METHODS.exclude?(method) + end + def method_missing(method, *args, &block) if @klass.respond_to?(method) scoping { @klass.public_send(method, *args, &block) } + elsif array_delegable?(method) + to_a.public_send(method, *args, &block) elsif arel.respond_to?(method) arel.public_send(method, *args, &block) else -- cgit v1.2.3 From db765ec741911ca8d9dfc9403f532d239301e435 Mon Sep 17 00:00:00 2001 From: Akshay Vishnoi Date: Wed, 18 Dec 2013 11:52:54 +0530 Subject: #none documentation updated [ci skip] --- activerecord/lib/active_record/relation/query_methods.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 4f4961d273..e7b8809fe0 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -631,12 +631,11 @@ module ActiveRecord self end - # Returns a chainable relation with zero records, specifically an - # instance of the ActiveRecord::NullRelation class. + # Returns a chainable relation with zero records. # - # The returned ActiveRecord::NullRelation inherits from Relation and implements the - # Null Object pattern. It is an object with defined null behavior and always returns an empty - # array of records without querying the database. + # The returned relation implements the Null Object pattern. It is an + # object with defined null behavior and always returns an empty array of + # records without querying the database. # # Any subsequent condition chained to the returned relation will continue # generating an empty relation and will not fire any query to the database. -- cgit v1.2.3 From 847e9a95da59f1263e6e5c15cd5ce5c9ec8260a6 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Mon, 16 Dec 2013 21:26:37 -0700 Subject: fix default select when from is used --- activerecord/lib/active_record/relation/query_methods.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index e7b8809fe0..3d0709266a 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -982,8 +982,10 @@ module ActiveRecord end def build_select(arel, selects) - unless selects.empty? + if !selects.empty? arel.project(*selects) + elsif from_value + arel.project(Arel.star) else arel.project(@klass.arel_table[Arel.star]) end -- cgit v1.2.3 From a2985e206709f7947ef427a527d72ff89824931a Mon Sep 17 00:00:00 2001 From: Kuldeep Aggarwal Date: Mon, 30 Dec 2013 01:53:02 +0530 Subject: raise `ArgumentError` exception if `Model.where.not` is called with `nil` argument --- activerecord/lib/active_record/relation/query_methods.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 3d0709266a..78da6a83ec 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -37,6 +37,8 @@ module ActiveRecord def not(opts, *rest) where_value = @scope.send(:build_where, opts, rest).map do |rel| case rel + when NilClass + raise ArgumentError, 'Invalid argument for .where.not(), got nil.' when Arel::Nodes::In Arel::Nodes::NotIn.new(rel.left, rel.right) when Arel::Nodes::Equality -- cgit v1.2.3 From 35cc32841ed343e3013cdf67e7330647d50d668b Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Fri, 10 Jan 2014 09:59:19 +0100 Subject: doc, API example on how to use `Model#exists?` with multiple IDs. [ci skip] Refs #13658 --- activerecord/lib/active_record/relation/finder_methods.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord/lib/active_record/relation') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 3963f2b3e0..4984dbd277 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -195,6 +195,7 @@ module ActiveRecord # Person.exists?(5) # Person.exists?('5') # Person.exists?(['name LIKE ?', "%#{query}%"]) + # Person.exists?(id: [1, 4, 8]) # Person.exists?(name: 'David') # Person.exists?(false) # Person.exists? -- cgit v1.2.3