Update_attributes y callbacks

Hola gente,

Tengo dos preguntas, una corta de respuesta rápida, y una más larga
cuya respuesta imagino que será debatible.

La primera:

¿Hay alguna forma de hacerle un update_attributes a un objeto AR sin
que salten los callbacks (before_save, etc.)? Un no o un sí y una
línea de código bastará =;-)

La segunda es la explicación de por qué necesito hacer esto, y si lo
consideráis algún tipo de anti-patrón, mala práctica, o “code smell”.

Tengo un modelo con un before_save que básicamente establece el valor
de un campo en función del valor de otro, y sólo si cambia otro
(víalos dirty objects, que por cierto están genial). Funciona, tiene su
test de unidad, y todo guay.

Luego tengo un test de integración (en verdad una Rspec Story) que
comprueba que cuando ese campo que se establece vía callback se
muestra determinada información en la vista. Obviamente (y digo
obviamente porque lo es para mí, pero agradecería si alguien me
discute esta premisa), para hacer ese test establezco el valor
directamente en la base de datos, en vez de depender del mecanismo
antes descrito, dado que no es lo que estoy testeando en esa
ocasión,pero claro, salta el callback y me machaca el valor que yo pongo,
dejándolo a nulo porque los otros campos no están establecidos.

¿Cómo lo véis? ¿Debería, en las precondiciones de este test, simular
el workflow habitual de la aplicación, o buscar la manera de
“puentearlo” para tener en la base de datos el valor que quiero tener
(ya que el hecho de que se establece cuando se tiene que establecer es
algo que ya tengo testeado en otro lugar)?.

Un saludo!


Sergio Gil Pérez de la Manga
e-mail > [email protected]
blog > http://www.lacoctelera.com/porras
now > http://twitter.com/porras

2008/9/15 Sergio Gil Pérez de la Manga [email protected]:

A las buenas :slight_smile:

¿Hay alguna forma de hacerle un update_attributes a un objeto AR sin
que salten los callbacks (before_save, etc.)? Un no o un sí y una
línea de código bastará =;-)

Nope, es equivale a attributes= && save.

Por cierto que tengo un parche reciente pendiente porque eso no ocurre
en una transaccion y a veces attributes= modifica la base de datos:

http://rails.lighthouseapp.com/projects/8994/tickets/922-has_many-through-transaction-rollback

2008/9/15 Sergio Gil Pérez de la Manga [email protected]

Creo recordar que existía un update_without_callbacks

2008/9/15 Sergio Gil Pérez de la Manga [email protected]:

Que yo vea no. Pero no sería complicado hacerlo, yo podría este código
dentro del test de integración del que hablas, de forma que fuera
local (no parece que se vaya utilizar mucho más, pero en ese caso lo
movería al test_helper.rb o lo que se utilice en RSpec):

module ActiveRecord
class Base
def update_attributes_without_validations(attributes)
self.attributes = attributes
save(false)
end
end
end

Sobre lo que hablas de si el test debe reproducir el funcionamiento de
la aplicación: un test de integración IMHO debería reproducir los
pasos del usuario por la aplicación, pero lo que sería lo idea simular
el funcionamiento normal de la aplicación, por otro lado si el código
de preparación “distrae” de la prueba a realizar, puentearlo no sería
una mala opción.

2008/9/15 Guillermo [email protected]:

que salten los callbacks (before_save, etc.)? Un no o un sí y una
línea de código bastará =;-)

Creo recordar que existía un update_without_callbacks

Pero ese no es #update_attributes, de hecho #update es un metodo
privado. Esa aproximacion seria llamar a #attributes= y luego
#save_without_callbacks. Es tecnicamente posible pero no es una buena
practica.

En general, los with/without son metodos “privados” ya que no
pertenecen a la interfaz de AR. Son artificios que salen de como se
consigue que AR sea modular por dentro (esto lo explico en la charla
de AR internals BTW).

Caso de hacer un hack se-que-es-una-guarrada-pero-me-hace-falta has de
ver tambien en que punto de la cadena de aliases te quedas. Todo lo
posterior a los callbacks lo pierdes porque para cuando se ejecuta ese
alias solo se ha llegado a validations.rb:

require ‘active_record/base’
require ‘active_record/observer’
require ‘active_record/query_cache’
require ‘active_record/validations’
require ‘active_record/callbacks’
require ‘active_record/reflection’
require ‘active_record/associations’
require ‘active_record/aggregations’
require ‘active_record/transactions’
require ‘active_record/timestamp’
require ‘active_record/locking/optimistic’
require ‘active_record/locking/pessimistic’
require ‘active_record/migration’
require ‘active_record/schema’
require ‘active_record/calculations’
require ‘active_record/serialization’
require ‘active_record/attribute_methods’