2011-09-01 10 views
7

estoy tratando de hacer estoomite silenciosamente agregar con la devolución de llamada de la asociación before_add en lugar de generar una excepción?

has_many :roles, :before_add => :enforce_unique 

def enforce_unique(assoc) 
    false if exists? assoc 
end 

A partir de los documentos: "Si una devolución de llamada before_add lanza una excepción, el objeto no se añaden a la colección". El uso de falsa anterior no impide que el complemento, así que estoy obligado a hacer esto:

def enforce_unique(assoc) 
    raise if exists? assoc 
end 

De esta manera, es cierto que no quede añadió, pero también plantea una excepción que tiene que ser manejado . No es muy útil para mí aquí. Preferiría que esto se comportara más como la devolución de llamada de AR previa a_guardado, donde devolver FALSO también evita guardar (o agregar) pero no genera una excepción.

En este caso anterior, preferiría que simplemente no agregue la asociación silenciosamente. ¿Hay alguna forma de hacer esto? Me falta algo? ¿O está planteando una excepción como la única opción aquí?

Respuesta

1

Si la asociación no es polimórfico se puede hacer algo como:

validates_uniqueness_of :name_of_model 

interior de papel donde nos name_of_model lo que está asociando con

1

esta pregunta es un poco viejo, pero me encontré con el mismo problema recientemente Así es como yo lo resolvió:

def enforce_unique |obj, x| 
    v = obj.roles 
    if i = v.index(x) 
    v.slice! i 
    end 
end 
2

La forma de resolverlo Creo que es utilizar throw y catch, que en Ruby están destinados para el control de flujo. Plantear una excepción no es una buena opción, ya que esta no es una circunstancia excepcional.

que terminé haciendo:

catch(:duplicate) do 
    association.create({}) 
end 

Y luego en el before_add de devolución de llamada, lo hice:

if(Class.where({}).first) 
    throw :duplicate 
end 

más con el tiro/atrapar aquí:

http://rubylearning.com/blog/2011/07/12/throw-catch-raise-rescue-im-so-confused/

0

Desde esta pregunta se trata de ahorrar en lugar de evitar que se incluya d en la lista temporal (por ejemplo, mediante un controlador que no está interesado en el control de sus modelos) podría intentar anular salvamento en el modelo relacionado y no guardarlo si el papel existe:

class Role < ActiveRecord::Base 
    belongs_to :user, inverse_of: :roles 

    def save 
    super unless self.new_record? && user.has_existing_role?(self) 
    end 
end 

Nota al margen: I don' t comprar el argumento del controlador delgado cuando se usa con el patrón de registro activo ya que la lógica de negocio debe colocarse en algún lugar. Con un patrón de dominio de negocio pobre como Active Record (sin referirse específicamente a la gema de Ruby AR) realmente necesita existir una capa superior (es decir, en la capa de controlador), puede usar objetos de servicio o el patrón de decorador como medio para lograr esto .

Otro enfoque sería anular los métodos de actualización como << para la asociación y silenciosamente dejar el rol si coincide con uno existente. Los detalles sobre los métodos de asociación anulables están en el ActiveRecord Association Class Methods Documentation

Cuestiones relacionadas