Ultimamente el desarrollo en Ruby/Rails me ha hecho pensar en el código de una manera anormal, cada vez mucho mas obstinado en los principios de DRY y KISS me quedo hasta horas analizando mis implementaciones y trato de que las mismas sean lo mas Ruby posible.

Para los que programan en Rails, muchos tendrán claro el funcionamiento de los métodos has_many o belongs_to, para los no tan expertos, es posible pasarle un bloque y definir métodos especiales para esa relación, con esto se puede lograr unos "syntatic sugar" bastante chéveres.

Por ejemplo asumiendo que tienes el típico ejemplo de User y Articles

RUBY:
  1. class Article < ActiveRecord::Base
  2.   belongs_to :user
  3. end
  4.  
  5. class User < ActiveRecord::Base
  6.   has_many :articles do
  7.     def in_date_range(start_date, end_date)
  8.          #Codigo de busqueda aqui...
  9.     end
  10.   end
  11. end

Luego una vez obtenida una instancia de usuario, pudieramos ubicar sus articulos en un intervalo de tiempo de una manera sencilla y elegante

RUBY:
  1. user = User.find(1)
  2. user.articles.in_date_range(params[:start_date], params[:end_date])

Ahora bien, cuando agregamos otro modelo a esta mezcla las cosas se ponen más interesantes. Vamos a asumir que tenemos un modelo Newspaper que también tiene muchos articles y además estos se van a pedir en un intervalo de tiempo. ¿Que hacemos? fácil, copia y pega el código de User y listo ya tienes el problema resuelto.

RUBY:
  1. class Newspaper < ActiveRecord::Base
  2.   has_many :users
  3.   has_many :articles do
  4.     def in_date_range(start_date, end_date)
  5.          #Codigo de busqueda aqui...
  6.     end
  7.   end
  8. end
  9.  
  10. class User < ActiveRecord::Base
  11.   belongs_to :newspaper
  12.   has_many :articles
  13. end
  14.  
  15. class Article < ActiveRecord::Base
  16.   belongs_to :user
  17.   belongs_to :newspaper
  18. end

Claro, esto no es DRY, si por casualidad tenemos otros modelos que tengan muchos articles, y por mala suerta se tenga que cambiar el código esto proceso se va a convertir en una agonía extrema.

Entre tanta pensadera se me ocurrio una brillante idea, (pensando para mi mismo te la comiste!) ¿Que tal si creamos un modulo en la carpeta de lib del proyecto y lo incluimos dentro del bloque que abrimos en el has_many? Algo así como

RUBY:
  1. #lib/date_query_extension.rb
  2. module DateQueryExtension
  3.   def in_date_range(start_date, end_date)
  4.     # codigo aqui
  5.   end
  6. end
  7.  
  8. # en los modelos
  9. has_many :articles { include DateQueryExtension }

Mucho mejor!, ahora cada vez que queramos cambiar el codigo de in_date_range, ya solo tenemos que hacerlo una vez y no en cada relacion has_many :articles.

Claro el detalle es que cuando veo la documentacion de ActiveRecord::Base, me doy cuenta de que la gente de Rails ya habia pensado en esto, y no sólo eso, sino que también te ofrecen en los métodos del módulo la información de la relación (tablas que se relacionan, foreign keys, etc) y también las intancias del objeto padre y los objetos hijos. Increible, fue la sensación de que equivocado estaba al pensar que a mi sólo se me habia ocurrido la cuestión. Rails ya lo tenía integrado en su framework, al final el código queda de la siguiente forma

RUBY:
  1. has_many :articles, :extend => DateQueryExtension

Al ver esta clase de cosas, pienso lo genial que es Rails y cómo este te hace recordar lo "pequeño saltamontes" que eres :-).


RSS feeds

Suscríbete a nuestros RSS Feeds