Aprendiendo a usar Scopes en Ruby on Rails

Marcelo Alarcon
4 min readMay 16, 2024

Suponte que tienes un modelo Movie que incluye todas las películas que quieres mostrar en tu sitio web pero desplegarlas todas de una vez no es tan útil para tus usuarios. Ellos quieren poder filtrarlas por categorías, por fecha de lanzamiento, por país de origen o por las ultimas novedades añadidas.

En este artículo, te mostraré cómo mejorar tu sitio web usando el metodo provisto por ruby on rails scope.

Tu tabla sería algo como esto, aunque puedes poner mas campos y relaciones externas como con una tabla artistas, pero no nos vamos a complicar con eso asi que solo tendriamos algo como esto en nuestro schema.rb:

create_table "movies", force: :cascade do |t|
t.string "title"
t.integer "year"
t.string "category"
t.string "country"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end

Solución # 1 (Crear un Helper)

Crear un Helper con métodos que ejecute el query que queremos en nuestro modelo Movies:

module MoviesHelper


def filter_by_newest
Movie.order(created_at: :desc)
end


def filter_by_category(category)
Movie.where(category: category)
end


def old_movies
Movie.where(year: ...30.years.ago.year)
end
end

Solución #2 (Crear métodos en el modelo)

Crear los mismos métodos en el Modelo. Nota que en el Modelo se debe usar la palabra reservada self antes del nombre del método porque queremos que sea un método de clase o sea se pueda acceder de la siguiente forma: Movie.filter_by_category(‘action’) y como es un método de clase dentro de nuestro query ya no necesitamos usar el nombre del Modelo por eso solo se usa where directamente…

class Movie < ApplicationRecord


def filter_by_newest
Movie.order(created_at: :desc)
end


def self.filter_by_category(category)
where(category: category)
end


def self.old_movies
where(year: ...30.years.ago.year)
end

Entendiendo el Problema…

Si solo vas a usar en tu modelo uno o dos filtros las soluciones planteadas arriba estarian bien, pero imaginte que tengas que aplicar unos 20 filtros o más? Te das cuenta cual es el problema? tu modelo quedaría lleno de métodos para filtrar en la base de datos lo que ocasionaría que tu código se sea difícil de leer, desorganizado y poco profesional. Así no te van a contratar pues man! 🤬

Demos la bienvenida a los Scopes:

Ruby on Rails es un framework super completo y proporciona un método llamado scopes. Este método te permite crear queries o consultas a la base de datos según ciertas reglas que tu defines para luego usarlas directamente en tu modelo.

Scopes

Lo único que tienes que hacer es al principio de tu modelo usar el método y dentro de un lambda poner el query que quieras ejecutar:

scope :nombre_de_tu_metodo, -> { query_que_quieras }

WTF? que es esto??? 🤯🤢, tranquilo, mira es un método de rails imaginatelo así:

scope(:nombre_de_tu_metodo, &lambda)

Ahora si? 😉

Ejemplo básico:

# Este scope nos devolverá todas las pelis que sean de EEUU.
scope :from_eeuu, -> { where(country: ‘eeuu’) }

# Y luego lo podriamos usar así:
eeuu_movies = Movies.from_eeuu

# La variable eeuu_movies contendrá todas las películas que tienen
# como país EEUU por ejemplo (Terminator, Matrix, Titanic, etc)

Ahora que ya conoces scope vamos a ver diferentes casos de uso:

Scope Básico

Este scope no recibe ningún argumento:

scope :newest, -> { order(created_at: :desc) }

Esto devolverá todas las películas ordenadas desde la última añadida o la más nueva. Por ejemplo:

Movies.newest

Scope con Parametros

Este scope recibe uno o mas argumentos y realiza el query:

scope :filter_by_category, ->(category) { where(category: category) }

Esto devolverá todas las películas filtradas por la categoría acción . Por ejemplo:

Movies.filter_by_category('action')

Default Scope

Este es un scope que se usara en absolutamente todas tus queries. Por ejemplo si quisieras que las peliculas salgan ordenadas en orden descendente podrías usar algo asi:

default_scope { order(created_at: :desc) }

❗Cuidado, porque esto se ejecutará en todas tus queries y puede resultar en salidas no esperadas.

Resultado final

El resultado final de nuestra implementanción de scopes se vería así:

class Movie < ApplicationRecord
scope :newest, -> { order(created_at: :desc) }
scope :this_year, -> { where(year: Time.current.year) }
scope :filter_by_category, ->(category) { where(category: category) }
scope :old_movies, -> { where(year: ...30.years.ago.year) }
end

Mas limpio, claro y profesional, verdad?😏

Conclusión

Ahora que ya sabes usar scopes estas un paso más en tu carrera como profesional de Ruby on Rails no te olvides que la clave es que vayas aprendiendo nuevas cosas todos los días.

Buena suerte en tu crecimiento profesional pero sobre todo en tu crecimiento espiritual.

--

--