Ответ 1
Отказ от ответственности: мои рельсы немного ржавые, но это или что-то вроде этого должно работать
ActionController::Base.new.expire_fragment(key, options = nil)
Я почти все пробовал, но кажется невозможным использовать expire_fragment от моделей? Я знаю, что ты не должен и это не-MVC, но, безусловно, есть какой-то способ сделать это.
Я создал модуль в lib/cache_helper.rb со всеми моими помощниками по истечению срока действия, внутри каждого - всего лишь куча вызовов expire_fragment. У меня есть все мои настройки кэширования подметей под/app/подметальные машины и включить "включить CacheHelper "в моем контроллере приложений, так что срок действия кеша приложение при вызове через контроллеры прекрасно работает.
Тогда у меня есть некоторые внешние демоны, и особенно некоторые повторяющиеся задачи cron, которые вызывают задачу рейка, которая вызывает определенную метод. Этот метод выполняет некоторые операции обработки и ввода записей в модель, после которой мне нужно истечь кеш.
Какой лучший способ сделать это, поскольку я не могу указать утилизатор кэша в модели. Кажется, что правильные наблюдатели являются лучшим решением, но жалуется на expire_fragment undefined и т.д., я даже попытался включить классы кэширования ActionController в наблюдателя но это не сработало. Мне бы хотелось, чтобы некоторые идеи о том, как создать решение для этого. Благодарю.
Отказ от ответственности: мои рельсы немного ржавые, но это или что-то вроде этого должно работать
ActionController::Base.new.expire_fragment(key, options = nil)
Решение, предлагаемое компанией 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')
Это довольно легко сделать. Вы можете реализовать предложение Ориона, но вы также можете реализовать более широкую технику, показанную ниже, которая дает вам доступ к текущему контроллеру из любой модели и для любой цели, которую вы решили нарушить разделение 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
, но это не обязательно сделайте это, чтобы получить что-то функциональное (хотя и уродливое и трудно поддерживаемое).
Удачи!
В одном из моих сценариев я использую следующий хак:
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.
Я немного рельсы noob, поэтому это может быть неверно или даже полезно, но, похоже, неправильно пытаться вызвать действия контроллера из модели.
Невозможно ли написать какое-либо действие в контроллере, которое делает то, что вы хотите, а затем вызвать действие контроллера из вашей задачи рейка?
Почему бы вам не потребоваться, чтобы ваши внешние задачи грабли вызывали метод истечения срока действия на контроллере. Тогда вы по-прежнему являетесь MVC-совместимым, вы не строите зависимость от какого-то взлома и т.д.
В этом случае, почему бы вам просто не поместить все функции демона/внешнего устройства на контроллер и не позвольте rake/cron просто вызвать это. Было бы легче поддерживать нагрузку.
- MarkusQ
Не будет ли проще и чисто просто передать текущий контроллер в качестве аргумента для вызова метода модели? Пример:
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
Вы можете получить доступ ко всем общедоступным методам и свойствам контроллера из модели. Пока вы не изменяете состояние контроллера, это должно быть хорошо.
Это может не сработать для того, что вы делаете, но вы можете определить пользовательский вызов на своей модели:
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
Сообщите мне, если это полезно!