R-003 — Slug de organização e produto vem do nome e não muda depois

TLDR: organizations.slug e products.slug são gerados do name via parameterize, sem limite de tamanho, e congelados depois de gravados. Único global nos dois — organização e produto.

Given / When / Then

Dado uma organização ou produto sendo salvo com slug em branco, Quando o registro é validado, Então o slug recebe o name passado por parameterize, sem truncamento.

Dado o slug preenchido à mão, Quando o registro é validado, Então o valor é normalizado pela mesma regra, em vez de ser aceito como veio.

Dado um registro que já tem slug gravado, Quando o name muda, Então o slug permanece o mesmo — é identificador estável para o ibft-backend resolver.

Dado um slug já usado no escopo de unicidade, Quando outro registro é validado com o mesmo slug, Então o registro é inválido e errors[:slug] é preenchido.

Tabela de decisão

Entidade name slug Válido?
Organization Curso de Inglês curso-de-ingles ✅
Organization Curso v1.0 curso-v1-0 ✅
Organization Curso A 1, com curso-a-1 existente curso-a-1 ❌ colisão global
Product (org X) Curso A 1, com curso-a-1 na org X curso-a-1 ❌ colisão global
Product (org Y) Curso A 1, com curso-a-1 na org X curso-a-1 ❌ colisão global — mesmo em org diferente
Organization @@@ "" ✅ allow_blank

Restrições

  • Coluna anulável e sem validação de presença: organização sem slug simplesmente não sincroniza.
  • Sem limite de tamanho: Slugable não tem opção de truncamento, é o mesmo concern usado por Checkout e CustomFieldOption, sem tratamento especial para organização e produto.
  • Colisão é erro de validação em registro novo, mas o backfill da migration resolve com sufixo -2/-3 — lá não há operador para corrigir.

Teste vinculado

spec/models/organization_spec.rb / spec/models/product_spec.rb — bloco describe "slug": derivação, normalização, estabilidade no rename e colisão (única global nos dois, mesmo em organizações diferentes).

Referências