2008-12-04 17 views
307

Digamos que tengo un modelo de Rails llamado Thing. Thing tiene un atributo url que puede opcionalmente establecerse en una URL en algún lugar de Internet. En el código de vista, necesito la lógica que hace lo siguiente:¿Se pueden utilizar los modificadores de enrutamiento Can Rails (es decir, mymodel_path (modelo)) en los modelos?

<% if thing.url.blank? %> 
<%= link_to('Text', thing_path(thing)) %> 
<% else %> 
<%= link_to('Text', thing.url) %> 
<% end %> 

Esta lógica condicional en la vista es fea. Por supuesto, podría construir una función de ayuda, lo que cambiaría la vista a este:

<%= thing_link('Text', thing) %> 

que resuelve el problema de la verbosidad, pero realmente preferiría tener la funcionalidad en el modelo mismo. En ese caso, el código de vista sería:

<%= link_to('Text', thing.link) %> 

Esto, obviamente, requeriría un método de enlace en el modelo. Esto es lo que tendría que contener:

def link 
    (self.url.blank?) ? thing_path(self) : self.url 
end 

Hasta el punto de la cuestión, thing_path() es un método no definido en el interior Código del modelo. Supongo que es posible "incorporar" algunos métodos de ayuda en el modelo, pero ¿cómo? ¿Y hay una razón real por la que el enrutamiento solo opera en el controlador y ve las capas de la aplicación? Puedo pensar en muchos casos en los que el código modelo puede necesitar tratar con URL (integrarse con sistemas externos, etc.).

+0

Un caso de uso sería: para generar URL acortado de goo.gl en un afterSave, – lulalala

+2

