В 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
Итак... почему не происходит удаление кэшированной страницы при обновлении модели? (Процесс: используя скрипт / консоль, я выбираю элементы из базы данных и сохраняю их, но соответствующие страницы не удаляются из кэша), и я также вызываю конкретный метод в модели Link, который обычно вызывает подметальная машина Ни то, ни другое не работает.
Если это имеет значение, кэшированный файл представляет собой хэш md5 со значением ключа в таблице ссылок. Кэшированная страница хранится в формате /l/45ed4aade64d427...99919cba2bd90f.html.
По сути, кажется, что Sweeper фактически не наблюдает за Link. Я также читал ( здесь), что возможно было бы просто добавить уборщик в config.active_record.observers в environment.rb, но это, похоже, не делало этого (и я не был уверен, что путь загрузки указан в app/sweepers в среде.rb это исключено).
7 ответов
Просто примечание: вы можете использовать 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
Поэтому я попробовал несколько разных подходов, чтобы увидеть, что работает, а что нет.
Опять же, чтобы подвести итог ситуации: моя цель состоит в том, чтобы истекать кэшированные страницы при обновлении объекта, но истекать их, не полагаясь на действие контроллера. Обычные подметальные машины используют линию в контроллере, чтобы уведомить подметальную машину о том, что она должна функционировать. В этом случае я не могу использовать строку в контроллере, так как обновление происходит внутри модели. Обычные учебники по уборке не работают, так как они предполагают, что ваше основное взаимодействие с объектом базы данных происходит через контроллер.
Если, читая это, вы видите способ ужесточить мой код, пожалуйста, прокомментируйте и дайте мне знать.
Во-первых, давайте посмотрим, что действительно работает, если вы тоже застряли на этом и нуждаетесь в помощи.
Из всего, что я пробовал, единственное, что действительно казалось работающим, это объявить команду after_update в Observer для модели. В этой команде я использовал явную команду для действия expire_page и включил путь, который был объявлен в rout.rb.
Так. Это работает:
В config/rout.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.
Теперь давайте посмотрим на некоторые вещи, которые не сработали, на случай, если вы гуглите по этому поводу.
Вызов "expire_page(...)" в методе after_update(link) в link_observer.rb не сработал, так как он возвратил ошибку "неопределенный метод expire_page"
Создание файла Sweeper, который наблюдал за моделью, не сработало. Я не мог найти никаких кодов ошибок, но он, казалось, даже не знал, что у него есть работа. Это было после явного вызова "config.load_paths += %W( #{RAILS_ROOT}/app/sweepers)" в 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). Не удалось заставить уборщик наблюдать, поэтому я остановился на решении сделать AR Observer, как описано выше, и вызвать ApplicationController.new.expire_fragment(...)
,
Я получил это работает. Единственное небольшое отличие в моей настройке состоит в том, что уборщик является частью двигателя 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
а затем с помощью метода пути, но он работал только для меня в разработке. Он не работал в производственном процессе, потому что мой производственный сервер использует относительный корень 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
Во всяком случае, надеюсь, это поможет.
Андрес Монтано
Я немного написал об этой теме здесь: Rails Cache Sweeper Confusion. Хотелось бы услышать ваше мнение.
Исходя из ответов @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
Я смог заставить его работать, добавив
ActionController::Base.expire_page(app.link_path(:md5 => @link.md5))
к методу в самой модели, которая обновляет базу данных. Это выглядит несколько странно, и я хотел бы знать, может ли кто-нибудь объяснить, почему он не работает с обычной установкой подметальной машины, и есть ли более элегантный способ справиться с этим.
Этот фрагмент кода (кроме настроек, которые я вставил для своего приложения) взят из этого поста на ruby-forum.com.