Utilizando Scopes em Ruby on Rails
Tags: rails, activerecord, ruby, tutorial
Published: 2022-04-29 13:31:07

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:

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

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.

```ruby
class User < ApplicationRecord
  scope :find_by_range_date, -> (start_date, end_date) { where(”created_at >= ? AND created_at <= ?“, start_date, end_date)}
end
```

## Scope vs Default Scope

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

```ruby
class User < ApplicationRecord
 default_scope { where(admin: true) }
end
```

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: 

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

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á!