probablemente debería envolverte modelo en un presentador si desea agregar lógica de vista, esto mantendrá las capas MVC separadas. Ver Draper (https://github.com/jcasimir/draper). – Kris

+2

Consulte también la sección "Generación de URL para rutas con nombre" en la documentación en http://api.rubyonrails.org/classes/ActionDispatch/Routing/UrlFor.html – Backo

Respuesta

11

Cualquier lógica que tiene que ver con lo que se muestra en la vista deben delegarse en un método de ayuda, como los métodos en el modelo son estrictamente para el manejo de datos.

Esto es lo que podría hacer:

# In the helper... 

def link_to_thing(text, thing) 
    (thing.url?) ? link_to(text, thing_path(thing)) : link_to(text, thing.url) 
end 

# In the view... 

<%= link_to_thing("text", @thing) %> 
+1

Lo que no me gusta de los métodos de ayuda en este caso: cuándo Miro link_to_thing(), tengo que decidir si es un asistente específico de la cosa o de toda la aplicación (fácilmente podría serlo). Necesito considerar verificar 2 archivos para la fuente. thing.link no deja dudas sobre el archivo fuente. –

+0

Además, ¿qué sucede si necesito usar esta funcionalidad en una tarea de Rake (para exportar un archivo CSV de URLs de cosas) tal vez ... yendo directamente al modelo sería mucho mejor en ese caso también. –

+0

Pero la diferencia es que el link_to no estaría disponible en el modelo, ya que es un ayudante de ActionView. Entonces, esto no funcionaría. Podrías hackear algunos valores por defecto de los atributos en el modelo, por lo que si no está configurado, tiene un valor predeterminado, pero eso depende de ti. –

1

Si bien puede haber una manera de que tienden a mantener ese tipo de lógica del modelo. Estoy de acuerdo en que no se debe poner esto en la vista (keep it skinny), pero menos que el modelo está volviendo una URL como una pieza de datos al controlador, el material de enrutamiento debe estar en el controlador.

+3

Re: "A menos que el modelo devuelva una URL como una pieza de datos". Eso es exactamente lo que está sucediendo aquí ... el Modelo "posee" este dato, que es un enlace a una URL en el sitio o fuera del sitio. En algunos casos, la URL se genera a partir de Rails Routing. En otros casos, la URL es información proporcionada por el usuario. –

-1

(Edit: olvidarse de mi balbuceo anterior ...)

Ok, puede haber situaciones donde va a ir bien con el modelo o para alguna otra url ... pero no realmente piensan esto pertenece al modelo, la vista (o tal vez el modelo) suena más apropiada.

Acerca de las rutas, por lo que yo sé las rutas es por las acciones de los controladores (por lo general wich "mágicamente" utiliza un punto de vista), no directamente a los puntos de vista. El controlador debe manejar todas las solicitudes, la vista debe presentar los resultados y el modelo debe manejar los datos y enviarlos a la vista o al controlador. He escuchado a muchas personas hablar de rutas a modelos (hasta el punto de que estoy empezando a dejarlo), pero según tengo entendido, las rutas se dirigen a los controladores. Por supuesto, una gran cantidad de controladores son controladores para un modelo y, a menudo se llama <modelname>sController (por ejemplo "UsersController" es el controlador del modelo "usuario").

Si usted se encuentra escribiendo cantidades desagradables de la lógica en una vista, intenta mover la lógica en un lugar más apropiado; la lógica de solicitud y comunicación interna probablemente pertenece al controlador, la lógica relacionada con los datos puede colocarse en el modelo (pero no la lógica de visualización, que incluye etiquetas de enlace, etc.) y la lógica puramente relacionada con la visualización se colocaría en un ayudante.

+0

Supongamos que tiene un modelo de imagen. Si la imagen tiene asociada una URL externa, apunte a esa URL. De lo contrario, dirígete a la página de la demostración de la imagen para subir una imagen. Solo una idea. –

+0

¿Qué tal este ejemplo menos genérico: link_to "imagen a tamaño completo", image.link El método de enlace en el modelo haría bien un enlace a la URL en el lugar (image_path), o con, por ejemplo, una dirección URL de Flickr Si el usuario proporcionó uno y .url se estableció en la imagen. –

165

que he encontrado la respuesta con respecto a cómo hacer esto por mí mismo.Dentro del código del modelo, sólo hay que poner:

para los carriles < = 2:

include ActionController::UrlWriter 

para los carriles 3:

include Rails.application.routes.url_helpers 

Este mágicamente hace thing_path(self) retorno de la URL de lo actual, o other_model_path(self.association_to_other_model) devolver alguna otra URL

+26

Solo una actualización, ya que lo anterior está en desuso. Esta es la actual incluye: 'include Rails.application.routes.url_helpers' – yalestar

+1

Obtengo NoMethodError (método indefinido 'optimize_routes_generation? 'for # ) cuando intento esto – moger777

592

En los carriles 3, 4, y 5 se puede utilizar:

Rails.application.routes.url_helpers 

por ejemplo

Rails.application.routes.url_helpers.posts_path 
Rails.application.routes.url_helpers.posts_url(:host => "example.com") 
+2

¡Funciona muy bien! Gracias por este invaluable consejo! –

+13

Ypu puede incluir esto en cualquier módulo y luego puede acceder a url-helpers allí. Thx –

+0

esto es un consejo profesional para seguro. – Orlando

99

También puede encontrar el siguiente enfoque más limpia de lo que incluye todos los métodos:

class Thing 
    delegate :url_helpers, to: 'Rails.application.routes' 

    def url 
    url_helpers.thing_path(self) 
    end 
end 
+6

La documentación sobre esto parecía difícil de encontrar, así que aquí hay un enlace decente con respecto al uso de delegado en ActiveSupport. http://www.simonecarletti.com/blog/2009/12/inside-ruby-on-rails-delegate/ – tlbrack

+1

Obtengo una 'variable local indefinida o método 'url_helpers' para Event: Class' error ... :( –

+0

Obtengo 'método indefinido url_helpers'. ¿Qué voy a hacer? – asiniy

Cuestiones relacionadas