rails examples example application active ruby-on-rails ruby directory-structure

ruby-on-rails - examples - ruby on rails database



OO Design in Rails: Donde poner cosas (4)

Realmente estoy disfrutando de Rails (aunque en general estoy RESTless), y disfruto Ruby siendo muy OO. Aún así, la tendencia a crear grandes subclases y controladores de ActiveRecord es bastante natural (incluso si usas un controlador por recurso) Si fueras a crear mundos de objetos más profundos, ¿dónde colocarías las clases (y los módulos, supongo)? Estoy preguntando acerca de las vistas (en los Ayudantes mismos), controladores y modelos.

Lib está bien, y he encontrado algunas soluciones para volver a cargar en un entorno de desarrollo , pero me gustaría saber si hay una mejor manera de hacer esto Realmente solo me preocupa que las clases crezcan demasiado. Además, ¿qué pasa con los motores y cómo encajan?


... la tendencia a crear grandes subclases y controladores de ActiveRecord es bastante natural ...

"enorme" es una palabra preocupante ... ;-)

¿Cómo se vuelven enormes sus controladores? Eso es algo que debería ver: idealmente, los controladores deberían ser delgados. Escogiendo una regla de oro del aire, sugeriría que si regularmente tiene más de, digamos, 5 o 6 líneas de código por método de controlador (acción), entonces sus controladores probablemente sean demasiado gordos. ¿Hay duplicación que podría pasar a una función auxiliar o un filtro? ¿Hay lógica de negocios que podría ser empujada hacia abajo en los modelos?

¿Cómo llegan a ser enormes tus modelos? ¿Debería buscar formas de reducir el número de responsabilidades en cada clase? ¿Hay algún comportamiento común que puedas extraer en mixins? ¿O áreas de funcionalidad que puedes delegar en clases de ayuda?

EDITAR: tratando de expandirse un poco, con suerte no distorsionando nada demasiado mal ...

Ayudantes: viven en la app/helpers y se utilizan principalmente para simplificar las vistas. Son específicos del controlador (también disponibles para todas las vistas para ese controlador) o generalmente disponibles ( module ApplicationHelper en application_helper.rb).

Filtros: supongamos que tiene la misma línea de código en varias acciones (con bastante frecuencia, la recuperación de un objeto mediante params[:id] o similar). Esa duplicación se puede abstraer primero a un método separado y luego a las acciones por completo declarando un filtro en la definición de clase, como por ejemplo before_filter :get_object . Consulte la Sección 6 en la Guía de ActionController Rails Deje que la programación declarativa sea su amigo.

Los modelos de refactorización son un poco más de una cosa religiosa. Disciples of Uncle Bob sugerirá, por ejemplo, que sigas los Cinco Mandamientos de SOLID . Joel y Jeff pueden recomendar un enfoque más, "pragmático", aunque parecían estar un poco más reconciliados posteriormente. Encontrar uno o más métodos dentro de una clase que operan en un subconjunto claramente definido de sus atributos es una forma de intentar identificar las clases que podrían ser refactorizadas fuera de su modelo derivado de ActiveRecord.

