Cómo refactorizar un controlador que maneja dos variantes de un modelo

Tengo un controlador ads_controller.rb que maneja todos los anuncios en nuestra plataforma. Este controlador también maneja un subconjunto del original ad.rbmodelo, que se llama house_ad.
(house_ad no es un modelo. Los anuncios internos son anuncios con una propiedad específica).

En muchos lugares del código de vista hay diferencias entre las cosas que se muestran para ads y house_ads (manejado con una variable de instancia @is_house_ad). Para tener un código más limpio, queremos eliminar este manejo adicional de house_ads dentro de ads_controller.rb y mover el comportamiento a un house_ads_controller.rb.

Desafortunadamente, no estoy muy familiarizado con ese tipo de problema, por lo que estoy buscando consejos sobre cómo abordarlo.

Nota::

Mi primera idea fue heredar house_ads_controller.rb desde ads_controller.rb y luego anule las funciones que diferencian entre anuncios normales y anuncios internos.

Resultó que esto es cierto para la mayoría de las funciones existentes y que, por lo tanto, esta solución produciría una gran cantidad de código duplicado que, en última instancia, dificultaría el mantenimiento.

Q: ¿Cuál sería la mejor manera de eliminar estas muchas ocurrencias de manejo de variaciones sin producir muchos códigos duplicados?

preguntado el 08 de noviembre de 11 a las 15:11

4 Respuestas

Parece que tiene mucho código en sus controladores, que en general es no particularmente idiomático para Rails. Idealmente, comience refactorizando este código en su ad y house_ad modelos, asegurándose de obedecer la herencia correctamente y tratando de no duplicar ningún código. Si lo necesita, debe utilizar Mixins, pueden ayudar.

Entonces, su controlador debería funcionar para ambos ad y house_ady, a lo sumo, es posible que necesite algunos ajustes en la lógica de su pantalla, pero debería ser mínimo si obtiene el diseño de sus modelos correctamente.

Por último, podrías refactorizar @is_house_ad fuera y en su lugar use model.is_a? Modele en su lógica de vista.

contestado el 23 de mayo de 17 a las 13:05

Según lo leí, la duplicación de código no está en su controlador sino en sus puntos de vista. Si ese es el caso, existen algunas alternativas:

Crearé un pequeño ejemplo para aclarar las cosas. Por ejemplo, suponga que su código contiene algo como esto

<% if @ad.is_house_ad? %>
  <%= @ad.house_ad_title %>
<% else %>
  <%= @ad.title %>
<% end %>

(y supongo que contendrá una tonelada de esos;)

Mueva el código a su AdsHelper

En su opinión, escriba:

<%= get_title(@ad) %>

En su AdsHelper usted escribe

def get_title(ad)
  ad.is_house_ad? ? ad.house_ad_title : ad.title
end

Esto aclarará la vista y colocará todo el código en un solo lugar. Pero, como puede ver, no está orientado a objetos.

Mueva más código a su modelo

Dado que su modelo sabe cómo debe interpretarse, este parece un lugar lógico. Pero es posible que esté introduciendo código de vista en su modelo.

En tu modelo

def show_title
  ad.is_house_ad? ? ad.house_ad_title : ad.title
end

En tu opinión:

<%= ad.show_title %>

Usa diferentes parciales

Esta es una solución muy simple y divide claramente las vistas.

Entonces, en su opinión, escriba:

<% if @ad.is_house_ad? %>
  <%= render :partial => 'house_ad' %>
<% else %>
  <%= render :partial => 'ad' %>
<% end %>

Entonces, en el parcial _ad.html.erb:

<%= @ad.title %>

Y en el parcial _house_ad.html.erb:

<%= @ad.ad_house_title %>

Creo que esto se combina mejor con una de las dos soluciones anteriores. Pero tal vez usar esto, pueda aliviar la necesidad de uno de los anteriores, depende de la complejidad de su código y vista.

Usar presentadores

En lugar de agregar código de vista a su modelo, crea un presentador. En este caso, incluso podría crear dos presentadores: un AdPresenter y un HouseAdPresenter. Es este objeto el que se instancia en el controlador, según el modelo. Y es este objeto el que luego se entrega a la vista. El presentador ocultará todas las diferencias, y si le preguntas el título, solo presentará el correcto.

El uso de presentador es un paradigma bien conocido, y hubo una railscast sobre esto recientemente.

Utilice STI

Quizás la solución más limpia, pero la que más trabajo, es usar la herencia de tabla única. Esto significa que tiene una tabla de base de datos, con un Type, que podría contener dos modelos (¿suena reconocible?). Esto significaría que tendrías dos modelos. Ad y HouseAd, que ambos heredan de una clase base. Esto también podría significar dos controladores, pero en su caso no creo que la duplicación esté en el controlador.

Dependiendo de su código / vista, creo que la forma más fácil de comenzar es introducir primero los parciales o comenzar a mover el código a los ayudantes o modelos.

Espero que esto ayude.

respondido 09 nov., 11:11

Dado lo que sabemos de la pregunta, me gustan especialmente sus dos últimas sugerencias: presentadores e STI. - calcetín

Es difícil responder sin ningún ejemplo concreto.

Si se trata de "pequeños trozos" de funcionalidad diferente a lo largo del código de la línea principal, esas partes se pueden refactorizar en métodos específicos de la clase.

Por ejemplo, si el método de clase más especializado es exactamente el mismo que el de la superclase, excepto por un fragmento de código en el medio, la superclase definiría un método de punto de extensión y lo mantendría vacío en sí mismo, y la subclase anularía.

Si se trata de un código similar a la inicialización, a menudo se puede mover a un before filtro (o after si es código hacia el final).

Sin embargo, Rails tiende hacia modelos gordos y controladores delgados. (A veces, esto es simplemente cambiar un problema por otro, pero esa es una discusión diferente). Vea si la funcionalidad específicamente alrededor del modelo se puede mover al modelo en sí.

respondido 08 nov., 11:20

Como no da ningún ejemplo sobre cuáles son las diferencias, no puedo dar consejos muy detallados. Pero lo que busca en general son patrones de diseño, de los cuales varios abordan problemas como los describe, por ejemplo, el patrón Decorator.

Quizás debería echar un vistazo a "Design Patterns in Ruby" de Russ Olsen (o cualquier libro similar, pero recomendaría este). Estoy seguro de que encontrará varias soluciones posibles sobre cómo cambiar su controlador o los modelos para limpiar su código sin construir herencias complicadas.

respondido 08 nov., 11:20

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.