rails manejo excepciones capturar ruby-on-rails exception exception-handling

ruby on rails - manejo - ¿Cuál es la mejor estrategia para manejar excepciones y errores en Rails?



manejo de excepciones en rails (2)

Me preguntaba si las personas compartirían sus mejores prácticas / estrategias sobre el manejo de excepciones y errores. Ahora no estoy preguntando cuándo lanzar una excepción (aquí se ha respondido exhaustivamente: SO: Cuándo lanzar una excepción ). Y no estoy usando esto para mi flujo de aplicaciones, pero hay excepciones legítimas que ocurren todo el tiempo. Por ejemplo, el más popular sería ActiveRecord :: RecordNotFound. ¿Cuál sería la mejor manera de manejarlo? ¿La forma seca?

En este momento estoy haciendo muchas comprobaciones dentro de mi controlador, por lo que si Post.find(5) devuelve Nil, Post.find(5) eso y lanzo un mensaje flash. Sin embargo, aunque esto es muy granular, es un poco engorroso en el sentido de que necesito buscar excepciones de este tipo en todos los controladores, mientras que la mayoría de ellos son esencialmente iguales y tienen que ver con registros no encontrados o registros relacionados no encontrados, como como Post.find(5) no se encuentra o si está intentando mostrar comentarios relacionados con una publicación que no existe, eso generaría una excepción (algo como Post.find(5).comments[0].created_at )

Sé que puede hacer algo como esto en ApplicationController y sobrescribirlo más adelante en un controlador / método en particular para obtener un soporte más granular, sin embargo, ¿esa sería una forma adecuada de hacerlo?

class ApplicationController < ActionController::Base rescue_from ActiveRecord::RecordInvalid do |exception| render :action => (exception.record.new_record? ? :new : :edit) end end

También funcionaría en caso Post.find(5) no se encuentre Post.find(5) , pero ¿qué pasa con Post.find(5).comments[0].created_at ? Post.find(5).comments[0].created_at decir que no puedo lanzar una excepción completa si la publicación existe pero no tiene comentarios, verdad?

Para resumir hasta ahora, estaba haciendo muchas comprobaciones manuales utilizando if / else / menos o case / when (y confieso que de vez en cuando comienza / rescata) y comprobando si no? ¿O vacío ?, etc., pero tiene que haber una mejor manera de lo que parece.

RESPUESTAS:

@Milan: Hola, Milán. Gracias por su respuesta. Estoy de acuerdo con lo que dijo y creo que hice mal uso de la palabra excepción. Lo que quise decir es que ahora mismo hago muchas cosas como:

if Post.exists?(params[:post_id]) @p = Post.find(params[:post_id]) else flash[:error] = " Can''t find Blog Post" end

Y hago mucho de este tipo de "manejo de excepciones", trato de evitar el uso de inicio / rescate. Pero me parece que este es un resultado / verificación / situación suficientemente común como para que exista una forma SECA de hacer esto, ¿no es así? ¿Cómo harías este tipo de cheque?

Además, ¿cómo lo manejaría en este caso? Digamos que quiere mostrar la fecha de creación de un comentario en su vista:

Last comment for this post at : <%= @post.comments[0].created_at %>

Y esta publicación no tiene ningún comentario. Tu puedes hacer

Last comment for this post at : <%= @post.comments.last.created_at unless @post.comments.empty? %>

Podrías hacer un check in en el controlador. Etc. Hay varias formas de hacerlo. ¿Pero cuál es la "mejor" manera de manejar esto?


El hecho de que realice muchas comprobaciones manuales para detectar excepciones sugiere que simplemente no las está utilizando correctamente. De hecho, ninguno de tus ejemplos es excepcional.

En cuanto a las publicaciones no existentes: debe esperar que los usuarios de su API (p. Ej., Un usuario que use su web a través del navegador) soliciten publicaciones no existentes.

Tu segundo ejemplo (Post.find (5) .comments [0] .created_at) tampoco es excepcional. Algunas publicaciones simplemente no tienen comentarios y lo sabes por adelantado. Entonces, ¿por qué debería eso lanzar una excepción?

Lo mismo ocurre con el ejemplo ActiveRecord :: RecordInvalid. Simplemente no hay razón para manejar este caso por medio de una excepción. Que un usuario ingrese algunos datos no válidos en un formulario es algo bastante habitual y no hay nada excepcional en ello.

