Usando UUID no Rails 7 com PostgreSQL
Tags: postgres, rails, uuid
Published: 2022-12-06 12:43:50

Esse post vai ser uma rápida instalação de uuid no rails 7.

Vamos começar criando uma nova aplicação e depois vamos tentar trocar o id padrão para uuid em um projeto existente.

Se você não sabe o que é uuid, eu recomendo esse [post](https://medium.com/trainingcenter/o-que-%C3%A9-uuid-porque-us%C3%A1-lo-ad7a66644a2b).

## Índice

* [Criando um projeto] (#novo)
* [Trocando id padrão para uuid em um projeto existente] (#existente)

***

## Criando um projeto <a name="novo"></a>

A nossa aplicação vai usar o PostgreSQL como banco de dado.

```bash
rails new uuid_rails -d=postgresql
```

Para ser capaz de usar uuid no postgresql, tem que ativar a extensão `pgcrypto`.

Então vamos criar uma migração para isso.

```bash
rails g migration enable_uuid
```

Abra o arquivo `db/migrate/enable_uuid.rb` e adicione o `enable_extension 'pgcrypto'`, como você pode ver abaixo.

```ruby
# db/migrate/enable_uuid
class EnableUuid < ActiveRecord::Migration[7.0]
  def change
    enable_extension 'pgcrypto'
  end
end
```

Uma rápida explicação sobre esse `pgcrypto`.

Essa extensão permite que nós sejamos capaz de usar função de criptografia e tem uma função chamado `gen_random_uuid` que ele gera um uuid para gente.

Bem, agora vamos colocar o uuid como chave primária.

## Definindo o uuid como chave primária

Vamos criar um generator para evitar o trabalho manual.

Crie um arquivo chamado de `generators.rb` no `config/initializers/` e coloque o código a seguir.

```ruby
# config/initializers/generators.rb
Rails.application.config.generators do |g|
  g.orm :active_record, primary_key_type: :uuid
end
```

Agora qualquer modelo criado, vai usar o uuid como chave primária.

Então vamos criar um modelo para ver que está funcionando.

## Criando o nosso modelo

Vamos criar um modelo chamado de publicação com título.

```bash
rails g model Publicacao titulo
```

Abra o arquivo `db/migrate/create_publicacaos.rb` para observar um coisa.

```ruby
# db/migrate/create_publicacaos.rb
class CreatePublicacaos < ActiveRecord::Migration[7.0]
  def change
    create_table :publicacaos, id: :uuid do |t|
      t.string :titulo

      t.timestamps
    end
  end
end
```

Podemos ver que ele está passando um argumento chamado id com valor `:uuid`, Então até aqui, ele fez um trabalho correto.

Agora vamos criar o banco de dados e migrar.

```bash
rails db:create db:migrate
```

Abra o `rails console` para criar uma nova publicação.

```irb
rails console

irb(main):001:0> Publicacao.create(titulo: "Nossa primeira publicação")

  TRANSACTION (0.4ms)  BEGIN
  Publicacao Create (7.0ms)  INSERT INTO "publicacaos" ("titulo", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["titulo", "Nossa primeira publicação"], ["created_at", "2022-12-06 10:43:48.903056"], ["updated_at", "2022-12-06 10:43:48.903056"]]
  TRANSACTION (1.3ms)  COMMIT
=> 
#<Publicacao:0x00007fe438a57340
 id: "fb030e22-5d0d-47d3-a856-cc5a028da2d3",
 titulo: "Nossa primeira publicação",
 created_at: Tue, 06 Dec 2022 10:43:48.903056000 UTC +00:00,
 updated_at: Tue, 06 Dec 2022 10:43:48.903056000 UTC +00:00>

```

Podemos ver que o id está com formato de uuid, então vamos criar outro para confirmar.

```irb
irb(main):002:0> Publicacao.create(titulo: "Nossa segunda publicação")

  TRANSACTION (0.9ms)  BEGIN
  Publicacao Create (0.9ms)  INSERT INTO "publicacaos" ("titulo", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["titulo", "Nossa segunda publicação"], ["created_at", "2022-12-06 10:45:00.606342"], ["updated_at", "2022-12-06 10:45:00.606342"]]
  TRANSACTION (4.0ms)  COMMIT
=> 
#<Publicacao:0x00007fe43a2b6490
 id: "4f539d04-43d4-427a-bcc8-b3fc54f3c3db",
 titulo: "Nossa segunda publicação",
 created_at: Tue, 06 Dec 2022 10:45:00.606342000 UTC +00:00,
 updated_at: Tue, 06 Dec 2022 10:45:00.606342000 UTC +00:00>
```

De fato, está criando o id como uuid.

Com isso, acabamos aqui, mas agora vamos para aqueles que quer mudar o id padrão para uuid com um projeto existente.

## Trocando id padrão para uuid em um projeto existente <a name="existente"></a>

Eu recomendo que você crie um backup do seu banco de dados antes de tentar aplicar isso no projeto existente.

Vamos criar um aplicação que use id padrão e depois mudarei ele.

```bash
rails new id_para_uuid_rails -d=postgresql

cd id_para_uuid_rails/
rails g model Publicacao titulo
rails db:create db:migrate
```

Agora vamos criar milhares de publicação.

Vamos para `rails console`.

```irb
rails console

irb(main):001:1> 1000.times do |i|
irb(main):002:1*   Publicacao.create titulo: i
irb(main):003:1> end
...
```

Pronto nós temos mil publicações e eles estão usando id padrão, agora vamos transformar eles em uuid.

Como eu estou usando PostgreSQL como banco de dado, vamos adicionar a extensão `pgcrypto`.

Para isso, vamos criar uma migração.

```bash
rails g migration enable_uuid
```

Agora vamos adicionar `enable_extension 'pgcrypto'` no `db/migrate/enable_uuid`.

```ruby
# db/migrate/enable_uuid
class EnableUuid < ActiveRecord::Migration[7.0]
  def change
    enable_extension 'pgcrypto'
  end
end
```

Agora vamos mudar o id.

Crie uma migração para fazer isso.

```bash
rails g migration IdToUUID
```

E adicione o código abaixo no `db/migrate/id_to_uuid`.

```ruby
# db/migrate/id_to_uuid 
class IdToUuid < ActiveRecord::Migration[7.0]
  def change
    add_column :publicacaos, :uuid, :uuid, default: "gen_random_uuid()", null: false

    change_table :publicacaos do |t|
      t.remove :id
      t.rename :uuid, :id
    end

    execute "ALTER TABLE publicacaos ADD PRIMARY KEY (id);"
  end
end
```

OBS: Se você está mudando em seu projeto, basta trocar a tabela publicacaos para a tabela que você está mudando, e se você quer mudar todos as tabelas, vai tem que copiar e colar, por exemplo:

```ruby
# db/migrate/id_to_uuid 
class IdToUuid < ActiveRecord::Migration[7.0]
  def change
    add_column :publicacaos, :uuid, :uuid, default: "gen_random_uuid()", null: false
    add_column :comentarios, :uuid, :uuid, default: "gen_random_uuid()", null: false


    change_table :publicacaos do |t|
      t.remove :id
      t.rename :uuid, :id
    end

    change_table :comentarios do |t|
      t.remove :id
      t.rename :uuid, :id
    end

    execute "ALTER TABLE publicacaos ADD PRIMARY KEY (id);"
    execute "ALTER TABLE comentarios ADD PRIMARY KEY (id);"
  end
end
```

Bem, vamos continuar.

Agora vamos migrar e rodar o `rails console` para ver se funcionou.

```irb
rails db:migrate
...
rails console

irb(main):001:0> Publicacao.first
  Publicacao Load (1.6ms)  SELECT "publicacaos".* FROM "publicacaos" ORDER BY "publicacaos"."id" ASC LIMIT $1  [["LIMIT", 1]]                            
=>                                                                    
#<Publicacao:0x00007f7cd4d96838                                       
 titulo: "299",                                                       
 created_at: Tue, 06 Dec 2022 12:00:24.017099000 UTC +00:00,          
 updated_at: Tue, 06 Dec 2022 12:00:24.017099000 UTC +00:00,          
 id: "002b5dbd-6087-4437-959a-32c06cf96c11"> 
```

Podemos ver que o id está de fato em uuid, mas vamos ver se todos estão realmente em formato de uuid.

```irb
irb(main):002:0> Publicacao.all.all? { |p| p.id.match? /^.{8}-.{4}-.{4}-.{17}$/ }
  Publicacao Load (4.0ms)  SELECT "publicacaos".* FROM "publicacaos"
=> true
```

Deu `true`, quer dizer que todos eles estão como uuid.

Com isso, nós acabamos aqui.

Caso tenha dado erro, comente o erro, que tentarei te ajudar.

Bem, então é isso, Tchau!