Los modelos de Rails no tienen que ser subclases de ActiveRecord :: Base, por cierto. O para decirlo de otra manera, un modelo no tiene que ser un análogo de una tabla, ni siquiera relacionado con nada almacenado. Aún mejor, siempre que nombre su archivo en la app/models acuerdo con las convenciones de Rails (llame #underscore en el nombre de la clase para averiguar qué buscará Rails), Rails lo encontrará sin require sea ​​necesario.



Debido a que Rails proporciona una estructura en términos de MVC, es natural terminar usando solo los contenedores de modelo, vista y controlador que se le proporcionan. El idioma típico para los principiantes (e incluso algunos programadores intermedios) es meter toda la lógica de la aplicación en el modelo (clase de base de datos), controlador o vista.

En algún momento, alguien señala el paradigma "modelo gordo, controlador delgado", y los desarrolladores intermedios eliminan rápidamente todo desde sus controladores y lo introducen en el modelo, que comienza a convertirse en una nueva papelera para la lógica de la aplicación.

Los controladores delgados son, de hecho, una buena idea, pero el corolario: poner todo en el modelo, no es realmente el mejor plan.

En Ruby, tienes un par de buenas opciones para hacer las cosas más modulares. Una respuesta bastante popular es simplemente usar módulos (generalmente escondidos en lib ) que contienen grupos de métodos, y luego incluir los módulos en las clases apropiadas. Esto ayuda en los casos en los que tiene categorías de funcionalidad que desea reutilizar en varias clases, pero donde la funcionalidad aún está ligada teóricamente a las clases.

Recuerda, cuando incluyes un módulo en una clase, los métodos se convierten en métodos de instancia de la clase, por lo que aún terminas con una clase que contiene una tonelada de métodos, simplemente están organizados en múltiples archivos.

Esta solución puede funcionar bien en algunos casos; en otros casos, querrá pensar en usar clases en su código que no sean modelos, vistas o controladores.

Una buena manera de pensarlo es el "principio de responsabilidad única", que dice que una clase debe ser responsable de una sola (o pequeña cantidad) de cosas. Sus modelos son responsables de la persistencia de los datos de su aplicación a la base de datos. Sus controladores son responsables de recibir una solicitud y devolver una respuesta viable.

Si tiene conceptos que no encajan perfectamente en esos cuadros (persistencia, gestión de solicitud / respuesta), probablemente quiera pensar en cómo modelar la idea en cuestión. Puede almacenar clases que no sean de modelo en app / classes, o en cualquier otro lugar, y agregar ese directorio a su ruta de carga haciendo lo siguiente:

config.load_paths << File.join(Rails.root, "app", "classes")

Si está utilizando pasajeros o JRuby, probablemente también desee agregar su ruta a las rutas de carga impacientes:

config.eager_load_paths << File.join(Rails.root, "app", "classes")

La conclusión es que una vez que llegue a un punto en Rails en el que se encuentre haciendo esta pregunta, es hora de reforzar sus habilidades de Ruby y comenzar a modelar clases que no son solo las clases de MVC que Rails le ofrece de forma predeterminada.

Actualización: esta respuesta se aplica a Rails 2.x y superior.


Actualización : el uso de Preocupaciones se ha confirmado como el nuevo valor predeterminado en Rails 4 .

Realmente depende de la naturaleza del propio módulo. Por lo general, coloco las extensiones de controlador / modelo en una carpeta / preocupaciones dentro de la aplicación.

# concerns/authentication.rb module Authentication ... end # controllers/application_controller.rb class ApplicationController include Authentication end # concerns/configurable.rb module Configurable ... end class Model include Indexable end # controllers/foo_controller.rb class FooController < ApplicationController include Indexable end # controllers/bar_controller.rb class BarController < ApplicationController include Indexable end

/ lib es mi opción preferida para bibliotecas de propósito general. Siempre tengo un espacio de nombres de proyecto en lib donde coloco todas las bibliotecas específicas de la aplicación.

/lib/myapp.rb module MyApp VERSION = ... end /lib/myapp/CacheKey.rb /lib/myapp/somecustomlib.rb

Las extensiones del núcleo de Ruby / Rails usualmente tienen lugar en inicializadores de configuración, por lo que las bibliotecas solo se cargan una vez en Rails boostrap.

/config/initializer/config.rb /config/initializer/core_ext/string.rb /config/initializer/core_ext/array.rb

Para los fragmentos de código reutilizables, a menudo creo (micro) complementos para poder reutilizarlos en otros proyectos.

Los archivos de ayuda generalmente contienen métodos de ayuda y, a veces, clases cuando el objeto está destinado a ser utilizado por los ayudantes (por ejemplo, Form Builders).

Este es un resumen muy general. Proporcione más detalles sobre ejemplos específicos si desea obtener sugerencias más personalizadas. :)