Search code examples
postgresqldeviseuuidruby-on-rails-4

Modify Devise to support UUID primary key


I want to modify Devise to make it work with a users table with a UUID primary key with PostgreSQL.

Here is the migration:

class DeviseCreateUsers < ActiveRecord::Migration

  def change
    create_table :users, id: false do |t|
      t.uuid :uuid, null: false
      # ...
    end

    change_table :users do |t|
      t.index :uuid, unique: true
      # ...
    end
  end

  def migrate(direction)
    super
    if direction == :up
      # This is only necessary because the following does not work:
      # t.uuid :uuid, primary: true, null: false
      execute "ALTER TABLE users ADD PRIMARY KEY (uuid);"
    end
  end
end

Here is the User model:

class User < ActiveRecord::Base

  primary_key = :uuid

  devise :database_authenticatable, :recoverable, :registerable,
    :rememberable, :trackable, :validatable

  validates :uuid, presence: true
  before_validation :ensure_uuid
  def ensure_uuid; self.uuid ||= SecureRandom.uuid end

end

Here is the error:

PG::Error: ERROR:  operator does not exist: uuid = integer
LINE 1: ...ECT  "users".* FROM "users"  WHERE "users"."uuid" = 1  ORDER...
                                                             ^
HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.
: SELECT  "users".* FROM "users"  WHERE "users"."uuid" = 1  ORDER BY "users"."uuid" ASC LIMIT 1

Extracted source (around line #5):

1    .navbar-inner
2      .container
3        = a_nav_tag "App", root_path
4        - if user_signed_in?
5          %ul.nav.pull-right
6            %li.dropdown#user_menu
7              %a.dropdown-toggle(data-toggle="dropdown" href="#")

As you can see above, user_signed_in? is broken. I expect there are several changes needed to move from a 'normal' auto-incrementing ID to a UUID.

For now, I'm just posting the question. I'll take a swing at this later today. If you happen to know how to do this -- or know of a Devise fork, I'd appreciate it.


Solution

  • Just clear your browser's cookie for the web app (in my case, localhost). The error above is caused because the session was retaining the old user primary key, 1.

    After that, things work in my testing. I hope this isn't just luck, it would be a good design if Devise was agnostic about the primary key. (In Devise's code, I saw no use of .id except in some tests.)