2012-07-14 14 views
17

Estoy usando la gema nested_form para mi relación AddressBook. Cuando los espacios en blanco de usuario fuera el valor de un Addr existente, que quieren eliminar esa Addr en lugar de guardar con un espacio en blanco valuecómo evitar guardar registros vacíos en un formulario de carriles anidados

class Person < ActiveRecord::Base 
    has_many :addrs, dependent: :destroy 
    attr_accessible :name, :addrs_attributes 
    accepts_nested_attributes_for :addrs, reject_if: :addr_blank, allow_destroy: true 

    def addr_blank(a) 
    valid? && a[:id].blank? && a[:value].blank? 
    end 

class Addr < ActiveRecord::Base 
    belongs_to :person 
    attr_accessible :kind, :label, :value, :person_id 

Mi método :reject_if funciona bien pero no me da todo lo que necesito

  1. valid? guarda mi Addrs en blanco alrededor a través de la validación
  2. a[:id].blank? evita rechazos cuando los espacios en blanco de usuarios fuera y registro existente

Ahora, necesito eliminar (en lugar de guardar) un Addr existente cuando el usuario blanquea el value. Además, estoy exponiendo Personas y Agregados a través de una API RESTful. Veo dos opciones posibles:

  1. proceso Deja la params hash para añadir el mágico _destroy=1 parámetro. IOW, emula la actividad del usuario al presionar el botón Eliminar.
  2. Encapsule esto dentro del modelo Addr de modo que una actualización con un value en blanco se considere efectivamente una eliminación.

Basado en el consejo aquí es como yo implementé:

people_controller.rb

def update 
    @person = Person.find(params[:id]) 
    @person.destroy_blank_addrs(params[:person]) 
    respond_to do |format| 
    ... 

person.rb

def destroy_blank_addrs(person_params) 
    if valid? && person_params[:addrs_attributes] 
    person_params[:addrs_attributes].each do |addr_params_array| 
     addr_params= addr_params_array[1] 
     addr_params[:_destroy] = '1' if !addr_params[:id].blank? && addr_params[:value].blank? 
    end 
    end 
end 
+0

De los dos, la opción 1. Uso Usted no quiere "mágica" como "si el valor del campo X está en blanco a continuación, eliminar el registro". – Zabba

+0

He actualizado la pregunta con la solución que sugirió. –

+0

@Zabba, estoy refaccionando este código 18 meses después y tenías razón. Mi idea de borrar el valor como "mágico" 'destroy_blank_addrs' fue muerte cerebral. También creo que cualquier solución que implique modificar directamente la matriz 'params' es una mala práctica. Cualquier postproceso se debe hacer después de 'assign_attributes' pero antes de' save' –

Respuesta

5

Una tercera alternativa sería agregar una devolución de llamada before_save en Perso n que eliminará todas las direcciones que estén en blanco. Esta idea tiene algún mérito, pero probablemente no vaya con eso.

De las dos opciones que presenta, no iré con el post-procesamiento de los parámetros. Funcionará, pero es demasiado trabajo. Además, el código del controlador se volverá un poco más desordenado y creo firmemente en un controlador muy delgado.

La opción más fácil, en mi opinión, es eliminar las direcciones en blanco después de guardar. Puede agregar Person#remove_blank_addresses() y luego llamar al guardar correctamente. No necesita pasar los parámetros; puede simplemente iterar las direcciones y eliminar las en blanco. Tiene la desventaja de crear direcciones vacías y luego destruirlas, pero de todos modos las necesitarías para actualizar personas.

Si hablamos de la solución más limpia (en mi opinión), introduciría una tercera clase que manejaría toda esa lógica y haría que el controlador la delegara. El controlador sería tan fácil de probar de forma aislada y luego puede escribir una especificación de modelo que verifique todos los detalles esenciales. Es un poco más trabajo y no puedo pensar en un buen nombre en este momento (PersonUpdater?), Pero podría ser una idea que vale la pena pensar.

+1

Gracias por la respuesta reflexiva, Stefan. Una tercera clase es la solución más limpia, pero demasiado esfuerzo. La idea de poner registros en el archivo db que están disponibles para algún proceso asincrónico (incluso solo por un instante) parece incorrecta. Otra idea es administrarlo en el front end en JS. El borrado es solo otra forma de presionar el botón eliminar. Entonces Blanking nunca llega a mi api REST. –

+0

En ese caso, tendría este código en el controlador. Es un poco más difícil de probar y de alguna manera se ve complicado en mis creencias sobre lo que debería estar en el controlador, pero debería ser la solución más limpia en su caso. –

+0

Gracias, opté por el enfoque del controlador porque me pareció más simple y agrega funcionalidad a mi API, donde los consumidores pueden eliminar los números de entrada borrando el valor. Si actualiza su respuesta, lo aceptaré. –

14
accepts_nested_attributes_for :addrs, 
    allow_destroy: true, 
    :reject_if => proc { |att| att[:name].blank? && attr[:description].blank? } 
8
accepts_nested_attributes_for :addrs, 
    allow_destroy: true, 
    reject_if: -> { |attr| [name, description].any? &:blank? } 
+0

Muy conciso. Incluyo una prueba 'válida 'para que las filas no se rechacen hasta que el usuario termine de resolver las validaciones. –

1
accepts_nested_attributes_for :addrs, 
    allow_destroy: true, 
    reject_if: :all_blank 
+0

Le permite especificar un Proc o un Símbolo que apunta a un método que verifica si se debe crear un registro para un determinado atributo de hash. El hash se pasa al Proc suministrado o al método y debe devolver verdadero o falso. Cuando se especifica no: reject_if, se generará un registro para todos los hashes de atributo que no tengan un valor _destroy que se evalúe como verdadero. Pasando: all_blank en lugar de un Proc creará un proceso que rechazará un registro donde todos los atributos están en blanco, excluyendo cualquier valor para _destroy. – hadees

Cuestiones relacionadas