DEV Community

Gabriel Schiavo
Gabriel Schiavo

Posted on • Updated on

Utilizando Scopes em Ruby on Rails

ActiveRecord é uma das engines dentro do core Rails, que trás consigo a implementação de um ORM (Object Relational Mapping), que é um conceito dentro da arquitetura de software na qual utilizamos técnicas de programação para converter/lidar com dados entre o banco de dados e a programação orientada a objetos, nesse caso, utilizando Ruby.

Ruby on Rails é um poderoso framework MVC que trás consigo várias soluções flexiveis e inteligentes para problemas já conhecidos dentro do desenvolvimento back-end.
Podemos citar algumas soluções que vem trazendo bastante poder aos desenvolvedores, porém hoje, trago uma funcionalidade que podemos utilizar nativamente em Rails, visto que vamos utilizar a RailsEngine::ActiveRecord

O funcionamento por “baixo dos panos” é poder fazer queries em SQL puro, porém utilizando os conceitos e métodos através de uma linguagem padrão.

Scopes em Ruby on Rails

• O que são scopes?

Geralmente utilizamos scopes para construir queries personalizadas dentro das entidades modelo da aplicação, e além disso, podemos utilizar o scope criado como um método de classe. Assim, fazemos uso desses scopes como qualquer outro método presente no ActiveRecord.

Sintaxe e parâmetros

Todo scope precisa receber dois argumentos, são eles:

• 1. Um nome, que será usado para chamar este scope no código.

• 2. Uma lambda, que implementa a query.

A sintaxe de um scope é definido por:

scope :name_of_your_scope, -> { where(“column_name = ?”, value)}

Também podemos passar diferentes parâmetros para utilizarmos ao fazer as nossas buscas. Para elucidar o conceito, suponhamos que seja necessário buscar em uma base de dados, os usuários que foram criados nos ultimos 30 dias.

Podemos declarar o scope para receber o parametro date da seguinte maneira:

class User < ApplicationRecord
  scope :get_users_by_date, -> (date) { where(*"created_at > ?"*, 30.days.ago if date.nil?) }
end
Enter fullscreen mode Exit fullscreen mode

Caso não seja passado nenhuma data, colocamos por padrão 30 dias a partir do dia de hoje.

Como resultado da chamada deste scope, recebemos um objeto do tipo ActiveRecord::Relation. Isso significa que podemos encadear e combinar diferentes scopes. Também, em termos de funcionalidade, o scope garante que erros sejam evitados caso o retorno da nossa query venha vazio.

Scopes com SQL puro

Outro jeito válido de declararmos scopes, é utilizando queries com SQL puro, assim conseguimos passar um ou mais parâmetros no scope e utilizá-los dentro da query, e claro, evitando injeção de outros parâmetros desnecessários na busca.

class User < ApplicationRecord
  scope :find_by_range_date, -> (start_date, end_date) { where(created_at >= ? AND created_at <= ?“, start_date, end_date)}
end
Enter fullscreen mode Exit fullscreen mode

Scope vs Default Scope

Quando declaramos um default_scope, garantimos que por padrão, essa query será aplicada automaticamente em nosso modelo.

class User < ApplicationRecord
 default_scope { where(admin: true) }
end
Enter fullscreen mode Exit fullscreen mode

Esse comportamento redefine por padrão o método .all do nosso modelo, portando devemos evitar seu uso, visto que alguns erros e tempo de depuração não sejam perdidos.

Conclusão

Um dos grandes potenciais do Rails é poder combinar ou encadear outros scopes diferentes, assim podemos criar queries mais fáceis de ler, ex:

def index
  date = Date.new(12/21/2021)
  @users = User.sort_by_creation(date).some_other_scope.where.not(role: admin)
end
Enter fullscreen mode Exit fullscreen mode

Assim, ao utilizarmos outros scopes e até os métodos padrões do ActiveRecord, podemos separar de forma mais limpa e clara, as responsabilidades e funcionalidades de cada query presente **nos modelos.

Outro ponto que não abordei ao longo deste guia, foi o uso de Lambdas para escrevermos os scopes utilizando apenas uma linha. Isso acontece pois a lambda é um objeto do tipo Proc. Basicamente, um proc, é um objeto que encapsula um bloco de código, exemplo:

lambda = ->(x) { x**2 }

lambda = Proc.new {|x| x**2 }

Ao fazer lambda.(3) ou lambda.call(3) em ambas as variavéis, vamos receber o resultado 9. Mas isso deixo pra abordar em meu proximo aprendizado, até mais, nos vemos lá!

Top comments (0)