В Rails Sweeper не получает вызов в настройке только для модели
Я работаю над приложением Rails, где я использую кеширование страниц для хранения статического html-вывода. Кэширование работает отлично. Однако у меня проблемы с истечением кешей.
Я считаю, что моя проблема частично связана с тем, что я не исчерпал кеш с моего контроллера. Все действия, необходимые для этого, обрабатываются в рамках модели. Кажется, что это должно быть выполнимо, но все ссылки на истечение срока действия кэша на основе модели, которые я нахожу, устарели или в противном случае не работают.
В моем файле environment.rb я звоню
config.load_paths += %W( #{RAILS_ROOT}/app/sweepers )
И у меня в папке /sweepers файл 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
Итак... Почему он не удаляет кешированную страницу при обновлении модели? (Процесс: используя script/console, я выбираю элементы из базы данных и сохраняю их, но их соответствующие страницы не удаляются из кеша), и я также вызываю конкретный метод в модели Link, который обычно вызывает подметальную машину. Ничего не работает.
Если это имеет значение, кешированный файл является хешем md5 от значения ключа в таблице Links. Кэшированная страница хранится как-то вроде /l/ 45ed4aade64d427... 99919cba2bd90f.html.
По существу, похоже, что Sweeper фактически не наблюдает за Link. Я также читал (здесь), что можно было бы просто добавить подметку в config.active_record.observers в environment.rb, но это не так, t, похоже, это делает (и я не был уверен, что load_path для приложений/подметаллов в environment.rb устранил это).
Ответы
Ответ 1
Просто примечание: вы можете использовать cache_sweeper
в 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
Ответ 2
Итак, я пробовал несколько разных подходов, чтобы посмотреть, что работает, а что нет.
Опять же, чтобы суммировать ситуацию: Моя цель - истечь кешированных страниц при обновлении объекта, но для их истечения, не полагаясь на действие контроллера. Обычные подметальные машины используют строку в контроллере для уведомления что он должен функционировать. В этом случае я не могу использовать строку в контроллере, так как обновление происходит внутри модели. Обычные обучающие программы для уборщиков не работают, поскольку они предполагают, что ваше основное взаимодействие с объектом базы данных осуществляется через контроллер.
Если, читая это, вы видите способ подтянуть мой код, прокомментируйте и дайте мне знать.
Во-первых, давайте посмотрим на то, что работает, если вы тоже застряли на этом, и вам нужна помощь.
Из всего, что я пробовал, единственное, что действительно работало, это объявить команду after_update в Observer для модели. В этой команде я использовал явную команду для действия expire_page и включил путь, который был объявлен в routes.rb.
Итак. Это работает:
В config/routes.rb:
map.link 'l/:md5.:format', :controller => 'links', :action => 'show'
В приложении /models/link _observer.rb:
def after_update(link)
ActionController::Base.expire_page(app.link_path(:md5 => link.md5))
end
Обратите внимание, что это "md5" относится к моему приложению. Возможно, вы захотите использовать: id или другой уникальный идентификатор.
Я также обнаружил, что объявление о том, что ActionController:: Base... строка из метода в модели, выполняющей обновление, работает. То есть, в Link.rb, в методе, который фактически обновляет базу данных, если я просто застрял всю эту строку, она сработала. Но так как я могу захотеть в будущем использовать этот кеш страниц для других методов, я бы предпочел, чтобы он был извлечен в Observer.
Теперь давайте посмотрим на некоторые вещи, которые НЕ РАБОТАЮТ, в случае, если для этого вы используете Google.
Вызов "expire_page (...)" в методе after_update (link) в link_observer.rb не работает, поскольку он возвратил ошибку < undefined `expire_page '
Создание файла Sweeper, который наблюдал модель, не работал. Я не мог найти никаких кодов ошибок, но, похоже, даже не знал, что у него есть работа. Это произошло после явного вызова "config.load_paths + =% W (# {RAILS_ROOT}/app/подметальные машины" ) в среде environment.rb. На всякий случай, когда я проглотил что-то в этом коде, вот оно:
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
В приведенном выше примере был файл link_sweeper.rb в каталоге, /app/sweepers. Я также попытался установить link_sweeper.rb в каталог app/models и попробовал называть его командой config.active_record.observers в среде environment.rb:
config.active_record.observers = :link_observer, :link_sweeper
Но это тоже не сработало.
Итак, да. Вполне возможно, что один из этих методов будет работать, и что я что-то испортил в коде. Но я думаю, что я сделал все по книге.
В конечном итоге, чтобы подвести итог: вместо использования Sweeper для истечения кэширования страниц вы хотите настроить обратный вызов after_ в модели Observer. Вы хотите использовать явный путь к методу 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
Надеюсь, это поможет кому-то еще по дороге. Опять же, если вы видите где-нибудь в моем нерабочем коде, где я должен был бы сделать что-то по-другому, пожалуйста, дайте мне знать. Если вы видите что-то в моем рабочем коде, которое может быть более жестким, сообщите мне об этом.
Ответ 3
У меня возникала такая же проблема при попытке выполнить кеширование фрагментов (рельсы 3). Не удалось заставить уборщику наблюдать, поэтому я решил, что это решение сделает AR Observer описанным выше и называет ApplicationController.new.expire_fragment(...)
.
Ответ 4
Я получил эту работу. Единственное небольшое отличие в моей настройке заключается в том, что подметальная машина является частью механизма Rails; что приводит к небольшим различиям (загрузка файла подкачки с требованием в init, вместо добавления его в путь загрузки в environment.rb и т.д.).
Итак, подкачка загружается в init.rb движка следующим образом:
require File.join(File.dirname(__FILE__), 'app', 'sweepers', cached_category_count_sweeper')
Я назвал его подметной машиной, потому что он "подметает" кеш, но я думаю, что это просто наблюдатель на модели:
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
Честно говоря, мне не нравится, когда нужно жестко закодировать путь, но я попытался добавить:
include ActionController:UrlWriter
а затем используя метод path, но он работал только для меня в разработке. Он не работал в производстве, потому что мой производственный сервер использует относительный корень url (вместо виртуальных хостов), а внутренний метод "page_cache_path" будет последовательно ошибочным, если он не сможет истечь.
Поскольку это наблюдатель, я добавил в environment.rb:
config.active_record.observers = :cached_category_count_sweeper
Наконец, контроллер, который использует кеш (не истекает, то есть через наблюдателя модели):
class CachedCategoryCountsController < ApplicationController
caches_page :index
# GET /cached_category_counts.xml
def index
...
end
end
Во всяком случае, надеюсь, что это поможет.
Андрес Монтано
Ответ 5
Мне удалось заставить его работать, добавив
ActionController::Base.expire_page(app.link_path(:md5 => @link.md5))
к методу самой модели, который обновляет базу данных. Это немного раздражает, и мне бы хотелось узнать, может ли кто-нибудь объяснить, почему он не работает с обычной установкой подметания, и если есть более элегантный способ справиться с этим.
Этот фрагмент кода (помимо настроек, которые я установил для собственного приложения) пришел из этого сообщения на ruby-forum.com.
Ответ 6
Я немного написал об этой теме: Rails Cache Sweeper Confusion. Хотелось бы услышать ваши мнения.
Ответ 7
На основании ответов @moiristo и @ZoogieZork, я предполагаю, что это сработает (непроверено).
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