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
Los scopes default
y available
pueden encadenarse uno tras otro:
Algo.default.available
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
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:
- By definition, model scopes return
ActiveRecord::Relation
objects- By definition, scopes have access to class methods
- Therefore,
ActiveRecord::Relation
objects have access to class methods
Español:
- Por definición, los scopes retornan instancias de
ActiveRecord::Relation
- Por definición, los scopes tienen acceso a los métodos de clase
- 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
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)
Algo en Castellano, que lujo!
Hola @lautarolobo que bueno que te agrade!
Estoy republicando contenido original de mi blog por estos lares :)