Novos operadores de comparação do Rails

Tutoriais - 20/Jan/2021 - por Henrique Morato

Queries em Rails, quando usadas com alguns operadores de comparação, podem ficar pouco legíveis e extensas. Para melhorar isso, o colaborador de longa data do Rails Ryuta Kamizono (@kamipo) trouxe uma solução neste Pull Request.

A funcionalidade, apesar de estar na branch principal, ainda está em testes e não tem previsão da versão de Rails na qual será integrada. O próprio @kamipo está fazendo algumas mudanças num outro branch onde estão sendo discutidas outras sintaxes.

Quais problemas queremos resolver?

À medida que aplicações crescem em tamanho, queries que usam strings vão ficando mais comuns. Mas, quando juntam cláusulas de where, nem sempre geram queries corretas.

Nesse ponto a sintaxe de hash do Rails é superior em gerar consultas quase sempre corretamente.

Com a mudança proposta, nós podemos evoluir queries como:

published_posts = Posts.where('published_at <= ?', Date.current)

para sintaxe de hash, tornando mais fáceis de utilizá-las em scopes, juntar com outras queries e outras funcionalidades. Por exemplo:

published_posts = Posts.where('published_at <=': Date.current)

Isso é extensível para os operadores >, <, <= e >=.

Impactos da mudança

O exemplo abaixo é citado pelo autor da funcionalidade do texto para definir como essa pequena mudança pode mudar totalmente a query.

class Post < ActiveRecord::Base
  attribute :created_at, :datetime, precision: 3
end

time = Time.now.utc # => 2020-06-24 10:11:12.123456 UTC

Post.create!(created_at: time) # => #<Post id: 1, created_at: "2020-06-24 10:11:12.123000">

# SELECT `posts`.* FROM `posts` WHERE (created_at >= '2020-06-24 10:11:12.123456')
Post.where("created_at >= ?", time) # => []

# SELECT `posts`.* FROM `posts` WHERE `posts`.`created_at` >= '2020-06-24 10:11:12.123000'
Post.where("created_at >=": time) # => [#<Post id: 1, created_at: "2020-06-24 10:11:12.123000">]

Dessa maneira, tornamos queries mais confiáveis usando sintaxe de hash.

Considerações

Várias mudanças ainda podem ser incluídas e as discussões continuam abertas, mas essa é uma funcionalidade com bastante potencial de expansão, inclusive para outros operadores.

Claro que esse texto não podia passar batido sem uma funcionalidade já incluída no Ruby que resolve alguns desses problemas beginless/endless ranges.

Dando uma alternativa a queries que antes não poderiam usar hash:

Post.where(id: 1..)

Post.where(id: ..10)

# Exemplos mais legais

Posts.where(published: Date.current..)
# SELECT "posts".* FROM "posts" WHERE "posts"."created_at" >= ? LIMIT ?  [["created_at", "2020-12-15"], ["LIMIT", 11]]

Posts.where(created_at: ...Date.current)
# SELECT "posts".* FROM "posts" WHERE "posts"."created_at" < ? LIMIT ?  [["created_at", "2020-12-15"], ["LIMIT", 11]]

Esperamos falar mais dessa sintaxe hash e mudanças futuras em breve, mas por esse texto é só :)

Foto de perfil do autor
Henrique Morato

Dev