I'm wanting to use UUIDs in an app I'm building and am running into a bit of a problem. Due to UUIDs (v4) not being sortable because they're randomly generated, I'm trying to override ActiveRecord::Base#first, but Rails isn't too pleased with that. It yells at me saying ArgumentError: You tried to define a scope named "first" on the model "Item", but Active Record already defined a class method with the same name.
Do I have to use a different method if I want to sort and have it sort correctly?
Here's the sauce:
# lib/sortable_uuid.rb
module SortableUUID
def self.included(base)
base.class_eval do
scope :first, -> { order("created_at").first }
scope :last, -> { order("created_at DESC").first }
end
end
end
# app/models/item.rb
class Item < ActiveRecord::Base
include SortableUUID
end
Rails 4.2, Ruby 2.2.2
Reference:
First of all,
first
andlast
aren't as simple as you seem to think they are: you're completely neglecting thelimit
argument that both of those methods support.Secondly,
scope
is little more than a fancy way of adding class methods that are intended to return queries. Your scopes are abusingscope
because they return single model instances rather than queries. You don't want to usescope
at all, you're just trying to replace thefirst
andlast
class methods so why don't you just override them? You'd need to override them properly though and that will require reading and understanding the Rails source so that you properly mimic whatfind_nth_with_limit
does. You'd want to overridesecond
,third
, ... and the rest of those silly methods while you're at it.If you don't feel right about replace
first
andlast
(a good thing IMO), then you could add a default scope to order things as desired:Of course, default scopes come with their own set of problems and sneaking things into the ORDER BY like this will probably force you into calling
reorder
any time you actually want to specify the ORDER BY; remember that multiple calls toorder
add new ordering conditions, they don't replace one that's already there.Alternatively, if you're using Rails6+, you can use Markus's
implicit_order_column
solution to avoid all the problems that default scopes can cause.I think you're going about this all wrong. Any time I see
M.first
I assume that something has been forgotten. Ordering things byid
is pretty much useless so you should always manually specify the order you want before using methods likefirst
andlast
.