В 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