Hola gente, Estoy usando Sweepers para expirar determinadas cachés cuando se guarda/actualiza/borra determinado modelo. Todo bien. Lo que ocurre que ahora me surge que estoy insertando modelos en la BD desde un script/rake/cron ... y sorprendido de mí veo que las cachésno se expiran. El problema está en que los Sweepers no están activados puesto que se declaran en el application.rb con el cache_sweeper. Leyendo esto: http://codelevy.com/articles/2008/03/04/rails-caching-sweepers-controllers-and-models Veo que no hay mucha solución, pero me gustaría preguntar si os ha pasado alguna vez y cómo lo habéis solucionado. O si tenéis alguna sugerencia. Gracias f.
on 31.07.2008 17:05
on 31.07.2008 17:40
No utilices cache, eso es de maricas :) Bromas a parte, ni idea. Uf como me afecta el calor. El 31 de julio de 2008 17:04, Fernando Guillen<fguillen.mail@gmail.com> escribió:
on 01.08.2008 11:00
Usando observers no te funcionaria? El observer mira directamente el modelo, asà que podrias decirle que desde allà se expiren las caches ...
on 01.08.2008 12:02
El día 1 de agosto de 2008 11:00, Francesc Esplugas <francesc.esplugas@gmail.com> escribió:> Usando observers no te funcionaria? Sip. Es lo que me han sugerido pero no me gusta salirme de el modo en que rails espera que se hagan las cosas. Por otro lado, no sé si desde los observes voy a tener fácil el acceso a métodos como: * expire_action Y al reconocimiento de rutas como: forums_topic_show_url( :forum_nicetitle => record.forums_forum.nicetitle, :category_nicetitle => record.forums_category.nicetitle, :topic_nicetitle => record.nicetitle ) No sé... Gracias f.
on 01.08.2008 17:18
On Fri, Aug 1, 2008 at 12:01, Fernando Guillen <fguillen.mail@gmail.com> wrote: > a métodos como: > > No sé... > > Gracias > f. > Ha sido difÃcil pero lo he conseguido (creo). Necesitas un Sweeper <http://rails-doc.org/rails/ActionController/Caching/Sweeping>, no un Observer normal y corriente <http://rails-doc.org/rails/ActiveRecord/Observer>, en otro caso no podrás hacer uso de las funciones expire_*. Yo tengo un modelo y un sweeper que le observa: --- app/models/product.rb --- class Product end --- eof --- --- app/models/product_sweeper.rb --- class ProductSweeper < ActionController::Caching::Sweeper observe Product def after_save(product) product.logger.info("Producto creado y <#{products_url}">) end end --- eof --- Vale, desde el script/console si hago lo siguiente: ActiveRecord::Base.observers = [ProductSweeper] ActiveRecord::Base.instantiate_observers ProductSweeper.instance.controller = ActionController.Base.new ProductSweeper.instance.controller.request = TestRequest.new p = Product.create :name => 'test' En el log aparece "Producto creado y <http://test.host/products>", que creo que demuestra que se podrÃa hacer funcionar los expire_* con los métodos hash_for_products_path y similares. Suerte.
on 01.08.2008 20:04
A mi esto me da un error ...
ProductSweeper.instance.controller.request = TestRequest.new
No existe la clase TestRequest.
2008/8/1 Daniel Rodriguez Troitiño <notzcoolx@yahoo.es>:
on 01.08.2008 20:36
2008/8/1 Francesc Esplugas <francesc.esplugas@gmail.com>: > A mi esto me da un error ... > > ProductSweeper.instance.controller.request = TestRequest.new > > No existe la clase TestRequest. > Ups. Mira que he repasado los pasos una y otra vez. Pues esa lÃnea siempre la escribÃa mal. La clase correcta es ActionController::TestRequest. Suerte.
on 01.08.2008 20:38
2008/8/1 Daniel Rodriguez Troitiño <notzcoolx@yahoo.es>: > ActionController::TestRequest. > > Suerte. > De hecho la lÃnea justo superior también está mal: ActionController::Base (dobles dos puntos en vez de punto). Lo siento.
on 02.08.2008 18:17
El día 1 de agosto de 2008 17:17, Daniel Rodriguez Troitiño<notzcoolx@yahoo.es> escribió:> > end > > En el log aparece "Producto creado y <http://test.host/products>", que > creo que demuestra que se podría hacer funcionar los expire_* con los > métodos hash_for_products_path y similares. > Daniel.. está genial lo que has conseguido :D. Pero se me ha vuelto a liar. He conseguido que el ejemplo que me pasaste funcionara, pero si intento generar urls con parámetros en forma de Hash, que es la forma que más uso, la he cagado: Ejemplo: class ForumsCommentSweeper < ActionController::Caching::Sweeper observe ForumsComment def after_save( record ) p( "ruta: #{ruta_guapa_url}" ) p( "ruta: #{ruta_guapa_con_parametro_url( 'X' )}" ) p( "ruta: #{ruta_guapa_con_parametro_url( :parametro => 'X' )}" ) end end Las 2 rutas están bien definidas en el routes.rb. : map.ruta_guapa 'ruta/guapa', :controller => 'develop', :action => 'simulate_login_form' map.ruta_guapa_con_parametro 'ruta/guapa/:parametro', :controller => 'develop', :action => 'simulate_login_form' La tercera peta con un: NoMethodError: You have a nil object when you didn't expect it! The error occurred while evaluating nil.rewrite from /Library/Ruby/Gems/1.8/gems/actionpack-2.0.2/lib/action_controller/base.rb:617:in `url_for' from (eval):17:in `ruta_guapa_con_parametro_url' from /Library/Ruby/Gems/1.8/gems/actionpack-2.0.2/lib/action_controller/caching.rb:665:in `send!' from /Library/Ruby/Gems/1.8/gems/actionpack-2.0.2/lib/action_controller/caching.rb:665:in `method_missing' from /Users/fguillen/Documents/develop-ror/lcforums/app/sweepers/forums_comment_sweeper.rb:14 <<el número de línea no coincide porque el paste está resumido>> :in `after_save' from /Library/Ruby/Gems/1.8/gems/activerecord-2.0.2/lib/active_record/observer.rb:157:in `send' He intentado revisar el código de cachin.rb y de base.rb y veo que el problema está en base.rb justo en este método: def url_for(options = nil) #:doc: case options || {} when String options when Hash @url.rewrite(rewrite_options(options)) else polymorphic_url(options) end end Si entra en el 'when Hash' estamos jodidos resulta que '@url' es nil y sacabó. He intentado ver como ser carga este '@url' y parece que se hace en el método del mismo fichero: def initialize_current_url @url = UrlRewriter.new(request, params.clone) end He intentado invocarlo desde la consola para que se inicializara con esto: ForumsCommentSweeper.instance.controller.initialize_current_url Pero es privado. También desde consola he probado: ForumsCommentSweeper.instance.controller.url = ActionController::UrlRewriter.new( ForumsCommentSweeper.instance.controller.request, nil ) Pero no le gustaba: NoMethodError: undefined method `url=' for #<ActionController::Base:0x2129d58> Vamos que estoy viendo que nos estamos metiendo un plato de espaguettis del que no vamos a salir. Si sale alguna sugerencia al rescate guay.. Sino intentaré borrar la cache de alguna manera cutre rollo borrar el fichero o cualquier barbaridad. Saludos, y muchas gracias. f. PD: a ver si no me he cargado nada de las gemas que he puesto log por todas partes.. :)
on 03.08.2008 02:24
2008/8/2 Fernando Guillen <fguillen.mail@gmail.com>: > > Las 2 rutas están bien definidas en el routes.rb. : > from /Library/Ruby/Gems/1.8/gems/actionpack-2.0.2/lib/action_controller/base.rb:617:in > `send' > @url.rewrite(rewrite_options(options)) > def initialize_current_url > > > > Primera cosa, no se si es por el ejemplo, pero ruta_guapa y ruta_guapa_con_parametro se pueden reducir asÃ: map.ruta_guapa 'ruta/guapa/:parametro', :controller => 'develop', :action => 'simulate_login_form', :parametro => nil Cuando no reciba parámetro Rails pasará nil (que por como es el ejemplo tú debes ya tener cuidado en simulate_login_form) y además, de gratis, quita la barra final de "ruta/guapa", con lo que quedan las rutas que tú tenÃas. Y lo siguiente, la "solución" (sigo con mi ejemplo, si no te importa): ActiveRecord::Base.observers = [ProductSweeper] ActiveRecord::Base.instantiate_observers ProductSweeper.instance.controller = ActionController::Base.new tr = ActionController::TestRequest.new ProductSweeper.instance.controller.request = tr ProductSweeper.instance.controller.instance_eval('@url = ActionController::UrlRewriter.new(tr, {})') Product.create :name => 'Name 22' En el after_save del swepper: product.logger.info("ruta guapa 1 #{ruta_guapa_path}") product.logger.info("ruta guapa 2 #{ruta_guapa_path('X')}") product.logger.info("ruta guapa 3 #{ruta_guapa_path(:parametro => 'X')}") En el log: ruta guapa 1 /ruta/guapa ruta guapa 2 /ruta/guapa/X ruta guapa 3 /ruta/guapa/X Es un asco tener que utilizar instance_eval, pero en Rails 2.1.0 funciona. Digo que es un asco porque al no ser un API pública tendrás que comprobar en cada versión de Rails si va a seguir funcionando. La única forma que he visto posible utilizando solo el API pública es realizar un request de verdad, mediante el método process, pero eso implicarÃa tener un controlador de verdad (mientras que en la solución que utilizamos no necesitamos realizar un request y no necesitamos un controlador). Rails con las rutas es un desastre de cuidado. DeberÃa ser mucho más abierto con su utilización. Suerte.
on 04.08.2008 09:31
El día 3 de agosto de 2008 2:23, Daniel Rodriguez Troitiño<notzcoolx@yahoo.es> escribió:> 2008/8/2 Fernando Guillen <fguillen.mail@gmail.com>: > Primera cosa, no se si es por el ejemplo, pero ruta_guapa y > ruta_guapa_con_parametro se pueden reducir así: > > map.ruta_guapa 'ruta/guapa/:parametro', :controller => 'develop', > :action => 'simulate_login_form', :parametro => nil > > Cuando no reciba parámetro Rails pasará nil (que por como es el > ejemplo tú debes ya tener cuidado en simulate_login_form) y además, de > gratis, quita la barra final de "ruta/guapa", con lo que quedan las > rutas que tú tenías. Oki.. apuntado.. gracias. > Product.create :name => 'Name 22' > Funciona perfectamente.. cargar las urls y expira la caché.. al principio me estaba volviendo loco porque no parecía estar expirando las cachés pero luego ví en el log que al ejecutarse desde consola se expiraban las cachés de test.host: * Expired fragment: test.host/topics/este-es-foro-1/esta-es-categoria-1/ver/nuevo-tema, lang = ["en"] (0.00007) en vez de localhost:3000: * Expired fragment: localhost.3000/topics/este-es-foro-1/esta-es-categoria-1/ver/nuevo-tema, lang = ["en"] (0.00005) Ahora, Daniel, tengo otro problema y es que no consigo dar 2 pasos seguidos en este punto ... Resulta que todo funciona bien en script/consola ... pero me está fallando en script/runner.. seguro que hay algo del modo de funcionar de estos 2 entornos que me pierdo.. Este es el código que me funciona en consola: ---- ActiveRecord::Base.observers = [ForumsCommentSweeper] ActiveRecord::Base.instantiate_observers ForumsCommentSweeper.instance.controller = ActionController::Base.new tr = ActionController::TestRequest.new ForumsCommentSweeper.instance.controller.request = tr ForumsCommentSweeper.instance.controller.instance_eval( '@url = ActionController::UrlRewriter.new(tr, {})' ) forums_comment_new = ForumsComment.new() forums_comment_new.body = "body" forums_comment_new.user_id = 1000 forums_comment_new.forums_topic_id = 5 forums_comment_new.save ---- Como ves es muy parecido al tuyo. Si lo ejecuto en con script/runner (lo meto en un fichero etc/prueba.rb y le doy a script/runner etc/prueba.rb) me dá un error al intentar resolver la url: ---- /Library/Ruby/Gems/1.8/gems/rails-2.0.2/lib/commands/runner.rb:45: /Library/Ruby/Gems/1.8/gems/actionpack-2.0.2/lib/action_controller/request.rb:168:in `ssl?': You have a nil object when you didn't expect it! (NoMethodError) You might have expected an instance of ActiveRecord::Base. The error occurred while evaluating nil.[] from /Library/Ruby/Gems/1.8/gems/actionpack-2.0.2/lib/action_controller/request.rb:163:in `protocol' from (eval):2:in `ruta_guapa_url' from /Library/Ruby/Gems/1.8/gems/actionpack-2.0.2/lib/action_controller/caching.rb:664:in `send!' from /Library/Ruby/Gems/1.8/gems/actionpack-2.0.2/lib/action_controller/caching.rb:664:in `method_missing' from /Users/fguillen/Documents/develop-ror/lcforums/app/sweepers/forums_comment_sweeper.rb:13:in `after_save' from /Library/Ruby/Gems/1.8/gems/activerecord-2.0.2/lib/active_record/observer.rb:157:in `send' ---- Llega hasta el sweeper perfectamente pero al intentar resolver la url peta.. en esta línea: p( "ruta1: #{ruta_guapa_url}" ) :/ Entonces.. ¿qué diferencia hay entre script/console y script/runner para que me falle esto? .. gracias. f.
on 04.08.2008 11:57
2008/8/4 Fernando Guillen <fguillen.mail@gmail.com>: > en vez de localhost:3000: > de estos 2 entornos que me pierdo.. > ForumsCommentSweeper.instance.controller.request = tr > Como ves es muy parecido al tuyo. > You might have expected an instance of ActiveRecord::Base. > from /Library/Ruby/Gems/1.8/gems/activerecord-2.0.2/lib/active_record/observer.rb:157:in > Entonces.. ¿qué diferencia hay entre script/console y script/runner > para que me falle esto? .. gracias. > > f. Es ¿imposible? que te suceda eso. He reproducido tu ejemplo lo mejor que he podido: utilizo una aplicación Rails 2.0.2, tengo un ForumsComment (con los atributos que tu mostrabas) y un ForumsCommentSweeper con un after_save que hace unos logger.info de las tres rutas de ejemplo que pusiste en un correo anterior (tengo la ruta que yo te propuse en el routes.rb). Tu ejemplo en la consola funciona perfectamente, tu ejemplo en un archivo etc/prueba.rb (dentro del directorio de la aplicación Rails, me imagino) funciona sin protestas (aparecen en el log los mensajes esperados). Lo único que he tenido que hacer que no aparece explicitamente en tus instrucciones es un "require 'action_controller/test_process'" al principio de prueba.rb. Digo que es imposible porque al construir TestRequest se crea una variable @env que es la que te está diciendo a tà que es nil (cuando se inicializa invariablemente a un hash vacio). Te debe faltar algo por ahà o algo, o el ejemplo que pones no es suficiente para reproducir el problema al completo. Lo siento. Suerte.
on 04.08.2008 12:17
Llego muy tarde al hilo: sólo avisar que convendría usar la misma versión de Rails para depurar el problema. Fernando: ¿cuesta mucho esfuerzo replicar el problema en una aplicación nueva? Si es más o menos rápido, quizá podrías colgarla en algún sitio para hacer alguna prueba sobre el mismo ejemplo (es un poco jaleo pero por lo que parece el problema lleva tiempo dando guerra).
on 04.08.2008 12:19
El día 4 de agosto de 2008 11:54, Daniel Rodriguez Troitiño<notzcoolx@yahoo.es> escribió:> etc/prueba.rb (dentro del directorio de la aplicación Rails, me > > Te debe faltar algo por ahí o algo, o el ejemplo que pones no es > suficiente para reproducir el problema al completo. Lo siento. > Daniel.. ahora sí que funciona todo :) Era el: ---- require 'action_controller/test_process' ---- Con esa línea ya funciona desde script/runner. Muchas gracias por todo. Voy a intentar escribir un post con todo esto referenciándote sino te importa. (si tienes una web o algo pásamela) Gracias otra vez. f.
on 04.08.2008 12:20
Jajaja, esto me pasa por llegar tarde... XD Enhorabuena a ambos! :D
on 04.08.2008 12:21
El día 4 de agosto de 2008 12:17, Raul Murciano <raul@murciano.net> escribió:> Llego muy tarde al hilo: sólo avisar que convendría usar la misma > versión de Rails para depurar el problema. > Fernando: ¿cuesta mucho esfuerzo replicar el problema en una > aplicación nueva? Si es más o menos rápido, quizá podrías colgarla en > algún sitio para hacer alguna prueba sobre el mismo ejemplo (es un > poco jaleo pero por lo que parece el problema lleva tiempo dando > guerra). > Si hubiéramos seguido con el poltergeist está claro que habría que haber hecho una aplicacioncita compartida para agilizar las pruebas. Sí que es jaleo, pero es como se deben hacer las cosas. Saludos f.