No Rails, um Sweeper não está sendo chamado em uma configuração somente de Modelo
-
13-09-2019 - |
Pergunta
Eu estou trabalhando em um aplicativo de Rails, onde eu estou usando o cache de página para armazenar a saída html estática. O caching funciona bem. Estou tendo problemas com a expiração dos caches, no entanto.
Eu acredito que o meu problema é, em parte, porque eu não estou expirando o cache do meu controlador. Todas as ações necessárias para este estão sendo tratadas no modelo. Isso parece que deve ser factível, mas todas as referências a expiração de cache baseada em modelo que eu estou achando parecem estar fora da data, ou de outra forma não estão funcionando.
No meu arquivo environment.rb, eu estou chamando
config.load_paths += %W( #{RAILS_ROOT}/app/sweepers )
E eu tenho, no um arquivo / pasta varredores, LinkSweeper:
class LinkSweeper < ActionController::Caching::Sweeper
observe Link
def after_update(link)
clear_links_cache(link)
end
def clear_links_cache(link)
# expire_page :controller => 'links', :action => 'show', :md5 => link.md5
expire_page '/l/'+ link.md5 + '.html'
end
end
Então ... por que não é eliminar a página em cache quando eu atualizar o modelo (Processo: usando script / console, eu estou selecionando itens de banco de dados e salvá-los, mas suas páginas correspondentes não está excluindo a partir do cache), e eu também estou chamando o método específico no modelo Fazer a ligação que normalmente invocar a vassoura. Nem obras.
Se é importante, o arquivo em cache é um hash MD5 fora um valor de chave na tabela de links. A página em cache está sendo armazenado como algo como / l / 45ed4aade64d427 ... 99919cba2bd90f.html.
Essencialmente, parece que a vassoura não é realmente observando o link. Eu também li ( aqui ) que pode ser possível simplesmente adicionar o varredor para config.active_record.observers em environment.rb, mas que não parecem fazê-lo (e eu não tinha certeza se o LOAD_PATH de app / varredores em environment.rb evitado isso).
Solução
Apenas uma nota:. Você pode usar cache_sweeper
em ApplicationController
class ApplicationController < ActionController::Base
cache_sweeper :my_sweeper
end
class MySweeper < ActionController::Caching::Sweeper
observe MyModel
def after_update(my_model)
expire_page(...)
end
end
Outras dicas
Então, eu tentei um número de diferentes abordagens, para ver o que funciona eo que não funciona.
Mais uma vez, para resumir a situação: Meu objetivo é a expirar páginas em cache quando um objeto atualizações, mas a expirar-los sem depender de uma ação de controlador varredores convencionais usam uma linha no controlador para notificar. o varredor que ele precisa para funcionar. Neste caso, eu não posso usar uma linha no controlador, como a atualização está acontecendo dentro do modelo. tutoriais vassoura normais não estão funcionando, como eles presumem que a sua principal interação com o objeto de banco de dados é através do controlador.
Se, ao ler isso, você vê uma maneira de apertar o meu código, por favor comentário e deixe-me saber.
Primeiro, vamos olhar para as coisas que funcionam DO, no caso de você está preso a este, também, e precisam de ajuda.
De todas as coisas que eu tentei, a única coisa que realmente parecia trabalho foi declarar um comando after_update no Observer para o modelo. Nesse comando, eu usei o comando explícito para a ação expire_page, e incluiu um caminho que tinha sido declarada em routes.rb.
Então. Isso funciona:
Em config / routes.rb:
map.link 'l/:md5.:format', :controller => 'links', :action => 'show'
Em app / models / link_observer.rb:
def after_update(link)
ActionController::Base.expire_page(app.link_path(:md5 => link.md5))
end
Note que que "md5" é específico para meu aplicativo. Você pode querer usar:. ID ou algum outro identificador único
Eu também achei que declarar que ActionController :: Base ... linha a partir do método no modelo que está fazendo a atualização funcionou. Isto é, dentro Link.rb, no método que está realmente atualizar o banco de dados, se eu apenas preso a linha inteira em, funcionou. Mas desde que eu poderia querer expiram que cache de página sobre outros métodos no futuro, eu prefiro tê-lo extraídos para o Observer.
Agora, vamos olhar para algumas coisas que não funcionou, caso você esteja pesquisando por aí para isso.
Calling "expire_page (...)" dentro do método after_update (link) dentro link_observer.rb não funcionou, pois ele retornou um "undefined method` expire_page '" erro
Criação de um arquivo Sweeper que observaram o modelo não funcionou. Eu não poderia encontrar qualquer código de erro, mas apenas parecia nem mesmo estar ciente de que ele tinha um trabalho a fazer. Este foi depois de chamar explicitamente "config.load_paths + =% W (# {RAILS_ROOT} / app / Vassouras)" dentro environment.rb. Apenas no caso de eu algo de dedos de gordura nesse código, aqui está:
class LinkSweeper < ActionController::Caching::Sweeper
observe Link
def after_update(link)
clear_links_cache(link)
end
def clear_links_cache(link)
# DID NOT WORK expire_page :controller => 'links', :action => 'show', :md5 => link.md5
# DID NOT WORK expire_page '/l/'+ link.md5 + '.html'
# DID NOT WORK ActionController::Base.expire_page(app.link_path(:md5 => link.md5))
end
end
Esse exemplo acima tiveram o arquivo link_sweeper.rb em um diretório, / app / varredores. Eu também tentei colocar link_sweeper.rb dentro do diretório app / models e tentou chamá-lo com os config.active_record.observers comando no environment.rb:
config.active_record.observers = :link_observer, :link_sweeper
Mas isso não funcionou, tampouco.
Então, sim. É bem possível que um desses métodos iria funcionar, e que eu errei alguma coisa no código. Mas eu acho que fiz tudo pelo livro.
Finalmente, para resumir: Ao invés de usar uma vassoura para expirar o cache de página, você quer configurar um callback after_ no Observer do modelo. Você vai querer usar o caminho explícito para o método Base.expire_page:
def after_update(<model>) # where <model> is the name of the model you're observing
ActionController::Base.expire_page(app.<model>_path(:id => <model>.id)) # where <model> is the name of the model you're observing
end
Esperamos que isso vai ajudar alguém no caminho. Novamente, se você ver em qualquer lugar no meu código não-trabalhando onde eu deveria ter feito algo diferente, por favor me avise. Se você ver algo no meu código de trabalho que pode ser mais apertado, por favor deixe-me saber que, também.
eu estava experimentando o mesmo problema ao tentar fazer o cache de fragmento (Rails 3). Não foi possível obter o varredor de observar, para povoada a solução para torná-lo um AR Observer como descrito acima e chamando ApplicationController.new.expire_fragment(...)
.
Eu fiz começar este trabalho. A única ligeira diferença na minha configuração é que o varredor é parte de um motor de Rails; que responde por pequenas diferenças (carregamento do arquivo de vassoura com um requerem no motor do init em vez de adicioná-lo ao caminho de carga em environment.rb, etc).
Assim, o varredor é carregado na init.rb do motor como este:
require File.join(File.dirname(__FILE__), 'app', 'sweepers', cached_category_count_sweeper')
I chamou-lhe uma vassoura porque "varre" o cache, mas eu acho que é apenas um observador do modelo:
class CachedCategoryCountSweeper < ActiveRecord::Observer
observe CategoryFeature
def before_save(cf)
expire_cache(cf.category_id_was) if cf.category_id_changed?
end
def after_save(cf)
expire_cache(cf.category_id)
end
def after_destroy(cf)
expire_cache(cf.category_id)
end
def expire_cache(c)
ApplicationController.expire_page("/categories/#{c}/counts.xml") if !c.nil?
end
end
Francamente, eu não gosto de ter que codificar o caminho, mas eu tentei adicionar:
include ActionController:UrlWriter
e, em seguida, usando o método do caminho, mas só funcionou para mim no desenvolvimento. Não funcionou na produção, porque o meu servidor de produção utiliza uma raiz url relativa (em vez de hosts virtuais) e o método "page_cache_path" interna seria consistentemente obter o errado caminho do arquivo para que ele não poderia expirar.
Uma vez que este é um observador, eu adicionado ao environment.rb:
config.active_record.observers = :cached_category_count_sweeper
Finalmente, o controlador que usa o cache (não expira-lo, o que é feito através do modelo de observador):
class CachedCategoryCountsController < ApplicationController
caches_page :index
# GET /cached_category_counts.xml
def index
...
end
end
De qualquer forma, espero que isso ajude.
Andres Montano
Eu tenho sido capaz de obtê-lo para o trabalho, por meio de adição
ActionController::Base.expire_page(app.link_path(:md5 => @link.md5))
para o método no próprio modelo que está atualizando o banco de dados. Isto parece um pouco hacky, embora, e eu gostaria de saber se alguém pode explicar por que ele não está funcionando com a configuração normal de vassoura, e se há uma maneira mais elegante de lidar com isso.
Esse trecho de código (além de personalizações que eu ponho no para o meu próprio app) veio de este post sobre ruby-forum.com .
Eu escrevi um pouco sobre este tópico aqui: Rails cache Sweeper Confusão . Gostaria de ouvir as suas opiniões.
Com base @moiristo e respostas 's @ZoogieZork, eu estou supondo que isso iria funcionar (não testado).
class LinkSweeper < ActiveRecord::Observer
include ActionController::Caching::Pages
# or if you want to expire fragments
#include ActionController::Caching::Fragments
observe Link
def after_update(link)
expire_page( ... )
#expire_fragment( ... )
end
end