DEV Community

Francisco Quintero 🇨🇴
Francisco Quintero 🇨🇴

Posted on • Edited on • Originally published at otroespacioblog.wordpress.com

¿Puede ActiveRecord::Relation recibir mensajes de métodos de clase? Sí, sí puede

Ruby on Rails es una criatura enorme. Ofrece muchas funcionalidades y elementos que hacen fácil la vida de quien usa el framework bajo el nombre de magia. Magia que muchas veces no tenemos ni idea de cómo funciona.

Algunos podrán decir que esa magia es muy mala al ocultar cosas a quien desarrolla. Otros dirán que es buena justamente por lo mismo. En todo caso, siempre está la posibilidad de examinar el código fuente y ver cómo toda esa magia trabaja bajo cuerda.

Hace algunos días me topé con una de esas magias que sin querer sabía cómo funcionaba pero no había analizado tanto.

 Métodos de Clase y Scopes

Los scopes en Rails son una buena forma de crear métodos para hacer consultas sin necesidad de definir un método de clase. Su sintaxis es más sencilla y en muchas ocasiones corta.

Uno de los beneficios explícitos de usarlos es poder encadenarlos para hacer una cadena de invocación:

class Algo < ActiveRecord::Base
  scope :default, -> { where(default: true }
  scope :available, -> { where(available: true }
end
Enter fullscreen mode Exit fullscreen mode

Los scopes default y available pueden encadenarse uno tras otro:

Algo.default.available
Enter fullscreen mode Exit fullscreen mode

Con métodos de clase no es esto posible.

 Entra ActiveRecord::Relation

Bueno, resulta que en un proyecto en el que trabajo estaba este método de clase que parecía no tener uso.

class Taker < ActiveRecord::Base
  def self.to_csv
    # implementación
  end
end
Enter fullscreen mode Exit fullscreen mode

Cuando en Sublime Text o Atom se hacía una búsqueda de Taker.to_csv no aparecía nada. Pensé que nada lo usaba. Permití que se borrara su test y esa implementación en el modelo.

Al par de días, un error aparece. ¿Qué será? Inicia el nuevo sprint, me asignan dicho bug y procedo a revisar qué ocurre. Como le habíamos hecho refactor a ese modelo, pensé que algo podría haberse movido a donde no correspondía así que empecé por lo obvio, comparar lo actual con un commit viejo pero no, no era por ahí.

Luego de analizar por un momento la situación, pensé en traer de nuevo ese viejo método borrado y probar. Y esa era la solución. Por alguna razón, ese método de clase era lo que faltaba.

Pero, ¿por qué un método de clase podría llamarse en una colección? Resulta que lo que llamaba al método era el resultado de una consulta con un #where. Uno creería que para usar métodos de clase habría que siempre usar el nombre de la clase y pasar el mensaje, es decir, el método. Resulta que no. Esa magia "oculta" de Rails lo hace posible.

La explicación está dada en Stack Overflow:

  1. By definition, model scopes return ActiveRecord::Relation objects
  2. By definition, scopes have access to class methods
  3. Therefore, ActiveRecord::Relation objects have access to class methods

Español:

  1. Por definición, los scopes retornan instancias de ActiveRecord::Relation
  2. Por definición, los scopes tienen acceso a los métodos de clase
  3. Por lo tanto, instancias ActiveRecord::Relation tienen acceso a métodos de clase

Ta da! Estuvo siempre sobre mis narices solo que no eran tan obvio.

El código culpable tenía la línea:

ts = filtered_ts.to_csv
Enter fullscreen mode Exit fullscreen mode

En primera medida creíamos que ese .to_csv era algún método de Rails como tal y no el que estaba definido en el modelo. Cuando aparece el bug comprobamos que no era así y está dado por la explicación anterior.

Esta situación nos tomó por sorpresa. El bug no fue algo mayor y tuvimos la fortuna de aprender algo más de la mucha magia que hay en el framework.


Este artículo fue publicado primero en mi blog personal, Otro Espacio Blog. Ahí escribo sobre todo lo que aprendo al programar y también sobre temas no relacionados a tecnología.

Top comments (2)

Collapse
 
lautarolobo profile image
Lautaro Lobo • Edited

Algo en Castellano, que lujo!

Collapse
 
cescquintero profile image
Francisco Quintero 🇨🇴

Hola @lautarolobo que bueno que te agrade!

Estoy republicando contenido original de mi blog por estos lares :)