Ordenando las Vistas (View) y utilizar Decoradores (Decorator) – Rails


Hey look!En este post hablé sobre el patrón Pattern Decorator” como una alternativa a los callbacks en los casos en que necesariamente nos implicaban en tareas que deberían ser separadas de nuestra lógica y comentaba que si pensamos en uno de los principios de SOLID Design Principles y concretamente en “Single Responsibility Principle”, en la que nos dice que un objeto sólo debería tener una única responsabilidad, en ese caso deberíamos pensar en aplicar el patrón “Pattern Decorator”, en vez de un callback.

Dicho esto, en esta otra ocasión quería hablar del mismo patrón pero aplicado a las vistas.

Piensa en los casos que tenemos Vistas en las que tenemos demasiada lógica dentro y cómo los Decorator (Decoradores) pueden ayudarnos a ordenar la vista y trasladar la lógica para que sea algo más mantenible. En esta ocasión he tomado como ejemplo de código un Railcast que utiliza Draper que me va muy bien para explicarlo. Seguramente que en alguna que otra ocasión, nos vamos a encontrar en la situación de tomar una decisión, cuando tenemos la responsabilidad de estar en un proyecto y en la que tengamos nuestras vistas, demasiada lógica aplicada, en ese caso ¿qué podemos hacer y cómo lo podemos mejorar?.

En mi opinión, deberíamos evitar este tipo de situaciones en la medida que nos sea posible. Pero veamos un ejemplo, en el que disponemos de la siguiente vista con la lógica aplicada dentro de la propia vista:

<div id=”profile”>

<%= link_to_if @user.url.present?, image_tag(“avatars/#{avatar_name(@user)}”, class: “avatar”), @user.url %>

<h1>

<%= link_to_if @user.url.present?, (@user.full_name.present? ? @user.full_name : @user.username), @user.url %>

</h1>

<dl> ……. <dt>Website:</dt>

<dd> <% if @user.url.present? %>                            

          <%= link_to @user.url, @user.url %>        

 <% else %>                          

          <span class=”none”>None given</span>          

<% end %>  

</dd> <dt>Twitter:</dt> <dd>      

<% if @user.twitter_name.present? %>                    

         <%= link_to @user.twitter_name, “http://twitter.com/#{@user.twitter_name}” %>      

<% else %>                    

         <span class=”none”>Without Social Network</span>    

 <% end %>

</dd> <dt>Bio:</dt> <dd>      

 <% if @user.bio.present? %>                      

          <%=raw Redcarpet.new(@user.bio, :hard_wrap, :filter_html, :autolink).to_html %>        

<% else %>                      

          <span class=”none”>Without Bio</span>        

 <% end %>   </dd> </dl> </div>

Como puedes comprobar, hemos aplicado una lógica demasiado ligada a una vista, me refiero a que estamos preguntando en la vista si  if @user.bio.present? y esto no debería formar parte de la vista. Ahora bien, hay una parte en la vista que hemos separado y en la que nos apoyamos en un Helper Method , que este caso es correcto hacerlo así y para evitar en la vista que tenga una lógica más aplicada. Esto lo hace el helper method avatar_name(user):

View:

<div id=”profile”>

<%= link_to_if @user.url.present?, image_tag(“avatars/#{avatar_name(@user)}”, class: “avatar”), @user.url %>

<h1>

<%= link_to_if @user.url.present?, (@user.full_name.present? ? @user.full_name : @user.username), @user.url %>

</h1> <dl>

Helper:

module UsersHelper

def avatar_name(user)

if user.avatar_image_name.present?          

           user.avatar_image_name

else          

          “default.png”

end

end

end

En nuestra vista de user show tendríamos también métodos que estarían relacionados con el modelo User:

View:

.………………. more code

<dt>Member Since:</dt>

<dd>

<%= @user.member_since %>

</dd>

Model:

class User < ActiveRecord::Base        

      def member_since              

             created_at.strftime(“%B %e, %Y”)        

       end

end

La solución a este tipo de problema, sería aplicando el patrón “Pattern Decorator”, lo que hacemos es trasladar toda nuestra lógica de Helper y Model directamente al decorador, con lo que nuestro código será más limpio en la vista:

Resultado de nuestra nueva View, creo que es mucho más limpia y no hay nada de lógica directa, el decorador que hemos implementado se encarga de ello:

<div id=”profile”>

<%= @user.avatar %>

<h1><%= @user.linked_name %></h1> <dl>

<dt>Username:</dt>

<dd><%= @user.username %></dd>

<dt>Member Since:</dt>

<dd><%= @user.member_since %></dd>

<dt>Website:</dt>

<dd><%= @user.website %></dd>

<dt>Twitter:</dt>

<dd><%= @user.twitter %></dd>

<dt>Bio:</dt> <dd><%= @user.bio %></dd> </dl></div>

Para el caso del decorador, lo que haremos es desarrollar toda la lógica que antes estaba dentro de la vista:

user_decorator.rb:

class UserDecorator < ApplicationDecorator

decorates :user allows :username

def avatar     

     site_link h.image_tag(“avatars/#{avatar_name}”, class: “avatar”)

end

def linked_name     

    site_link(model.full_name.present? ? model.full_name : model.username)

end

def website     

    handle_none model.url do             

        h.link_to model.url, model.url     

    end

end

def twitter     

    handle_none model.twitter_name do             

         h.link_to model.twitter_name, “http://twitter.com/#{model.twitter_name}”     

    end

end

def bio       

     handle_none model.bio do             

           markdown(model.bio)     

     end

end

def member_since     

      model.created_at.strftime(“%B %e, %Y”)

end

private

def handle_none(value)     

     if value.present?         

         yield     

     else         

        h.content_tag :span, “None given”, class: “none”     

     end

end

def site_link(content)     

      h.link_to_if model.url.present?, content, model.url

end

def avatar_name     

      if model.avatar_image_name.present?         

               model.avatar_image_name     

       else         

            “default.png”     

        end

end

end

Finalmente podríamos decir que los patrones decoradores pueden utilizarse para establecer nuevos comportamiento por encima de los objetos existentes, sin modificar las clases existentes, además tienen más sentido cuando se modifica el comportamiento de los métodos existentes, en lugar de añadir nuevos métodos.

¿Tienes demasiada lógica en tus vistas? ¿cómo las gestionas?

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s