Я почти все перепробовал, но использовать это кажется невозможным expire_fragment из моделей? Я знаю, что ты не должен, и это не-MVC, но наверняка есть какой-то способ сделать это.
Я создал модуль в lib / cache_helper.rb со всеми моими помощниками по истечению срока действия, внутри каждого - всего лишь набор вызовов expire_fragment. У меня есть все мои очистители кеша настраиваются в / app / sweepers и имеют CacheHelper "в моем контроллере приложения, поэтому срок действия кеша в приложение при вызове через контроллеры работает нормально.
Тогда дело в том, что у меня есть некоторые внешние демоны и особенно некоторые повторяющиеся задачи cron, которые вызывают задачу rake, которая вызывает определенные метод. Этот метод выполняет некоторую обработку и вводит записи в модель, после которой мне нужно истечь срок действия кеша.
Как лучше всего это сделать, поскольку я не могу указать очиститель кеша в модели. Прямые наблюдатели кажутся лучшим решением, но тогда это жалуется на то, что expire_fragment не определен и т. д., я даже попытался включить классы кеширования ActionController в наблюдателя но это не сработало. Мне бы хотелось найти идеи, как создать решение за это. Спасибо.





В одном из своих скриптов я использую следующий хак:
require 'action_controller/test_process'
sweepers = [ApartmentSweeper]
ActiveRecord::Base.observers = sweepers
ActiveRecord::Base.instantiate_observers
controller = ActionController::Base.new
controller.request = ActionController::TestRequest.new
controller.instance_eval do
@url = ActionController::UrlRewriter.new(request, {})
end
sweepers.each do |sweeper|
sweeper.instance.controller = controller
end
Затем, как только вызываются обратные вызовы ActiveRecord, очистители могут вызывать expire_fragment.
Это отличный трюк, и я бы не назвал его взломом. Вы устанавливаете состояние, достаточное для того, чтобы подметальная машина выполняла свою работу, и позволяете всему остальному работать, как обычно.
В качестве побочного примечания: мне нужно было заменить «controller.request = ActionController :: TestRequest.new» на «controller.request = request», иначе очиститель будет пытаться истечь кеши для несуществующих хостов. К счастью, объект «запрос» доступен через чистильщик.
Отказ от ответственности: мои рельсы немного заржавели, но это или что-то в этом роде должно работать
ActionController::Base.new.expire_fragment(key, options = nil)
Кто-нибудь пробовал это? У меня нет под рукой рельсов, но я уверен, что это решит проблему ...
Я пробовал это в Rails 3.0.0.rc, и он отлично работает, спасибо! Он не удаляет иерархию папок в каталоге / tmp / cache, но удаляет файл кеша, и это то, что ему нужно сделать.
Это не работает в Rails 2.3 - возможно, из-за плагина. Он говорит «NoMethodError (неопределенный метод` rewrite 'для nil: NilClass) »при выполнении указанной выше строки изнутри подметальной машины. Вызов метода «перезаписи» выходит за рамки моей компетенции - мое приложение не определяет такую функцию, как и любые плагины, которые я использую.
Я получаю следующую ошибку в Rails 3.1 rc4: NoMethodError: undefined method 'host' for nil:NilClass from "actionpack-3.1.0.rc4/lib/action_controller/metal/url_for.rb:30:in 'url_options'". Я предполагаю, что объект запроса отсутствует в новом экземпляре контроллера. Предложения?
Работает шарм тут на рельсах 3.1.1, рубин 1.9.2-п290
У меня это не работает. Если это не сработает и для вас, попробуйте Rails.cache.delete_ matched 'views / site_search_f orm *'
Я немного новичок в рельсах, поэтому это может быть неправильно или даже полезно, но кажется неправильным пытаться вызывать действия контроллера изнутри модели.
Разве невозможно написать действие в контроллере, которое делает то, что вы хотите, а затем вызывать действие контроллера из вашей задачи rake?
Если вы изменяете записи базы данных извне контроллера, вам необходимо каким-то образом сделать недействительными кэшированные представления, например когда задание cron импортирует данные в вашу базу данных. Rails здесь не хватает важного клея. Очиститель кеша запускается только после выполнения действия на контроллере. А как насчет «действий», предпринятых вне контроллеров, то есть фоновых заданий? Здесь также должна быть возможность прикрепить подметальные машины.
Почему бы вашим внешним задачам не вызывать метод истечения срока действия на контроллере. Тогда вы по-прежнему соответствуете MVC, вы не строите зависимость от каких-то хаков для определения области видимости и т. д.
Если на то пошло, почему бы вам просто не поместить все функции демона / внешних функций на контроллер, а rake / cron просто вызовут это. Было бы легче поддерживать нагрузки.
- MarkusQ
Это несколько забавно, но неконструктивно.
Это может не сработать для того, что вы делаете, но вы можете определить собственный обратный вызов в своей модели:
class SomeModel < ActiveRecord::Base
define_callback :after_exploded
def explode
... do something that invalidates your cache ...
callback :after_exploded
end
end
Затем вы можете использовать подметальную машину, как обычно:
class SomeModelSweeper < ActionController::Caching::Sweeper
observe SomeModel
def after_exploded(model)
... expire your cache
end
end
Сообщите мне, если это будет полезно!
Сделать это довольно просто. Вы можете реализовать предложение Ориона, но вы также можете реализовать более широкую технику, показанную ниже, которая дает вам доступ к текущему контроллеру из любой модели и для какой бы цели вы ни решили нарушить разделение MVC (например, возиться с кешем фрагментов, получить доступ к current_user, создание путей / URL-адресов и т. д.)
Для получения доступа к контроллер текущего запроса (если есть) из модели любой, добавьте следующее в environment.rb или, что гораздо лучше, в новый плагин (например, создайте vendor/plugins/controller_from_model/init.rb, содержащий приведенный ниже код):
module ActiveRecord
class Base
protected
def self.thread_safe_current_controller #:nodoc:
Thread.current[:current_controller]
end
def self.thread_safe_current_controller=(controller) #:nodoc:
Thread.current[:current_controller] = controller
end
# pick up the correct current_controller version
# from @@allow_concurrency
if @@allow_concurrency
alias_method :current_controller, :thread_safe_current_controller
alias_method :current_controller=, :thread_safe_current_controller=
else
cattr_accessor :current_controller
end
end
end
Затем в app/controllers/application.rb
class ApplicationController < ActionController::Base
before_filter { |controller|
# all models in this thread/process refer to this controller
# while processing this request
ActiveRecord::Base.current_controller = controller
}
...
Тогда из любой модели,
if controller = ActiveRecord::Base.current_controller
# called from within a user request
else
# no controller is available, didn't get here from a request - maybe irb?
fi
Во всяком случае, в вашем конкретном случае вы можете захотеть ввести код в свои различные потомки ActiveRecord::Base при загрузке соответствующих классов контроллера, чтобы фактический код, поддерживающий контроллер, по-прежнему находился в app/controllers/*.rb, но это не обязательно делать, чтобы что-то получить. функциональный (хотя и уродливый и сложный в обслуживании).
Повеселись!
Спасибо за это! Чтобы заставить его работать, мне пришлось поместить код в environment.rb (он не работал в плагине) и изменить @@ allow_concurrency на ActionController :: Base.allow_concurrency (что, я надеюсь, то же самое).
Да начнется стрельба ногами!
Не будет ли проще и проще просто передать текущий контроллер в качестве аргумента вызова метода модели? Как следующее:
def delete_cascade(controller)
self.categories.each do |c|
c.delete_cascade(controller)
controller.expire_fragment(%r{article_manager/list/#{c.id}.*})
end
PtSection.delete(self.id)
controller.expire_fragment(%r{category_manager/list/#{self.id}.*})
end
Вы можете получить доступ ко всем общедоступным методам и свойствам контроллера из модели. Пока вы не изменяете состояние контроллера, все будет в порядке.
Решение, предоставленное Orion, работает отлично. В качестве улучшения и для удобства я поместил следующий код в config/initializers/active_record_expire_fragment.rb
class ActiveRecord::Base
def expire_fragment(*args)
ActionController::Base.new.expire_fragment(*args)
end
end
Теперь вы можете использовать expire_fragment во всех экземплярах ActiveRecord :: Base, например. User.first.expire_fragment('user-stats')
Это самый простой способ сделать это imho, очень часто делали похожие вещи с разными помощниками
Maurycy, Спасибо за пример и фрагмент, попробую. Это что-то, что я могу просто вставить в модель?