diff options
author | Jason Meller <jason.meller@mandiant.com> | 2014-01-18 00:51:34 -0500 |
---|---|---|
committer | Jason Meller <jason.meller@mandiant.com> | 2014-01-20 16:58:18 -0500 |
commit | cafe31a078276dbf941bd8b30f0caddc878c0830 (patch) | |
tree | 5c6b3f1d480c2d5aa2600dd144b932c950394a11 /activerecord/lib/active_record | |
parent | 5a09f13a34426a02a305a12dfc90ced9bdd6c46e (diff) | |
download | rails-cafe31a078276dbf941bd8b30f0caddc878c0830.tar.gz rails-cafe31a078276dbf941bd8b30f0caddc878c0830.tar.bz2 rails-cafe31a078276dbf941bd8b30f0caddc878c0830.zip |
Ensure #second acts like #first AR finder
This commit bring the famous ordinal Array instance methods defined
in ActiveSupport into ActiveRecord as fully-fledged finders.
These finders ensure a default ascending order of the table's primary
key, and utilize the OFFSET SQL verb to locate the user's desired
record. If an offset is defined in the query, calling #second adds
to the offset to get the actual desired record.
Fixes #13743.
Diffstat (limited to 'activerecord/lib/active_record')
-rw-r--r-- | activerecord/lib/active_record/querying.rb | 1 | ||||
-rw-r--r-- | activerecord/lib/active_record/relation.rb | 4 | ||||
-rw-r--r-- | activerecord/lib/active_record/relation/finder_methods.rb | 94 |
3 files changed, 91 insertions, 8 deletions
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb index fd4c973504..ef138c6f80 100644 --- a/activerecord/lib/active_record/querying.rb +++ b/activerecord/lib/active_record/querying.rb @@ -1,6 +1,7 @@ module ActiveRecord module Querying delegate :find, :take, :take!, :first, :first!, :last, :last!, :exists?, :any?, :many?, to: :all + delegate :second, :second!, :third, :third!, :fourth, :fourth!, :fifth, :fifth!, :forty_two, :forty_two!, to: :all delegate :first_or_create, :first_or_create!, :first_or_initialize, to: :all delegate :find_or_create_by, :find_or_create_by!, :find_or_initialize_by, to: :all delegate :find_by, :find_by!, to: :all diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 745c6cf349..f152891888 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -20,10 +20,11 @@ module ActiveRecord alias :model :klass alias :loaded? :loaded - def initialize(klass, table, values = {}) + def initialize(klass, table, values = {}, offsets = {}) @klass = klass @table = table @values = values + @offsets = offsets @loaded = false end @@ -498,6 +499,7 @@ module ActiveRecord @first = @last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil @should_eager_load = @join_dependency = nil @records = [] + @offsets = {} self end diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 4984dbd277..f2ac351a8b 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -127,9 +127,9 @@ module ActiveRecord # def first(limit = nil) if limit - find_first_with_limit(limit) + find_nth_with_limit(offset_value, limit) else - find_first + find_nth(offset_value) end end @@ -172,6 +172,86 @@ module ActiveRecord last or raise RecordNotFound end + # Find the second record. + # If no order is defined it will order by primary key. + # + # Person.second # returns the second object fetched by SELECT * FROM people + # 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(offset_value ? offset_value + 1 : 1) + end + + # Same as +second+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record + # is found. + def second! + second or raise RecordNotFound + end + + # Find the third record. + # If no order is defined it will order by primary key. + # + # Person.third # returns the third object fetched by SELECT * FROM people + # 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(offset_value ? offset_value + 2 : 2) + end + + # Same as +third+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record + # is found. + def third! + third or raise RecordNotFound + end + + # Find the fourth record. + # If no order is defined it will order by primary key. + # + # Person.fourth # returns the fourth object fetched by SELECT * FROM people + # 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(offset_value ? offset_value + 3 : 3) + end + + # Same as +fourth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record + # is found. + def fourth! + fourth or raise RecordNotFound + end + + # Find the fifth record. + # If no order is defined it will order by primary key. + # + # Person.fifth # returns the fifth object fetched by SELECT * FROM people + # 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(offset_value ? offset_value + 4 : 4) + end + + # Same as +fifth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record + # is found. + def fifth! + fifth or raise RecordNotFound + end + + # Find the forty-second record. Also known as accessing "the reddit". + # If no order is defined it will order by primary key. + # + # Person.forty_two # returns the forty-second object fetched by SELECT * FROM people + # Person.offset(3).forty_two # returns the fifth object from OFFSET 3 (which is OFFSET 44) + # Person.where(["user_name = :u", { u: user_name }]).forty_two + def forty_two + find_nth(offset_value ? offset_value + 41 : 41) + end + + # Same as +forty_two+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record + # is found. + def forty_two! + forty_two or raise RecordNotFound + end + # 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: # @@ -364,19 +444,19 @@ module ActiveRecord end end - def find_first + def find_nth(offset) if loaded? @records.first else - @first ||= find_first_with_limit(1).first + @offsets[offset] ||= find_nth_with_limit(offset, 1).first end end - def find_first_with_limit(limit) + def find_nth_with_limit(offset, limit) if order_values.empty? && primary_key - order(arel_table[primary_key].asc).limit(limit).to_a + order(arel_table[primary_key].asc).limit(limit).offset(offset).to_a else - limit(limit).to_a + limit(limit).offset(offset).to_a end end |