Sortable UUIDs and overriding ActiveRecord::Base

非 Y 不嫁゛ 提交于 2021-02-06 20:00:26

问题


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:

  • http://blog.nakonieczny.it/posts/rails-support-for-uuid/
  • http://linhmtran168.github.io/blog/2014/03/17/postgres-uuid-in-rails/ ( Drawbacks section )

回答1:


First of all, first and last aren't as simple as you seem to think they are: you're completely neglecting the limit 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 abusing scope because they return single model instances rather than queries. You don't want to use scope at all, you're just trying to replace the first and last 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 what find_nth_with_limit does. You'd want to override second, third, ... and the rest of those silly methods while you're at it.

If you don't feel right about replace first and last (a good thing IMO), then you could add a default scope to order things as desired:

default_scope -> { order(:created_at) }

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 to order 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 by id is pretty much useless so you should always manually specify the order you want before using methods like first and last.




回答2:


Rails 6 (currently in version 6.0.0rc1) comes to rescue with implicit_order_column!

To order by created_at and make .first, .last, .second etc. respect it is as simple as:

class ApplicationRecord < ActiveRecord::Base
  self.implicit_order_column = :created_at
end



回答3:


After replacing id with uuid, I experienced some weirdness in the way associations were allocating foreign keys, and it wasn't that .last and .first, but instead because I simply forgot to add default: 'gen_random_uuid()' to one of the tables using a uuid. Once I fixed that, the problem was solved.

create_table :appointments, id: :uuid, default: 'gen_random_uuid()' do |t|


来源:https://stackoverflow.com/questions/30825609/sortable-uuids-and-overriding-activerecordbase

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!