aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md10
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb8
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb49
-rw-r--r--activerecord/test/cases/invalid_connection_test.rb2
-rw-r--r--activerecord/test/cases/relations_test.rb29
5 files changed, 80 insertions, 18 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 2551841aaf..9144ab6695 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,13 @@
+* When calling `first` with a `limit` argument, return directly from the
+ `loaded?` records if available.
+
+ *Ben Woosley*
+
+* Deprecate sending the `offset` argument to `find_nth`. Please use the
+ `offset` method on relation instead.
+
+ *Ben Woosley*
+
## Rails 5.0.0.beta1 (December 18, 2015) ##
* Order the result of `find(ids)` to match the passed array, if the relation
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 37e4eb24a8..863d8d3b8f 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -623,6 +623,7 @@ module ActiveRecord
# it can be helpful to provide these in a migration's +change+ method so it can be reverted.
# In that case, +options+ and the block will be used by create_table.
def drop_table(table_name, options = {})
+ create_table_info_cache.delete(table_name) if create_table_info_cache.key?(table_name)
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
end
@@ -1018,9 +1019,12 @@ module ActiveRecord
end
end
+ def create_table_info_cache # :nodoc:
+ @create_table_info_cache ||= {}
+ end
+
def create_table_info(table_name) # :nodoc:
- @create_table_info_cache = {}
- @create_table_info_cache[table_name] ||= select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
+ create_table_info_cache[table_name] ||= select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
end
def create_table_definition(name, temporary = false, options = nil, as = nil) # :nodoc:
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 19244bcf95..3cbb12a09d 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -117,9 +117,9 @@ module ActiveRecord
#
def first(limit = nil)
if limit
- find_nth_with_limit(offset_index, limit)
+ find_nth_with_limit_and_offset(0, limit, offset: offset_index)
else
- find_nth(0, offset_index)
+ find_nth 0
end
end
@@ -169,7 +169,7 @@ module ActiveRecord
# Person.offset(3).second # returns the second object from OFFSET 3 (which is OFFSET 4)
# Person.where(["user_name = :u", { u: user_name }]).second
def second
- find_nth(1, offset_index)
+ find_nth 1
end
# Same as #second but raises ActiveRecord::RecordNotFound if no record
@@ -185,7 +185,7 @@ module ActiveRecord
# Person.offset(3).third # returns the third object from OFFSET 3 (which is OFFSET 5)
# Person.where(["user_name = :u", { u: user_name }]).third
def third
- find_nth(2, offset_index)
+ find_nth 2
end
# Same as #third but raises ActiveRecord::RecordNotFound if no record
@@ -201,7 +201,7 @@ module ActiveRecord
# Person.offset(3).fourth # returns the fourth object from OFFSET 3 (which is OFFSET 6)
# Person.where(["user_name = :u", { u: user_name }]).fourth
def fourth
- find_nth(3, offset_index)
+ find_nth 3
end
# Same as #fourth but raises ActiveRecord::RecordNotFound if no record
@@ -217,7 +217,7 @@ module ActiveRecord
# Person.offset(3).fifth # returns the fifth object from OFFSET 3 (which is OFFSET 7)
# Person.where(["user_name = :u", { u: user_name }]).fifth
def fifth
- find_nth(4, offset_index)
+ find_nth 4
end
# Same as #fifth but raises ActiveRecord::RecordNotFound if no record
@@ -233,7 +233,7 @@ module ActiveRecord
# Person.offset(3).forty_two # returns the forty-second object from OFFSET 3 (which is OFFSET 44)
# Person.where(["user_name = :u", { u: user_name }]).forty_two
def forty_two
- find_nth(41, offset_index)
+ find_nth 41
end
# Same as #forty_two but raises ActiveRecord::RecordNotFound if no record
@@ -488,27 +488,39 @@ module ActiveRecord
end
end
- def find_nth(index, offset)
+ def find_nth(index, offset = nil)
if loaded?
@records[index]
else
- offset += index
- @offsets[offset] ||= find_nth_with_limit(offset, 1).first
+ # TODO: once the offset argument is removed we rely on offset_index
+ # within find_nth_with_limit, rather than pass it in via
+ # find_nth_with_limit_and_offset
+ if offset
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
+ Passing an offset argument to find_nth is deprecated,
+ please use Relation#offset instead.
+ MSG
+ else
+ offset = offset_index
+ end
+ @offsets[offset + index] ||= find_nth_with_limit_and_offset(index, 1, offset: offset).first
end
end
def find_nth!(index)
- find_nth(index, offset_index) or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
+ find_nth(index) or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
end
- def find_nth_with_limit(offset, limit)
+ def find_nth_with_limit(index, limit)
+ # TODO: once the offset argument is removed from find_nth,
+ # find_nth_with_limit_and_offset can be merged into this method
relation = if order_values.empty? && primary_key
order(arel_table[primary_key].asc)
else
self
end
- relation = relation.offset(offset) unless offset.zero?
+ relation = relation.offset(index) unless index.zero?
relation.limit(limit).to_a
end
@@ -524,5 +536,16 @@ module ActiveRecord
end
end
end
+
+ private
+
+ def find_nth_with_limit_and_offset(index, limit, offset:) # :nodoc:
+ if loaded?
+ @records[index, limit]
+ else
+ index += offset
+ find_nth_with_limit(index, limit)
+ end
+ end
end
end
diff --git a/activerecord/test/cases/invalid_connection_test.rb b/activerecord/test/cases/invalid_connection_test.rb
index c26623e3ca..a16b52751a 100644
--- a/activerecord/test/cases/invalid_connection_test.rb
+++ b/activerecord/test/cases/invalid_connection_test.rb
@@ -1,5 +1,6 @@
require "cases/helper"
+if current_adapter?(:Mysql2Adapter)
class TestAdapterWithInvalidConnection < ActiveRecord::TestCase
self.use_transactional_tests = false
@@ -20,3 +21,4 @@ class TestAdapterWithInvalidConnection < ActiveRecord::TestCase
assert_equal "#{Bird.name} (call '#{Bird.name}.connection' to establish a connection)", Bird.inspect
end
end
+end
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 7149c7d072..0638edacbd 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -111,15 +111,38 @@ class RelationTest < ActiveRecord::TestCase
def test_loaded_first
topics = Topic.all.order('id ASC')
+ topics.to_a # force load
- assert_queries(1) do
- topics.to_a # force load
- 2.times { assert_equal "The First Topic", topics.first.title }
+ assert_no_queries do
+ assert_equal "The First Topic", topics.first.title
end
assert topics.loaded?
end
+ def test_loaded_first_with_limit
+ topics = Topic.all.order('id ASC')
+ topics.to_a # force load
+
+ assert_no_queries do
+ assert_equal ["The First Topic",
+ "The Second Topic of the day"], topics.first(2).map(&:title)
+ end
+
+ assert topics.loaded?
+ end
+
+ def test_first_get_more_than_available
+ topics = Topic.all.order('id ASC')
+ unloaded_first = topics.first(10)
+ topics.to_a # force load
+
+ assert_no_queries do
+ loaded_first = topics.first(10)
+ assert_equal unloaded_first, loaded_first
+ end
+ end
+
def test_reload
topics = Topic.all