El uso del mecanismo de excepción para este tipo de situaciones puede ser muy conveniente en algunas situaciones, pero es incorrecto por las razones mencionadas anteriormente.

Dicho esto, no significa que no pueda SECAR el código que resume estas situaciones. Existe una gran posibilidad de que pueda hacerlo al menos en cierta medida, ya que estas son situaciones bastante comunes.

Entonces, ¿qué pasa con las excepciones? Bueno, la primera regla es: úsalos lo más dispersos posible.

Si realmente necesitas usarlos, hay dos tipos de excepciones en general (como lo veo):

  1. excepciones que no interrumpen el flujo de trabajo general del usuario dentro de su aplicación (imagine una excepción dentro de la rutina de generación de imágenes en miniatura de su perfil) y puede ocultarlas al usuario o simplemente notificarle el problema y sus consecuencias cuando sea necesario

  2. excepciones que impiden al usuario utilizar la aplicación en absoluto. Estos son el último recurso y deben manejarse a través del error interno del servidor 500 en las aplicaciones web.

rescue_from a usar el método rescue_from en el ApplicationController solo para este último, ya que hay lugares más apropiados para el primer tipo y ApplicationController como la más alta de las clases de controladores parece ser el lugar correcto al que recurrir en tales circunstancias (aunque hoy en día, algún tipo de middleware de Rack podría ser un lugar aún más apropiado para poner tal cosa).

- EDITAR -

La parte constructiva:

En cuanto a lo primero, mi consejo sería comenzar a usar find_by_id en lugar de find, ya que no lanza una excepción pero devuelve nil si no tiene éxito. Tu código se vería algo como esto entonces:

unless @p = Post.find_by_id(params[:id]) flash[:error] = "Can''t find Blog Post" end

que es mucho menos hablador.

Otro lenguaje común para SECAR este tipo de situaciones es usar el controlador before_filters para configurar las variables de uso frecuente (como @p en este caso). Después de eso, su controlador puede verse como sigue

controller PostsController before_filter :set_post, :only => [:create, :show, :destroy, :update] def show flash[:error] = "Can''t find Blog Post" unless @p end private def set_post @p = Post.find_by_id(params[:id]) end end

En cuanto a la segunda situación (último comentario no existente), una solución obvia a este problema es mover todo el asunto a un ayudante:

# This is just your way of finding out the time of the last comment moved into a # helper. I''m not saying it''s the best one ;) def last_comment_datetime(post) comments = post.comments if comments.empty? "No comments, yet." else "Last comment for this post at: #{comments.last.created_at}" end end

Entonces, en tus puntos de vista, simplemente llamarías

<%= last_comment_datetime(post) %>

De esta manera, el caso de borde (publicación sin comentarios) se manejará en su propio lugar y no saturará la vista.

Lo sé, ninguno de estos sugiere ningún patrón para manejar los errores en Rails, pero tal vez con algunas refactorizaciones como estas, encuentre que gran parte de la necesidad de algún tipo de estrategia para el manejo de excepciones / errores simplemente desaparece.


Las excepciones son por circunstancias excepcionales. La entrada de usuario incorrecta normalmente no es excepcional; En todo caso, es bastante común. Cuando tiene una circunstancia excepcional, desea darse toda la información posible. En mi experiencia, la mejor manera de hacerlo es mejorar religiosamente su manejo de excepciones basado en la experiencia de depuración. Cuando te topas con una excepción, lo primero que debes hacer es escribir una prueba de unidad para ello. Lo segundo que debe hacer es determinar si hay más información que se puede agregar a la excepción. Más información en este caso usualmente toma la forma de capturar la excepción más arriba en la pila y manejarla o lanzar una nueva excepción más informativa que tiene la ventaja de un contexto adicional. Mi regla personal es que no me gusta atrapar excepciones de más de tres niveles en la pila. Si una excepción tiene que viajar más allá de eso, debe detectarlo antes.

En cuanto a la exposición de errores en la interfaz de usuario, las declaraciones de case / case están totalmente bien, siempre y cuando no las anide demasiado. Ahí es cuando este tipo de código se vuelve difícil de mantener. Puedes abstraer esto si se convierte en un problema.

Por ejemplo:

def flash_assert(conditional, message) return true if conditional flash[:error] = message return false end flash_assert(Post.exists?(params[:post_id]), "Can''t find Blog Post") or return